689 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2014 MediaTek Inc.4 * Author: James Liao <jamesjj.liao@mediatek.com>5 */6 7#include <linux/bitops.h>8#include <linux/clk-provider.h>9#include <linux/err.h>10#include <linux/io.h>11#include <linux/mfd/syscon.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_address.h>15#include <linux/platform_device.h>16#include <linux/pm_runtime.h>17#include <linux/slab.h>18 19#include "clk-mtk.h"20#include "clk-gate.h"21#include "clk-mux.h"22 23const struct mtk_gate_regs cg_regs_dummy = { 0, 0, 0 };24EXPORT_SYMBOL_GPL(cg_regs_dummy);25 26static int mtk_clk_dummy_enable(struct clk_hw *hw)27{28 return 0;29}30 31static void mtk_clk_dummy_disable(struct clk_hw *hw) { }32 33const struct clk_ops mtk_clk_dummy_ops = {34 .enable = mtk_clk_dummy_enable,35 .disable = mtk_clk_dummy_disable,36};37EXPORT_SYMBOL_GPL(mtk_clk_dummy_ops);38 39static void mtk_init_clk_data(struct clk_hw_onecell_data *clk_data,40 unsigned int clk_num)41{42 int i;43 44 clk_data->num = clk_num;45 46 for (i = 0; i < clk_num; i++)47 clk_data->hws[i] = ERR_PTR(-ENOENT);48}49 50struct clk_hw_onecell_data *mtk_devm_alloc_clk_data(struct device *dev,51 unsigned int clk_num)52{53 struct clk_hw_onecell_data *clk_data;54 55 clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, clk_num),56 GFP_KERNEL);57 if (!clk_data)58 return NULL;59 60 mtk_init_clk_data(clk_data, clk_num);61 62 return clk_data;63}64EXPORT_SYMBOL_GPL(mtk_devm_alloc_clk_data);65 66struct clk_hw_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)67{68 struct clk_hw_onecell_data *clk_data;69 70 clk_data = kzalloc(struct_size(clk_data, hws, clk_num), GFP_KERNEL);71 if (!clk_data)72 return NULL;73 74 mtk_init_clk_data(clk_data, clk_num);75 76 return clk_data;77}78EXPORT_SYMBOL_GPL(mtk_alloc_clk_data);79 80void mtk_free_clk_data(struct clk_hw_onecell_data *clk_data)81{82 kfree(clk_data);83}84EXPORT_SYMBOL_GPL(mtk_free_clk_data);85 86int mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks, int num,87 struct clk_hw_onecell_data *clk_data)88{89 int i;90 struct clk_hw *hw;91 92 if (!clk_data)93 return -ENOMEM;94 95 for (i = 0; i < num; i++) {96 const struct mtk_fixed_clk *rc = &clks[i];97 98 if (!IS_ERR_OR_NULL(clk_data->hws[rc->id])) {99 pr_warn("Trying to register duplicate clock ID: %d\n", rc->id);100 continue;101 }102 103 hw = clk_hw_register_fixed_rate(NULL, rc->name, rc->parent, 0,104 rc->rate);105 106 if (IS_ERR(hw)) {107 pr_err("Failed to register clk %s: %pe\n", rc->name,108 hw);109 goto err;110 }111 112 clk_data->hws[rc->id] = hw;113 }114 115 return 0;116 117err:118 while (--i >= 0) {119 const struct mtk_fixed_clk *rc = &clks[i];120 121 if (IS_ERR_OR_NULL(clk_data->hws[rc->id]))122 continue;123 124 clk_hw_unregister_fixed_rate(clk_data->hws[rc->id]);125 clk_data->hws[rc->id] = ERR_PTR(-ENOENT);126 }127 128 return PTR_ERR(hw);129}130EXPORT_SYMBOL_GPL(mtk_clk_register_fixed_clks);131 132void mtk_clk_unregister_fixed_clks(const struct mtk_fixed_clk *clks, int num,133 struct clk_hw_onecell_data *clk_data)134{135 int i;136 137 if (!clk_data)138 return;139 140 for (i = num; i > 0; i--) {141 const struct mtk_fixed_clk *rc = &clks[i - 1];142 143 if (IS_ERR_OR_NULL(clk_data->hws[rc->id]))144 continue;145 146 clk_hw_unregister_fixed_rate(clk_data->hws[rc->id]);147 clk_data->hws[rc->id] = ERR_PTR(-ENOENT);148 }149}150EXPORT_SYMBOL_GPL(mtk_clk_unregister_fixed_clks);151 152int mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num,153 struct clk_hw_onecell_data *clk_data)154{155 int i;156 struct clk_hw *hw;157 158 if (!clk_data)159 return -ENOMEM;160 161 for (i = 0; i < num; i++) {162 const struct mtk_fixed_factor *ff = &clks[i];163 164 if (!IS_ERR_OR_NULL(clk_data->hws[ff->id])) {165 pr_warn("Trying to register duplicate clock ID: %d\n", ff->id);166 continue;167 }168 169 hw = clk_hw_register_fixed_factor(NULL, ff->name, ff->parent_name,170 ff->flags, ff->mult, ff->div);171 172 if (IS_ERR(hw)) {173 pr_err("Failed to register clk %s: %pe\n", ff->name,174 hw);175 goto err;176 }177 178 clk_data->hws[ff->id] = hw;179 }180 181 return 0;182 183err:184 while (--i >= 0) {185 const struct mtk_fixed_factor *ff = &clks[i];186 187 if (IS_ERR_OR_NULL(clk_data->hws[ff->id]))188 continue;189 190 clk_hw_unregister_fixed_factor(clk_data->hws[ff->id]);191 clk_data->hws[ff->id] = ERR_PTR(-ENOENT);192 }193 194 return PTR_ERR(hw);195}196EXPORT_SYMBOL_GPL(mtk_clk_register_factors);197 198void mtk_clk_unregister_factors(const struct mtk_fixed_factor *clks, int num,199 struct clk_hw_onecell_data *clk_data)200{201 int i;202 203 if (!clk_data)204 return;205 206 for (i = num; i > 0; i--) {207 const struct mtk_fixed_factor *ff = &clks[i - 1];208 209 if (IS_ERR_OR_NULL(clk_data->hws[ff->id]))210 continue;211 212 clk_hw_unregister_fixed_factor(clk_data->hws[ff->id]);213 clk_data->hws[ff->id] = ERR_PTR(-ENOENT);214 }215}216EXPORT_SYMBOL_GPL(mtk_clk_unregister_factors);217 218static struct clk_hw *mtk_clk_register_composite(struct device *dev,219 const struct mtk_composite *mc, void __iomem *base, spinlock_t *lock)220{221 struct clk_hw *hw;222 struct clk_mux *mux = NULL;223 struct clk_gate *gate = NULL;224 struct clk_divider *div = NULL;225 struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL;226 const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL;227 const char * const *parent_names;228 const char *parent;229 int num_parents;230 int ret;231 232 if (mc->mux_shift >= 0) {233 mux = kzalloc(sizeof(*mux), GFP_KERNEL);234 if (!mux)235 return ERR_PTR(-ENOMEM);236 237 mux->reg = base + mc->mux_reg;238 mux->mask = BIT(mc->mux_width) - 1;239 mux->shift = mc->mux_shift;240 mux->lock = lock;241 mux->flags = mc->mux_flags;242 mux_hw = &mux->hw;243 mux_ops = &clk_mux_ops;244 245 parent_names = mc->parent_names;246 num_parents = mc->num_parents;247 } else {248 parent = mc->parent;249 parent_names = &parent;250 num_parents = 1;251 }252 253 if (mc->gate_shift >= 0) {254 gate = kzalloc(sizeof(*gate), GFP_KERNEL);255 if (!gate) {256 ret = -ENOMEM;257 goto err_out;258 }259 260 gate->reg = base + mc->gate_reg;261 gate->bit_idx = mc->gate_shift;262 gate->flags = CLK_GATE_SET_TO_DISABLE;263 gate->lock = lock;264 265 gate_hw = &gate->hw;266 gate_ops = &clk_gate_ops;267 }268 269 if (mc->divider_shift >= 0) {270 div = kzalloc(sizeof(*div), GFP_KERNEL);271 if (!div) {272 ret = -ENOMEM;273 goto err_out;274 }275 276 div->reg = base + mc->divider_reg;277 div->shift = mc->divider_shift;278 div->width = mc->divider_width;279 div->lock = lock;280 281 div_hw = &div->hw;282 div_ops = &clk_divider_ops;283 }284 285 hw = clk_hw_register_composite(dev, mc->name, parent_names, num_parents,286 mux_hw, mux_ops,287 div_hw, div_ops,288 gate_hw, gate_ops,289 mc->flags);290 291 if (IS_ERR(hw)) {292 ret = PTR_ERR(hw);293 goto err_out;294 }295 296 return hw;297err_out:298 kfree(div);299 kfree(gate);300 kfree(mux);301 302 return ERR_PTR(ret);303}304 305static void mtk_clk_unregister_composite(struct clk_hw *hw)306{307 struct clk_composite *composite;308 struct clk_mux *mux = NULL;309 struct clk_gate *gate = NULL;310 struct clk_divider *div = NULL;311 312 if (!hw)313 return;314 315 composite = to_clk_composite(hw);316 if (composite->mux_hw)317 mux = to_clk_mux(composite->mux_hw);318 if (composite->gate_hw)319 gate = to_clk_gate(composite->gate_hw);320 if (composite->rate_hw)321 div = to_clk_divider(composite->rate_hw);322 323 clk_hw_unregister_composite(hw);324 kfree(div);325 kfree(gate);326 kfree(mux);327}328 329int mtk_clk_register_composites(struct device *dev,330 const struct mtk_composite *mcs, int num,331 void __iomem *base, spinlock_t *lock,332 struct clk_hw_onecell_data *clk_data)333{334 struct clk_hw *hw;335 int i;336 337 if (!clk_data)338 return -ENOMEM;339 340 for (i = 0; i < num; i++) {341 const struct mtk_composite *mc = &mcs[i];342 343 if (!IS_ERR_OR_NULL(clk_data->hws[mc->id])) {344 pr_warn("Trying to register duplicate clock ID: %d\n",345 mc->id);346 continue;347 }348 349 hw = mtk_clk_register_composite(dev, mc, base, lock);350 351 if (IS_ERR(hw)) {352 pr_err("Failed to register clk %s: %pe\n", mc->name,353 hw);354 goto err;355 }356 357 clk_data->hws[mc->id] = hw;358 }359 360 return 0;361 362err:363 while (--i >= 0) {364 const struct mtk_composite *mc = &mcs[i];365 366 if (IS_ERR_OR_NULL(clk_data->hws[mcs->id]))367 continue;368 369 mtk_clk_unregister_composite(clk_data->hws[mc->id]);370 clk_data->hws[mc->id] = ERR_PTR(-ENOENT);371 }372 373 return PTR_ERR(hw);374}375EXPORT_SYMBOL_GPL(mtk_clk_register_composites);376 377void mtk_clk_unregister_composites(const struct mtk_composite *mcs, int num,378 struct clk_hw_onecell_data *clk_data)379{380 int i;381 382 if (!clk_data)383 return;384 385 for (i = num; i > 0; i--) {386 const struct mtk_composite *mc = &mcs[i - 1];387 388 if (IS_ERR_OR_NULL(clk_data->hws[mc->id]))389 continue;390 391 mtk_clk_unregister_composite(clk_data->hws[mc->id]);392 clk_data->hws[mc->id] = ERR_PTR(-ENOENT);393 }394}395EXPORT_SYMBOL_GPL(mtk_clk_unregister_composites);396 397int mtk_clk_register_dividers(struct device *dev,398 const struct mtk_clk_divider *mcds, int num,399 void __iomem *base, spinlock_t *lock,400 struct clk_hw_onecell_data *clk_data)401{402 struct clk_hw *hw;403 int i;404 405 if (!clk_data)406 return -ENOMEM;407 408 for (i = 0; i < num; i++) {409 const struct mtk_clk_divider *mcd = &mcds[i];410 411 if (!IS_ERR_OR_NULL(clk_data->hws[mcd->id])) {412 pr_warn("Trying to register duplicate clock ID: %d\n",413 mcd->id);414 continue;415 }416 417 hw = clk_hw_register_divider(dev, mcd->name, mcd->parent_name,418 mcd->flags, base + mcd->div_reg, mcd->div_shift,419 mcd->div_width, mcd->clk_divider_flags, lock);420 421 if (IS_ERR(hw)) {422 pr_err("Failed to register clk %s: %pe\n", mcd->name,423 hw);424 goto err;425 }426 427 clk_data->hws[mcd->id] = hw;428 }429 430 return 0;431 432err:433 while (--i >= 0) {434 const struct mtk_clk_divider *mcd = &mcds[i];435 436 if (IS_ERR_OR_NULL(clk_data->hws[mcd->id]))437 continue;438 439 clk_hw_unregister_divider(clk_data->hws[mcd->id]);440 clk_data->hws[mcd->id] = ERR_PTR(-ENOENT);441 }442 443 return PTR_ERR(hw);444}445EXPORT_SYMBOL_GPL(mtk_clk_register_dividers);446 447void mtk_clk_unregister_dividers(const struct mtk_clk_divider *mcds, int num,448 struct clk_hw_onecell_data *clk_data)449{450 int i;451 452 if (!clk_data)453 return;454 455 for (i = num; i > 0; i--) {456 const struct mtk_clk_divider *mcd = &mcds[i - 1];457 458 if (IS_ERR_OR_NULL(clk_data->hws[mcd->id]))459 continue;460 461 clk_hw_unregister_divider(clk_data->hws[mcd->id]);462 clk_data->hws[mcd->id] = ERR_PTR(-ENOENT);463 }464}465EXPORT_SYMBOL_GPL(mtk_clk_unregister_dividers);466 467static int __mtk_clk_simple_probe(struct platform_device *pdev,468 struct device_node *node)469{470 const struct platform_device_id *id;471 const struct mtk_clk_desc *mcd;472 struct clk_hw_onecell_data *clk_data;473 void __iomem *base = NULL;474 int num_clks, r;475 476 mcd = device_get_match_data(&pdev->dev);477 if (!mcd) {478 /* Clock driver wasn't registered from devicetree */479 id = platform_get_device_id(pdev);480 if (id)481 mcd = (const struct mtk_clk_desc *)id->driver_data;482 483 if (!mcd)484 return -EINVAL;485 }486 487 /* Composite and divider clocks needs us to pass iomem pointer */488 if (mcd->composite_clks || mcd->divider_clks) {489 if (!mcd->shared_io)490 base = devm_platform_ioremap_resource(pdev, 0);491 else492 base = of_iomap(node, 0);493 494 if (IS_ERR_OR_NULL(base))495 return IS_ERR(base) ? PTR_ERR(base) : -ENOMEM;496 }497 498 499 if (mcd->need_runtime_pm) {500 devm_pm_runtime_enable(&pdev->dev);501 /*502 * Do a pm_runtime_resume_and_get() to workaround a possible503 * deadlock between clk_register() and the genpd framework.504 */505 r = pm_runtime_resume_and_get(&pdev->dev);506 if (r)507 return r;508 }509 510 /* Calculate how many clk_hw_onecell_data entries to allocate */511 num_clks = mcd->num_clks + mcd->num_composite_clks;512 num_clks += mcd->num_fixed_clks + mcd->num_factor_clks;513 num_clks += mcd->num_mux_clks + mcd->num_divider_clks;514 515 clk_data = mtk_alloc_clk_data(num_clks);516 if (!clk_data) {517 r = -ENOMEM;518 goto free_base;519 }520 521 if (mcd->fixed_clks) {522 r = mtk_clk_register_fixed_clks(mcd->fixed_clks,523 mcd->num_fixed_clks, clk_data);524 if (r)525 goto free_data;526 }527 528 if (mcd->factor_clks) {529 r = mtk_clk_register_factors(mcd->factor_clks,530 mcd->num_factor_clks, clk_data);531 if (r)532 goto unregister_fixed_clks;533 }534 535 if (mcd->mux_clks) {536 r = mtk_clk_register_muxes(&pdev->dev, mcd->mux_clks,537 mcd->num_mux_clks, node,538 mcd->clk_lock, clk_data);539 if (r)540 goto unregister_factors;541 }542 543 if (mcd->composite_clks) {544 /* We don't check composite_lock because it's optional */545 r = mtk_clk_register_composites(&pdev->dev,546 mcd->composite_clks,547 mcd->num_composite_clks,548 base, mcd->clk_lock, clk_data);549 if (r)550 goto unregister_muxes;551 }552 553 if (mcd->divider_clks) {554 r = mtk_clk_register_dividers(&pdev->dev,555 mcd->divider_clks,556 mcd->num_divider_clks,557 base, mcd->clk_lock, clk_data);558 if (r)559 goto unregister_composites;560 }561 562 if (mcd->clks) {563 r = mtk_clk_register_gates(&pdev->dev, node, mcd->clks,564 mcd->num_clks, clk_data);565 if (r)566 goto unregister_dividers;567 }568 569 if (mcd->clk_notifier_func) {570 struct clk *mfg_mux = clk_data->hws[mcd->mfg_clk_idx]->clk;571 572 r = mcd->clk_notifier_func(&pdev->dev, mfg_mux);573 if (r)574 goto unregister_clks;575 }576 577 r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);578 if (r)579 goto unregister_clks;580 581 platform_set_drvdata(pdev, clk_data);582 583 if (mcd->rst_desc) {584 r = mtk_register_reset_controller_with_dev(&pdev->dev,585 mcd->rst_desc);586 if (r)587 goto unregister_clks;588 }589 590 if (mcd->need_runtime_pm)591 pm_runtime_put(&pdev->dev);592 593 return r;594 595unregister_clks:596 if (mcd->clks)597 mtk_clk_unregister_gates(mcd->clks, mcd->num_clks, clk_data);598unregister_dividers:599 if (mcd->divider_clks)600 mtk_clk_unregister_dividers(mcd->divider_clks,601 mcd->num_divider_clks, clk_data);602unregister_composites:603 if (mcd->composite_clks)604 mtk_clk_unregister_composites(mcd->composite_clks,605 mcd->num_composite_clks, clk_data);606unregister_muxes:607 if (mcd->mux_clks)608 mtk_clk_unregister_muxes(mcd->mux_clks,609 mcd->num_mux_clks, clk_data);610unregister_factors:611 if (mcd->factor_clks)612 mtk_clk_unregister_factors(mcd->factor_clks,613 mcd->num_factor_clks, clk_data);614unregister_fixed_clks:615 if (mcd->fixed_clks)616 mtk_clk_unregister_fixed_clks(mcd->fixed_clks,617 mcd->num_fixed_clks, clk_data);618free_data:619 mtk_free_clk_data(clk_data);620free_base:621 if (mcd->shared_io && base)622 iounmap(base);623 624 if (mcd->need_runtime_pm)625 pm_runtime_put(&pdev->dev);626 return r;627}628 629static void __mtk_clk_simple_remove(struct platform_device *pdev,630 struct device_node *node)631{632 struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);633 const struct mtk_clk_desc *mcd = device_get_match_data(&pdev->dev);634 635 of_clk_del_provider(node);636 if (mcd->clks)637 mtk_clk_unregister_gates(mcd->clks, mcd->num_clks, clk_data);638 if (mcd->divider_clks)639 mtk_clk_unregister_dividers(mcd->divider_clks,640 mcd->num_divider_clks, clk_data);641 if (mcd->composite_clks)642 mtk_clk_unregister_composites(mcd->composite_clks,643 mcd->num_composite_clks, clk_data);644 if (mcd->mux_clks)645 mtk_clk_unregister_muxes(mcd->mux_clks,646 mcd->num_mux_clks, clk_data);647 if (mcd->factor_clks)648 mtk_clk_unregister_factors(mcd->factor_clks,649 mcd->num_factor_clks, clk_data);650 if (mcd->fixed_clks)651 mtk_clk_unregister_fixed_clks(mcd->fixed_clks,652 mcd->num_fixed_clks, clk_data);653 mtk_free_clk_data(clk_data);654}655 656int mtk_clk_pdev_probe(struct platform_device *pdev)657{658 struct device *dev = &pdev->dev;659 struct device_node *node = dev->parent->of_node;660 661 return __mtk_clk_simple_probe(pdev, node);662}663EXPORT_SYMBOL_GPL(mtk_clk_pdev_probe);664 665int mtk_clk_simple_probe(struct platform_device *pdev)666{667 struct device_node *node = pdev->dev.of_node;668 669 return __mtk_clk_simple_probe(pdev, node);670}671EXPORT_SYMBOL_GPL(mtk_clk_simple_probe);672 673void mtk_clk_pdev_remove(struct platform_device *pdev)674{675 struct device *dev = &pdev->dev;676 struct device_node *node = dev->parent->of_node;677 678 __mtk_clk_simple_remove(pdev, node);679}680EXPORT_SYMBOL_GPL(mtk_clk_pdev_remove);681 682void mtk_clk_simple_remove(struct platform_device *pdev)683{684 __mtk_clk_simple_remove(pdev, pdev->dev.of_node);685}686EXPORT_SYMBOL_GPL(mtk_clk_simple_remove);687 688MODULE_LICENSE("GPL");689