1d41f59fdSChunyan Zhang // SPDX-License-Identifier: GPL-2.0 2d41f59fdSChunyan Zhang // 3d41f59fdSChunyan Zhang // Spreadtrum clock infrastructure 4d41f59fdSChunyan Zhang // 5d41f59fdSChunyan Zhang // Copyright (C) 2017 Spreadtrum, Inc. 6d41f59fdSChunyan Zhang // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 7d41f59fdSChunyan Zhang 8d41f59fdSChunyan Zhang #include <linux/mfd/syscon.h> 9d41f59fdSChunyan Zhang #include <linux/module.h> 10d41f59fdSChunyan Zhang #include <linux/of_address.h> 11d41f59fdSChunyan Zhang #include <linux/of_platform.h> 12d41f59fdSChunyan Zhang #include <linux/regmap.h> 13d41f59fdSChunyan Zhang 14d41f59fdSChunyan Zhang #include "common.h" 15d41f59fdSChunyan Zhang 16d41f59fdSChunyan Zhang static const struct regmap_config sprdclk_regmap_config = { 17d41f59fdSChunyan Zhang .reg_bits = 32, 18d41f59fdSChunyan Zhang .reg_stride = 4, 19d41f59fdSChunyan Zhang .val_bits = 32, 20d41f59fdSChunyan Zhang .fast_io = true, 21d41f59fdSChunyan Zhang }; 22d41f59fdSChunyan Zhang 23d41f59fdSChunyan Zhang static void sprd_clk_set_regmap(const struct sprd_clk_desc *desc, 24d41f59fdSChunyan Zhang struct regmap *regmap) 25d41f59fdSChunyan Zhang { 26d41f59fdSChunyan Zhang int i; 27d41f59fdSChunyan Zhang struct sprd_clk_common *cclk; 28d41f59fdSChunyan Zhang 29d41f59fdSChunyan Zhang for (i = 0; i < desc->num_clk_clks; i++) { 30d41f59fdSChunyan Zhang cclk = desc->clk_clks[i]; 31d41f59fdSChunyan Zhang if (!cclk) 32d41f59fdSChunyan Zhang continue; 33d41f59fdSChunyan Zhang 34d41f59fdSChunyan Zhang cclk->regmap = regmap; 35d41f59fdSChunyan Zhang } 36d41f59fdSChunyan Zhang } 37d41f59fdSChunyan Zhang 38d41f59fdSChunyan Zhang int sprd_clk_regmap_init(struct platform_device *pdev, 39d41f59fdSChunyan Zhang const struct sprd_clk_desc *desc) 40d41f59fdSChunyan Zhang { 41d41f59fdSChunyan Zhang void __iomem *base; 42f95e8c79SChunyan Zhang struct device *dev = &pdev->dev; 4391e6455bSLiang He struct device_node *node = dev->of_node, *np; 44d41f59fdSChunyan Zhang struct regmap *regmap; 45*47d43086SChunyan Zhang struct resource *res; 46*47d43086SChunyan Zhang struct regmap_config reg_config = sprdclk_regmap_config; 47d41f59fdSChunyan Zhang 48d41f59fdSChunyan Zhang if (of_find_property(node, "sprd,syscon", NULL)) { 49d41f59fdSChunyan Zhang regmap = syscon_regmap_lookup_by_phandle(node, "sprd,syscon"); 509629dbdaSBaolin Wang if (IS_ERR(regmap)) { 51d41f59fdSChunyan Zhang pr_err("%s: failed to get syscon regmap\n", __func__); 52d41f59fdSChunyan Zhang return PTR_ERR(regmap); 53d41f59fdSChunyan Zhang } 5491e6455bSLiang He } else if (of_device_is_compatible(np = of_get_parent(node), "syscon") || 5591e6455bSLiang He (of_node_put(np), 0)) { 5691e6455bSLiang He regmap = device_node_to_regmap(np); 5791e6455bSLiang He of_node_put(np); 58f95e8c79SChunyan Zhang if (IS_ERR(regmap)) { 59f95e8c79SChunyan Zhang dev_err(dev, "failed to get regmap from its parent.\n"); 60f95e8c79SChunyan Zhang return PTR_ERR(regmap); 61f95e8c79SChunyan Zhang } 62d41f59fdSChunyan Zhang } else { 63*47d43086SChunyan Zhang base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 6469b39d25SChunyan Zhang if (IS_ERR(base)) 6569b39d25SChunyan Zhang return PTR_ERR(base); 6669b39d25SChunyan Zhang 67*47d43086SChunyan Zhang reg_config.max_register = resource_size(res) - reg_config.reg_stride; 68*47d43086SChunyan Zhang 69d41f59fdSChunyan Zhang regmap = devm_regmap_init_mmio(&pdev->dev, base, 70*47d43086SChunyan Zhang ®_config); 7178f52969SChunyan Zhang if (IS_ERR(regmap)) { 72d41f59fdSChunyan Zhang pr_err("failed to init regmap\n"); 73d41f59fdSChunyan Zhang return PTR_ERR(regmap); 74d41f59fdSChunyan Zhang } 75d41f59fdSChunyan Zhang } 76d41f59fdSChunyan Zhang 77d41f59fdSChunyan Zhang sprd_clk_set_regmap(desc, regmap); 78d41f59fdSChunyan Zhang 79d41f59fdSChunyan Zhang return 0; 80d41f59fdSChunyan Zhang } 81d41f59fdSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_clk_regmap_init); 82d41f59fdSChunyan Zhang 83d41f59fdSChunyan Zhang int sprd_clk_probe(struct device *dev, struct clk_hw_onecell_data *clkhw) 84d41f59fdSChunyan Zhang { 85d41f59fdSChunyan Zhang int i, ret; 86d41f59fdSChunyan Zhang struct clk_hw *hw; 87d41f59fdSChunyan Zhang 88d41f59fdSChunyan Zhang for (i = 0; i < clkhw->num; i++) { 89f6c90df8SStephen Boyd const char *name; 90d41f59fdSChunyan Zhang 91d41f59fdSChunyan Zhang hw = clkhw->hws[i]; 92d41f59fdSChunyan Zhang if (!hw) 93d41f59fdSChunyan Zhang continue; 94d41f59fdSChunyan Zhang 95f6c90df8SStephen Boyd name = hw->init->name; 96d41f59fdSChunyan Zhang ret = devm_clk_hw_register(dev, hw); 97d41f59fdSChunyan Zhang if (ret) { 98d41f59fdSChunyan Zhang dev_err(dev, "Couldn't register clock %d - %s\n", 99f6c90df8SStephen Boyd i, name); 100d41f59fdSChunyan Zhang return ret; 101d41f59fdSChunyan Zhang } 102d41f59fdSChunyan Zhang } 103d41f59fdSChunyan Zhang 104d41f59fdSChunyan Zhang ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clkhw); 105d41f59fdSChunyan Zhang if (ret) 106d41f59fdSChunyan Zhang dev_err(dev, "Failed to add clock provider\n"); 107d41f59fdSChunyan Zhang 108d41f59fdSChunyan Zhang return ret; 109d41f59fdSChunyan Zhang } 110d41f59fdSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_clk_probe); 111d41f59fdSChunyan Zhang 112d41f59fdSChunyan Zhang MODULE_LICENSE("GPL v2"); 113