xref: /linux/drivers/clk/x86/clk-pmc-atom.c (revision 0119dc6132d2110df8f3545bd0ffe29aa0752d6b)
11141d9d0SIrina Tirdea /*
21141d9d0SIrina Tirdea  * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs
31141d9d0SIrina Tirdea  *
41141d9d0SIrina Tirdea  * Copyright (C) 2016, Intel Corporation
51141d9d0SIrina Tirdea  * Author: Irina Tirdea <irina.tirdea@intel.com>
61141d9d0SIrina Tirdea  *
71141d9d0SIrina Tirdea  * This program is free software; you can redistribute it and/or modify it
81141d9d0SIrina Tirdea  * under the terms and conditions of the GNU General Public License,
91141d9d0SIrina Tirdea  * version 2, as published by the Free Software Foundation.
101141d9d0SIrina Tirdea  *
111141d9d0SIrina Tirdea  * This program is distributed in the hope it will be useful, but WITHOUT
121141d9d0SIrina Tirdea  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
131141d9d0SIrina Tirdea  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
141141d9d0SIrina Tirdea  * more details.
151141d9d0SIrina Tirdea  */
161141d9d0SIrina Tirdea 
171141d9d0SIrina Tirdea #include <linux/clk-provider.h>
181141d9d0SIrina Tirdea #include <linux/clkdev.h>
191141d9d0SIrina Tirdea #include <linux/err.h>
201141d9d0SIrina Tirdea #include <linux/platform_data/x86/clk-pmc-atom.h>
211141d9d0SIrina Tirdea #include <linux/platform_device.h>
221141d9d0SIrina Tirdea #include <linux/slab.h>
231141d9d0SIrina Tirdea 
241141d9d0SIrina Tirdea #define PLT_CLK_NAME_BASE	"pmc_plt_clk"
251141d9d0SIrina Tirdea 
261141d9d0SIrina Tirdea #define PMC_CLK_CTL_OFFSET		0x60
271141d9d0SIrina Tirdea #define PMC_CLK_CTL_SIZE		4
281141d9d0SIrina Tirdea #define PMC_CLK_NUM			6
291141d9d0SIrina Tirdea #define PMC_CLK_CTL_GATED_ON_D3		0x0
301141d9d0SIrina Tirdea #define PMC_CLK_CTL_FORCE_ON		0x1
311141d9d0SIrina Tirdea #define PMC_CLK_CTL_FORCE_OFF		0x2
321141d9d0SIrina Tirdea #define PMC_CLK_CTL_RESERVED		0x3
331141d9d0SIrina Tirdea #define PMC_MASK_CLK_CTL		GENMASK(1, 0)
341141d9d0SIrina Tirdea #define PMC_MASK_CLK_FREQ		BIT(2)
351141d9d0SIrina Tirdea #define PMC_CLK_FREQ_XTAL		(0 << 2)	/* 25 MHz */
361141d9d0SIrina Tirdea #define PMC_CLK_FREQ_PLL		(1 << 2)	/* 19.2 MHz */
371141d9d0SIrina Tirdea 
381141d9d0SIrina Tirdea struct clk_plt_fixed {
391141d9d0SIrina Tirdea 	struct clk_hw *clk;
401141d9d0SIrina Tirdea 	struct clk_lookup *lookup;
411141d9d0SIrina Tirdea };
421141d9d0SIrina Tirdea 
431141d9d0SIrina Tirdea struct clk_plt {
441141d9d0SIrina Tirdea 	struct clk_hw hw;
451141d9d0SIrina Tirdea 	void __iomem *reg;
461141d9d0SIrina Tirdea 	struct clk_lookup *lookup;
471141d9d0SIrina Tirdea 	/* protect access to PMC registers */
481141d9d0SIrina Tirdea 	spinlock_t lock;
491141d9d0SIrina Tirdea };
501141d9d0SIrina Tirdea 
511141d9d0SIrina Tirdea #define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw)
521141d9d0SIrina Tirdea 
531141d9d0SIrina Tirdea struct clk_plt_data {
541141d9d0SIrina Tirdea 	struct clk_plt_fixed **parents;
551141d9d0SIrina Tirdea 	u8 nparents;
561141d9d0SIrina Tirdea 	struct clk_plt *clks[PMC_CLK_NUM];
5741ee7cafSPierre-Louis Bossart 	struct clk_lookup *mclk_lookup;
581141d9d0SIrina Tirdea };
591141d9d0SIrina Tirdea 
601141d9d0SIrina Tirdea /* Return an index in parent table */
611141d9d0SIrina Tirdea static inline int plt_reg_to_parent(int reg)
621141d9d0SIrina Tirdea {
631141d9d0SIrina Tirdea 	switch (reg & PMC_MASK_CLK_FREQ) {
641141d9d0SIrina Tirdea 	default:
651141d9d0SIrina Tirdea 	case PMC_CLK_FREQ_XTAL:
661141d9d0SIrina Tirdea 		return 0;
671141d9d0SIrina Tirdea 	case PMC_CLK_FREQ_PLL:
681141d9d0SIrina Tirdea 		return 1;
691141d9d0SIrina Tirdea 	}
701141d9d0SIrina Tirdea }
711141d9d0SIrina Tirdea 
721141d9d0SIrina Tirdea /* Return clk index of parent */
731141d9d0SIrina Tirdea static inline int plt_parent_to_reg(int index)
741141d9d0SIrina Tirdea {
751141d9d0SIrina Tirdea 	switch (index) {
761141d9d0SIrina Tirdea 	default:
771141d9d0SIrina Tirdea 	case 0:
781141d9d0SIrina Tirdea 		return PMC_CLK_FREQ_XTAL;
791141d9d0SIrina Tirdea 	case 1:
801141d9d0SIrina Tirdea 		return PMC_CLK_FREQ_PLL;
811141d9d0SIrina Tirdea 	}
821141d9d0SIrina Tirdea }
831141d9d0SIrina Tirdea 
841141d9d0SIrina Tirdea /* Abstract status in simpler enabled/disabled value */
851141d9d0SIrina Tirdea static inline int plt_reg_to_enabled(int reg)
861141d9d0SIrina Tirdea {
871141d9d0SIrina Tirdea 	switch (reg & PMC_MASK_CLK_CTL) {
881141d9d0SIrina Tirdea 	case PMC_CLK_CTL_GATED_ON_D3:
891141d9d0SIrina Tirdea 	case PMC_CLK_CTL_FORCE_ON:
901141d9d0SIrina Tirdea 		return 1;	/* enabled */
911141d9d0SIrina Tirdea 	case PMC_CLK_CTL_FORCE_OFF:
921141d9d0SIrina Tirdea 	case PMC_CLK_CTL_RESERVED:
931141d9d0SIrina Tirdea 	default:
941141d9d0SIrina Tirdea 		return 0;	/* disabled */
951141d9d0SIrina Tirdea 	}
961141d9d0SIrina Tirdea }
971141d9d0SIrina Tirdea 
981141d9d0SIrina Tirdea static void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val)
991141d9d0SIrina Tirdea {
1001141d9d0SIrina Tirdea 	u32 tmp;
1011141d9d0SIrina Tirdea 	unsigned long flags;
1021141d9d0SIrina Tirdea 
1031141d9d0SIrina Tirdea 	spin_lock_irqsave(&clk->lock, flags);
1041141d9d0SIrina Tirdea 
1051141d9d0SIrina Tirdea 	tmp = readl(clk->reg);
1061141d9d0SIrina Tirdea 	tmp = (tmp & ~mask) | (val & mask);
1071141d9d0SIrina Tirdea 	writel(tmp, clk->reg);
1081141d9d0SIrina Tirdea 
1091141d9d0SIrina Tirdea 	spin_unlock_irqrestore(&clk->lock, flags);
1101141d9d0SIrina Tirdea }
1111141d9d0SIrina Tirdea 
1121141d9d0SIrina Tirdea static int plt_clk_set_parent(struct clk_hw *hw, u8 index)
1131141d9d0SIrina Tirdea {
1141141d9d0SIrina Tirdea 	struct clk_plt *clk = to_clk_plt(hw);
1151141d9d0SIrina Tirdea 
1161141d9d0SIrina Tirdea 	plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index));
1171141d9d0SIrina Tirdea 
1181141d9d0SIrina Tirdea 	return 0;
1191141d9d0SIrina Tirdea }
1201141d9d0SIrina Tirdea 
1211141d9d0SIrina Tirdea static u8 plt_clk_get_parent(struct clk_hw *hw)
1221141d9d0SIrina Tirdea {
1231141d9d0SIrina Tirdea 	struct clk_plt *clk = to_clk_plt(hw);
1241141d9d0SIrina Tirdea 	u32 value;
1251141d9d0SIrina Tirdea 
1261141d9d0SIrina Tirdea 	value = readl(clk->reg);
1271141d9d0SIrina Tirdea 
1281141d9d0SIrina Tirdea 	return plt_reg_to_parent(value);
1291141d9d0SIrina Tirdea }
1301141d9d0SIrina Tirdea 
1311141d9d0SIrina Tirdea static int plt_clk_enable(struct clk_hw *hw)
1321141d9d0SIrina Tirdea {
1331141d9d0SIrina Tirdea 	struct clk_plt *clk = to_clk_plt(hw);
1341141d9d0SIrina Tirdea 
1351141d9d0SIrina Tirdea 	plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON);
1361141d9d0SIrina Tirdea 
1371141d9d0SIrina Tirdea 	return 0;
1381141d9d0SIrina Tirdea }
1391141d9d0SIrina Tirdea 
1401141d9d0SIrina Tirdea static void plt_clk_disable(struct clk_hw *hw)
1411141d9d0SIrina Tirdea {
1421141d9d0SIrina Tirdea 	struct clk_plt *clk = to_clk_plt(hw);
1431141d9d0SIrina Tirdea 
1441141d9d0SIrina Tirdea 	plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF);
1451141d9d0SIrina Tirdea }
1461141d9d0SIrina Tirdea 
1471141d9d0SIrina Tirdea static int plt_clk_is_enabled(struct clk_hw *hw)
1481141d9d0SIrina Tirdea {
1491141d9d0SIrina Tirdea 	struct clk_plt *clk = to_clk_plt(hw);
1501141d9d0SIrina Tirdea 	u32 value;
1511141d9d0SIrina Tirdea 
1521141d9d0SIrina Tirdea 	value = readl(clk->reg);
1531141d9d0SIrina Tirdea 
1541141d9d0SIrina Tirdea 	return plt_reg_to_enabled(value);
1551141d9d0SIrina Tirdea }
1561141d9d0SIrina Tirdea 
1571141d9d0SIrina Tirdea static const struct clk_ops plt_clk_ops = {
1581141d9d0SIrina Tirdea 	.enable = plt_clk_enable,
1591141d9d0SIrina Tirdea 	.disable = plt_clk_disable,
1601141d9d0SIrina Tirdea 	.is_enabled = plt_clk_is_enabled,
1611141d9d0SIrina Tirdea 	.get_parent = plt_clk_get_parent,
1621141d9d0SIrina Tirdea 	.set_parent = plt_clk_set_parent,
1631141d9d0SIrina Tirdea 	.determine_rate = __clk_mux_determine_rate,
1641141d9d0SIrina Tirdea };
1651141d9d0SIrina Tirdea 
1661141d9d0SIrina Tirdea static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id,
1671141d9d0SIrina Tirdea 					void __iomem *base,
1681141d9d0SIrina Tirdea 					const char **parent_names,
1691141d9d0SIrina Tirdea 					int num_parents)
1701141d9d0SIrina Tirdea {
1711141d9d0SIrina Tirdea 	struct clk_plt *pclk;
1721141d9d0SIrina Tirdea 	struct clk_init_data init;
1731141d9d0SIrina Tirdea 	int ret;
1741141d9d0SIrina Tirdea 
1751141d9d0SIrina Tirdea 	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
1761141d9d0SIrina Tirdea 	if (!pclk)
1771141d9d0SIrina Tirdea 		return ERR_PTR(-ENOMEM);
1781141d9d0SIrina Tirdea 
1791141d9d0SIrina Tirdea 	init.name =  kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id);
1801141d9d0SIrina Tirdea 	init.ops = &plt_clk_ops;
1811141d9d0SIrina Tirdea 	init.flags = 0;
1821141d9d0SIrina Tirdea 	init.parent_names = parent_names;
1831141d9d0SIrina Tirdea 	init.num_parents = num_parents;
1841141d9d0SIrina Tirdea 
1851141d9d0SIrina Tirdea 	pclk->hw.init = &init;
1861141d9d0SIrina Tirdea 	pclk->reg = base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE;
1871141d9d0SIrina Tirdea 	spin_lock_init(&pclk->lock);
1881141d9d0SIrina Tirdea 
1891141d9d0SIrina Tirdea 	ret = devm_clk_hw_register(&pdev->dev, &pclk->hw);
1901141d9d0SIrina Tirdea 	if (ret) {
1911141d9d0SIrina Tirdea 		pclk = ERR_PTR(ret);
1921141d9d0SIrina Tirdea 		goto err_free_init;
1931141d9d0SIrina Tirdea 	}
1941141d9d0SIrina Tirdea 
1951141d9d0SIrina Tirdea 	pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL);
1961141d9d0SIrina Tirdea 	if (!pclk->lookup) {
1971141d9d0SIrina Tirdea 		pclk = ERR_PTR(-ENOMEM);
1981141d9d0SIrina Tirdea 		goto err_free_init;
1991141d9d0SIrina Tirdea 	}
2001141d9d0SIrina Tirdea 
2011141d9d0SIrina Tirdea err_free_init:
2021141d9d0SIrina Tirdea 	kfree(init.name);
2031141d9d0SIrina Tirdea 	return pclk;
2041141d9d0SIrina Tirdea }
2051141d9d0SIrina Tirdea 
2061141d9d0SIrina Tirdea static void plt_clk_unregister(struct clk_plt *pclk)
2071141d9d0SIrina Tirdea {
2081141d9d0SIrina Tirdea 	clkdev_drop(pclk->lookup);
2091141d9d0SIrina Tirdea }
2101141d9d0SIrina Tirdea 
2111141d9d0SIrina Tirdea static struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev,
2121141d9d0SIrina Tirdea 						 const char *name,
2131141d9d0SIrina Tirdea 						 const char *parent_name,
2141141d9d0SIrina Tirdea 						 unsigned long fixed_rate)
2151141d9d0SIrina Tirdea {
2161141d9d0SIrina Tirdea 	struct clk_plt_fixed *pclk;
2171141d9d0SIrina Tirdea 
2181141d9d0SIrina Tirdea 	pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL);
2191141d9d0SIrina Tirdea 	if (!pclk)
2201141d9d0SIrina Tirdea 		return ERR_PTR(-ENOMEM);
2211141d9d0SIrina Tirdea 
2221141d9d0SIrina Tirdea 	pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name,
2231141d9d0SIrina Tirdea 					       0, fixed_rate);
2241141d9d0SIrina Tirdea 	if (IS_ERR(pclk->clk))
2251141d9d0SIrina Tirdea 		return ERR_CAST(pclk->clk);
2261141d9d0SIrina Tirdea 
2271141d9d0SIrina Tirdea 	pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL);
2281141d9d0SIrina Tirdea 	if (!pclk->lookup) {
2291141d9d0SIrina Tirdea 		clk_hw_unregister_fixed_rate(pclk->clk);
2301141d9d0SIrina Tirdea 		return ERR_PTR(-ENOMEM);
2311141d9d0SIrina Tirdea 	}
2321141d9d0SIrina Tirdea 
2331141d9d0SIrina Tirdea 	return pclk;
2341141d9d0SIrina Tirdea }
2351141d9d0SIrina Tirdea 
2361141d9d0SIrina Tirdea static void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk)
2371141d9d0SIrina Tirdea {
2381141d9d0SIrina Tirdea 	clkdev_drop(pclk->lookup);
2391141d9d0SIrina Tirdea 	clk_hw_unregister_fixed_rate(pclk->clk);
2401141d9d0SIrina Tirdea }
2411141d9d0SIrina Tirdea 
2421141d9d0SIrina Tirdea static void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data,
2431141d9d0SIrina Tirdea 					       unsigned int i)
2441141d9d0SIrina Tirdea {
2451141d9d0SIrina Tirdea 	while (i--)
2461141d9d0SIrina Tirdea 		plt_clk_unregister_fixed_rate(data->parents[i]);
2471141d9d0SIrina Tirdea }
2481141d9d0SIrina Tirdea 
2491141d9d0SIrina Tirdea static void plt_clk_free_parent_names_loop(const char **parent_names,
2501141d9d0SIrina Tirdea 					   unsigned int i)
2511141d9d0SIrina Tirdea {
2521141d9d0SIrina Tirdea 	while (i--)
2531141d9d0SIrina Tirdea 		kfree_const(parent_names[i]);
2541141d9d0SIrina Tirdea 	kfree(parent_names);
2551141d9d0SIrina Tirdea }
2561141d9d0SIrina Tirdea 
2571141d9d0SIrina Tirdea static void plt_clk_unregister_loop(struct clk_plt_data *data,
2581141d9d0SIrina Tirdea 				    unsigned int i)
2591141d9d0SIrina Tirdea {
2601141d9d0SIrina Tirdea 	while (i--)
2611141d9d0SIrina Tirdea 		plt_clk_unregister(data->clks[i]);
2621141d9d0SIrina Tirdea }
2631141d9d0SIrina Tirdea 
2641141d9d0SIrina Tirdea static const char **plt_clk_register_parents(struct platform_device *pdev,
2651141d9d0SIrina Tirdea 					     struct clk_plt_data *data,
2661141d9d0SIrina Tirdea 					     const struct pmc_clk *clks)
2671141d9d0SIrina Tirdea {
2681141d9d0SIrina Tirdea 	const char **parent_names;
2691141d9d0SIrina Tirdea 	unsigned int i;
2701141d9d0SIrina Tirdea 	int err;
2711141d9d0SIrina Tirdea 	int nparents = 0;
2721141d9d0SIrina Tirdea 
2731141d9d0SIrina Tirdea 	data->nparents = 0;
2741141d9d0SIrina Tirdea 	while (clks[nparents].name)
2751141d9d0SIrina Tirdea 		nparents++;
2761141d9d0SIrina Tirdea 
2771141d9d0SIrina Tirdea 	data->parents = devm_kcalloc(&pdev->dev, nparents,
2781141d9d0SIrina Tirdea 				     sizeof(*data->parents), GFP_KERNEL);
2791141d9d0SIrina Tirdea 	if (!data->parents)
2801141d9d0SIrina Tirdea 		return ERR_PTR(-ENOMEM);
2811141d9d0SIrina Tirdea 
2821141d9d0SIrina Tirdea 	parent_names = kcalloc(nparents, sizeof(*parent_names),
2831141d9d0SIrina Tirdea 			       GFP_KERNEL);
2841141d9d0SIrina Tirdea 	if (!parent_names)
2851141d9d0SIrina Tirdea 		return ERR_PTR(-ENOMEM);
2861141d9d0SIrina Tirdea 
2871141d9d0SIrina Tirdea 	for (i = 0; i < nparents; i++) {
2881141d9d0SIrina Tirdea 		data->parents[i] =
2891141d9d0SIrina Tirdea 			plt_clk_register_fixed_rate(pdev, clks[i].name,
2901141d9d0SIrina Tirdea 						    clks[i].parent_name,
2911141d9d0SIrina Tirdea 						    clks[i].freq);
2921141d9d0SIrina Tirdea 		if (IS_ERR(data->parents[i])) {
2931141d9d0SIrina Tirdea 			err = PTR_ERR(data->parents[i]);
2941141d9d0SIrina Tirdea 			goto err_unreg;
2951141d9d0SIrina Tirdea 		}
2961141d9d0SIrina Tirdea 		parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL);
2971141d9d0SIrina Tirdea 	}
2981141d9d0SIrina Tirdea 
2991141d9d0SIrina Tirdea 	data->nparents = nparents;
3001141d9d0SIrina Tirdea 	return parent_names;
3011141d9d0SIrina Tirdea 
3021141d9d0SIrina Tirdea err_unreg:
3031141d9d0SIrina Tirdea 	plt_clk_unregister_fixed_rate_loop(data, i);
3041141d9d0SIrina Tirdea 	plt_clk_free_parent_names_loop(parent_names, i);
3051141d9d0SIrina Tirdea 	return ERR_PTR(err);
3061141d9d0SIrina Tirdea }
3071141d9d0SIrina Tirdea 
3081141d9d0SIrina Tirdea static void plt_clk_unregister_parents(struct clk_plt_data *data)
3091141d9d0SIrina Tirdea {
3101141d9d0SIrina Tirdea 	plt_clk_unregister_fixed_rate_loop(data, data->nparents);
3111141d9d0SIrina Tirdea }
3121141d9d0SIrina Tirdea 
3131141d9d0SIrina Tirdea static int plt_clk_probe(struct platform_device *pdev)
3141141d9d0SIrina Tirdea {
3151141d9d0SIrina Tirdea 	const struct pmc_clk_data *pmc_data;
3161141d9d0SIrina Tirdea 	const char **parent_names;
3171141d9d0SIrina Tirdea 	struct clk_plt_data *data;
3181141d9d0SIrina Tirdea 	unsigned int i;
3191141d9d0SIrina Tirdea 	int err;
3201141d9d0SIrina Tirdea 
3211141d9d0SIrina Tirdea 	pmc_data = dev_get_platdata(&pdev->dev);
3221141d9d0SIrina Tirdea 	if (!pmc_data || !pmc_data->clks)
3231141d9d0SIrina Tirdea 		return -EINVAL;
3241141d9d0SIrina Tirdea 
3251141d9d0SIrina Tirdea 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
3261141d9d0SIrina Tirdea 	if (!data)
3271141d9d0SIrina Tirdea 		return -ENOMEM;
3281141d9d0SIrina Tirdea 
3291141d9d0SIrina Tirdea 	parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks);
3301141d9d0SIrina Tirdea 	if (IS_ERR(parent_names))
3311141d9d0SIrina Tirdea 		return PTR_ERR(parent_names);
3321141d9d0SIrina Tirdea 
3331141d9d0SIrina Tirdea 	for (i = 0; i < PMC_CLK_NUM; i++) {
3341141d9d0SIrina Tirdea 		data->clks[i] = plt_clk_register(pdev, i, pmc_data->base,
3351141d9d0SIrina Tirdea 						 parent_names, data->nparents);
3361141d9d0SIrina Tirdea 		if (IS_ERR(data->clks[i])) {
3371141d9d0SIrina Tirdea 			err = PTR_ERR(data->clks[i]);
3381141d9d0SIrina Tirdea 			goto err_unreg_clk_plt;
3391141d9d0SIrina Tirdea 		}
3401141d9d0SIrina Tirdea 	}
34141ee7cafSPierre-Louis Bossart 	data->mclk_lookup = clkdev_hw_create(&data->clks[3]->hw, "mclk", NULL);
342*0119dc61SDan Carpenter 	if (!data->mclk_lookup) {
343*0119dc61SDan Carpenter 		err = -ENOMEM;
34441ee7cafSPierre-Louis Bossart 		goto err_unreg_clk_plt;
34541ee7cafSPierre-Louis Bossart 	}
3461141d9d0SIrina Tirdea 
3471141d9d0SIrina Tirdea 	plt_clk_free_parent_names_loop(parent_names, data->nparents);
3481141d9d0SIrina Tirdea 
3491141d9d0SIrina Tirdea 	platform_set_drvdata(pdev, data);
3501141d9d0SIrina Tirdea 	return 0;
3511141d9d0SIrina Tirdea 
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 
3591141d9d0SIrina Tirdea static int 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 
36541ee7cafSPierre-Louis Bossart 	clkdev_drop(data->mclk_lookup);
3661141d9d0SIrina Tirdea 	plt_clk_unregister_loop(data, PMC_CLK_NUM);
3671141d9d0SIrina Tirdea 	plt_clk_unregister_parents(data);
3681141d9d0SIrina Tirdea 	return 0;
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,
3761141d9d0SIrina Tirdea 	.remove = plt_clk_remove,
3771141d9d0SIrina Tirdea };
3781141d9d0SIrina Tirdea builtin_platform_driver(plt_clk_driver);
379