18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel Atom platform clocks driver for BayTrail and CherryTrail SoCs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016, Intel Corporation 68c2ecf20Sopenharmony_ci * Author: Irina Tirdea <irina.tirdea@intel.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 108c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_data/x86/clk-pmc-atom.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define PLT_CLK_NAME_BASE "pmc_plt_clk" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define PMC_CLK_CTL_OFFSET 0x60 208c2ecf20Sopenharmony_ci#define PMC_CLK_CTL_SIZE 4 218c2ecf20Sopenharmony_ci#define PMC_CLK_NUM 6 228c2ecf20Sopenharmony_ci#define PMC_CLK_CTL_GATED_ON_D3 0x0 238c2ecf20Sopenharmony_ci#define PMC_CLK_CTL_FORCE_ON 0x1 248c2ecf20Sopenharmony_ci#define PMC_CLK_CTL_FORCE_OFF 0x2 258c2ecf20Sopenharmony_ci#define PMC_CLK_CTL_RESERVED 0x3 268c2ecf20Sopenharmony_ci#define PMC_MASK_CLK_CTL GENMASK(1, 0) 278c2ecf20Sopenharmony_ci#define PMC_MASK_CLK_FREQ BIT(2) 288c2ecf20Sopenharmony_ci#define PMC_CLK_FREQ_XTAL (0 << 2) /* 25 MHz */ 298c2ecf20Sopenharmony_ci#define PMC_CLK_FREQ_PLL (1 << 2) /* 19.2 MHz */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct clk_plt_fixed { 328c2ecf20Sopenharmony_ci struct clk_hw *clk; 338c2ecf20Sopenharmony_ci struct clk_lookup *lookup; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct clk_plt { 378c2ecf20Sopenharmony_ci struct clk_hw hw; 388c2ecf20Sopenharmony_ci void __iomem *reg; 398c2ecf20Sopenharmony_ci struct clk_lookup *lookup; 408c2ecf20Sopenharmony_ci /* protect access to PMC registers */ 418c2ecf20Sopenharmony_ci spinlock_t lock; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define to_clk_plt(_hw) container_of(_hw, struct clk_plt, hw) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct clk_plt_data { 478c2ecf20Sopenharmony_ci struct clk_plt_fixed **parents; 488c2ecf20Sopenharmony_ci u8 nparents; 498c2ecf20Sopenharmony_ci struct clk_plt *clks[PMC_CLK_NUM]; 508c2ecf20Sopenharmony_ci struct clk_lookup *mclk_lookup; 518c2ecf20Sopenharmony_ci struct clk_lookup *ether_clk_lookup; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Return an index in parent table */ 558c2ecf20Sopenharmony_cistatic inline int plt_reg_to_parent(int reg) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci switch (reg & PMC_MASK_CLK_FREQ) { 588c2ecf20Sopenharmony_ci default: 598c2ecf20Sopenharmony_ci case PMC_CLK_FREQ_XTAL: 608c2ecf20Sopenharmony_ci return 0; 618c2ecf20Sopenharmony_ci case PMC_CLK_FREQ_PLL: 628c2ecf20Sopenharmony_ci return 1; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* Return clk index of parent */ 678c2ecf20Sopenharmony_cistatic inline int plt_parent_to_reg(int index) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci switch (index) { 708c2ecf20Sopenharmony_ci default: 718c2ecf20Sopenharmony_ci case 0: 728c2ecf20Sopenharmony_ci return PMC_CLK_FREQ_XTAL; 738c2ecf20Sopenharmony_ci case 1: 748c2ecf20Sopenharmony_ci return PMC_CLK_FREQ_PLL; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Abstract status in simpler enabled/disabled value */ 798c2ecf20Sopenharmony_cistatic inline int plt_reg_to_enabled(int reg) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci switch (reg & PMC_MASK_CLK_CTL) { 828c2ecf20Sopenharmony_ci case PMC_CLK_CTL_GATED_ON_D3: 838c2ecf20Sopenharmony_ci case PMC_CLK_CTL_FORCE_ON: 848c2ecf20Sopenharmony_ci return 1; /* enabled */ 858c2ecf20Sopenharmony_ci case PMC_CLK_CTL_FORCE_OFF: 868c2ecf20Sopenharmony_ci case PMC_CLK_CTL_RESERVED: 878c2ecf20Sopenharmony_ci default: 888c2ecf20Sopenharmony_ci return 0; /* disabled */ 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void plt_clk_reg_update(struct clk_plt *clk, u32 mask, u32 val) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u32 tmp; 958c2ecf20Sopenharmony_ci unsigned long flags; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci spin_lock_irqsave(&clk->lock, flags); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci tmp = readl(clk->reg); 1008c2ecf20Sopenharmony_ci tmp = (tmp & ~mask) | (val & mask); 1018c2ecf20Sopenharmony_ci writel(tmp, clk->reg); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clk->lock, flags); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int plt_clk_set_parent(struct clk_hw *hw, u8 index) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct clk_plt *clk = to_clk_plt(hw); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci plt_clk_reg_update(clk, PMC_MASK_CLK_FREQ, plt_parent_to_reg(index)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic u8 plt_clk_get_parent(struct clk_hw *hw) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct clk_plt *clk = to_clk_plt(hw); 1188c2ecf20Sopenharmony_ci u32 value; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci value = readl(clk->reg); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return plt_reg_to_parent(value); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic int plt_clk_enable(struct clk_hw *hw) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct clk_plt *clk = to_clk_plt(hw); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_ON); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void plt_clk_disable(struct clk_hw *hw) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct clk_plt *clk = to_clk_plt(hw); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci plt_clk_reg_update(clk, PMC_MASK_CLK_CTL, PMC_CLK_CTL_FORCE_OFF); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int plt_clk_is_enabled(struct clk_hw *hw) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct clk_plt *clk = to_clk_plt(hw); 1448c2ecf20Sopenharmony_ci u32 value; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci value = readl(clk->reg); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return plt_reg_to_enabled(value); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const struct clk_ops plt_clk_ops = { 1528c2ecf20Sopenharmony_ci .enable = plt_clk_enable, 1538c2ecf20Sopenharmony_ci .disable = plt_clk_disable, 1548c2ecf20Sopenharmony_ci .is_enabled = plt_clk_is_enabled, 1558c2ecf20Sopenharmony_ci .get_parent = plt_clk_get_parent, 1568c2ecf20Sopenharmony_ci .set_parent = plt_clk_set_parent, 1578c2ecf20Sopenharmony_ci .determine_rate = __clk_mux_determine_rate, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, 1618c2ecf20Sopenharmony_ci const struct pmc_clk_data *pmc_data, 1628c2ecf20Sopenharmony_ci const char **parent_names, 1638c2ecf20Sopenharmony_ci int num_parents) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct clk_plt *pclk; 1668c2ecf20Sopenharmony_ci struct clk_init_data init; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); 1708c2ecf20Sopenharmony_ci if (!pclk) 1718c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci init.name = kasprintf(GFP_KERNEL, "%s_%d", PLT_CLK_NAME_BASE, id); 1748c2ecf20Sopenharmony_ci init.ops = &plt_clk_ops; 1758c2ecf20Sopenharmony_ci init.flags = 0; 1768c2ecf20Sopenharmony_ci init.parent_names = parent_names; 1778c2ecf20Sopenharmony_ci init.num_parents = num_parents; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci pclk->hw.init = &init; 1808c2ecf20Sopenharmony_ci pclk->reg = pmc_data->base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE; 1818c2ecf20Sopenharmony_ci spin_lock_init(&pclk->lock); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * On some systems, the pmc_plt_clocks already enabled by the 1858c2ecf20Sopenharmony_ci * firmware are being marked as critical to avoid them being 1868c2ecf20Sopenharmony_ci * gated by the clock framework. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci if (pmc_data->critical && plt_clk_is_enabled(&pclk->hw)) 1898c2ecf20Sopenharmony_ci init.flags |= CLK_IS_CRITICAL; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(&pdev->dev, &pclk->hw); 1928c2ecf20Sopenharmony_ci if (ret) { 1938c2ecf20Sopenharmony_ci pclk = ERR_PTR(ret); 1948c2ecf20Sopenharmony_ci goto err_free_init; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci pclk->lookup = clkdev_hw_create(&pclk->hw, init.name, NULL); 1988c2ecf20Sopenharmony_ci if (!pclk->lookup) { 1998c2ecf20Sopenharmony_ci pclk = ERR_PTR(-ENOMEM); 2008c2ecf20Sopenharmony_ci goto err_free_init; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cierr_free_init: 2048c2ecf20Sopenharmony_ci kfree(init.name); 2058c2ecf20Sopenharmony_ci return pclk; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void plt_clk_unregister(struct clk_plt *pclk) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci clkdev_drop(pclk->lookup); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic struct clk_plt_fixed *plt_clk_register_fixed_rate(struct platform_device *pdev, 2148c2ecf20Sopenharmony_ci const char *name, 2158c2ecf20Sopenharmony_ci const char *parent_name, 2168c2ecf20Sopenharmony_ci unsigned long fixed_rate) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct clk_plt_fixed *pclk; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci pclk = devm_kzalloc(&pdev->dev, sizeof(*pclk), GFP_KERNEL); 2218c2ecf20Sopenharmony_ci if (!pclk) 2228c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci pclk->clk = clk_hw_register_fixed_rate(&pdev->dev, name, parent_name, 2258c2ecf20Sopenharmony_ci 0, fixed_rate); 2268c2ecf20Sopenharmony_ci if (IS_ERR(pclk->clk)) 2278c2ecf20Sopenharmony_ci return ERR_CAST(pclk->clk); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci pclk->lookup = clkdev_hw_create(pclk->clk, name, NULL); 2308c2ecf20Sopenharmony_ci if (!pclk->lookup) { 2318c2ecf20Sopenharmony_ci clk_hw_unregister_fixed_rate(pclk->clk); 2328c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return pclk; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void plt_clk_unregister_fixed_rate(struct clk_plt_fixed *pclk) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci clkdev_drop(pclk->lookup); 2418c2ecf20Sopenharmony_ci clk_hw_unregister_fixed_rate(pclk->clk); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic void plt_clk_unregister_fixed_rate_loop(struct clk_plt_data *data, 2458c2ecf20Sopenharmony_ci unsigned int i) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci while (i--) 2488c2ecf20Sopenharmony_ci plt_clk_unregister_fixed_rate(data->parents[i]); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic void plt_clk_free_parent_names_loop(const char **parent_names, 2528c2ecf20Sopenharmony_ci unsigned int i) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci while (i--) 2558c2ecf20Sopenharmony_ci kfree_const(parent_names[i]); 2568c2ecf20Sopenharmony_ci kfree(parent_names); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void plt_clk_unregister_loop(struct clk_plt_data *data, 2608c2ecf20Sopenharmony_ci unsigned int i) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci while (i--) 2638c2ecf20Sopenharmony_ci plt_clk_unregister(data->clks[i]); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic const char **plt_clk_register_parents(struct platform_device *pdev, 2678c2ecf20Sopenharmony_ci struct clk_plt_data *data, 2688c2ecf20Sopenharmony_ci const struct pmc_clk *clks) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci const char **parent_names; 2718c2ecf20Sopenharmony_ci unsigned int i; 2728c2ecf20Sopenharmony_ci int err; 2738c2ecf20Sopenharmony_ci int nparents = 0; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci data->nparents = 0; 2768c2ecf20Sopenharmony_ci while (clks[nparents].name) 2778c2ecf20Sopenharmony_ci nparents++; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci data->parents = devm_kcalloc(&pdev->dev, nparents, 2808c2ecf20Sopenharmony_ci sizeof(*data->parents), GFP_KERNEL); 2818c2ecf20Sopenharmony_ci if (!data->parents) 2828c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci parent_names = kcalloc(nparents, sizeof(*parent_names), 2858c2ecf20Sopenharmony_ci GFP_KERNEL); 2868c2ecf20Sopenharmony_ci if (!parent_names) 2878c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci for (i = 0; i < nparents; i++) { 2908c2ecf20Sopenharmony_ci data->parents[i] = 2918c2ecf20Sopenharmony_ci plt_clk_register_fixed_rate(pdev, clks[i].name, 2928c2ecf20Sopenharmony_ci clks[i].parent_name, 2938c2ecf20Sopenharmony_ci clks[i].freq); 2948c2ecf20Sopenharmony_ci if (IS_ERR(data->parents[i])) { 2958c2ecf20Sopenharmony_ci err = PTR_ERR(data->parents[i]); 2968c2ecf20Sopenharmony_ci goto err_unreg; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci parent_names[i] = kstrdup_const(clks[i].name, GFP_KERNEL); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci data->nparents = nparents; 3028c2ecf20Sopenharmony_ci return parent_names; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cierr_unreg: 3058c2ecf20Sopenharmony_ci plt_clk_unregister_fixed_rate_loop(data, i); 3068c2ecf20Sopenharmony_ci plt_clk_free_parent_names_loop(parent_names, i); 3078c2ecf20Sopenharmony_ci return ERR_PTR(err); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic void plt_clk_unregister_parents(struct clk_plt_data *data) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci plt_clk_unregister_fixed_rate_loop(data, data->nparents); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int plt_clk_probe(struct platform_device *pdev) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci const struct pmc_clk_data *pmc_data; 3188c2ecf20Sopenharmony_ci const char **parent_names; 3198c2ecf20Sopenharmony_ci struct clk_plt_data *data; 3208c2ecf20Sopenharmony_ci unsigned int i; 3218c2ecf20Sopenharmony_ci int err; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci pmc_data = dev_get_platdata(&pdev->dev); 3248c2ecf20Sopenharmony_ci if (!pmc_data || !pmc_data->clks) 3258c2ecf20Sopenharmony_ci return -EINVAL; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 3288c2ecf20Sopenharmony_ci if (!data) 3298c2ecf20Sopenharmony_ci return -ENOMEM; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci parent_names = plt_clk_register_parents(pdev, data, pmc_data->clks); 3328c2ecf20Sopenharmony_ci if (IS_ERR(parent_names)) 3338c2ecf20Sopenharmony_ci return PTR_ERR(parent_names); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci for (i = 0; i < PMC_CLK_NUM; i++) { 3368c2ecf20Sopenharmony_ci data->clks[i] = plt_clk_register(pdev, i, pmc_data, 3378c2ecf20Sopenharmony_ci parent_names, data->nparents); 3388c2ecf20Sopenharmony_ci if (IS_ERR(data->clks[i])) { 3398c2ecf20Sopenharmony_ci err = PTR_ERR(data->clks[i]); 3408c2ecf20Sopenharmony_ci goto err_unreg_clk_plt; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci data->mclk_lookup = clkdev_hw_create(&data->clks[3]->hw, "mclk", NULL); 3448c2ecf20Sopenharmony_ci if (!data->mclk_lookup) { 3458c2ecf20Sopenharmony_ci err = -ENOMEM; 3468c2ecf20Sopenharmony_ci goto err_unreg_clk_plt; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci data->ether_clk_lookup = clkdev_hw_create(&data->clks[4]->hw, 3508c2ecf20Sopenharmony_ci "ether_clk", NULL); 3518c2ecf20Sopenharmony_ci if (!data->ether_clk_lookup) { 3528c2ecf20Sopenharmony_ci err = -ENOMEM; 3538c2ecf20Sopenharmony_ci goto err_drop_mclk; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci plt_clk_free_parent_names_loop(parent_names, data->nparents); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, data); 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cierr_drop_mclk: 3628c2ecf20Sopenharmony_ci clkdev_drop(data->mclk_lookup); 3638c2ecf20Sopenharmony_cierr_unreg_clk_plt: 3648c2ecf20Sopenharmony_ci plt_clk_unregister_loop(data, i); 3658c2ecf20Sopenharmony_ci plt_clk_unregister_parents(data); 3668c2ecf20Sopenharmony_ci plt_clk_free_parent_names_loop(parent_names, data->nparents); 3678c2ecf20Sopenharmony_ci return err; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic int plt_clk_remove(struct platform_device *pdev) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct clk_plt_data *data; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci data = platform_get_drvdata(pdev); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci clkdev_drop(data->ether_clk_lookup); 3778c2ecf20Sopenharmony_ci clkdev_drop(data->mclk_lookup); 3788c2ecf20Sopenharmony_ci plt_clk_unregister_loop(data, PMC_CLK_NUM); 3798c2ecf20Sopenharmony_ci plt_clk_unregister_parents(data); 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic struct platform_driver plt_clk_driver = { 3848c2ecf20Sopenharmony_ci .driver = { 3858c2ecf20Sopenharmony_ci .name = "clk-pmc-atom", 3868c2ecf20Sopenharmony_ci }, 3878c2ecf20Sopenharmony_ci .probe = plt_clk_probe, 3888c2ecf20Sopenharmony_ci .remove = plt_clk_remove, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_cibuiltin_platform_driver(plt_clk_driver); 391