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