15ec8cbbcSInochi Amaoto // SPDX-License-Identifier: GPL-2.0-only 25ec8cbbcSInochi Amaoto 3*99669468SYixun Lan #include <linux/clk-provider.h> 4*99669468SYixun Lan #include <linux/device/devres.h> 5*99669468SYixun Lan #include <linux/mfd/syscon.h> 65ec8cbbcSInochi Amaoto #include <linux/module.h> 7*99669468SYixun Lan #include <linux/of.h> 8*99669468SYixun Lan #include <linux/slab.h> 9*99669468SYixun Lan #include <soc/spacemit/ccu.h> 10*99669468SYixun Lan 11*99669468SYixun Lan #include "ccu_common.h" 12*99669468SYixun Lan 13*99669468SYixun Lan static DEFINE_IDA(auxiliary_ids); 14*99669468SYixun Lan static int spacemit_ccu_register(struct device *dev, 15*99669468SYixun Lan struct regmap *regmap, 16*99669468SYixun Lan struct regmap *lock_regmap, 17*99669468SYixun Lan const struct spacemit_ccu_data *data) 18*99669468SYixun Lan { 19*99669468SYixun Lan struct clk_hw_onecell_data *clk_data; 20*99669468SYixun Lan int i, ret; 21*99669468SYixun Lan 22*99669468SYixun Lan /* Nothing to do if the CCU does not implement any clocks */ 23*99669468SYixun Lan if (!data->hws) 24*99669468SYixun Lan return 0; 25*99669468SYixun Lan 26*99669468SYixun Lan clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, data->num), 27*99669468SYixun Lan GFP_KERNEL); 28*99669468SYixun Lan if (!clk_data) 29*99669468SYixun Lan return -ENOMEM; 30*99669468SYixun Lan 31*99669468SYixun Lan clk_data->num = data->num; 32*99669468SYixun Lan 33*99669468SYixun Lan for (i = 0; i < data->num; i++) { 34*99669468SYixun Lan struct clk_hw *hw = data->hws[i]; 35*99669468SYixun Lan struct ccu_common *common; 36*99669468SYixun Lan const char *name; 37*99669468SYixun Lan 38*99669468SYixun Lan if (!hw) { 39*99669468SYixun Lan clk_data->hws[i] = ERR_PTR(-ENOENT); 40*99669468SYixun Lan continue; 41*99669468SYixun Lan } 42*99669468SYixun Lan 43*99669468SYixun Lan name = hw->init->name; 44*99669468SYixun Lan 45*99669468SYixun Lan common = hw_to_ccu_common(hw); 46*99669468SYixun Lan common->regmap = regmap; 47*99669468SYixun Lan common->lock_regmap = lock_regmap; 48*99669468SYixun Lan 49*99669468SYixun Lan ret = devm_clk_hw_register(dev, hw); 50*99669468SYixun Lan if (ret) { 51*99669468SYixun Lan dev_err(dev, "Cannot register clock %d - %s\n", 52*99669468SYixun Lan i, name); 53*99669468SYixun Lan return ret; 54*99669468SYixun Lan } 55*99669468SYixun Lan 56*99669468SYixun Lan clk_data->hws[i] = hw; 57*99669468SYixun Lan } 58*99669468SYixun Lan 59*99669468SYixun Lan ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); 60*99669468SYixun Lan if (ret) 61*99669468SYixun Lan dev_err(dev, "failed to add clock hardware provider (%d)\n", ret); 62*99669468SYixun Lan 63*99669468SYixun Lan return ret; 64*99669468SYixun Lan } 65*99669468SYixun Lan 66*99669468SYixun Lan static void spacemit_cadev_release(struct device *dev) 67*99669468SYixun Lan { 68*99669468SYixun Lan struct auxiliary_device *adev = to_auxiliary_dev(dev); 69*99669468SYixun Lan 70*99669468SYixun Lan ida_free(&auxiliary_ids, adev->id); 71*99669468SYixun Lan kfree(to_spacemit_ccu_adev(adev)); 72*99669468SYixun Lan } 73*99669468SYixun Lan 74*99669468SYixun Lan static void spacemit_adev_unregister(void *data) 75*99669468SYixun Lan { 76*99669468SYixun Lan struct auxiliary_device *adev = data; 77*99669468SYixun Lan 78*99669468SYixun Lan auxiliary_device_delete(adev); 79*99669468SYixun Lan auxiliary_device_uninit(adev); 80*99669468SYixun Lan } 81*99669468SYixun Lan 82*99669468SYixun Lan static int spacemit_ccu_reset_register(struct device *dev, 83*99669468SYixun Lan struct regmap *regmap, 84*99669468SYixun Lan const char *reset_name) 85*99669468SYixun Lan { 86*99669468SYixun Lan struct spacemit_ccu_adev *cadev; 87*99669468SYixun Lan struct auxiliary_device *adev; 88*99669468SYixun Lan int ret; 89*99669468SYixun Lan 90*99669468SYixun Lan /* Nothing to do if the CCU does not implement a reset controller */ 91*99669468SYixun Lan if (!reset_name) 92*99669468SYixun Lan return 0; 93*99669468SYixun Lan 94*99669468SYixun Lan cadev = kzalloc(sizeof(*cadev), GFP_KERNEL); 95*99669468SYixun Lan if (!cadev) 96*99669468SYixun Lan return -ENOMEM; 97*99669468SYixun Lan 98*99669468SYixun Lan cadev->regmap = regmap; 99*99669468SYixun Lan 100*99669468SYixun Lan adev = &cadev->adev; 101*99669468SYixun Lan adev->name = reset_name; 102*99669468SYixun Lan adev->dev.parent = dev; 103*99669468SYixun Lan adev->dev.release = spacemit_cadev_release; 104*99669468SYixun Lan adev->dev.of_node = dev->of_node; 105*99669468SYixun Lan ret = ida_alloc(&auxiliary_ids, GFP_KERNEL); 106*99669468SYixun Lan if (ret < 0) 107*99669468SYixun Lan goto err_free_cadev; 108*99669468SYixun Lan adev->id = ret; 109*99669468SYixun Lan 110*99669468SYixun Lan ret = auxiliary_device_init(adev); 111*99669468SYixun Lan if (ret) 112*99669468SYixun Lan goto err_free_aux_id; 113*99669468SYixun Lan 114*99669468SYixun Lan ret = auxiliary_device_add(adev); 115*99669468SYixun Lan if (ret) { 116*99669468SYixun Lan auxiliary_device_uninit(adev); 117*99669468SYixun Lan return ret; 118*99669468SYixun Lan } 119*99669468SYixun Lan 120*99669468SYixun Lan return devm_add_action_or_reset(dev, spacemit_adev_unregister, adev); 121*99669468SYixun Lan 122*99669468SYixun Lan err_free_aux_id: 123*99669468SYixun Lan ida_free(&auxiliary_ids, adev->id); 124*99669468SYixun Lan err_free_cadev: 125*99669468SYixun Lan kfree(cadev); 126*99669468SYixun Lan 127*99669468SYixun Lan return ret; 128*99669468SYixun Lan } 129*99669468SYixun Lan 130*99669468SYixun Lan int spacemit_ccu_probe(struct platform_device *pdev, const char *compat) 131*99669468SYixun Lan { 132*99669468SYixun Lan struct regmap *base_regmap, *lock_regmap = NULL; 133*99669468SYixun Lan const struct spacemit_ccu_data *data; 134*99669468SYixun Lan struct device *dev = &pdev->dev; 135*99669468SYixun Lan int ret; 136*99669468SYixun Lan 137*99669468SYixun Lan base_regmap = device_node_to_regmap(dev->of_node); 138*99669468SYixun Lan if (IS_ERR(base_regmap)) 139*99669468SYixun Lan return dev_err_probe(dev, PTR_ERR(base_regmap), 140*99669468SYixun Lan "failed to get regmap\n"); 141*99669468SYixun Lan 142*99669468SYixun Lan /* 143*99669468SYixun Lan * The lock status of PLLs locate in MPMU region, while PLLs themselves 144*99669468SYixun Lan * are in APBS region. Reference to MPMU syscon is required to check PLL 145*99669468SYixun Lan * status. 146*99669468SYixun Lan */ 147*99669468SYixun Lan if (compat && of_device_is_compatible(dev->of_node, compat)) { 148*99669468SYixun Lan struct device_node *mpmu = of_parse_phandle(dev->of_node, 149*99669468SYixun Lan "spacemit,mpmu", 0); 150*99669468SYixun Lan if (!mpmu) 151*99669468SYixun Lan return dev_err_probe(dev, -ENODEV, 152*99669468SYixun Lan "Cannot parse MPMU region\n"); 153*99669468SYixun Lan 154*99669468SYixun Lan lock_regmap = device_node_to_regmap(mpmu); 155*99669468SYixun Lan of_node_put(mpmu); 156*99669468SYixun Lan 157*99669468SYixun Lan if (IS_ERR(lock_regmap)) 158*99669468SYixun Lan return dev_err_probe(dev, PTR_ERR(lock_regmap), 159*99669468SYixun Lan "failed to get lock regmap\n"); 160*99669468SYixun Lan } 161*99669468SYixun Lan 162*99669468SYixun Lan data = of_device_get_match_data(dev); 163*99669468SYixun Lan 164*99669468SYixun Lan ret = spacemit_ccu_register(dev, base_regmap, lock_regmap, data); 165*99669468SYixun Lan if (ret) 166*99669468SYixun Lan return dev_err_probe(dev, ret, "failed to register clocks\n"); 167*99669468SYixun Lan 168*99669468SYixun Lan ret = spacemit_ccu_reset_register(dev, base_regmap, data->reset_name); 169*99669468SYixun Lan if (ret) 170*99669468SYixun Lan return dev_err_probe(dev, ret, "failed to register resets\n"); 171*99669468SYixun Lan 172*99669468SYixun Lan return 0; 173*99669468SYixun Lan } 174*99669468SYixun Lan EXPORT_SYMBOL_NS_GPL(spacemit_ccu_probe, "CLK_SPACEMIT"); 1755ec8cbbcSInochi Amaoto 1765ec8cbbcSInochi Amaoto MODULE_DESCRIPTION("SpacemiT CCU common clock driver"); 1775ec8cbbcSInochi Amaoto MODULE_LICENSE("GPL"); 178