1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org> 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/mfd/syscon.h> 8 #include <linux/module.h> 9 #include <linux/of_device.h> 10 #include <linux/platform_device.h> 11 #include <linux/regmap.h> 12 13 #include "meson-clkc-utils.h" 14 15 struct clk_hw *meson_clk_hw_get(struct of_phandle_args *clkspec, void *clk_hw_data) 16 { 17 const struct meson_clk_hw_data *data = clk_hw_data; 18 unsigned int idx = clkspec->args[0]; 19 20 if (idx >= data->num) { 21 pr_err("%s: invalid index %u\n", __func__, idx); 22 return ERR_PTR(-EINVAL); 23 } 24 25 return data->hws[idx]; 26 } 27 EXPORT_SYMBOL_NS_GPL(meson_clk_hw_get, "CLK_MESON"); 28 29 static int meson_clkc_init(struct device *dev, struct regmap *map) 30 { 31 const struct meson_clkc_data *data; 32 struct clk_hw *hw; 33 int ret, i; 34 35 data = of_device_get_match_data(dev); 36 if (!data) 37 return -EINVAL; 38 39 if (data->init_count) 40 regmap_multi_reg_write(map, data->init_regs, data->init_count); 41 42 for (i = 0; i < data->hw_clks.num; i++) { 43 hw = data->hw_clks.hws[i]; 44 45 /* array might be sparse */ 46 if (!hw) 47 continue; 48 49 ret = devm_clk_hw_register(dev, hw); 50 if (ret) { 51 dev_err(dev, "registering %s clock failed\n", 52 hw->init->name); 53 return ret; 54 } 55 } 56 57 return devm_of_clk_add_hw_provider(dev, meson_clk_hw_get, (void *)&data->hw_clks); 58 } 59 60 int meson_clkc_syscon_probe(struct platform_device *pdev) 61 { 62 struct device *dev = &pdev->dev; 63 struct device_node *np; 64 struct regmap *map; 65 66 np = of_get_parent(dev->of_node); 67 map = syscon_node_to_regmap(np); 68 of_node_put(np); 69 if (IS_ERR(map)) { 70 dev_err(dev, "failed to get parent syscon regmap\n"); 71 return PTR_ERR(map); 72 } 73 74 return meson_clkc_init(dev, map); 75 } 76 EXPORT_SYMBOL_NS_GPL(meson_clkc_syscon_probe, "CLK_MESON"); 77 78 int meson_clkc_mmio_probe(struct platform_device *pdev) 79 { 80 const struct meson_clkc_data *data; 81 struct device *dev = &pdev->dev; 82 struct resource *res; 83 void __iomem *base; 84 struct regmap *map; 85 struct regmap_config regmap_cfg = { 86 .reg_bits = 32, 87 .val_bits = 32, 88 .reg_stride = 4, 89 }; 90 91 data = of_device_get_match_data(dev); 92 if (!data) 93 return -EINVAL; 94 95 base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 96 if (IS_ERR(base)) 97 return PTR_ERR(base); 98 99 regmap_cfg.max_register = resource_size(res) - regmap_cfg.reg_stride; 100 101 map = devm_regmap_init_mmio(dev, base, ®map_cfg); 102 if (IS_ERR(map)) 103 return PTR_ERR(map); 104 105 return meson_clkc_init(dev, map); 106 } 107 EXPORT_SYMBOL_NS_GPL(meson_clkc_mmio_probe, "CLK_MESON"); 108 109 MODULE_DESCRIPTION("Amlogic Clock Controller Utilities"); 110 MODULE_LICENSE("GPL"); 111 MODULE_IMPORT_NS("CLK_MESON"); 112