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 resource *res; 267 struct clk *clk; 268 void __iomem *base; 269 int ret; 270 271 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 272 if (!priv) 273 return -ENOMEM; 274 275 /* get the resource */ 276 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 277 base = devm_ioremap_resource(&pdev->dev, res); 278 if (IS_ERR(base)) 279 return PTR_ERR(base); 280 281 priv->base = base; 282 dev_set_drvdata(&pdev->dev, priv); 283 284 /* get the clock */ 285 clk = devm_clk_get(&pdev->dev, NULL); 286 if (IS_ERR(clk)) 287 return PTR_ERR(clk); 288 289 ret = clk_prepare_enable(clk); 290 if (ret) 291 return ret; 292 293 /* parse the device node */ 294 ret = weim_parse_dt(pdev); 295 if (ret) 296 clk_disable_unprepare(clk); 297 else 298 dev_info(&pdev->dev, "Driver registered.\n"); 299 300 return ret; 301 } 302 303 #if IS_ENABLED(CONFIG_OF_DYNAMIC) 304 static int of_weim_notify(struct notifier_block *nb, unsigned long action, 305 void *arg) 306 { 307 const struct imx_weim_devtype *devtype; 308 struct of_reconfig_data *rd = arg; 309 const struct of_device_id *of_id; 310 struct platform_device *pdev; 311 int ret = NOTIFY_OK; 312 313 switch (of_reconfig_get_state_change(action, rd)) { 314 case OF_RECONFIG_CHANGE_ADD: 315 of_id = of_match_node(weim_id_table, rd->dn->parent); 316 if (!of_id) 317 return NOTIFY_OK; /* not for us */ 318 319 devtype = of_id->data; 320 321 pdev = of_find_device_by_node(rd->dn->parent); 322 if (!pdev) { 323 pr_err("%s: could not find platform device for '%pOF'\n", 324 __func__, rd->dn->parent); 325 326 return notifier_from_errno(-EINVAL); 327 } 328 329 if (weim_timing_setup(&pdev->dev, rd->dn, devtype)) 330 dev_warn(&pdev->dev, 331 "Failed to setup timing for '%pOF'\n", rd->dn); 332 333 if (!of_node_check_flag(rd->dn, OF_POPULATED)) { 334 if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { 335 dev_err(&pdev->dev, 336 "Failed to create child device '%pOF'\n", 337 rd->dn); 338 ret = notifier_from_errno(-EINVAL); 339 } 340 } 341 342 platform_device_put(pdev); 343 344 break; 345 case OF_RECONFIG_CHANGE_REMOVE: 346 if (!of_node_check_flag(rd->dn, OF_POPULATED)) 347 return NOTIFY_OK; /* device already destroyed */ 348 349 of_id = of_match_node(weim_id_table, rd->dn->parent); 350 if (!of_id) 351 return NOTIFY_OK; /* not for us */ 352 353 pdev = of_find_device_by_node(rd->dn); 354 if (!pdev) { 355 dev_err(&pdev->dev, 356 "Could not find platform device for '%pOF'\n", 357 rd->dn); 358 359 ret = notifier_from_errno(-EINVAL); 360 } else { 361 of_platform_device_destroy(&pdev->dev, NULL); 362 platform_device_put(pdev); 363 } 364 365 break; 366 default: 367 break; 368 } 369 370 return ret; 371 } 372 373 struct notifier_block weim_of_notifier = { 374 .notifier_call = of_weim_notify, 375 }; 376 #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 377 378 static struct platform_driver weim_driver = { 379 .driver = { 380 .name = "imx-weim", 381 .of_match_table = weim_id_table, 382 }, 383 .probe = weim_probe, 384 }; 385 386 static int __init weim_init(void) 387 { 388 #if IS_ENABLED(CONFIG_OF_DYNAMIC) 389 WARN_ON(of_reconfig_notifier_register(&weim_of_notifier)); 390 #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 391 392 return platform_driver_register(&weim_driver); 393 } 394 module_init(weim_init); 395 396 static void __exit weim_exit(void) 397 { 398 #if IS_ENABLED(CONFIG_OF_DYNAMIC) 399 of_reconfig_notifier_unregister(&weim_of_notifier); 400 #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 401 402 return platform_driver_unregister(&weim_driver); 403 404 } 405 module_exit(weim_exit); 406 407 MODULE_AUTHOR("Freescale Semiconductor Inc."); 408 MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 409 MODULE_LICENSE("GPL"); 410