12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21141d9d0SIrina Tirdea /*
31141d9d0SIrina Tirdea * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs
41141d9d0SIrina Tirdea *
51141d9d0SIrina Tirdea * Copyright (C) 2016, Intel Corporation
61141d9d0SIrina Tirdea * Author: Irina Tirdea <irina.tirdea@intel.com>
71141d9d0SIrina Tirdea */
81141d9d0SIrina Tirdea
91141d9d0SIrina Tirdea #include <linux/clk-provider.h>
101141d9d0SIrina Tirdea #include <linux/clkdev.h>
111141d9d0SIrina Tirdea #include <linux/err.h>
1262e59c4eSStephen Boyd #include <linux/io.h>
131141d9d0SIrina Tirdea #include <linux/platform_data/x86/clk-pmc-atom.h>
14dbab9afeSHans de Goede #include <linux/platform_data/x86/pmc_atom.h>
151141d9d0SIrina Tirdea #include <linux/platform_device.h>
161141d9d0SIrina Tirdea #include <linux/slab.h>
171141d9d0SIrina Tirdea
181141d9d0SIrina Tirdea #define PLT_CLK_NAME_BASE "pmc_plt_clk"
191141d9d0SIrina Tirdea
201141d9d0SIrina Tirdea struct clk_plt_fixed {
211141d9d0SIrina Tirdea struct clk_hw *clk;
221141d9d0SIrina Tirdea struct clk_lookup *lookup;
231141d9d0SIrina Tirdea };
241141d9d0SIrina Tirdea
251141d9d0SIrina Tirdea struct clk_plt {
261141d9d0SIrina Tirdea struct clk_hw hw;
271141d9d0SIrina Tirdea void __iomem *reg;
281141d9d0SIrina Tirdea struct clk_lookup *lookup;
291141d9d0SIrina Tirdea /* protect access to PMC registers */
301141d9d0SIrina Tirdea spinlock_t lock;
311141d9d0SIrina Tirdea };
321141d9d0SIrina Tirdea
331141d9d0SIrina Tirdea #define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
341141d9d0SIrina Tirdea
351141d9d0SIrina Tirdea struct clk_plt_data {
361141d9d0SIrina Tirdea struct clk_plt_fixed **parents;
371141d9d0SIrina Tirdea u8 nparents;
381141d9d0SIrina Tirdea struct clk_plt *clks[PMC_CLK_NUM];
3941ee7cafSPierre-Louis Bossart struct clk_lookup *mclk_lookup;
40b1e3454dSHans de Goede struct clk_lookup *ether_clk_lookup;
411141d9d0SIrina Tirdea };
421141d9d0SIrina Tirdea
431141d9d0SIrina Tirdea /* Return an index in parent table */
plt_reg_to_parent(int reg)441141d9d0SIrina Tirdea static inline int plt_reg_to_parent(int reg)
451141d9d0SIrina Tirdea {
461141d9d0SIrina Tirdea switch (reg & PMC_MASK_CLK_FREQ) {
471141d9d0SIrina Tirdea default:
481141d9d0SIrina Tirdea case PMC_CLK_FREQ_XTAL:
491141d9d0SIrina Tirdea return 0;
501141d9d0SIrina Tirdea case PMC_CLK_FREQ_PLL:
511141d9d0SIrina Tirdea return 1;
521141d9d0SIrina Tirdea }
531141d9d0SIrina Tirdea }
541141d9d0SIrina Tirdea
551141d9d0SIrina Tirdea /* Return clk index of parent */
plt_parent_to_reg(int index)561141d9d0SIrina Tirdea static inline int plt_parent_to_reg(int index)
571141d9d0SIrina Tirdea {
581141d9d0SIrina Tirdea switch (index) {
591141d9d0SIrina Tirdea default:
601141d9d0SIrina Tirdea case 0:
611141d9d0SIrina Tirdea return PMC_CLK_FREQ_XTAL;
621141d9d0SIrina Tirdea case 1:
631141d9d0SIrina Tirdea return PMC_CLK_FREQ_PLL;
641141d9d0SIrina Tirdea }
651141d9d0SIrina Tirdea }
661141d9d0SIrina Tirdea
671141d9d0SIrina Tirdea /* Abstract status in simpler enabled/disabled value */
plt_reg_to_enabled(int reg)681141d9d0SIrina Tirdea static inline int plt_reg_to_enabled(int reg)
691141d9d0SIrina Tirdea {
701141d9d0SIrina Tirdea switch (reg & PMC_MASK_CLK_CTL) {
711141d9d0SIrina Tirdea case PMC_CLK_CTL_GATED_ON_D3:
721141d9d0SIrina Tirdea case PMC_CLK_CTL_FORCE_ON:
731141d9d0SIrina Tirdea return 1; /* enabled */
741141d9d0SIrina Tirdea case PMC_CLK_CTL_FORCE_OFF:
751141d9d0SIrina Tirdea case PMC_CLK_CTL_RESERVED:
761141d9d0SIrina Tirdea default:
771141d9d0SIrina Tirdea return 0; /* disabled */
781141d9d0SIrina Tirdea }
791141d9d0SIrina Tirdea }
801141d9d0SIrina Tirdea
plt_clk_reg_update(struct clk_plt * clk,u32 mask,u32 val)811141d9d0SIrina Tirdea static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val)
821141d9d0SIrina Tirdea {
831141d9d0SIrina Tirdea u32 tmp;
841141d9d0SIrina Tirdea unsigned long flags;
851141d9d0SIrina Tirdea
861141d9d0SIrina Tirdea spin_lock_irqsave(&clk->lock, flags);
871141d9d0SIrina Tirdea
881141d9d0SIrina Tirdea tmp = readl(clk->reg);
891141d9d0SIrina Tirdea tmp = (tmp & ~mask) | (val & mask);
901141d9d0SIrina Tirdea writel(tmp, clk->reg);
911141d9d0SIrina Tirdea
921141d9d0SIrina Tirdea spin_unlock_irqrestore(&clk->lock, flags);
931141d9d0SIrina Tirdea }
941141d9d0SIrina Tirdea
plt_clk_set_parent(struct clk_hw * hw,u8 index)951141d9d0SIrina Tirdea static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
961141d9d0SIrina Tirdea {
971141d9d0SIrina Tirdea struct clk_plt *clk = to_clk_plt(hw);
981141d9d0SIrina Tirdea
991141d9d0SIrina Tirdea plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index));
1001141d9d0SIrina Tirdea
1011141d9d0SIrina Tirdea return 0;
1021141d9d0SIrina Tirdea }
1031141d9d0SIrina Tirdea
plt_clk_get_parent(struct clk_hw * hw)1041141d9d0SIrina Tirdea static u8 plt_clk_get_parent(struct clk_hw *hw)
1051141d9d0SIrina Tirdea {
1061141d9d0SIrina Tirdea struct clk_plt *clk = to_clk_plt(hw);
1071141d9d0SIrina Tirdea u32 value;
1081141d9d0SIrina Tirdea
1091141d9d0SIrina Tirdea value = readl(clk->reg);
1101141d9d0SIrina Tirdea
1111141d9d0SIrina Tirdea return plt_reg_to_parent(value);
1121141d9d0SIrina Tirdea }
1131141d9d0SIrina Tirdea
plt_clk_enable(struct clk_hw * hw)1141141d9d0SIrina Tirdea static int plt_clk_enable(struct clk_hw *hw)
1151141d9d0SIrina Tirdea {
1161141d9d0SIrina Tirdea struct clk_plt *clk = to_clk_plt(hw);
1171141d9d0SIrina Tirdea
1181141d9d0SIrina Tirdea plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
1191141d9d0SIrina Tirdea
1201141d9d0SIrina Tirdea return 0;
1211141d9d0SIrina Tirdea }
1221141d9d0SIrina Tirdea
plt_clk_disable(struct clk_hw * hw)1231141d9d0SIrina Tirdea static void plt_clk_disable(struct clk_hw *hw)
1241141d9d0SIrina Tirdea {
1251141d9d0SIrina Tirdea struct clk_plt *clk = to_clk_plt(hw);
1261141d9d0SIrina Tirdea
1271141d9d0SIrina Tirdea plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
1281141d9d0SIrina Tirdea }
1291141d9d0SIrina Tirdea
plt_clk_is_enabled(struct clk_hw * hw)1301141d9d0SIrina Tirdea static int plt_clk_is_enabled(struct clk_hw *hw)
1311141d9d0SIrina Tirdea {
1321141d9d0SIrina Tirdea struct clk_plt *clk = to_clk_plt(hw);
1331141d9d0SIrina Tirdea u32 value;
1341141d9d0SIrina Tirdea
1351141d9d0SIrina Tirdea value = readl(clk->reg);
1361141d9d0SIrina Tirdea
1371141d9d0SIrina Tirdea return plt_reg_to_enabled(value);
1381141d9d0SIrina Tirdea }
1391141d9d0SIrina Tirdea
1401141d9d0SIrina Tirdea static const struct clk_ops plt_clk_ops = {
1411141d9d0SIrina Tirdea .enable = plt_clk_enable,
1421141d9d0SIrina Tirdea .disable = plt_clk_disable,
1431141d9d0SIrina Tirdea .is_enabled = plt_clk_is_enabled,
1441141d9d0SIrina Tirdea .get_parent = plt_clk_get_parent,
1451141d9d0SIrina Tirdea .set_parent = plt_clk_set_parent,
1461141d9d0SIrina Tirdea .determine_rate = __clk_mux_determine_rate,
1471141d9d0SIrina Tirdea };
1481141d9d0SIrina Tirdea
plt_clk_register(struct platform_device * pdev,int id,const struct pmc_clk_data * pmc_data,const char ** parent_names,int num_parents)1491141d9d0SIrina Tirdea static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
1507c2e0713SDavid Müller const struct pmc_clk_data *pmc_data,
1511141d9d0SIrina Tirdea const char **parent_names,
1521141d9d0SIrina Tirdea int num_parents)
1531141d9d0SIrina Tirdea {
1541141d9d0SIrina Tirdea struct clk_plt *pclk;
1551141d9d0SIrina Tirdea struct clk_init_data init;
1561141d9d0SIrina Tirdea int ret;
1571141d9d0SIrina Tirdea
1581141d9d0SIrina Tirdea pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
1591141d9d0SIrina Tirdea if (!pclk)
1601141d9d0SIrina Tirdea return ERR_PTR(-ENOMEM);
1611141d9d0SIrina Tirdea
1621141d9d0SIrina Tirdea init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
1631141d9d0SIrina Tirdea init.ops = &plt_clk_ops;
1641141d9d0SIrina Tirdea init.flags = 0;
1651141d9d0SIrina Tirdea init.parent_names = parent_names;
1661141d9d0SIrina Tirdea init.num_parents = num_parents;
1671141d9d0SIrina Tirdea
1681141d9d0SIrina Tirdea pclk->hw.init = &init;
1697c2e0713SDavid Müller pclk->reg = pmc_data->base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
1701141d9d0SIrina Tirdea spin_lock_init(&pclk->lock);
1711141d9d0SIrina Tirdea
1727c2e0713SDavid Müller /*
1737c2e0713SDavid Müller * On some systems, the pmc_plt_clocks already enabled by the
1747c2e0713SDavid Müller * firmware are being marked as critical to avoid them being
1757c2e0713SDavid Müller * gated by the clock framework.
1767c2e0713SDavid Müller */
1777c2e0713SDavid Müller if (pmc_data->critical && plt_clk_is_enabled(&pclk->hw))
1787c2e0713SDavid Müller init.flags |= CLK_IS_CRITICAL;
1797c2e0713SDavid Müller
1801141d9d0SIrina Tirdea ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
1811141d9d0SIrina Tirdea if (ret) {
1821141d9d0SIrina Tirdea pclk = ERR_PTR(ret);
1831141d9d0SIrina Tirdea goto err_free_init;
1841141d9d0SIrina Tirdea }
1851141d9d0SIrina Tirdea
1861141d9d0SIrina Tirdea pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL);
1871141d9d0SIrina Tirdea if (!pclk->lookup) {
1881141d9d0SIrina Tirdea pclk = ERR_PTR(-ENOMEM);
1891141d9d0SIrina Tirdea goto err_free_init;
1901141d9d0SIrina Tirdea }
1911141d9d0SIrina Tirdea
1921141d9d0SIrina Tirdea err_free_init:
1931141d9d0SIrina Tirdea kfree(init.name);
1941141d9d0SIrina Tirdea return pclk;
1951141d9d0SIrina Tirdea }
1961141d9d0SIrina Tirdea
plt_clk_unregister(struct clk_plt * pclk)1971141d9d0SIrina Tirdea static void plt_clk_unregister(struct clk_plt *pclk)
1981141d9d0SIrina Tirdea {
1991141d9d0SIrina Tirdea clkdev_drop(pclk->lookup);
2001141d9d0SIrina Tirdea }
2011141d9d0SIrina Tirdea
plt_clk_register_fixed_rate(struct platform_device * pdev,const char * name,const char * parent_name,unsigned long fixed_rate)2021141d9d0SIrina Tirdea static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
2031141d9d0SIrina Tirdea const char *name,
2041141d9d0SIrina Tirdea const char *parent_name,
2051141d9d0SIrina Tirdea unsigned long fixed_rate)
2061141d9d0SIrina Tirdea {
2071141d9d0SIrina Tirdea struct clk_plt_fixed *pclk;
2081141d9d0SIrina Tirdea
2091141d9d0SIrina Tirdea pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
2101141d9d0SIrina Tirdea if (!pclk)
2111141d9d0SIrina Tirdea return ERR_PTR(-ENOMEM);
2121141d9d0SIrina Tirdea
2131141d9d0SIrina Tirdea pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name,
2141141d9d0SIrina Tirdea 0, fixed_rate);
2151141d9d0SIrina Tirdea if (IS_ERR(pclk->clk))
2161141d9d0SIrina Tirdea return ERR_CAST(pclk->clk);
2171141d9d0SIrina Tirdea
2181141d9d0SIrina Tirdea pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL);
2191141d9d0SIrina Tirdea if (!pclk->lookup) {
2201141d9d0SIrina Tirdea clk_hw_unregister_fixed_rate(pclk->clk);
2211141d9d0SIrina Tirdea return ERR_PTR(-ENOMEM);
2221141d9d0SIrina Tirdea }
2231141d9d0SIrina Tirdea
2241141d9d0SIrina Tirdea return pclk;
2251141d9d0SIrina Tirdea }
2261141d9d0SIrina Tirdea
plt_clk_unregister_fixed_rate(struct clk_plt_fixed * pclk)2271141d9d0SIrina Tirdea static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
2281141d9d0SIrina Tirdea {
2291141d9d0SIrina Tirdea clkdev_drop(pclk->lookup);
2301141d9d0SIrina Tirdea clk_hw_unregister_fixed_rate(pclk->clk);
2311141d9d0SIrina Tirdea }
2321141d9d0SIrina Tirdea
plt_clk_unregister_fixed_rate_loop(struct clk_plt_data * data,unsigned int i)2331141d9d0SIrina Tirdea static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data,
2341141d9d0SIrina Tirdea unsigned int i)
2351141d9d0SIrina Tirdea {
2361141d9d0SIrina Tirdea while (i--)
2371141d9d0SIrina Tirdea plt_clk_unregister_fixed_rate(data->parents[i]);
2381141d9d0SIrina Tirdea }
2391141d9d0SIrina Tirdea
plt_clk_free_parent_names_loop(const char ** parent_names,unsigned int i)2401141d9d0SIrina Tirdea static void plt_clk_free_parent_names_loop(const char **parent_names,
2411141d9d0SIrina Tirdea unsigned int i)
2421141d9d0SIrina Tirdea {
2431141d9d0SIrina Tirdea while (i--)
2441141d9d0SIrina Tirdea kfree_const(parent_names[i]);
2451141d9d0SIrina Tirdea kfree(parent_names);
2461141d9d0SIrina Tirdea }
2471141d9d0SIrina Tirdea
plt_clk_unregister_loop(struct clk_plt_data * data,unsigned int i)2481141d9d0SIrina Tirdea static void plt_clk_unregister_loop(struct clk_plt_data *data,
2491141d9d0SIrina Tirdea unsigned int i)
2501141d9d0SIrina Tirdea {
2511141d9d0SIrina Tirdea while (i--)
2521141d9d0SIrina Tirdea plt_clk_unregister(data->clks[i]);
2531141d9d0SIrina Tirdea }
2541141d9d0SIrina Tirdea
plt_clk_register_parents(struct platform_device * pdev,struct clk_plt_data * data,const struct pmc_clk * clks)2551141d9d0SIrina Tirdea static const char **plt_clk_register_parents(struct platform_device *pdev,
2561141d9d0SIrina Tirdea struct clk_plt_data *data,
2571141d9d0SIrina Tirdea const struct pmc_clk *clks)
2581141d9d0SIrina Tirdea {
2591141d9d0SIrina Tirdea const char **parent_names;
2601141d9d0SIrina Tirdea unsigned int i;
2611141d9d0SIrina Tirdea int err;
2621141d9d0SIrina Tirdea int nparents = 0;
2631141d9d0SIrina Tirdea
2641141d9d0SIrina Tirdea data->nparents = 0;
2651141d9d0SIrina Tirdea while (clks[nparents].name)
2661141d9d0SIrina Tirdea nparents++;
2671141d9d0SIrina Tirdea
2681141d9d0SIrina Tirdea data->parents = devm_kcalloc(&pdev->dev, nparents,
2691141d9d0SIrina Tirdea sizeof(*data->parents), GFP_KERNEL);
2701141d9d0SIrina Tirdea if (!data->parents)
2711141d9d0SIrina Tirdea return ERR_PTR(-ENOMEM);
2721141d9d0SIrina Tirdea
2731141d9d0SIrina Tirdea parent_names = kcalloc(nparents, sizeof(*parent_names),
2741141d9d0SIrina Tirdea GFP_KERNEL);
2751141d9d0SIrina Tirdea if (!parent_names)
2761141d9d0SIrina Tirdea return ERR_PTR(-ENOMEM);
2771141d9d0SIrina Tirdea
2781141d9d0SIrina Tirdea for (i = 0; i < nparents; i++) {
2791141d9d0SIrina Tirdea data->parents[i] =
2801141d9d0SIrina Tirdea plt_clk_register_fixed_rate(pdev, clks[i].name,
2811141d9d0SIrina Tirdea clks[i].parent_name,
2821141d9d0SIrina Tirdea clks[i].freq);
2831141d9d0SIrina Tirdea if (IS_ERR(data->parents[i])) {
2841141d9d0SIrina Tirdea err = PTR_ERR(data->parents[i]);
2851141d9d0SIrina Tirdea goto err_unreg;
2861141d9d0SIrina Tirdea }
2871141d9d0SIrina Tirdea parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
2881141d9d0SIrina Tirdea }
2891141d9d0SIrina Tirdea
2901141d9d0SIrina Tirdea data->nparents = nparents;
2911141d9d0SIrina Tirdea return parent_names;
2921141d9d0SIrina Tirdea
2931141d9d0SIrina Tirdea err_unreg:
2941141d9d0SIrina Tirdea plt_clk_unregister_fixed_rate_loop(data, i);
2951141d9d0SIrina Tirdea plt_clk_free_parent_names_loop(parent_names, i);
2961141d9d0SIrina Tirdea return ERR_PTR(err);
2971141d9d0SIrina Tirdea }
2981141d9d0SIrina Tirdea
plt_clk_unregister_parents(struct clk_plt_data * data)2991141d9d0SIrina Tirdea static void plt_clk_unregister_parents(struct clk_plt_data *data)
3001141d9d0SIrina Tirdea {
3011141d9d0SIrina Tirdea plt_clk_unregister_fixed_rate_loop(data, data->nparents);
3021141d9d0SIrina Tirdea }
3031141d9d0SIrina Tirdea
plt_clk_probe(struct platform_device * pdev)3041141d9d0SIrina Tirdea static int plt_clk_probe(struct platform_device *pdev)
3051141d9d0SIrina Tirdea {
3061141d9d0SIrina Tirdea const struct pmc_clk_data *pmc_data;
3071141d9d0SIrina Tirdea const char **parent_names;
3081141d9d0SIrina Tirdea struct clk_plt_data *data;
3091141d9d0SIrina Tirdea unsigned int i;
3101141d9d0SIrina Tirdea int err;
3111141d9d0SIrina Tirdea
3121141d9d0SIrina Tirdea pmc_data = dev_get_platdata(&pdev->dev);
3131141d9d0SIrina Tirdea if (!pmc_data || !pmc_data->clks)
3141141d9d0SIrina Tirdea return -EINVAL;
3151141d9d0SIrina Tirdea
3161141d9d0SIrina Tirdea data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
3171141d9d0SIrina Tirdea if (!data)
3181141d9d0SIrina Tirdea return -ENOMEM;
3191141d9d0SIrina Tirdea
3201141d9d0SIrina Tirdea parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks);
3211141d9d0SIrina Tirdea if (IS_ERR(parent_names))
3221141d9d0SIrina Tirdea return PTR_ERR(parent_names);
3231141d9d0SIrina Tirdea
3241141d9d0SIrina Tirdea for (i = 0; i < PMC_CLK_NUM; i++) {
3257c2e0713SDavid Müller data->clks[i] = plt_clk_register(pdev, i, pmc_data,
3261141d9d0SIrina Tirdea parent_names, data->nparents);
3271141d9d0SIrina Tirdea if (IS_ERR(data->clks[i])) {
3281141d9d0SIrina Tirdea err = PTR_ERR(data->clks[i]);
3291141d9d0SIrina Tirdea goto err_unreg_clk_plt;
3301141d9d0SIrina Tirdea }
3311141d9d0SIrina Tirdea }
33241ee7cafSPierre-Louis Bossart data->mclk_lookup = clkdev_hw_create(&data->clks[3]->hw, "mclk", NULL);
3330119dc61SDan Carpenter if (!data->mclk_lookup) {
3340119dc61SDan Carpenter err = -ENOMEM;
33541ee7cafSPierre-Louis Bossart goto err_unreg_clk_plt;
33641ee7cafSPierre-Louis Bossart }
3371141d9d0SIrina Tirdea
338b1e3454dSHans de Goede data->ether_clk_lookup = clkdev_hw_create(&data->clks[4]->hw,
339b1e3454dSHans de Goede "ether_clk", NULL);
340b1e3454dSHans de Goede if (!data->ether_clk_lookup) {
341b1e3454dSHans de Goede err = -ENOMEM;
342b1e3454dSHans de Goede goto err_drop_mclk;
343b1e3454dSHans de Goede }
344b1e3454dSHans de Goede
3451141d9d0SIrina Tirdea plt_clk_free_parent_names_loop(parent_names, data->nparents);
3461141d9d0SIrina Tirdea
3471141d9d0SIrina Tirdea platform_set_drvdata(pdev, data);
3481141d9d0SIrina Tirdea return 0;
3491141d9d0SIrina Tirdea
350b1e3454dSHans de Goede err_drop_mclk:
351b1e3454dSHans de Goede clkdev_drop(data->mclk_lookup);
3521141d9d0SIrina Tirdea err_unreg_clk_plt:
3531141d9d0SIrina Tirdea plt_clk_unregister_loop(data, i);
3541141d9d0SIrina Tirdea plt_clk_unregister_parents(data);
3551141d9d0SIrina Tirdea plt_clk_free_parent_names_loop(parent_names, data->nparents);
3561141d9d0SIrina Tirdea return err;
3571141d9d0SIrina Tirdea }
3581141d9d0SIrina Tirdea
plt_clk_remove(struct platform_device * pdev)3594690d246SUwe Kleine-König static void plt_clk_remove(struct platform_device *pdev)
3601141d9d0SIrina Tirdea {
3611141d9d0SIrina Tirdea struct clk_plt_data *data;
3621141d9d0SIrina Tirdea
3631141d9d0SIrina Tirdea data = platform_get_drvdata(pdev);
3641141d9d0SIrina Tirdea
365b1e3454dSHans de Goede clkdev_drop(data->ether_clk_lookup);
36641ee7cafSPierre-Louis Bossart clkdev_drop(data->mclk_lookup);
3671141d9d0SIrina Tirdea plt_clk_unregister_loop(data, PMC_CLK_NUM);
3681141d9d0SIrina Tirdea plt_clk_unregister_parents(data);
3691141d9d0SIrina Tirdea }
3701141d9d0SIrina Tirdea
3711141d9d0SIrina Tirdea static struct platform_driver plt_clk_driver = {
3721141d9d0SIrina Tirdea .driver = {
3731141d9d0SIrina Tirdea .name = "clk-pmc-atom",
3741141d9d0SIrina Tirdea },
3751141d9d0SIrina Tirdea .probe = plt_clk_probe,
376*f00b45dbSUwe Kleine-König .remove = plt_clk_remove,
3771141d9d0SIrina Tirdea };
3781141d9d0SIrina Tirdea builtin_platform_driver(plt_clk_driver);
379