1 /* 2 * EIM driver for Freescale's i.MX chips 3 * 4 * Copyright (C) 2013 Freescale Semiconductor, Inc. 5 * 6 * This file is licensed under the terms of the GNU General Public 7 * License version 2. This program is licensed "as is" without any 8 * warranty of any kind, whether express or implied. 9 */ 10 #include <linux/module.h> 11 #include <linux/clk.h> 12 #include <linux/io.h> 13 #include <linux/of_device.h> 14 #include <linux/mfd/syscon.h> 15 #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 16 #include <linux/regmap.h> 17 18 struct imx_weim_devtype { 19 unsigned int cs_count; 20 unsigned int cs_regs_count; 21 unsigned int cs_stride; 22 unsigned int wcr_offset; 23 unsigned int wcr_bcm; 24 unsigned int wcr_cont_bclk; 25 }; 26 27 static const struct imx_weim_devtype imx1_weim_devtype = { 28 .cs_count = 6, 29 .cs_regs_count = 2, 30 .cs_stride = 0x08, 31 }; 32 33 static const struct imx_weim_devtype imx27_weim_devtype = { 34 .cs_count = 6, 35 .cs_regs_count = 3, 36 .cs_stride = 0x10, 37 }; 38 39 static const struct imx_weim_devtype imx50_weim_devtype = { 40 .cs_count = 4, 41 .cs_regs_count = 6, 42 .cs_stride = 0x18, 43 .wcr_offset = 0x90, 44 .wcr_bcm = BIT(0), 45 .wcr_cont_bclk = BIT(3), 46 }; 47 48 static const struct imx_weim_devtype imx51_weim_devtype = { 49 .cs_count = 6, 50 .cs_regs_count = 6, 51 .cs_stride = 0x18, 52 }; 53 54 #define MAX_CS_REGS_COUNT 6 55 #define MAX_CS_COUNT 6 56 #define OF_REG_SIZE 3 57 58 struct cs_timing { 59 bool is_applied; 60 u32 regs[MAX_CS_REGS_COUNT]; 61 }; 62 63 struct cs_timing_state { 64 struct cs_timing cs[MAX_CS_COUNT]; 65 }; 66 67 struct weim_priv { 68 void __iomem *base; 69 struct cs_timing_state timing_state; 70 }; 71 72 static const struct of_device_id weim_id_table[] = { 73 /* i.MX1/21 */ 74 { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, }, 75 /* i.MX25/27/31/35 */ 76 { .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, }, 77 /* i.MX50/53/6Q */ 78 { .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, }, 79 { .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, }, 80 /* i.MX51 */ 81 { .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, }, 82 { } 83 }; 84 MODULE_DEVICE_TABLE(of, weim_id_table); 85 86 static int imx_weim_gpr_setup(struct platform_device *pdev) 87 { 88 struct device_node *np = pdev->dev.of_node; 89 struct property *prop; 90 const __be32 *p; 91 struct regmap *gpr; 92 u32 gprvals[4] = { 93 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */ 94 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */ 95 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */ 96 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */ 97 }; 98 u32 gprval = 0; 99 u32 val; 100 int cs = 0; 101 int i = 0; 102 103 gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr"); 104 if (IS_ERR(gpr)) { 105 dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n"); 106 return 0; 107 } 108 109 of_property_for_each_u32(np, "ranges", prop, p, val) { 110 if (i % 4 == 0) { 111 cs = val; 112 } else if (i % 4 == 3 && val) { 113 val = (val / SZ_32M) | 1; 114 gprval |= val << cs * 3; 115 } 116 i++; 117 } 118 119 if (i == 0 || i % 4) 120 goto err; 121 122 for (i = 0; i < ARRAY_SIZE(gprvals); i++) { 123 if (gprval == gprvals[i]) { 124 /* Found it. Set up IOMUXC_GPR1[11:0] with it. */ 125 regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval); 126 return 0; 127 } 128 } 129 130 err: 131 dev_err(&pdev->dev, "Invalid 'ranges' configuration\n"); 132 return -EINVAL; 133 } 134 135 /* Parse and set the timing for this device. */ 136 static int weim_timing_setup(struct device *dev, struct device_node *np, 137 const struct imx_weim_devtype *devtype) 138 { 139 u32 cs_idx, value[MAX_CS_REGS_COUNT]; 140 int i, ret; 141 int reg_idx, num_regs; 142 struct cs_timing *cst; 143 struct weim_priv *priv; 144 struct cs_timing_state *ts; 145 void __iomem *base; 146 147 if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) 148 return -EINVAL; 149 if (WARN_ON(devtype->cs_count > MAX_CS_COUNT)) 150 return -EINVAL; 151 152 priv = dev_get_drvdata(dev); 153 base = priv->base; 154 ts = &priv->timing_state; 155 156 ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", 157 value, devtype->cs_regs_count); 158 if (ret) 159 return ret; 160 161 /* 162 * the child node's "reg" property may contain multiple address ranges, 163 * extract the chip select for each. 164 */ 165 num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE); 166 if (num_regs < 0) 167 return num_regs; 168 if (!num_regs) 169 return -EINVAL; 170 for (reg_idx = 0; reg_idx < num_regs; reg_idx++) { 171 /* get the CS index from this child node's "reg" property. */ 172 ret = of_property_read_u32_index(np, "reg", 173 reg_idx * OF_REG_SIZE, &cs_idx); 174 if (ret) 175 break; 176 177 if (cs_idx >= devtype->cs_count) 178 return -EINVAL; 179 180 /* prevent re-configuring a CS that's already been configured */ 181 cst = &ts->cs[cs_idx]; 182 if (cst->is_applied && memcmp(value, cst->regs, 183 devtype->cs_regs_count * sizeof(u32))) { 184 dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np); 185 return -EINVAL; 186 } 187 188 /* set the timing for WEIM */ 189 for (i = 0; i < devtype->cs_regs_count; i++) 190 writel(value[i], 191 base + cs_idx * devtype->cs_stride + i * 4); 192 if (!cst->is_applied) { 193 cst->is_applied = true; 194 memcpy(cst->regs, value, 195 devtype->cs_regs_count * sizeof(u32)); 196 } 197 } 198 199 return 0; 200 } 201 202 static int weim_parse_dt(struct platform_device *pdev) 203 { 204 const struct of_device_id *of_id = of_match_device(weim_id_table, 205 &pdev->dev); 206 const struct imx_weim_devtype *devtype = of_id->data; 207 struct device_node *child; 208 int ret, have_child = 0; 209 struct weim_priv *priv; 210 void __iomem *base; 211 u32 reg; 212 213 if (devtype == &imx50_weim_devtype) { 214 ret = imx_weim_gpr_setup(pdev); 215 if (ret) 216 return ret; 217 } 218 219 priv = dev_get_drvdata(&pdev->dev); 220 base = priv->base; 221 222 if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) { 223 if (devtype->wcr_bcm) { 224 reg = readl(base + devtype->wcr_offset); 225 reg |= devtype->wcr_bcm; 226 227 if (of_property_read_bool(pdev->dev.of_node, 228 "fsl,continuous-burst-clk")) { 229 if (devtype->wcr_cont_bclk) { 230 reg |= devtype->wcr_cont_bclk; 231 } else { 232 dev_err(&pdev->dev, 233 "continuous burst clk not supported.\n"); 234 return -EINVAL; 235 } 236 } 237 238 writel(reg, base + devtype->wcr_offset); 239 } else { 240 dev_err(&pdev->dev, "burst clk mode not supported.\n"); 241 return -EINVAL; 242 } 243 } 244 245 for_each_available_child_of_node(pdev->dev.of_node, child) { 246 ret = weim_timing_setup(&pdev->dev, child, devtype); 247 if (ret) 248 dev_warn(&pdev->dev, "%pOF set timing failed.\n", 249 child); 250 else 251 have_child = 1; 252 } 253 254 if (have_child) 255 ret = of_platform_default_populate(pdev->dev.of_node, 256 NULL, &pdev->dev); 257 if (ret) 258 dev_err(&pdev->dev, "%pOF fail to create devices.\n", 259 pdev->dev.of_node); 260 return ret; 261 } 262 263 static int weim_probe(struct platform_device *pdev) 264 { 265 struct weim_priv *priv; 266 struct clk *clk; 267 void __iomem *base; 268 int ret; 269 270 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 271 if (!priv) 272 return -ENOMEM; 273 274 /* get the resource */ 275 base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 276 if (IS_ERR(base)) 277 return PTR_ERR(base); 278 279 priv->base = base; 280 dev_set_drvdata(&pdev->dev, priv); 281 282 /* get the clock */ 283 clk = devm_clk_get(&pdev->dev, NULL); 284 if (IS_ERR(clk)) 285 return PTR_ERR(clk); 286 287 ret = clk_prepare_enable(clk); 288 if (ret) 289 return ret; 290 291 /* parse the device node */ 292 ret = weim_parse_dt(pdev); 293 if (ret) 294 clk_disable_unprepare(clk); 295 else 296 dev_info(&pdev->dev, "Driver registered.\n"); 297 298 return ret; 299 } 300 301 #if IS_ENABLED(CONFIG_OF_DYNAMIC) 302 static int of_weim_notify(struct notifier_block *nb, unsigned long action, 303 void *arg) 304 { 305 const struct imx_weim_devtype *devtype; 306 struct of_reconfig_data *rd = arg; 307 const struct of_device_id *of_id; 308 struct platform_device *pdev; 309 int ret = NOTIFY_OK; 310 311 switch (of_reconfig_get_state_change(action, rd)) { 312 case OF_RECONFIG_CHANGE_ADD: 313 of_id = of_match_node(weim_id_table, rd->dn->parent); 314 if (!of_id) 315 return NOTIFY_OK; /* not for us */ 316 317 devtype = of_id->data; 318 319 pdev = of_find_device_by_node(rd->dn->parent); 320 if (!pdev) { 321 pr_err("%s: could not find platform device for '%pOF'\n", 322 __func__, rd->dn->parent); 323 324 return notifier_from_errno(-EINVAL); 325 } 326 327 if (weim_timing_setup(&pdev->dev, rd->dn, devtype)) 328 dev_warn(&pdev->dev, 329 "Failed to setup timing for '%pOF'\n", rd->dn); 330 331 if (!of_node_check_flag(rd->dn, OF_POPULATED)) { 332 if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { 333 dev_err(&pdev->dev, 334 "Failed to create child device '%pOF'\n", 335 rd->dn); 336 ret = notifier_from_errno(-EINVAL); 337 } 338 } 339 340 platform_device_put(pdev); 341 342 break; 343 case OF_RECONFIG_CHANGE_REMOVE: 344 if (!of_node_check_flag(rd->dn, OF_POPULATED)) 345 return NOTIFY_OK; /* device already destroyed */ 346 347 of_id = of_match_node(weim_id_table, rd->dn->parent); 348 if (!of_id) 349 return NOTIFY_OK; /* not for us */ 350 351 pdev = of_find_device_by_node(rd->dn); 352 if (!pdev) { 353 pr_err("Could not find platform device for '%pOF'\n", 354 rd->dn); 355 356 ret = notifier_from_errno(-EINVAL); 357 } else { 358 of_platform_device_destroy(&pdev->dev, NULL); 359 platform_device_put(pdev); 360 } 361 362 break; 363 default: 364 break; 365 } 366 367 return ret; 368 } 369 370 static struct notifier_block weim_of_notifier = { 371 .notifier_call = of_weim_notify, 372 }; 373 #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 374 375 static struct platform_driver weim_driver = { 376 .driver = { 377 .name = "imx-weim", 378 .of_match_table = weim_id_table, 379 }, 380 .probe = weim_probe, 381 }; 382 383 static int __init weim_init(void) 384 { 385 #if IS_ENABLED(CONFIG_OF_DYNAMIC) 386 WARN_ON(of_reconfig_notifier_register(&weim_of_notifier)); 387 #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 388 389 return platform_driver_register(&weim_driver); 390 } 391 module_init(weim_init); 392 393 static void __exit weim_exit(void) 394 { 395 #if IS_ENABLED(CONFIG_OF_DYNAMIC) 396 of_reconfig_notifier_unregister(&weim_of_notifier); 397 #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 398 399 return platform_driver_unregister(&weim_driver); 400 401 } 402 module_exit(weim_exit); 403 404 MODULE_AUTHOR("Freescale Semiconductor Inc."); 405 MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 406 MODULE_LICENSE("GPL"); 407