0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/init.h>
0011 #include <linux/clk.h>
0012 #include <linux/clk-provider.h>
0013 #include <linux/slab.h>
0014 #include <linux/io.h>
0015 #include <linux/of.h>
0016 #include <linux/of_address.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/pm_runtime.h>
0019 #include <linux/clk/ti.h>
0020
0021 #include "clock.h"
0022
0023 #define DRA7_ATL_INSTANCES 4
0024
0025 #define DRA7_ATL_PPMR_REG(id) (0x200 + (id * 0x80))
0026 #define DRA7_ATL_BBSR_REG(id) (0x204 + (id * 0x80))
0027 #define DRA7_ATL_ATLCR_REG(id) (0x208 + (id * 0x80))
0028 #define DRA7_ATL_SWEN_REG(id) (0x210 + (id * 0x80))
0029 #define DRA7_ATL_BWSMUX_REG(id) (0x214 + (id * 0x80))
0030 #define DRA7_ATL_AWSMUX_REG(id) (0x218 + (id * 0x80))
0031 #define DRA7_ATL_PCLKMUX_REG(id) (0x21c + (id * 0x80))
0032
0033 #define DRA7_ATL_SWEN BIT(0)
0034 #define DRA7_ATL_DIVIDER_MASK (0x1f)
0035 #define DRA7_ATL_PCLKMUX BIT(0)
0036 struct dra7_atl_clock_info;
0037
0038 struct dra7_atl_desc {
0039 struct clk *clk;
0040 struct clk_hw hw;
0041 struct dra7_atl_clock_info *cinfo;
0042 int id;
0043
0044 bool probed;
0045 bool valid;
0046 bool enabled;
0047 u32 bws;
0048 u32 aws;
0049 u32 divider;
0050 };
0051
0052 struct dra7_atl_clock_info {
0053 struct device *dev;
0054 void __iomem *iobase;
0055
0056 struct dra7_atl_desc *cdesc;
0057 };
0058
0059 #define to_atl_desc(_hw) container_of(_hw, struct dra7_atl_desc, hw)
0060
0061 static inline void atl_write(struct dra7_atl_clock_info *cinfo, u32 reg,
0062 u32 val)
0063 {
0064 __raw_writel(val, cinfo->iobase + reg);
0065 }
0066
0067 static inline int atl_read(struct dra7_atl_clock_info *cinfo, u32 reg)
0068 {
0069 return __raw_readl(cinfo->iobase + reg);
0070 }
0071
0072 static int atl_clk_enable(struct clk_hw *hw)
0073 {
0074 struct dra7_atl_desc *cdesc = to_atl_desc(hw);
0075
0076 if (!cdesc->probed)
0077 goto out;
0078
0079 if (unlikely(!cdesc->valid))
0080 dev_warn(cdesc->cinfo->dev, "atl%d has not been configured\n",
0081 cdesc->id);
0082 pm_runtime_get_sync(cdesc->cinfo->dev);
0083
0084 atl_write(cdesc->cinfo, DRA7_ATL_ATLCR_REG(cdesc->id),
0085 cdesc->divider - 1);
0086 atl_write(cdesc->cinfo, DRA7_ATL_SWEN_REG(cdesc->id), DRA7_ATL_SWEN);
0087
0088 out:
0089 cdesc->enabled = true;
0090
0091 return 0;
0092 }
0093
0094 static void atl_clk_disable(struct clk_hw *hw)
0095 {
0096 struct dra7_atl_desc *cdesc = to_atl_desc(hw);
0097
0098 if (!cdesc->probed)
0099 goto out;
0100
0101 atl_write(cdesc->cinfo, DRA7_ATL_SWEN_REG(cdesc->id), 0);
0102 pm_runtime_put_sync(cdesc->cinfo->dev);
0103
0104 out:
0105 cdesc->enabled = false;
0106 }
0107
0108 static int atl_clk_is_enabled(struct clk_hw *hw)
0109 {
0110 struct dra7_atl_desc *cdesc = to_atl_desc(hw);
0111
0112 return cdesc->enabled;
0113 }
0114
0115 static unsigned long atl_clk_recalc_rate(struct clk_hw *hw,
0116 unsigned long parent_rate)
0117 {
0118 struct dra7_atl_desc *cdesc = to_atl_desc(hw);
0119
0120 return parent_rate / cdesc->divider;
0121 }
0122
0123 static long atl_clk_round_rate(struct clk_hw *hw, unsigned long rate,
0124 unsigned long *parent_rate)
0125 {
0126 unsigned divider;
0127
0128 divider = (*parent_rate + rate / 2) / rate;
0129 if (divider > DRA7_ATL_DIVIDER_MASK + 1)
0130 divider = DRA7_ATL_DIVIDER_MASK + 1;
0131
0132 return *parent_rate / divider;
0133 }
0134
0135 static int atl_clk_set_rate(struct clk_hw *hw, unsigned long rate,
0136 unsigned long parent_rate)
0137 {
0138 struct dra7_atl_desc *cdesc;
0139 u32 divider;
0140
0141 if (!hw || !rate)
0142 return -EINVAL;
0143
0144 cdesc = to_atl_desc(hw);
0145 divider = ((parent_rate + rate / 2) / rate) - 1;
0146 if (divider > DRA7_ATL_DIVIDER_MASK)
0147 divider = DRA7_ATL_DIVIDER_MASK;
0148
0149 cdesc->divider = divider + 1;
0150
0151 return 0;
0152 }
0153
0154 static const struct clk_ops atl_clk_ops = {
0155 .enable = atl_clk_enable,
0156 .disable = atl_clk_disable,
0157 .is_enabled = atl_clk_is_enabled,
0158 .recalc_rate = atl_clk_recalc_rate,
0159 .round_rate = atl_clk_round_rate,
0160 .set_rate = atl_clk_set_rate,
0161 };
0162
0163 static void __init of_dra7_atl_clock_setup(struct device_node *node)
0164 {
0165 struct dra7_atl_desc *clk_hw = NULL;
0166 struct clk_init_data init = { NULL };
0167 const char **parent_names = NULL;
0168 const char *name;
0169 struct clk *clk;
0170
0171 clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
0172 if (!clk_hw) {
0173 pr_err("%s: could not allocate dra7_atl_desc\n", __func__);
0174 return;
0175 }
0176
0177 clk_hw->hw.init = &init;
0178 clk_hw->divider = 1;
0179 name = ti_dt_clk_name(node);
0180 init.name = name;
0181 init.ops = &atl_clk_ops;
0182 init.flags = CLK_IGNORE_UNUSED;
0183 init.num_parents = of_clk_get_parent_count(node);
0184
0185 if (init.num_parents != 1) {
0186 pr_err("%s: atl clock %pOFn must have 1 parent\n", __func__,
0187 node);
0188 goto cleanup;
0189 }
0190
0191 parent_names = kzalloc(sizeof(char *), GFP_KERNEL);
0192
0193 if (!parent_names)
0194 goto cleanup;
0195
0196 parent_names[0] = of_clk_get_parent_name(node, 0);
0197
0198 init.parent_names = parent_names;
0199
0200 clk = ti_clk_register(NULL, &clk_hw->hw, name);
0201
0202 if (!IS_ERR(clk)) {
0203 of_clk_add_provider(node, of_clk_src_simple_get, clk);
0204 kfree(parent_names);
0205 return;
0206 }
0207 cleanup:
0208 kfree(parent_names);
0209 kfree(clk_hw);
0210 }
0211 CLK_OF_DECLARE(dra7_atl_clock, "ti,dra7-atl-clock", of_dra7_atl_clock_setup);
0212
0213 static int of_dra7_atl_clk_probe(struct platform_device *pdev)
0214 {
0215 struct device_node *node = pdev->dev.of_node;
0216 struct dra7_atl_clock_info *cinfo;
0217 int i;
0218 int ret = 0;
0219
0220 if (!node)
0221 return -ENODEV;
0222
0223 cinfo = devm_kzalloc(&pdev->dev, sizeof(*cinfo), GFP_KERNEL);
0224 if (!cinfo)
0225 return -ENOMEM;
0226
0227 cinfo->iobase = of_iomap(node, 0);
0228 cinfo->dev = &pdev->dev;
0229 pm_runtime_enable(cinfo->dev);
0230
0231 pm_runtime_get_sync(cinfo->dev);
0232 atl_write(cinfo, DRA7_ATL_PCLKMUX_REG(0), DRA7_ATL_PCLKMUX);
0233
0234 for (i = 0; i < DRA7_ATL_INSTANCES; i++) {
0235 struct device_node *cfg_node;
0236 char prop[5];
0237 struct dra7_atl_desc *cdesc;
0238 struct of_phandle_args clkspec;
0239 struct clk *clk;
0240 int rc;
0241
0242 rc = of_parse_phandle_with_args(node, "ti,provided-clocks",
0243 NULL, i, &clkspec);
0244
0245 if (rc) {
0246 pr_err("%s: failed to lookup atl clock %d\n", __func__,
0247 i);
0248 return -EINVAL;
0249 }
0250
0251 clk = of_clk_get_from_provider(&clkspec);
0252 if (IS_ERR(clk)) {
0253 pr_err("%s: failed to get atl clock %d from provider\n",
0254 __func__, i);
0255 return PTR_ERR(clk);
0256 }
0257
0258 cdesc = to_atl_desc(__clk_get_hw(clk));
0259 cdesc->cinfo = cinfo;
0260 cdesc->id = i;
0261
0262
0263 snprintf(prop, sizeof(prop), "atl%u", i);
0264 cfg_node = of_get_child_by_name(node, prop);
0265 if (cfg_node) {
0266 ret = of_property_read_u32(cfg_node, "bws",
0267 &cdesc->bws);
0268 ret |= of_property_read_u32(cfg_node, "aws",
0269 &cdesc->aws);
0270 if (!ret) {
0271 cdesc->valid = true;
0272 atl_write(cinfo, DRA7_ATL_BWSMUX_REG(i),
0273 cdesc->bws);
0274 atl_write(cinfo, DRA7_ATL_AWSMUX_REG(i),
0275 cdesc->aws);
0276 }
0277 of_node_put(cfg_node);
0278 }
0279
0280 cdesc->probed = true;
0281
0282
0283
0284
0285 if (cdesc->enabled)
0286 atl_clk_enable(__clk_get_hw(clk));
0287 }
0288 pm_runtime_put_sync(cinfo->dev);
0289
0290 return ret;
0291 }
0292
0293 static const struct of_device_id of_dra7_atl_clk_match_tbl[] = {
0294 { .compatible = "ti,dra7-atl", },
0295 {},
0296 };
0297
0298 static struct platform_driver dra7_atl_clk_driver = {
0299 .driver = {
0300 .name = "dra7-atl",
0301 .suppress_bind_attrs = true,
0302 .of_match_table = of_dra7_atl_clk_match_tbl,
0303 },
0304 .probe = of_dra7_atl_clk_probe,
0305 };
0306 builtin_platform_driver(dra7_atl_clk_driver);