val &= ~GENMASK(7, 6);
        writel(val | (2 << 6), reg + SUN4I_AHB_REG);
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun4i_a10_ccu_setup(struct device_node *node)
 
        if (IS_ERR(reg))
                return PTR_ERR(reg);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a100_r_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a100_r_ccu_desc);
 }
 
 static const struct of_device_id sun50i_a100_r_ccu_ids[] = {
 
                writel(val, reg + sun50i_a100_usb2_clk_regs[i]);
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a100_ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a100_ccu_desc);
        if (ret)
                return ret;
 
 
 
        writel(0x515, reg + SUN50I_A64_PLL_MIPI_REG);
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_a64_ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_a64_ccu_desc);
        if (ret)
                return ret;
 
 
                return;
        }
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun50i_h6_r_ccu_setup(struct device_node *node)
 
        val |= BIT(24);
        writel(val, reg + SUN50I_H6_HDMI_CEC_CLK_REG);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun50i_h6_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun50i_h6_ccu_desc);
 }
 
 static const struct of_device_id sun50i_h6_ccu_ids[] = {
 
        val |= BIT(24);
        writel(val, reg + SUN50I_H616_HDMI_CEC_CLK_REG);
 
-       i = sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc);
-       if (i)
-               pr_err("%pOF: probing clocks fails: %d\n", node, i);
+       of_sunxi_ccu_probe(node, reg, &sun50i_h616_ccu_desc);
 }
 
 CLK_OF_DECLARE(sun50i_h616_ccu, "allwinner,sun50i-h616-ccu",
 
        val &= ~GENMASK(7, 6);
        writel(val | (2 << 6), reg + SUN5I_AHB_REG);
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun5i_a10s_ccu_setup(struct device_node *node)
 
        val |= 0x3 << 12;
        writel(val, reg + SUN6I_A31_AHB1_REG);
 
-       sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &sun6i_a31_ccu_desc);
 
        ccu_mux_notifier_register(pll_cpu_clk.common.hw.clk,
                                  &sun6i_a31_cpu_nb);
 
        val &= ~BIT(16);
        writel(val, reg + SUN8I_A23_PLL_MIPI_REG);
 
-       sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &sun8i_a23_ccu_desc);
 }
 CLK_OF_DECLARE(sun8i_a23_ccu, "allwinner,sun8i-a23-ccu",
               sun8i_a23_ccu_setup);
 
        val &= ~BIT(16);
        writel(val, reg + SUN8I_A33_PLL_MIPI_REG);
 
-       sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &sun8i_a33_ccu_desc);
 
        /* Gate then ungate PLL CPU after any rate changes */
        ccu_pll_notifier_register(&sun8i_a33_pll_cpu_nb);
 
        sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C0CPUX_REG);
        sun8i_a83t_cpu_pll_fixup(reg + SUN8I_A83T_PLL_C1CPUX_REG);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_a83t_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_a83t_ccu_desc);
 }
 
 static const struct of_device_id sun8i_a83t_ccu_ids[] = {
 
                goto err_disable_mod_clk;
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, ccu_desc);
        if (ret)
                goto err_assert_reset;
 
 
        val &= ~GENMASK(19, 16);
        writel(val | (0 << 16), reg + SUN8I_H3_PLL_AUDIO_REG);
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 
        /* Gate then ungate PLL CPU after any rate changes */
        ccu_pll_notifier_register(&sun8i_h3_pll_cpu_nb);
 
                return;
        }
 
-       sunxi_ccu_probe(node, reg, desc);
+       of_sunxi_ccu_probe(node, reg, desc);
 }
 
 static void __init sun8i_a83t_r_ccu_setup(struct device_node *node)
 
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg, &sun8i_r40_ccu_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun8i_r40_ccu_desc);
        if (ret)
                return ret;
 
 
        val &= ~GENMASK(19, 16);
        writel(val, reg + SUN8I_V3S_PLL_AUDIO_REG);
 
-       sunxi_ccu_probe(node, reg, ccu_desc);
+       of_sunxi_ccu_probe(node, reg, ccu_desc);
 }
 
 static void __init sun8i_v3s_ccu_setup(struct device_node *node)
 
                goto err_disable_clk;
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
-                             &sun9i_a80_de_clk_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_de_clk_desc);
        if (ret)
                goto err_assert_reset;
 
 
                return ret;
        }
 
-       ret = sunxi_ccu_probe(pdev->dev.of_node, reg,
-                             &sun9i_a80_usb_clk_desc);
+       ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_usb_clk_desc);
        if (ret)
                goto err_disable_clk;
 
 
        sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C0CPUX_REG);
        sun9i_a80_cpu_pll_fixup(reg + SUN9I_A80_PLL_C1CPUX_REG);
 
-       return sunxi_ccu_probe(pdev->dev.of_node, reg, &sun9i_a80_ccu_desc);
+       return devm_sunxi_ccu_probe(&pdev->dev, reg, &sun9i_a80_ccu_desc);
 }
 
 static const struct of_device_id sun9i_a80_ccu_ids[] = {
 
        val &= ~GENMASK(19, 16);
        writel(val | (3 << 16), reg + SUNIV_PLL_AUDIO_REG);
 
-       sunxi_ccu_probe(node, reg, &suniv_ccu_desc);
+       of_sunxi_ccu_probe(node, reg, &suniv_ccu_desc);
 
        /* Gate then ungate PLL CPU after any rate changes */
        ccu_pll_notifier_register(&suniv_pll_cpu_nb);
 
 
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
+#include <linux/device.h>
 #include <linux/iopoll.h>
 #include <linux/slab.h>
 
 #include "ccu_gate.h"
 #include "ccu_reset.h"
 
+struct sunxi_ccu {
+       const struct sunxi_ccu_desc     *desc;
+       struct ccu_reset                reset;
+};
+
 static DEFINE_SPINLOCK(ccu_lock);
 
 void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock)
                                     &pll_nb->clk_nb);
 }
 
-int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
-                   const struct sunxi_ccu_desc *desc)
+static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev,
+                          struct device_node *node, void __iomem *reg,
+                          const struct sunxi_ccu_desc *desc)
 {
        struct ccu_reset *reset;
        int i, ret;
 
+       ccu->desc = desc;
+
        for (i = 0; i < desc->num_ccu_clks; i++) {
                struct ccu_common *cclk = desc->ccu_clks[i];
 
                        continue;
 
                name = hw->init->name;
-               ret = of_clk_hw_register(node, hw);
+               if (dev)
+                       ret = clk_hw_register(dev, hw);
+               else
+                       ret = of_clk_hw_register(node, hw);
                if (ret) {
                        pr_err("Couldn't register clock %d - %s\n", i, name);
                        goto err_clk_unreg;
        if (ret)
                goto err_clk_unreg;
 
-       reset = kzalloc(sizeof(*reset), GFP_KERNEL);
-       if (!reset) {
-               ret = -ENOMEM;
-               goto err_alloc_reset;
-       }
-
+       reset = &ccu->reset;
        reset->rcdev.of_node = node;
        reset->rcdev.ops = &ccu_reset_ops;
-       reset->rcdev.owner = THIS_MODULE;
+       reset->rcdev.owner = dev ? dev->driver->owner : THIS_MODULE;
        reset->rcdev.nr_resets = desc->num_resets;
        reset->base = reg;
        reset->lock = &ccu_lock;
 
        ret = reset_controller_register(&reset->rcdev);
        if (ret)
-               goto err_of_clk_unreg;
+               goto err_del_provider;
 
        return 0;
 
-err_of_clk_unreg:
-       kfree(reset);
-err_alloc_reset:
+err_del_provider:
        of_clk_del_provider(node);
 err_clk_unreg:
        while (--i >= 0) {
        }
        return ret;
 }
+
+static void devm_sunxi_ccu_release(struct device *dev, void *res)
+{
+       struct sunxi_ccu *ccu = res;
+       const struct sunxi_ccu_desc *desc = ccu->desc;
+       int i;
+
+       reset_controller_unregister(&ccu->reset.rcdev);
+       of_clk_del_provider(dev->of_node);
+
+       for (i = 0; i < desc->hw_clks->num; i++) {
+               struct clk_hw *hw = desc->hw_clks->hws[i];
+
+               if (!hw)
+                       continue;
+               clk_hw_unregister(hw);
+       }
+}
+
+int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
+                        const struct sunxi_ccu_desc *desc)
+{
+       struct sunxi_ccu *ccu;
+       int ret;
+
+       ccu = devres_alloc(devm_sunxi_ccu_release, sizeof(*ccu), GFP_KERNEL);
+       if (!ccu)
+               return -ENOMEM;
+
+       ret = sunxi_ccu_probe(ccu, dev, dev->of_node, reg, desc);
+       if (ret) {
+               devres_free(ccu);
+               return ret;
+       }
+
+       devres_add(dev, ccu);
+
+       return 0;
+}
+
+void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+                       const struct sunxi_ccu_desc *desc)
+{
+       struct sunxi_ccu *ccu;
+       int ret;
+
+       ccu = kzalloc(sizeof(*ccu), GFP_KERNEL);
+       if (!ccu)
+               return;
+
+       ret = sunxi_ccu_probe(ccu, NULL, node, reg, desc);
+       if (ret) {
+               pr_err("%pOF: probing clocks failed: %d\n", node, ret);
+               kfree(ccu);
+       }
+}
 
 
 int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb);
 
-int sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
-                   const struct sunxi_ccu_desc *desc);
+int devm_sunxi_ccu_probe(struct device *dev, void __iomem *reg,
+                        const struct sunxi_ccu_desc *desc);
+void of_sunxi_ccu_probe(struct device_node *node, void __iomem *reg,
+                       const struct sunxi_ccu_desc *desc);
 
 #endif /* _COMMON_H_ */