Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: davinci: New driver for TI DA8XX CFGCHIP clocks

This adds a new driver for the gate and multiplexer clocks in the
CFGCHIPn syscon registers on TI DA8XX-type SoCs.

Signed-off-by: David Lechner <david@lechnology.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>

authored by

David Lechner and committed by
Stephen Boyd
1e88a8d6 0c92c717

+462
+2
drivers/clk/davinci/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 3 3 ifeq ($(CONFIG_COMMON_CLK), y) 4 + obj-$(CONFIG_ARCH_DAVINCI_DA8XX) += da8xx-cfgchip.o 5 + 4 6 obj-y += pll.o 5 7 obj-$(CONFIG_ARCH_DAVINCI_DA830) += pll-da830.o 6 8 obj-$(CONFIG_ARCH_DAVINCI_DA850) += pll-da850.o
+439
drivers/clk/davinci/da8xx-cfgchip.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Clock driver for DA8xx/AM17xx/AM18xx/OMAP-L13x CFGCHIP 4 + * 5 + * Copyright (C) 2018 David Lechner <david@lechnology.com> 6 + */ 7 + 8 + #include <linux/clk-provider.h> 9 + #include <linux/clk.h> 10 + #include <linux/clkdev.h> 11 + #include <linux/init.h> 12 + #include <linux/mfd/da8xx-cfgchip.h> 13 + #include <linux/mfd/syscon.h> 14 + #include <linux/of_device.h> 15 + #include <linux/of.h> 16 + #include <linux/platform_data/clk-da8xx-cfgchip.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/regmap.h> 19 + #include <linux/slab.h> 20 + 21 + /* --- Gate clocks --- */ 22 + 23 + #define DA8XX_GATE_CLOCK_IS_DIV4P5 BIT(1) 24 + 25 + struct da8xx_cfgchip_gate_clk_info { 26 + const char *name; 27 + u32 cfgchip; 28 + u32 bit; 29 + u32 flags; 30 + }; 31 + 32 + struct da8xx_cfgchip_gate_clk { 33 + struct clk_hw hw; 34 + struct regmap *regmap; 35 + u32 reg; 36 + u32 mask; 37 + }; 38 + 39 + #define to_da8xx_cfgchip_gate_clk(_hw) \ 40 + container_of((_hw), struct da8xx_cfgchip_gate_clk, hw) 41 + 42 + static int da8xx_cfgchip_gate_clk_enable(struct clk_hw *hw) 43 + { 44 + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); 45 + 46 + return regmap_write_bits(clk->regmap, clk->reg, clk->mask, clk->mask); 47 + } 48 + 49 + static void da8xx_cfgchip_gate_clk_disable(struct clk_hw *hw) 50 + { 51 + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); 52 + 53 + regmap_write_bits(clk->regmap, clk->reg, clk->mask, 0); 54 + } 55 + 56 + static int da8xx_cfgchip_gate_clk_is_enabled(struct clk_hw *hw) 57 + { 58 + struct da8xx_cfgchip_gate_clk *clk = to_da8xx_cfgchip_gate_clk(hw); 59 + unsigned int val; 60 + 61 + regmap_read(clk->regmap, clk->reg, &val); 62 + 63 + return !!(val & clk->mask); 64 + } 65 + 66 + static unsigned long da8xx_cfgchip_div4p5_recalc_rate(struct clk_hw *hw, 67 + unsigned long parent_rate) 68 + { 69 + /* this clock divides by 4.5 */ 70 + return parent_rate * 2 / 9; 71 + } 72 + 73 + static const struct clk_ops da8xx_cfgchip_gate_clk_ops = { 74 + .enable = da8xx_cfgchip_gate_clk_enable, 75 + .disable = da8xx_cfgchip_gate_clk_disable, 76 + .is_enabled = da8xx_cfgchip_gate_clk_is_enabled, 77 + }; 78 + 79 + static const struct clk_ops da8xx_cfgchip_div4p5_clk_ops = { 80 + .enable = da8xx_cfgchip_gate_clk_enable, 81 + .disable = da8xx_cfgchip_gate_clk_disable, 82 + .is_enabled = da8xx_cfgchip_gate_clk_is_enabled, 83 + .recalc_rate = da8xx_cfgchip_div4p5_recalc_rate, 84 + }; 85 + 86 + static struct da8xx_cfgchip_gate_clk * __init 87 + da8xx_cfgchip_gate_clk_register(struct device *dev, 88 + const struct da8xx_cfgchip_gate_clk_info *info, 89 + struct regmap *regmap) 90 + { 91 + struct clk *parent; 92 + const char *parent_name; 93 + struct da8xx_cfgchip_gate_clk *gate; 94 + struct clk_init_data init; 95 + int ret; 96 + 97 + parent = devm_clk_get(dev, NULL); 98 + if (IS_ERR(parent)) 99 + return ERR_CAST(parent); 100 + 101 + parent_name = __clk_get_name(parent); 102 + 103 + gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL); 104 + if (!gate) 105 + return ERR_PTR(-ENOMEM); 106 + 107 + init.name = info->name; 108 + if (info->flags & DA8XX_GATE_CLOCK_IS_DIV4P5) 109 + init.ops = &da8xx_cfgchip_div4p5_clk_ops; 110 + else 111 + init.ops = &da8xx_cfgchip_gate_clk_ops; 112 + init.parent_names = &parent_name; 113 + init.num_parents = 1; 114 + init.flags = 0; 115 + 116 + gate->hw.init = &init; 117 + gate->regmap = regmap; 118 + gate->reg = info->cfgchip; 119 + gate->mask = info->bit; 120 + 121 + ret = devm_clk_hw_register(dev, &gate->hw); 122 + if (ret < 0) 123 + return ERR_PTR(ret); 124 + 125 + return gate; 126 + } 127 + 128 + static const struct da8xx_cfgchip_gate_clk_info da8xx_tbclksync_info __initconst = { 129 + .name = "ehrpwm_tbclk", 130 + .cfgchip = CFGCHIP(1), 131 + .bit = CFGCHIP1_TBCLKSYNC, 132 + }; 133 + 134 + static int __init da8xx_cfgchip_register_tbclk(struct device *dev, 135 + struct regmap *regmap) 136 + { 137 + struct da8xx_cfgchip_gate_clk *gate; 138 + 139 + gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_tbclksync_info, 140 + regmap); 141 + if (IS_ERR(gate)) 142 + return PTR_ERR(gate); 143 + 144 + clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.0"); 145 + clk_hw_register_clkdev(&gate->hw, "tbclk", "ehrpwm.1"); 146 + 147 + return 0; 148 + } 149 + 150 + static const struct da8xx_cfgchip_gate_clk_info da8xx_div4p5ena_info __initconst = { 151 + .name = "div4.5", 152 + .cfgchip = CFGCHIP(3), 153 + .bit = CFGCHIP3_DIV45PENA, 154 + .flags = DA8XX_GATE_CLOCK_IS_DIV4P5, 155 + }; 156 + 157 + static int __init da8xx_cfgchip_register_div4p5(struct device *dev, 158 + struct regmap *regmap) 159 + { 160 + struct da8xx_cfgchip_gate_clk *gate; 161 + 162 + gate = da8xx_cfgchip_gate_clk_register(dev, &da8xx_div4p5ena_info, regmap); 163 + if (IS_ERR(gate)) 164 + return PTR_ERR(gate); 165 + 166 + return 0; 167 + } 168 + 169 + static int __init 170 + of_da8xx_cfgchip_gate_clk_init(struct device *dev, 171 + const struct da8xx_cfgchip_gate_clk_info *info, 172 + struct regmap *regmap) 173 + { 174 + struct da8xx_cfgchip_gate_clk *gate; 175 + 176 + gate = da8xx_cfgchip_gate_clk_register(dev, info, regmap); 177 + if (IS_ERR(gate)) 178 + return PTR_ERR(gate); 179 + 180 + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, gate); 181 + } 182 + 183 + static int __init of_da8xx_tbclksync_init(struct device *dev, 184 + struct regmap *regmap) 185 + { 186 + return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_tbclksync_info, regmap); 187 + } 188 + 189 + static int __init of_da8xx_div4p5ena_init(struct device *dev, 190 + struct regmap *regmap) 191 + { 192 + return of_da8xx_cfgchip_gate_clk_init(dev, &da8xx_div4p5ena_info, regmap); 193 + } 194 + 195 + /* --- MUX clocks --- */ 196 + 197 + struct da8xx_cfgchip_mux_clk_info { 198 + const char *name; 199 + const char *parent0; 200 + const char *parent1; 201 + u32 cfgchip; 202 + u32 bit; 203 + }; 204 + 205 + struct da8xx_cfgchip_mux_clk { 206 + struct clk_hw hw; 207 + struct regmap *regmap; 208 + u32 reg; 209 + u32 mask; 210 + }; 211 + 212 + #define to_da8xx_cfgchip_mux_clk(_hw) \ 213 + container_of((_hw), struct da8xx_cfgchip_mux_clk, hw) 214 + 215 + static int da8xx_cfgchip_mux_clk_set_parent(struct clk_hw *hw, u8 index) 216 + { 217 + struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw); 218 + unsigned int val = index ? clk->mask : 0; 219 + 220 + return regmap_write_bits(clk->regmap, clk->reg, clk->mask, val); 221 + } 222 + 223 + static u8 da8xx_cfgchip_mux_clk_get_parent(struct clk_hw *hw) 224 + { 225 + struct da8xx_cfgchip_mux_clk *clk = to_da8xx_cfgchip_mux_clk(hw); 226 + unsigned int val; 227 + 228 + regmap_read(clk->regmap, clk->reg, &val); 229 + 230 + return (val & clk->mask) ? 1 : 0; 231 + } 232 + 233 + static const struct clk_ops da8xx_cfgchip_mux_clk_ops = { 234 + .set_parent = da8xx_cfgchip_mux_clk_set_parent, 235 + .get_parent = da8xx_cfgchip_mux_clk_get_parent, 236 + }; 237 + 238 + static struct da8xx_cfgchip_mux_clk * __init 239 + da8xx_cfgchip_mux_clk_register(struct device *dev, 240 + const struct da8xx_cfgchip_mux_clk_info *info, 241 + struct regmap *regmap) 242 + { 243 + const char * const parent_names[] = { info->parent0, info->parent1 }; 244 + struct da8xx_cfgchip_mux_clk *mux; 245 + struct clk_init_data init; 246 + int ret; 247 + 248 + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); 249 + if (!mux) 250 + return ERR_PTR(-ENOMEM); 251 + 252 + init.name = info->name; 253 + init.ops = &da8xx_cfgchip_mux_clk_ops; 254 + init.parent_names = parent_names; 255 + init.num_parents = 2; 256 + init.flags = 0; 257 + 258 + mux->hw.init = &init; 259 + mux->regmap = regmap; 260 + mux->reg = info->cfgchip; 261 + mux->mask = info->bit; 262 + 263 + ret = devm_clk_hw_register(dev, &mux->hw); 264 + if (ret < 0) 265 + return ERR_PTR(ret); 266 + 267 + return mux; 268 + } 269 + 270 + static const struct da8xx_cfgchip_mux_clk_info da850_async1_info __initconst = { 271 + .name = "async1", 272 + .parent0 = "pll0_sysclk3", 273 + .parent1 = "div4.5", 274 + .cfgchip = CFGCHIP(3), 275 + .bit = CFGCHIP3_EMA_CLKSRC, 276 + }; 277 + 278 + static int __init da8xx_cfgchip_register_async1(struct device *dev, 279 + struct regmap *regmap) 280 + { 281 + struct da8xx_cfgchip_mux_clk *mux; 282 + 283 + mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async1_info, regmap); 284 + if (IS_ERR(mux)) 285 + return PTR_ERR(mux); 286 + 287 + clk_hw_register_clkdev(&mux->hw, "async1", "da850-psc0"); 288 + 289 + return 0; 290 + } 291 + 292 + static const struct da8xx_cfgchip_mux_clk_info da850_async3_info __initconst = { 293 + .name = "async3", 294 + .parent0 = "pll0_sysclk2", 295 + .parent1 = "pll1_sysclk2", 296 + .cfgchip = CFGCHIP(3), 297 + .bit = CFGCHIP3_ASYNC3_CLKSRC, 298 + }; 299 + 300 + static int __init da850_cfgchip_register_async3(struct device *dev, 301 + struct regmap *regmap) 302 + { 303 + struct da8xx_cfgchip_mux_clk *mux; 304 + struct clk_hw *parent; 305 + 306 + mux = da8xx_cfgchip_mux_clk_register(dev, &da850_async3_info, regmap); 307 + if (IS_ERR(mux)) 308 + return PTR_ERR(mux); 309 + 310 + clk_hw_register_clkdev(&mux->hw, "async3", "da850-psc1"); 311 + 312 + /* pll1_sysclk2 is not affected by CPU scaling, so use it for async3 */ 313 + parent = clk_hw_get_parent_by_index(&mux->hw, 1); 314 + if (parent) 315 + clk_set_parent(mux->hw.clk, parent->clk); 316 + else 317 + dev_warn(dev, "Failed to find async3 parent clock\n"); 318 + 319 + return 0; 320 + } 321 + 322 + static int __init 323 + of_da8xx_cfgchip_init_mux_clock(struct device *dev, 324 + const struct da8xx_cfgchip_mux_clk_info *info, 325 + struct regmap *regmap) 326 + { 327 + struct da8xx_cfgchip_mux_clk *mux; 328 + 329 + mux = da8xx_cfgchip_mux_clk_register(dev, info, regmap); 330 + if (IS_ERR(mux)) 331 + return PTR_ERR(mux); 332 + 333 + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &mux->hw); 334 + } 335 + 336 + static int __init of_da850_async1_init(struct device *dev, struct regmap *regmap) 337 + { 338 + return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async1_info, regmap); 339 + } 340 + 341 + static int __init of_da850_async3_init(struct device *dev, struct regmap *regmap) 342 + { 343 + return of_da8xx_cfgchip_init_mux_clock(dev, &da850_async3_info, regmap); 344 + } 345 + 346 + /* --- platform device --- */ 347 + 348 + static const struct of_device_id da8xx_cfgchip_of_match[] = { 349 + { 350 + .compatible = "ti,da830-tbclksync", 351 + .data = of_da8xx_tbclksync_init, 352 + }, 353 + { 354 + .compatible = "ti,da830-div4p5ena", 355 + .data = of_da8xx_div4p5ena_init, 356 + }, 357 + { 358 + .compatible = "ti,da850-async1-clksrc", 359 + .data = of_da850_async1_init, 360 + }, 361 + { 362 + .compatible = "ti,da850-async3-clksrc", 363 + .data = of_da850_async3_init, 364 + }, 365 + { } 366 + }; 367 + 368 + static const struct platform_device_id da8xx_cfgchip_id_table[] = { 369 + { 370 + .name = "da830-tbclksync", 371 + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_tbclk, 372 + }, 373 + { 374 + .name = "da830-div4p5ena", 375 + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_div4p5, 376 + }, 377 + { 378 + .name = "da850-async1-clksrc", 379 + .driver_data = (kernel_ulong_t)da8xx_cfgchip_register_async1, 380 + }, 381 + { 382 + .name = "da850-async3-clksrc", 383 + .driver_data = (kernel_ulong_t)da850_cfgchip_register_async3, 384 + }, 385 + { } 386 + }; 387 + 388 + typedef int (*da8xx_cfgchip_init)(struct device *dev, struct regmap *regmap); 389 + 390 + static int da8xx_cfgchip_probe(struct platform_device *pdev) 391 + { 392 + struct device *dev = &pdev->dev; 393 + struct da8xx_cfgchip_clk_platform_data *pdata = dev->platform_data; 394 + const struct of_device_id *of_id; 395 + da8xx_cfgchip_init clk_init = NULL; 396 + struct regmap *regmap = NULL; 397 + 398 + of_id = of_match_device(da8xx_cfgchip_of_match, dev); 399 + if (of_id) { 400 + struct device_node *parent; 401 + 402 + clk_init = of_id->data; 403 + parent = of_get_parent(dev->of_node); 404 + regmap = syscon_node_to_regmap(parent); 405 + of_node_put(parent); 406 + } else if (pdev->id_entry && pdata) { 407 + clk_init = (void *)pdev->id_entry->driver_data; 408 + regmap = pdata->cfgchip; 409 + } 410 + 411 + if (!clk_init) { 412 + dev_err(dev, "unable to find driver data\n"); 413 + return -EINVAL; 414 + } 415 + 416 + if (IS_ERR_OR_NULL(regmap)) { 417 + dev_err(dev, "no regmap for CFGCHIP syscon\n"); 418 + return regmap ? PTR_ERR(regmap) : -ENOENT; 419 + } 420 + 421 + return clk_init(dev, regmap); 422 + } 423 + 424 + static struct platform_driver da8xx_cfgchip_driver = { 425 + .probe = da8xx_cfgchip_probe, 426 + .driver = { 427 + .name = "da8xx-cfgchip-clk", 428 + .of_match_table = da8xx_cfgchip_of_match, 429 + }, 430 + .id_table = da8xx_cfgchip_id_table, 431 + }; 432 + 433 + static int __init da8xx_cfgchip_driver_init(void) 434 + { 435 + return platform_driver_register(&da8xx_cfgchip_driver); 436 + } 437 + 438 + /* has to be postcore_initcall because PSC devices depend on the async3 clock */ 439 + postcore_initcall(da8xx_cfgchip_driver_init);
+21
include/linux/platform_data/clk-da8xx-cfgchip.h
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * clk-da8xx-cfgchip - TI DaVinci DA8xx CFGCHIP clock driver 4 + * 5 + * Copyright (C) 2018 David Lechner <david@lechnology.com> 6 + */ 7 + 8 + #ifndef __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__ 9 + #define __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__ 10 + 11 + #include <linux/regmap.h> 12 + 13 + /** 14 + * da8xx_cfgchip_clk_platform_data 15 + * @cfgchip: CFGCHIP syscon regmap 16 + */ 17 + struct da8xx_cfgchip_clk_platform_data { 18 + struct regmap *cfgchip; 19 + }; 20 + 21 + #endif /* __LINUX_PLATFORM_DATA_CLK_DA8XX_CFGCHIP_H__ */