344 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Hisilicon clock driver4 *5 * Copyright (c) 2012-2013 Hisilicon Limited.6 * Copyright (c) 2012-2013 Linaro Limited.7 *8 * Author: Haojian Zhuang <haojian.zhuang@linaro.org>9 * Xin Li <li.xin@linaro.org>10 */11 12#include <linux/kernel.h>13#include <linux/clkdev.h>14#include <linux/clk-provider.h>15#include <linux/delay.h>16#include <linux/io.h>17#include <linux/of.h>18#include <linux/of_address.h>19#include <linux/platform_device.h>20#include <linux/slab.h>21 22#include "clk.h"23 24static DEFINE_SPINLOCK(hisi_clk_lock);25 26struct hisi_clock_data *hisi_clk_alloc(struct platform_device *pdev,27 int nr_clks)28{29 struct hisi_clock_data *clk_data;30 struct resource *res;31 struct clk **clk_table;32 33 clk_data = devm_kmalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);34 if (!clk_data)35 return NULL;36 37 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);38 if (!res)39 return NULL;40 clk_data->base = devm_ioremap(&pdev->dev,41 res->start, resource_size(res));42 if (!clk_data->base)43 return NULL;44 45 clk_table = devm_kmalloc_array(&pdev->dev, nr_clks,46 sizeof(*clk_table),47 GFP_KERNEL);48 if (!clk_table)49 return NULL;50 51 clk_data->clk_data.clks = clk_table;52 clk_data->clk_data.clk_num = nr_clks;53 54 return clk_data;55}56EXPORT_SYMBOL_GPL(hisi_clk_alloc);57 58struct hisi_clock_data *hisi_clk_init(struct device_node *np,59 int nr_clks)60{61 struct hisi_clock_data *clk_data;62 struct clk **clk_table;63 void __iomem *base;64 65 base = of_iomap(np, 0);66 if (!base) {67 pr_err("%s: failed to map clock registers\n", __func__);68 goto err;69 }70 71 clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);72 if (!clk_data)73 goto err;74 75 clk_data->base = base;76 clk_table = kcalloc(nr_clks, sizeof(*clk_table), GFP_KERNEL);77 if (!clk_table)78 goto err_data;79 80 clk_data->clk_data.clks = clk_table;81 clk_data->clk_data.clk_num = nr_clks;82 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data->clk_data);83 return clk_data;84err_data:85 kfree(clk_data);86err:87 return NULL;88}89EXPORT_SYMBOL_GPL(hisi_clk_init);90 91int hisi_clk_register_fixed_rate(const struct hisi_fixed_rate_clock *clks,92 int nums, struct hisi_clock_data *data)93{94 struct clk *clk;95 int i;96 97 for (i = 0; i < nums; i++) {98 clk = clk_register_fixed_rate(NULL, clks[i].name,99 clks[i].parent_name,100 clks[i].flags,101 clks[i].fixed_rate);102 if (IS_ERR(clk)) {103 pr_err("%s: failed to register clock %s\n",104 __func__, clks[i].name);105 goto err;106 }107 data->clk_data.clks[clks[i].id] = clk;108 }109 110 return 0;111 112err:113 while (i--)114 clk_unregister_fixed_rate(data->clk_data.clks[clks[i].id]);115 116 return PTR_ERR(clk);117}118EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_rate);119 120int hisi_clk_register_fixed_factor(const struct hisi_fixed_factor_clock *clks,121 int nums,122 struct hisi_clock_data *data)123{124 struct clk *clk;125 int i;126 127 for (i = 0; i < nums; i++) {128 clk = clk_register_fixed_factor(NULL, clks[i].name,129 clks[i].parent_name,130 clks[i].flags, clks[i].mult,131 clks[i].div);132 if (IS_ERR(clk)) {133 pr_err("%s: failed to register clock %s\n",134 __func__, clks[i].name);135 goto err;136 }137 data->clk_data.clks[clks[i].id] = clk;138 }139 140 return 0;141 142err:143 while (i--)144 clk_unregister_fixed_factor(data->clk_data.clks[clks[i].id]);145 146 return PTR_ERR(clk);147}148EXPORT_SYMBOL_GPL(hisi_clk_register_fixed_factor);149 150int hisi_clk_register_mux(const struct hisi_mux_clock *clks,151 int nums, struct hisi_clock_data *data)152{153 struct clk *clk;154 void __iomem *base = data->base;155 int i;156 157 for (i = 0; i < nums; i++) {158 u32 mask = BIT(clks[i].width) - 1;159 160 clk = clk_register_mux_table(NULL, clks[i].name,161 clks[i].parent_names,162 clks[i].num_parents, clks[i].flags,163 base + clks[i].offset, clks[i].shift,164 mask, clks[i].mux_flags,165 clks[i].table, &hisi_clk_lock);166 if (IS_ERR(clk)) {167 pr_err("%s: failed to register clock %s\n",168 __func__, clks[i].name);169 goto err;170 }171 172 if (clks[i].alias)173 clk_register_clkdev(clk, clks[i].alias, NULL);174 175 data->clk_data.clks[clks[i].id] = clk;176 }177 178 return 0;179 180err:181 while (i--)182 clk_unregister_mux(data->clk_data.clks[clks[i].id]);183 184 return PTR_ERR(clk);185}186EXPORT_SYMBOL_GPL(hisi_clk_register_mux);187 188int hisi_clk_register_phase(struct device *dev,189 const struct hisi_phase_clock *clks,190 int nums, struct hisi_clock_data *data)191{192 void __iomem *base = data->base;193 struct clk *clk;194 int i;195 196 for (i = 0; i < nums; i++) {197 clk = clk_register_hisi_phase(dev, &clks[i], base,198 &hisi_clk_lock);199 if (IS_ERR(clk)) {200 pr_err("%s: failed to register clock %s\n", __func__,201 clks[i].name);202 return PTR_ERR(clk);203 }204 205 data->clk_data.clks[clks[i].id] = clk;206 }207 208 return 0;209}210EXPORT_SYMBOL_GPL(hisi_clk_register_phase);211 212int hisi_clk_register_divider(const struct hisi_divider_clock *clks,213 int nums, struct hisi_clock_data *data)214{215 struct clk *clk;216 void __iomem *base = data->base;217 int i;218 219 for (i = 0; i < nums; i++) {220 clk = clk_register_divider_table(NULL, clks[i].name,221 clks[i].parent_name,222 clks[i].flags,223 base + clks[i].offset,224 clks[i].shift, clks[i].width,225 clks[i].div_flags,226 clks[i].table,227 &hisi_clk_lock);228 if (IS_ERR(clk)) {229 pr_err("%s: failed to register clock %s\n",230 __func__, clks[i].name);231 goto err;232 }233 234 if (clks[i].alias)235 clk_register_clkdev(clk, clks[i].alias, NULL);236 237 data->clk_data.clks[clks[i].id] = clk;238 }239 240 return 0;241 242err:243 while (i--)244 clk_unregister_divider(data->clk_data.clks[clks[i].id]);245 246 return PTR_ERR(clk);247}248EXPORT_SYMBOL_GPL(hisi_clk_register_divider);249 250int hisi_clk_register_gate(const struct hisi_gate_clock *clks,251 int nums, struct hisi_clock_data *data)252{253 struct clk *clk;254 void __iomem *base = data->base;255 int i;256 257 for (i = 0; i < nums; i++) {258 clk = clk_register_gate(NULL, clks[i].name,259 clks[i].parent_name,260 clks[i].flags,261 base + clks[i].offset,262 clks[i].bit_idx,263 clks[i].gate_flags,264 &hisi_clk_lock);265 if (IS_ERR(clk)) {266 pr_err("%s: failed to register clock %s\n",267 __func__, clks[i].name);268 goto err;269 }270 271 if (clks[i].alias)272 clk_register_clkdev(clk, clks[i].alias, NULL);273 274 data->clk_data.clks[clks[i].id] = clk;275 }276 277 return 0;278 279err:280 while (i--)281 clk_unregister_gate(data->clk_data.clks[clks[i].id]);282 283 return PTR_ERR(clk);284}285EXPORT_SYMBOL_GPL(hisi_clk_register_gate);286 287void hisi_clk_register_gate_sep(const struct hisi_gate_clock *clks,288 int nums, struct hisi_clock_data *data)289{290 struct clk *clk;291 void __iomem *base = data->base;292 int i;293 294 for (i = 0; i < nums; i++) {295 clk = hisi_register_clkgate_sep(NULL, clks[i].name,296 clks[i].parent_name,297 clks[i].flags,298 base + clks[i].offset,299 clks[i].bit_idx,300 clks[i].gate_flags,301 &hisi_clk_lock);302 if (IS_ERR(clk)) {303 pr_err("%s: failed to register clock %s\n",304 __func__, clks[i].name);305 continue;306 }307 308 if (clks[i].alias)309 clk_register_clkdev(clk, clks[i].alias, NULL);310 311 data->clk_data.clks[clks[i].id] = clk;312 }313}314EXPORT_SYMBOL_GPL(hisi_clk_register_gate_sep);315 316void __init hi6220_clk_register_divider(const struct hi6220_divider_clock *clks,317 int nums, struct hisi_clock_data *data)318{319 struct clk *clk;320 void __iomem *base = data->base;321 int i;322 323 for (i = 0; i < nums; i++) {324 clk = hi6220_register_clkdiv(NULL, clks[i].name,325 clks[i].parent_name,326 clks[i].flags,327 base + clks[i].offset,328 clks[i].shift,329 clks[i].width,330 clks[i].mask_bit,331 &hisi_clk_lock);332 if (IS_ERR(clk)) {333 pr_err("%s: failed to register clock %s\n",334 __func__, clks[i].name);335 continue;336 }337 338 if (clks[i].alias)339 clk_register_clkdev(clk, clks[i].alias, NULL);340 341 data->clk_data.clks[clks[i].id] = clk;342 }343}344