|
8 | 8 | * Andrew F. Davis <afd@ti.com> |
9 | 9 | */ |
10 | 10 |
|
| 11 | +#include <linux/clk-provider.h> |
11 | 12 | #include <linux/dma-mapping.h> |
12 | 13 | #include <linux/io.h> |
13 | 14 | #include <linux/mfd/syscon.h> |
|
16 | 17 | #include <linux/of_device.h> |
17 | 18 | #include <linux/pm_runtime.h> |
18 | 19 | #include <linux/pruss_driver.h> |
| 20 | +#include <linux/regmap.h> |
| 21 | +#include <linux/slab.h> |
19 | 22 |
|
20 | 23 | /** |
21 | 24 | * struct pruss_private_data - PRUSS driver private data |
22 | 25 | * @has_no_sharedram: flag to indicate the absence of PRUSS Shared Data RAM |
| 26 | + * @has_core_mux_clock: flag to indicate the presence of PRUSS core clock |
23 | 27 | */ |
24 | 28 | struct pruss_private_data { |
25 | 29 | bool has_no_sharedram; |
| 30 | + bool has_core_mux_clock; |
| 31 | +}; |
| 32 | + |
| 33 | +static void pruss_of_free_clk_provider(void *data) |
| 34 | +{ |
| 35 | + struct device_node *clk_mux_np = data; |
| 36 | + |
| 37 | + of_clk_del_provider(clk_mux_np); |
| 38 | + of_node_put(clk_mux_np); |
| 39 | +} |
| 40 | + |
| 41 | +static int pruss_clk_mux_setup(struct pruss *pruss, struct clk *clk_mux, |
| 42 | + char *mux_name, struct device_node *clks_np) |
| 43 | +{ |
| 44 | + struct device_node *clk_mux_np; |
| 45 | + struct device *dev = pruss->dev; |
| 46 | + char *clk_mux_name; |
| 47 | + unsigned int num_parents; |
| 48 | + const char **parent_names; |
| 49 | + void __iomem *reg; |
| 50 | + u32 reg_offset; |
| 51 | + int ret; |
| 52 | + |
| 53 | + clk_mux_np = of_get_child_by_name(clks_np, mux_name); |
| 54 | + if (!clk_mux_np) { |
| 55 | + dev_err(dev, "%pOF is missing its '%s' node\n", clks_np, |
| 56 | + mux_name); |
| 57 | + return -ENODEV; |
| 58 | + } |
| 59 | + |
| 60 | + num_parents = of_clk_get_parent_count(clk_mux_np); |
| 61 | + if (num_parents < 1) { |
| 62 | + dev_err(dev, "mux-clock %pOF must have parents\n", clk_mux_np); |
| 63 | + ret = -EINVAL; |
| 64 | + goto put_clk_mux_np; |
| 65 | + } |
| 66 | + |
| 67 | + parent_names = devm_kcalloc(dev, sizeof(*parent_names), num_parents, |
| 68 | + GFP_KERNEL); |
| 69 | + if (!parent_names) { |
| 70 | + ret = -ENOMEM; |
| 71 | + goto put_clk_mux_np; |
| 72 | + } |
| 73 | + |
| 74 | + of_clk_parent_fill(clk_mux_np, parent_names, num_parents); |
| 75 | + |
| 76 | + clk_mux_name = devm_kasprintf(dev, GFP_KERNEL, "%s.%pOFn", |
| 77 | + dev_name(dev), clk_mux_np); |
| 78 | + if (!clk_mux_name) { |
| 79 | + ret = -ENOMEM; |
| 80 | + goto put_clk_mux_np; |
| 81 | + } |
| 82 | + |
| 83 | + ret = of_property_read_u32(clk_mux_np, "reg", ®_offset); |
| 84 | + if (ret) |
| 85 | + goto put_clk_mux_np; |
| 86 | + |
| 87 | + reg = pruss->cfg_base + reg_offset; |
| 88 | + |
| 89 | + clk_mux = clk_register_mux(NULL, clk_mux_name, parent_names, |
| 90 | + num_parents, 0, reg, 0, 1, 0, NULL); |
| 91 | + if (IS_ERR(clk_mux)) { |
| 92 | + ret = PTR_ERR(clk_mux); |
| 93 | + goto put_clk_mux_np; |
| 94 | + } |
| 95 | + |
| 96 | + ret = devm_add_action_or_reset(dev, (void(*)(void *))clk_unregister_mux, |
| 97 | + clk_mux); |
| 98 | + if (ret) { |
| 99 | + dev_err(dev, "failed to add clkmux unregister action %d", ret); |
| 100 | + goto put_clk_mux_np; |
| 101 | + } |
| 102 | + |
| 103 | + ret = of_clk_add_provider(clk_mux_np, of_clk_src_simple_get, clk_mux); |
| 104 | + if (ret) |
| 105 | + goto put_clk_mux_np; |
| 106 | + |
| 107 | + ret = devm_add_action_or_reset(dev, pruss_of_free_clk_provider, |
| 108 | + clk_mux_np); |
| 109 | + if (ret) { |
| 110 | + dev_err(dev, "failed to add clkmux free action %d", ret); |
| 111 | + goto put_clk_mux_np; |
| 112 | + } |
| 113 | + |
| 114 | + return 0; |
| 115 | + |
| 116 | +put_clk_mux_np: |
| 117 | + of_node_put(clk_mux_np); |
| 118 | + return ret; |
| 119 | +} |
| 120 | + |
| 121 | +static int pruss_clk_init(struct pruss *pruss, struct device_node *cfg_node) |
| 122 | +{ |
| 123 | + const struct pruss_private_data *data; |
| 124 | + struct device_node *clks_np; |
| 125 | + struct device *dev = pruss->dev; |
| 126 | + int ret = 0; |
| 127 | + |
| 128 | + data = of_device_get_match_data(dev); |
| 129 | + if (IS_ERR(data)) |
| 130 | + return -ENODEV; |
| 131 | + |
| 132 | + clks_np = of_get_child_by_name(cfg_node, "clocks"); |
| 133 | + if (!clks_np) { |
| 134 | + dev_err(dev, "%pOF is missing its 'clocks' node\n", clks_np); |
| 135 | + return -ENODEV; |
| 136 | + } |
| 137 | + |
| 138 | + if (data && data->has_core_mux_clock) { |
| 139 | + ret = pruss_clk_mux_setup(pruss, pruss->core_clk_mux, |
| 140 | + "coreclk-mux", clks_np); |
| 141 | + if (ret) { |
| 142 | + dev_err(dev, "failed to setup coreclk-mux\n"); |
| 143 | + goto put_clks_node; |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + ret = pruss_clk_mux_setup(pruss, pruss->iep_clk_mux, "iepclk-mux", |
| 148 | + clks_np); |
| 149 | + if (ret) { |
| 150 | + dev_err(dev, "failed to setup iepclk-mux\n"); |
| 151 | + goto put_clks_node; |
| 152 | + } |
| 153 | + |
| 154 | +put_clks_node: |
| 155 | + of_node_put(clks_np); |
| 156 | + |
| 157 | + return ret; |
| 158 | +} |
| 159 | + |
| 160 | +static struct regmap_config regmap_conf = { |
| 161 | + .reg_bits = 32, |
| 162 | + .val_bits = 32, |
| 163 | + .reg_stride = 4, |
26 | 164 | }; |
27 | 165 |
|
28 | 166 | static int pruss_probe(struct platform_device *pdev) |
@@ -114,21 +252,49 @@ static int pruss_probe(struct platform_device *pdev) |
114 | 252 | goto rpm_put; |
115 | 253 | } |
116 | 254 |
|
117 | | - pruss->cfg_regmap = syscon_node_to_regmap(child); |
118 | | - of_node_put(child); |
| 255 | + if (of_address_to_resource(child, 0, &res)) { |
| 256 | + ret = -ENOMEM; |
| 257 | + goto node_put; |
| 258 | + } |
| 259 | + |
| 260 | + pruss->cfg_base = devm_ioremap(dev, res.start, resource_size(&res)); |
| 261 | + if (!pruss->cfg_base) { |
| 262 | + ret = -ENOMEM; |
| 263 | + goto node_put; |
| 264 | + } |
| 265 | + |
| 266 | + regmap_conf.name = kasprintf(GFP_KERNEL, "%pOFn@%llx", child, |
| 267 | + (u64)res.start); |
| 268 | + regmap_conf.max_register = resource_size(&res) - 4; |
| 269 | + |
| 270 | + pruss->cfg_regmap = devm_regmap_init_mmio(dev, pruss->cfg_base, |
| 271 | + ®map_conf); |
| 272 | + kfree(regmap_conf.name); |
119 | 273 | if (IS_ERR(pruss->cfg_regmap)) { |
120 | | - ret = -ENODEV; |
121 | | - goto rpm_put; |
| 274 | + dev_err(dev, "regmap_init_mmio failed for cfg, ret = %ld\n", |
| 275 | + PTR_ERR(pruss->cfg_regmap)); |
| 276 | + ret = PTR_ERR(pruss->cfg_regmap); |
| 277 | + goto node_put; |
| 278 | + } |
| 279 | + |
| 280 | + ret = pruss_clk_init(pruss, child); |
| 281 | + if (ret) { |
| 282 | + dev_err(dev, "failed to setup coreclk-mux\n"); |
| 283 | + goto node_put; |
122 | 284 | } |
123 | 285 |
|
124 | 286 | ret = devm_of_platform_populate(dev); |
125 | 287 | if (ret) { |
126 | 288 | dev_err(dev, "failed to register child devices\n"); |
127 | | - goto rpm_put; |
| 289 | + goto node_put; |
128 | 290 | } |
129 | 291 |
|
| 292 | + of_node_put(child); |
| 293 | + |
130 | 294 | return 0; |
131 | 295 |
|
| 296 | +node_put: |
| 297 | + of_node_put(child); |
132 | 298 | rpm_put: |
133 | 299 | pm_runtime_put_sync(dev); |
134 | 300 | rpm_disable: |
@@ -157,14 +323,18 @@ static const struct pruss_private_data am437x_pruss0_data = { |
157 | 323 | .has_no_sharedram = true, |
158 | 324 | }; |
159 | 325 |
|
| 326 | +static const struct pruss_private_data am65x_j721e_pruss_data = { |
| 327 | + .has_core_mux_clock = true, |
| 328 | +}; |
| 329 | + |
160 | 330 | static const struct of_device_id pruss_of_match[] = { |
161 | 331 | { .compatible = "ti,am3356-pruss" }, |
162 | 332 | { .compatible = "ti,am4376-pruss0", .data = &am437x_pruss0_data, }, |
163 | 333 | { .compatible = "ti,am4376-pruss1", .data = &am437x_pruss1_data, }, |
164 | 334 | { .compatible = "ti,am5728-pruss" }, |
165 | 335 | { .compatible = "ti,k2g-pruss" }, |
166 | | - { .compatible = "ti,am654-icssg" }, |
167 | | - { .compatible = "ti,j721e-icssg" }, |
| 336 | + { .compatible = "ti,am654-icssg", .data = &am65x_j721e_pruss_data, }, |
| 337 | + { .compatible = "ti,j721e-icssg", .data = &am65x_j721e_pruss_data, }, |
168 | 338 | {}, |
169 | 339 | }; |
170 | 340 | MODULE_DEVICE_TABLE(of, pruss_of_match); |
|
0 commit comments