18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Common Clock Framework support for s3c24xx external clock output. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 98c2ecf20Sopenharmony_ci#include <linux/slab.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_data/clk-s3c2410.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include "clk.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define MUX_DCLK0 0 198c2ecf20Sopenharmony_ci#define MUX_DCLK1 1 208c2ecf20Sopenharmony_ci#define DIV_DCLK0 2 218c2ecf20Sopenharmony_ci#define DIV_DCLK1 3 228c2ecf20Sopenharmony_ci#define GATE_DCLK0 4 238c2ecf20Sopenharmony_ci#define GATE_DCLK1 5 248c2ecf20Sopenharmony_ci#define MUX_CLKOUT0 6 258c2ecf20Sopenharmony_ci#define MUX_CLKOUT1 7 268c2ecf20Sopenharmony_ci#define DCLK_MAX_CLKS (MUX_CLKOUT1 + 1) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cienum supported_socs { 298c2ecf20Sopenharmony_ci S3C2410, 308c2ecf20Sopenharmony_ci S3C2412, 318c2ecf20Sopenharmony_ci S3C2440, 328c2ecf20Sopenharmony_ci S3C2443, 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistruct s3c24xx_dclk_drv_data { 368c2ecf20Sopenharmony_ci const char **clkout0_parent_names; 378c2ecf20Sopenharmony_ci int clkout0_num_parents; 388c2ecf20Sopenharmony_ci const char **clkout1_parent_names; 398c2ecf20Sopenharmony_ci int clkout1_num_parents; 408c2ecf20Sopenharmony_ci const char **mux_parent_names; 418c2ecf20Sopenharmony_ci int mux_num_parents; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Clock for output-parent selection in misccr 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct s3c24xx_clkout { 498c2ecf20Sopenharmony_ci struct clk_hw hw; 508c2ecf20Sopenharmony_ci u32 mask; 518c2ecf20Sopenharmony_ci u8 shift; 528c2ecf20Sopenharmony_ci unsigned int (*modify_misccr)(unsigned int clr, unsigned int chg); 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define to_s3c24xx_clkout(_hw) container_of(_hw, struct s3c24xx_clkout, hw) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic u8 s3c24xx_clkout_get_parent(struct clk_hw *hw) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw); 608c2ecf20Sopenharmony_ci int num_parents = clk_hw_get_num_parents(hw); 618c2ecf20Sopenharmony_ci u32 val; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci val = clkout->modify_misccr(0, 0) >> clkout->shift; 648c2ecf20Sopenharmony_ci val >>= clkout->shift; 658c2ecf20Sopenharmony_ci val &= clkout->mask; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (val >= num_parents) 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return val; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci clkout->modify_misccr((clkout->mask << clkout->shift), 788c2ecf20Sopenharmony_ci (index << clkout->shift)); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return 0; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic const struct clk_ops s3c24xx_clkout_ops = { 848c2ecf20Sopenharmony_ci .get_parent = s3c24xx_clkout_get_parent, 858c2ecf20Sopenharmony_ci .set_parent = s3c24xx_clkout_set_parent, 868c2ecf20Sopenharmony_ci .determine_rate = __clk_mux_determine_rate, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic struct clk_hw *s3c24xx_register_clkout(struct device *dev, 908c2ecf20Sopenharmony_ci const char *name, const char **parent_names, u8 num_parents, 918c2ecf20Sopenharmony_ci u8 shift, u32 mask) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct s3c2410_clk_platform_data *pdata = dev_get_platdata(dev); 948c2ecf20Sopenharmony_ci struct s3c24xx_clkout *clkout; 958c2ecf20Sopenharmony_ci struct clk_init_data init; 968c2ecf20Sopenharmony_ci int ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci if (!pdata) 998c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* allocate the clkout */ 1028c2ecf20Sopenharmony_ci clkout = kzalloc(sizeof(*clkout), GFP_KERNEL); 1038c2ecf20Sopenharmony_ci if (!clkout) 1048c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci init.name = name; 1078c2ecf20Sopenharmony_ci init.ops = &s3c24xx_clkout_ops; 1088c2ecf20Sopenharmony_ci init.flags = 0; 1098c2ecf20Sopenharmony_ci init.parent_names = parent_names; 1108c2ecf20Sopenharmony_ci init.num_parents = num_parents; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci clkout->shift = shift; 1138c2ecf20Sopenharmony_ci clkout->mask = mask; 1148c2ecf20Sopenharmony_ci clkout->hw.init = &init; 1158c2ecf20Sopenharmony_ci clkout->modify_misccr = pdata->modify_misccr; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci ret = clk_hw_register(dev, &clkout->hw); 1188c2ecf20Sopenharmony_ci if (ret) 1198c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return &clkout->hw; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * dclk and clkout init 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistruct s3c24xx_dclk { 1298c2ecf20Sopenharmony_ci struct device *dev; 1308c2ecf20Sopenharmony_ci void __iomem *base; 1318c2ecf20Sopenharmony_ci struct notifier_block dclk0_div_change_nb; 1328c2ecf20Sopenharmony_ci struct notifier_block dclk1_div_change_nb; 1338c2ecf20Sopenharmony_ci spinlock_t dclk_lock; 1348c2ecf20Sopenharmony_ci unsigned long reg_save; 1358c2ecf20Sopenharmony_ci /* clk_data must be the last entry in the structure */ 1368c2ecf20Sopenharmony_ci struct clk_hw_onecell_data clk_data; 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#define to_s3c24xx_dclk0(x) \ 1408c2ecf20Sopenharmony_ci container_of(x, struct s3c24xx_dclk, dclk0_div_change_nb) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci#define to_s3c24xx_dclk1(x) \ 1438c2ecf20Sopenharmony_ci container_of(x, struct s3c24xx_dclk, dclk1_div_change_nb) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic const char *dclk_s3c2410_p[] = { "pclk", "uclk" }; 1468c2ecf20Sopenharmony_cistatic const char *clkout0_s3c2410_p[] = { "mpll", "upll", "fclk", "hclk", "pclk", 1478c2ecf20Sopenharmony_ci "gate_dclk0" }; 1488c2ecf20Sopenharmony_cistatic const char *clkout1_s3c2410_p[] = { "mpll", "upll", "fclk", "hclk", "pclk", 1498c2ecf20Sopenharmony_ci "gate_dclk1" }; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic const char *clkout0_s3c2412_p[] = { "mpll", "upll", "rtc_clkout", 1528c2ecf20Sopenharmony_ci "hclk", "pclk", "gate_dclk0" }; 1538c2ecf20Sopenharmony_cistatic const char *clkout1_s3c2412_p[] = { "xti", "upll", "fclk", "hclk", "pclk", 1548c2ecf20Sopenharmony_ci "gate_dclk1" }; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic const char *clkout0_s3c2440_p[] = { "xti", "upll", "fclk", "hclk", "pclk", 1578c2ecf20Sopenharmony_ci "gate_dclk0" }; 1588c2ecf20Sopenharmony_cistatic const char *clkout1_s3c2440_p[] = { "mpll", "upll", "rtc_clkout", 1598c2ecf20Sopenharmony_ci "hclk", "pclk", "gate_dclk1" }; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const char *dclk_s3c2443_p[] = { "pclk", "epll" }; 1628c2ecf20Sopenharmony_cistatic const char *clkout0_s3c2443_p[] = { "xti", "epll", "armclk", "hclk", "pclk", 1638c2ecf20Sopenharmony_ci "gate_dclk0" }; 1648c2ecf20Sopenharmony_cistatic const char *clkout1_s3c2443_p[] = { "dummy", "epll", "rtc_clkout", 1658c2ecf20Sopenharmony_ci "hclk", "pclk", "gate_dclk1" }; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define DCLKCON_DCLK_DIV_MASK 0xf 1688c2ecf20Sopenharmony_ci#define DCLKCON_DCLK0_DIV_SHIFT 4 1698c2ecf20Sopenharmony_ci#define DCLKCON_DCLK0_CMP_SHIFT 8 1708c2ecf20Sopenharmony_ci#define DCLKCON_DCLK1_DIV_SHIFT 20 1718c2ecf20Sopenharmony_ci#define DCLKCON_DCLK1_CMP_SHIFT 24 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic void s3c24xx_dclk_update_cmp(struct s3c24xx_dclk *s3c24xx_dclk, 1748c2ecf20Sopenharmony_ci int div_shift, int cmp_shift) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci unsigned long flags = 0; 1778c2ecf20Sopenharmony_ci u32 dclk_con, div, cmp; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci spin_lock_irqsave(&s3c24xx_dclk->dclk_lock, flags); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci dclk_con = readl_relaxed(s3c24xx_dclk->base); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci div = ((dclk_con >> div_shift) & DCLKCON_DCLK_DIV_MASK) + 1; 1848c2ecf20Sopenharmony_ci cmp = ((div + 1) / 2) - 1; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci dclk_con &= ~(DCLKCON_DCLK_DIV_MASK << cmp_shift); 1878c2ecf20Sopenharmony_ci dclk_con |= (cmp << cmp_shift); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci writel_relaxed(dclk_con, s3c24xx_dclk->base); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&s3c24xx_dclk->dclk_lock, flags); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int s3c24xx_dclk0_div_notify(struct notifier_block *nb, 1958c2ecf20Sopenharmony_ci unsigned long event, void *data) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk0(nb); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (event == POST_RATE_CHANGE) { 2008c2ecf20Sopenharmony_ci s3c24xx_dclk_update_cmp(s3c24xx_dclk, 2018c2ecf20Sopenharmony_ci DCLKCON_DCLK0_DIV_SHIFT, DCLKCON_DCLK0_CMP_SHIFT); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int s3c24xx_dclk1_div_notify(struct notifier_block *nb, 2088c2ecf20Sopenharmony_ci unsigned long event, void *data) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk1(nb); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (event == POST_RATE_CHANGE) { 2138c2ecf20Sopenharmony_ci s3c24xx_dclk_update_cmp(s3c24xx_dclk, 2148c2ecf20Sopenharmony_ci DCLKCON_DCLK1_DIV_SHIFT, DCLKCON_DCLK1_CMP_SHIFT); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2218c2ecf20Sopenharmony_cistatic int s3c24xx_dclk_suspend(struct device *dev) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct s3c24xx_dclk *s3c24xx_dclk = dev_get_drvdata(dev); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci s3c24xx_dclk->reg_save = readl_relaxed(s3c24xx_dclk->base); 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int s3c24xx_dclk_resume(struct device *dev) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct s3c24xx_dclk *s3c24xx_dclk = dev_get_drvdata(dev); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci writel_relaxed(s3c24xx_dclk->reg_save, s3c24xx_dclk->base); 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci#endif 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(s3c24xx_dclk_pm_ops, 2398c2ecf20Sopenharmony_ci s3c24xx_dclk_suspend, s3c24xx_dclk_resume); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int s3c24xx_dclk_probe(struct platform_device *pdev) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct s3c24xx_dclk *s3c24xx_dclk; 2448c2ecf20Sopenharmony_ci struct s3c24xx_dclk_drv_data *dclk_variant; 2458c2ecf20Sopenharmony_ci struct clk_hw **clk_table; 2468c2ecf20Sopenharmony_ci int ret, i; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci s3c24xx_dclk = devm_kzalloc(&pdev->dev, 2498c2ecf20Sopenharmony_ci struct_size(s3c24xx_dclk, clk_data.hws, 2508c2ecf20Sopenharmony_ci DCLK_MAX_CLKS), 2518c2ecf20Sopenharmony_ci GFP_KERNEL); 2528c2ecf20Sopenharmony_ci if (!s3c24xx_dclk) 2538c2ecf20Sopenharmony_ci return -ENOMEM; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci clk_table = s3c24xx_dclk->clk_data.hws; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci s3c24xx_dclk->dev = &pdev->dev; 2588c2ecf20Sopenharmony_ci s3c24xx_dclk->clk_data.num = DCLK_MAX_CLKS; 2598c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, s3c24xx_dclk); 2608c2ecf20Sopenharmony_ci spin_lock_init(&s3c24xx_dclk->dclk_lock); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci s3c24xx_dclk->base = devm_platform_ioremap_resource(pdev, 0); 2638c2ecf20Sopenharmony_ci if (IS_ERR(s3c24xx_dclk->base)) 2648c2ecf20Sopenharmony_ci return PTR_ERR(s3c24xx_dclk->base); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci dclk_variant = (struct s3c24xx_dclk_drv_data *) 2678c2ecf20Sopenharmony_ci platform_get_device_id(pdev)->driver_data; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci clk_table[MUX_DCLK0] = clk_hw_register_mux(&pdev->dev, "mux_dclk0", 2718c2ecf20Sopenharmony_ci dclk_variant->mux_parent_names, 2728c2ecf20Sopenharmony_ci dclk_variant->mux_num_parents, 0, 2738c2ecf20Sopenharmony_ci s3c24xx_dclk->base, 1, 1, 0, 2748c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk_lock); 2758c2ecf20Sopenharmony_ci clk_table[MUX_DCLK1] = clk_hw_register_mux(&pdev->dev, "mux_dclk1", 2768c2ecf20Sopenharmony_ci dclk_variant->mux_parent_names, 2778c2ecf20Sopenharmony_ci dclk_variant->mux_num_parents, 0, 2788c2ecf20Sopenharmony_ci s3c24xx_dclk->base, 17, 1, 0, 2798c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk_lock); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci clk_table[DIV_DCLK0] = clk_hw_register_divider(&pdev->dev, "div_dclk0", 2828c2ecf20Sopenharmony_ci "mux_dclk0", 0, s3c24xx_dclk->base, 2838c2ecf20Sopenharmony_ci 4, 4, 0, &s3c24xx_dclk->dclk_lock); 2848c2ecf20Sopenharmony_ci clk_table[DIV_DCLK1] = clk_hw_register_divider(&pdev->dev, "div_dclk1", 2858c2ecf20Sopenharmony_ci "mux_dclk1", 0, s3c24xx_dclk->base, 2868c2ecf20Sopenharmony_ci 20, 4, 0, &s3c24xx_dclk->dclk_lock); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci clk_table[GATE_DCLK0] = clk_hw_register_gate(&pdev->dev, "gate_dclk0", 2898c2ecf20Sopenharmony_ci "div_dclk0", CLK_SET_RATE_PARENT, 2908c2ecf20Sopenharmony_ci s3c24xx_dclk->base, 0, 0, 2918c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk_lock); 2928c2ecf20Sopenharmony_ci clk_table[GATE_DCLK1] = clk_hw_register_gate(&pdev->dev, "gate_dclk1", 2938c2ecf20Sopenharmony_ci "div_dclk1", CLK_SET_RATE_PARENT, 2948c2ecf20Sopenharmony_ci s3c24xx_dclk->base, 16, 0, 2958c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk_lock); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci clk_table[MUX_CLKOUT0] = s3c24xx_register_clkout(&pdev->dev, 2988c2ecf20Sopenharmony_ci "clkout0", dclk_variant->clkout0_parent_names, 2998c2ecf20Sopenharmony_ci dclk_variant->clkout0_num_parents, 4, 7); 3008c2ecf20Sopenharmony_ci clk_table[MUX_CLKOUT1] = s3c24xx_register_clkout(&pdev->dev, 3018c2ecf20Sopenharmony_ci "clkout1", dclk_variant->clkout1_parent_names, 3028c2ecf20Sopenharmony_ci dclk_variant->clkout1_num_parents, 8, 7); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci for (i = 0; i < DCLK_MAX_CLKS; i++) 3058c2ecf20Sopenharmony_ci if (IS_ERR(clk_table[i])) { 3068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "clock %d failed to register\n", i); 3078c2ecf20Sopenharmony_ci ret = PTR_ERR(clk_table[i]); 3088c2ecf20Sopenharmony_ci goto err_clk_register; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci ret = clk_hw_register_clkdev(clk_table[MUX_DCLK0], "dclk0", NULL); 3128c2ecf20Sopenharmony_ci if (!ret) 3138c2ecf20Sopenharmony_ci ret = clk_hw_register_clkdev(clk_table[MUX_DCLK1], "dclk1", 3148c2ecf20Sopenharmony_ci NULL); 3158c2ecf20Sopenharmony_ci if (!ret) 3168c2ecf20Sopenharmony_ci ret = clk_hw_register_clkdev(clk_table[MUX_CLKOUT0], 3178c2ecf20Sopenharmony_ci "clkout0", NULL); 3188c2ecf20Sopenharmony_ci if (!ret) 3198c2ecf20Sopenharmony_ci ret = clk_hw_register_clkdev(clk_table[MUX_CLKOUT1], 3208c2ecf20Sopenharmony_ci "clkout1", NULL); 3218c2ecf20Sopenharmony_ci if (ret) { 3228c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register aliases, %d\n", ret); 3238c2ecf20Sopenharmony_ci goto err_clk_register; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci s3c24xx_dclk->dclk0_div_change_nb.notifier_call = 3278c2ecf20Sopenharmony_ci s3c24xx_dclk0_div_notify; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci s3c24xx_dclk->dclk1_div_change_nb.notifier_call = 3308c2ecf20Sopenharmony_ci s3c24xx_dclk1_div_notify; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = clk_notifier_register(clk_table[DIV_DCLK0]->clk, 3338c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk0_div_change_nb); 3348c2ecf20Sopenharmony_ci if (ret) 3358c2ecf20Sopenharmony_ci goto err_clk_register; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci ret = clk_notifier_register(clk_table[DIV_DCLK1]->clk, 3388c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk1_div_change_nb); 3398c2ecf20Sopenharmony_ci if (ret) 3408c2ecf20Sopenharmony_ci goto err_dclk_notify; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cierr_dclk_notify: 3458c2ecf20Sopenharmony_ci clk_notifier_unregister(clk_table[DIV_DCLK0]->clk, 3468c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk0_div_change_nb); 3478c2ecf20Sopenharmony_cierr_clk_register: 3488c2ecf20Sopenharmony_ci for (i = 0; i < DCLK_MAX_CLKS; i++) 3498c2ecf20Sopenharmony_ci if (clk_table[i] && !IS_ERR(clk_table[i])) 3508c2ecf20Sopenharmony_ci clk_hw_unregister(clk_table[i]); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return ret; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int s3c24xx_dclk_remove(struct platform_device *pdev) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev); 3588c2ecf20Sopenharmony_ci struct clk_hw **clk_table = s3c24xx_dclk->clk_data.hws; 3598c2ecf20Sopenharmony_ci int i; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci clk_notifier_unregister(clk_table[DIV_DCLK1]->clk, 3628c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk1_div_change_nb); 3638c2ecf20Sopenharmony_ci clk_notifier_unregister(clk_table[DIV_DCLK0]->clk, 3648c2ecf20Sopenharmony_ci &s3c24xx_dclk->dclk0_div_change_nb); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci for (i = 0; i < DCLK_MAX_CLKS; i++) 3678c2ecf20Sopenharmony_ci clk_hw_unregister(clk_table[i]); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci return 0; 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic struct s3c24xx_dclk_drv_data dclk_variants[] = { 3738c2ecf20Sopenharmony_ci [S3C2410] = { 3748c2ecf20Sopenharmony_ci .clkout0_parent_names = clkout0_s3c2410_p, 3758c2ecf20Sopenharmony_ci .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2410_p), 3768c2ecf20Sopenharmony_ci .clkout1_parent_names = clkout1_s3c2410_p, 3778c2ecf20Sopenharmony_ci .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2410_p), 3788c2ecf20Sopenharmony_ci .mux_parent_names = dclk_s3c2410_p, 3798c2ecf20Sopenharmony_ci .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), 3808c2ecf20Sopenharmony_ci }, 3818c2ecf20Sopenharmony_ci [S3C2412] = { 3828c2ecf20Sopenharmony_ci .clkout0_parent_names = clkout0_s3c2412_p, 3838c2ecf20Sopenharmony_ci .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2412_p), 3848c2ecf20Sopenharmony_ci .clkout1_parent_names = clkout1_s3c2412_p, 3858c2ecf20Sopenharmony_ci .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2412_p), 3868c2ecf20Sopenharmony_ci .mux_parent_names = dclk_s3c2410_p, 3878c2ecf20Sopenharmony_ci .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), 3888c2ecf20Sopenharmony_ci }, 3898c2ecf20Sopenharmony_ci [S3C2440] = { 3908c2ecf20Sopenharmony_ci .clkout0_parent_names = clkout0_s3c2440_p, 3918c2ecf20Sopenharmony_ci .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2440_p), 3928c2ecf20Sopenharmony_ci .clkout1_parent_names = clkout1_s3c2440_p, 3938c2ecf20Sopenharmony_ci .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2440_p), 3948c2ecf20Sopenharmony_ci .mux_parent_names = dclk_s3c2410_p, 3958c2ecf20Sopenharmony_ci .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p), 3968c2ecf20Sopenharmony_ci }, 3978c2ecf20Sopenharmony_ci [S3C2443] = { 3988c2ecf20Sopenharmony_ci .clkout0_parent_names = clkout0_s3c2443_p, 3998c2ecf20Sopenharmony_ci .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2443_p), 4008c2ecf20Sopenharmony_ci .clkout1_parent_names = clkout1_s3c2443_p, 4018c2ecf20Sopenharmony_ci .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2443_p), 4028c2ecf20Sopenharmony_ci .mux_parent_names = dclk_s3c2443_p, 4038c2ecf20Sopenharmony_ci .mux_num_parents = ARRAY_SIZE(dclk_s3c2443_p), 4048c2ecf20Sopenharmony_ci }, 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic const struct platform_device_id s3c24xx_dclk_driver_ids[] = { 4088c2ecf20Sopenharmony_ci { 4098c2ecf20Sopenharmony_ci .name = "s3c2410-dclk", 4108c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&dclk_variants[S3C2410], 4118c2ecf20Sopenharmony_ci }, { 4128c2ecf20Sopenharmony_ci .name = "s3c2412-dclk", 4138c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&dclk_variants[S3C2412], 4148c2ecf20Sopenharmony_ci }, { 4158c2ecf20Sopenharmony_ci .name = "s3c2440-dclk", 4168c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&dclk_variants[S3C2440], 4178c2ecf20Sopenharmony_ci }, { 4188c2ecf20Sopenharmony_ci .name = "s3c2443-dclk", 4198c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&dclk_variants[S3C2443], 4208c2ecf20Sopenharmony_ci }, 4218c2ecf20Sopenharmony_ci { } 4228c2ecf20Sopenharmony_ci}; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, s3c24xx_dclk_driver_ids); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic struct platform_driver s3c24xx_dclk_driver = { 4278c2ecf20Sopenharmony_ci .driver = { 4288c2ecf20Sopenharmony_ci .name = "s3c24xx-dclk", 4298c2ecf20Sopenharmony_ci .pm = &s3c24xx_dclk_pm_ops, 4308c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 4318c2ecf20Sopenharmony_ci }, 4328c2ecf20Sopenharmony_ci .probe = s3c24xx_dclk_probe, 4338c2ecf20Sopenharmony_ci .remove = s3c24xx_dclk_remove, 4348c2ecf20Sopenharmony_ci .id_table = s3c24xx_dclk_driver_ids, 4358c2ecf20Sopenharmony_ci}; 4368c2ecf20Sopenharmony_cimodule_platform_driver(s3c24xx_dclk_driver); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 4398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); 4408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for the S3C24XX external clock outputs"); 441