1*16216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 20aa0c95fSHaojian Zhuang /* 30aa0c95fSHaojian Zhuang * Hisilicon clock driver 40aa0c95fSHaojian Zhuang * 50aa0c95fSHaojian Zhuang * Copyright (c) 2012-2013 Hisilicon Limited. 60aa0c95fSHaojian Zhuang * Copyright (c) 2012-2013 Linaro Limited. 70aa0c95fSHaojian Zhuang * 80aa0c95fSHaojian Zhuang * Author: Haojian Zhuang <haojian.zhuang@linaro.org> 90aa0c95fSHaojian Zhuang * Xin Li <li.xin@linaro.org> 100aa0c95fSHaojian Zhuang */ 110aa0c95fSHaojian Zhuang 120aa0c95fSHaojian Zhuang #include <linux/kernel.h> 130aa0c95fSHaojian Zhuang #include <linux/clkdev.h> 14593438e4SStephen Boyd #include <linux/clk-provider.h> 150aa0c95fSHaojian Zhuang #include <linux/delay.h> 160aa0c95fSHaojian Zhuang #include <linux/io.h> 170aa0c95fSHaojian Zhuang #include <linux/of.h> 180aa0c95fSHaojian Zhuang #include <linux/of_address.h> 190aa0c95fSHaojian Zhuang #include <linux/of_device.h> 200aa0c95fSHaojian Zhuang #include <linux/slab.h> 210aa0c95fSHaojian Zhuang 220aa0c95fSHaojian Zhuang #include "clk.h" 230aa0c95fSHaojian Zhuang 240aa0c95fSHaojian Zhuang static DEFINE_SPINLOCK(hisi_clk_lock); 250aa0c95fSHaojian Zhuang 2632226916SJiancheng Xue struct hisi_clock_data *hisi_clk_alloc(struct platform_device *pdev, 2732226916SJiancheng Xue int nr_clks) 2832226916SJiancheng Xue { 2932226916SJiancheng Xue struct hisi_clock_data *clk_data; 3032226916SJiancheng Xue struct resource *res; 3132226916SJiancheng Xue struct clk **clk_table; 3232226916SJiancheng Xue 3332226916SJiancheng Xue clk_data = devm_kmalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL); 3432226916SJiancheng Xue if (!clk_data) 3532226916SJiancheng Xue return NULL; 3632226916SJiancheng Xue 3732226916SJiancheng Xue res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 38c744b63bSWei Yongjun if (!res) 39c744b63bSWei Yongjun return NULL; 4032226916SJiancheng Xue clk_data->base = devm_ioremap(&pdev->dev, 4132226916SJiancheng Xue res->start, resource_size(res)); 4232226916SJiancheng Xue if (!clk_data->base) 4332226916SJiancheng Xue return NULL; 4432226916SJiancheng Xue 458d9bdc46SMarkus Elfring clk_table = devm_kmalloc_array(&pdev->dev, nr_clks, 468d9bdc46SMarkus Elfring sizeof(*clk_table), 4732226916SJiancheng Xue GFP_KERNEL); 4832226916SJiancheng Xue if (!clk_table) 4932226916SJiancheng Xue return NULL; 5032226916SJiancheng Xue 5132226916SJiancheng Xue clk_data->clk_data.clks = clk_table; 5232226916SJiancheng Xue clk_data->clk_data.clk_num = nr_clks; 5332226916SJiancheng Xue 5432226916SJiancheng Xue return clk_data; 5532226916SJiancheng Xue } 5632226916SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_alloc); 5732226916SJiancheng Xue 58f6ff57c8SJiancheng Xue struct hisi_clock_data *hisi_clk_init(struct device_node *np, 5975af25f5SHaojian Zhuang int nr_clks) 600aa0c95fSHaojian Zhuang { 6175af25f5SHaojian Zhuang struct hisi_clock_data *clk_data; 6275af25f5SHaojian Zhuang struct clk **clk_table; 6375af25f5SHaojian Zhuang void __iomem *base; 6475af25f5SHaojian Zhuang 6575af25f5SHaojian Zhuang base = of_iomap(np, 0); 6675af25f5SHaojian Zhuang if (!base) { 671fb6dd9dSLeo Yan pr_err("%s: failed to map clock registers\n", __func__); 6875af25f5SHaojian Zhuang goto err; 6975af25f5SHaojian Zhuang } 7075af25f5SHaojian Zhuang 7175af25f5SHaojian Zhuang clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); 72840e5632SMarkus Elfring if (!clk_data) 7375af25f5SHaojian Zhuang goto err; 74840e5632SMarkus Elfring 7575af25f5SHaojian Zhuang clk_data->base = base; 767b9bae17SMarkus Elfring clk_table = kcalloc(nr_clks, sizeof(*clk_table), GFP_KERNEL); 77840e5632SMarkus Elfring if (!clk_table) 7875af25f5SHaojian Zhuang goto err_data; 79840e5632SMarkus Elfring 8075af25f5SHaojian Zhuang clk_data->clk_data.clks = clk_table; 8175af25f5SHaojian Zhuang clk_data->clk_data.clk_num = nr_clks; 8275af25f5SHaojian Zhuang of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data); 8375af25f5SHaojian Zhuang return clk_data; 8475af25f5SHaojian Zhuang err_data: 8575af25f5SHaojian Zhuang kfree(clk_data); 8675af25f5SHaojian Zhuang err: 8775af25f5SHaojian Zhuang return NULL; 880aa0c95fSHaojian Zhuang } 89f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_init); 900aa0c95fSHaojian Zhuang 915497f668SJiancheng Xue int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks, 9275af25f5SHaojian Zhuang int nums, struct hisi_clock_data *data) 930aa0c95fSHaojian Zhuang { 940aa0c95fSHaojian Zhuang struct clk *clk; 950aa0c95fSHaojian Zhuang int i; 960aa0c95fSHaojian Zhuang 970aa0c95fSHaojian Zhuang for (i = 0; i < nums; i++) { 980aa0c95fSHaojian Zhuang clk = clk_register_fixed_rate(NULL, clks[i].name, 990aa0c95fSHaojian Zhuang clks[i].parent_name, 1000aa0c95fSHaojian Zhuang clks[i].flags, 1010aa0c95fSHaojian Zhuang clks[i].fixed_rate); 1020aa0c95fSHaojian Zhuang if (IS_ERR(clk)) { 1030aa0c95fSHaojian Zhuang pr_err("%s: failed to register clock %s\n", 1040aa0c95fSHaojian Zhuang __func__, clks[i].name); 1055497f668SJiancheng Xue goto err; 1060aa0c95fSHaojian Zhuang } 10775af25f5SHaojian Zhuang data->clk_data.clks[clks[i].id] = clk; 1080aa0c95fSHaojian Zhuang } 1095497f668SJiancheng Xue 1105497f668SJiancheng Xue return 0; 1115497f668SJiancheng Xue 1125497f668SJiancheng Xue err: 1135497f668SJiancheng Xue while (i--) 1145497f668SJiancheng Xue clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]); 1155497f668SJiancheng Xue 1165497f668SJiancheng Xue return PTR_ERR(clk); 1170aa0c95fSHaojian Zhuang } 118f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate); 1190aa0c95fSHaojian Zhuang 1205497f668SJiancheng Xue int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks, 12175af25f5SHaojian Zhuang int nums, 12275af25f5SHaojian Zhuang struct hisi_clock_data *data) 1230aa0c95fSHaojian Zhuang { 1240aa0c95fSHaojian Zhuang struct clk *clk; 1250aa0c95fSHaojian Zhuang int i; 1260aa0c95fSHaojian Zhuang 1270aa0c95fSHaojian Zhuang for (i = 0; i < nums; i++) { 1280aa0c95fSHaojian Zhuang clk = clk_register_fixed_factor(NULL, clks[i].name, 1290aa0c95fSHaojian Zhuang clks[i].parent_name, 1300aa0c95fSHaojian Zhuang clks[i].flags, clks[i].mult, 1310aa0c95fSHaojian Zhuang clks[i].div); 1320aa0c95fSHaojian Zhuang if (IS_ERR(clk)) { 1330aa0c95fSHaojian Zhuang pr_err("%s: failed to register clock %s\n", 1340aa0c95fSHaojian Zhuang __func__, clks[i].name); 1355497f668SJiancheng Xue goto err; 1360aa0c95fSHaojian Zhuang } 13775af25f5SHaojian Zhuang data->clk_data.clks[clks[i].id] = clk; 1380aa0c95fSHaojian Zhuang } 1395497f668SJiancheng Xue 1405497f668SJiancheng Xue return 0; 1415497f668SJiancheng Xue 1425497f668SJiancheng Xue err: 1435497f668SJiancheng Xue while (i--) 1445497f668SJiancheng Xue clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]); 1455497f668SJiancheng Xue 1465497f668SJiancheng Xue return PTR_ERR(clk); 1470aa0c95fSHaojian Zhuang } 148f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor); 1490aa0c95fSHaojian Zhuang 1505497f668SJiancheng Xue int hisi_clk_register_mux(const struct hisi_mux_clock *clks, 15175af25f5SHaojian Zhuang int nums, struct hisi_clock_data *data) 1520aa0c95fSHaojian Zhuang { 1530aa0c95fSHaojian Zhuang struct clk *clk; 15475af25f5SHaojian Zhuang void __iomem *base = data->base; 1550aa0c95fSHaojian Zhuang int i; 1560aa0c95fSHaojian Zhuang 1570aa0c95fSHaojian Zhuang for (i = 0; i < nums; i++) { 158156342a1SZhangfei Gao u32 mask = BIT(clks[i].width) - 1; 159156342a1SZhangfei Gao 160156342a1SZhangfei Gao clk = clk_register_mux_table(NULL, clks[i].name, 161156342a1SZhangfei Gao clks[i].parent_names, 1620aa0c95fSHaojian Zhuang clks[i].num_parents, clks[i].flags, 1630aa0c95fSHaojian Zhuang base + clks[i].offset, clks[i].shift, 164156342a1SZhangfei Gao mask, clks[i].mux_flags, 165156342a1SZhangfei Gao clks[i].table, &hisi_clk_lock); 1660aa0c95fSHaojian Zhuang if (IS_ERR(clk)) { 1670aa0c95fSHaojian Zhuang pr_err("%s: failed to register clock %s\n", 1680aa0c95fSHaojian Zhuang __func__, clks[i].name); 1695497f668SJiancheng Xue goto err; 1700aa0c95fSHaojian Zhuang } 1710aa0c95fSHaojian Zhuang 1720aa0c95fSHaojian Zhuang if (clks[i].alias) 1730aa0c95fSHaojian Zhuang clk_register_clkdev(clk, clks[i].alias, NULL); 1740aa0c95fSHaojian Zhuang 17575af25f5SHaojian Zhuang data->clk_data.clks[clks[i].id] = clk; 1760aa0c95fSHaojian Zhuang } 1775497f668SJiancheng Xue 1785497f668SJiancheng Xue return 0; 1795497f668SJiancheng Xue 1805497f668SJiancheng Xue err: 1815497f668SJiancheng Xue while (i--) 1825497f668SJiancheng Xue clk_unregister_mux(data->clk_data.clks[clks[i].id]); 1835497f668SJiancheng Xue 1845497f668SJiancheng Xue return PTR_ERR(clk); 1850aa0c95fSHaojian Zhuang } 186f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_mux); 1870aa0c95fSHaojian Zhuang 188811f67ccStianshuliang int hisi_clk_register_phase(struct device *dev, 189811f67ccStianshuliang const struct hisi_phase_clock *clks, 190811f67ccStianshuliang int nums, struct hisi_clock_data *data) 191811f67ccStianshuliang { 192811f67ccStianshuliang void __iomem *base = data->base; 193811f67ccStianshuliang struct clk *clk; 194811f67ccStianshuliang int i; 195811f67ccStianshuliang 196811f67ccStianshuliang for (i = 0; i < nums; i++) { 197811f67ccStianshuliang clk = clk_register_hisi_phase(dev, &clks[i], base, 198811f67ccStianshuliang &hisi_clk_lock); 199811f67ccStianshuliang if (IS_ERR(clk)) { 200811f67ccStianshuliang pr_err("%s: failed to register clock %s\n", __func__, 201811f67ccStianshuliang clks[i].name); 202811f67ccStianshuliang return PTR_ERR(clk); 203811f67ccStianshuliang } 204811f67ccStianshuliang 205811f67ccStianshuliang data->clk_data.clks[clks[i].id] = clk; 206811f67ccStianshuliang } 207811f67ccStianshuliang 208811f67ccStianshuliang return 0; 209811f67ccStianshuliang } 210811f67ccStianshuliang EXPORT_SYMBOL_GPL(hisi_clk_register_phase); 211811f67ccStianshuliang 2125497f668SJiancheng Xue int hisi_clk_register_divider(const struct hisi_divider_clock *clks, 21375af25f5SHaojian Zhuang int nums, struct hisi_clock_data *data) 2140aa0c95fSHaojian Zhuang { 2150aa0c95fSHaojian Zhuang struct clk *clk; 21675af25f5SHaojian Zhuang void __iomem *base = data->base; 2170aa0c95fSHaojian Zhuang int i; 2180aa0c95fSHaojian Zhuang 2190aa0c95fSHaojian Zhuang for (i = 0; i < nums; i++) { 2200aa0c95fSHaojian Zhuang clk = clk_register_divider_table(NULL, clks[i].name, 2210aa0c95fSHaojian Zhuang clks[i].parent_name, 2220aa0c95fSHaojian Zhuang clks[i].flags, 2230aa0c95fSHaojian Zhuang base + clks[i].offset, 2240aa0c95fSHaojian Zhuang clks[i].shift, clks[i].width, 2250aa0c95fSHaojian Zhuang clks[i].div_flags, 2260aa0c95fSHaojian Zhuang clks[i].table, 2270aa0c95fSHaojian Zhuang &hisi_clk_lock); 2280aa0c95fSHaojian Zhuang if (IS_ERR(clk)) { 2290aa0c95fSHaojian Zhuang pr_err("%s: failed to register clock %s\n", 2300aa0c95fSHaojian Zhuang __func__, clks[i].name); 2315497f668SJiancheng Xue goto err; 2320aa0c95fSHaojian Zhuang } 2330aa0c95fSHaojian Zhuang 2340aa0c95fSHaojian Zhuang if (clks[i].alias) 2350aa0c95fSHaojian Zhuang clk_register_clkdev(clk, clks[i].alias, NULL); 2360aa0c95fSHaojian Zhuang 23775af25f5SHaojian Zhuang data->clk_data.clks[clks[i].id] = clk; 2380aa0c95fSHaojian Zhuang } 2395497f668SJiancheng Xue 2405497f668SJiancheng Xue return 0; 2415497f668SJiancheng Xue 2425497f668SJiancheng Xue err: 2435497f668SJiancheng Xue while (i--) 2445497f668SJiancheng Xue clk_unregister_divider(data->clk_data.clks[clks[i].id]); 2455497f668SJiancheng Xue 2465497f668SJiancheng Xue return PTR_ERR(clk); 2470aa0c95fSHaojian Zhuang } 248f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_divider); 2490aa0c95fSHaojian Zhuang 2505497f668SJiancheng Xue int hisi_clk_register_gate(const struct hisi_gate_clock *clks, 2518b9dcb6cSZhangfei Gao int nums, struct hisi_clock_data *data) 2528b9dcb6cSZhangfei Gao { 2538b9dcb6cSZhangfei Gao struct clk *clk; 2548b9dcb6cSZhangfei Gao void __iomem *base = data->base; 2558b9dcb6cSZhangfei Gao int i; 2568b9dcb6cSZhangfei Gao 2578b9dcb6cSZhangfei Gao for (i = 0; i < nums; i++) { 2588b9dcb6cSZhangfei Gao clk = clk_register_gate(NULL, clks[i].name, 2598b9dcb6cSZhangfei Gao clks[i].parent_name, 2608b9dcb6cSZhangfei Gao clks[i].flags, 2618b9dcb6cSZhangfei Gao base + clks[i].offset, 2628b9dcb6cSZhangfei Gao clks[i].bit_idx, 2638b9dcb6cSZhangfei Gao clks[i].gate_flags, 2648b9dcb6cSZhangfei Gao &hisi_clk_lock); 2658b9dcb6cSZhangfei Gao if (IS_ERR(clk)) { 2668b9dcb6cSZhangfei Gao pr_err("%s: failed to register clock %s\n", 2678b9dcb6cSZhangfei Gao __func__, clks[i].name); 2685497f668SJiancheng Xue goto err; 2698b9dcb6cSZhangfei Gao } 2708b9dcb6cSZhangfei Gao 2718b9dcb6cSZhangfei Gao if (clks[i].alias) 2728b9dcb6cSZhangfei Gao clk_register_clkdev(clk, clks[i].alias, NULL); 2738b9dcb6cSZhangfei Gao 2748b9dcb6cSZhangfei Gao data->clk_data.clks[clks[i].id] = clk; 2758b9dcb6cSZhangfei Gao } 2765497f668SJiancheng Xue 2775497f668SJiancheng Xue return 0; 2785497f668SJiancheng Xue 2795497f668SJiancheng Xue err: 2805497f668SJiancheng Xue while (i--) 2815497f668SJiancheng Xue clk_unregister_gate(data->clk_data.clks[clks[i].id]); 2825497f668SJiancheng Xue 2835497f668SJiancheng Xue return PTR_ERR(clk); 2848b9dcb6cSZhangfei Gao } 285f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_gate); 2868b9dcb6cSZhangfei Gao 287f6ff57c8SJiancheng Xue void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks, 28875af25f5SHaojian Zhuang int nums, struct hisi_clock_data *data) 2890aa0c95fSHaojian Zhuang { 2900aa0c95fSHaojian Zhuang struct clk *clk; 29175af25f5SHaojian Zhuang void __iomem *base = data->base; 2920aa0c95fSHaojian Zhuang int i; 2930aa0c95fSHaojian Zhuang 2940aa0c95fSHaojian Zhuang for (i = 0; i < nums; i++) { 2950aa0c95fSHaojian Zhuang clk = hisi_register_clkgate_sep(NULL, clks[i].name, 2960aa0c95fSHaojian Zhuang clks[i].parent_name, 2970aa0c95fSHaojian Zhuang clks[i].flags, 2980aa0c95fSHaojian Zhuang base + clks[i].offset, 2990aa0c95fSHaojian Zhuang clks[i].bit_idx, 3000aa0c95fSHaojian Zhuang clks[i].gate_flags, 3010aa0c95fSHaojian Zhuang &hisi_clk_lock); 3020aa0c95fSHaojian Zhuang if (IS_ERR(clk)) { 3030aa0c95fSHaojian Zhuang pr_err("%s: failed to register clock %s\n", 3040aa0c95fSHaojian Zhuang __func__, clks[i].name); 3050aa0c95fSHaojian Zhuang continue; 3060aa0c95fSHaojian Zhuang } 3070aa0c95fSHaojian Zhuang 3080aa0c95fSHaojian Zhuang if (clks[i].alias) 3090aa0c95fSHaojian Zhuang clk_register_clkdev(clk, clks[i].alias, NULL); 3100aa0c95fSHaojian Zhuang 31175af25f5SHaojian Zhuang data->clk_data.clks[clks[i].id] = clk; 3120aa0c95fSHaojian Zhuang } 3130aa0c95fSHaojian Zhuang } 314f6ff57c8SJiancheng Xue EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep); 31572ea4861SBintian Wang 316f6ff57c8SJiancheng Xue void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks, 31772ea4861SBintian Wang int nums, struct hisi_clock_data *data) 31872ea4861SBintian Wang { 31972ea4861SBintian Wang struct clk *clk; 32072ea4861SBintian Wang void __iomem *base = data->base; 32172ea4861SBintian Wang int i; 32272ea4861SBintian Wang 32372ea4861SBintian Wang for (i = 0; i < nums; i++) { 32472ea4861SBintian Wang clk = hi6220_register_clkdiv(NULL, clks[i].name, 32572ea4861SBintian Wang clks[i].parent_name, 32672ea4861SBintian Wang clks[i].flags, 32772ea4861SBintian Wang base + clks[i].offset, 32872ea4861SBintian Wang clks[i].shift, 32972ea4861SBintian Wang clks[i].width, 33072ea4861SBintian Wang clks[i].mask_bit, 33172ea4861SBintian Wang &hisi_clk_lock); 33272ea4861SBintian Wang if (IS_ERR(clk)) { 33372ea4861SBintian Wang pr_err("%s: failed to register clock %s\n", 33472ea4861SBintian Wang __func__, clks[i].name); 33572ea4861SBintian Wang continue; 33672ea4861SBintian Wang } 33772ea4861SBintian Wang 33872ea4861SBintian Wang if (clks[i].alias) 33972ea4861SBintian Wang clk_register_clkdev(clk, clks[i].alias, NULL); 34072ea4861SBintian Wang 34172ea4861SBintian Wang data->clk_data.clks[clks[i].id] = clk; 34272ea4861SBintian Wang } 34372ea4861SBintian Wang } 344