imx-weim.c (9819B)
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 18struct 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 27static const struct imx_weim_devtype imx1_weim_devtype = { 28 .cs_count = 6, 29 .cs_regs_count = 2, 30 .cs_stride = 0x08, 31}; 32 33static const struct imx_weim_devtype imx27_weim_devtype = { 34 .cs_count = 6, 35 .cs_regs_count = 3, 36 .cs_stride = 0x10, 37}; 38 39static 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 48static 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 58struct cs_timing { 59 bool is_applied; 60 u32 regs[MAX_CS_REGS_COUNT]; 61}; 62 63struct cs_timing_state { 64 struct cs_timing cs[MAX_CS_COUNT]; 65}; 66 67struct weim_priv { 68 void __iomem *base; 69 struct cs_timing_state timing_state; 70}; 71 72static 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}; 84MODULE_DEVICE_TABLE(of, weim_id_table); 85 86static 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 130err: 131 dev_err(&pdev->dev, "Invalid 'ranges' configuration\n"); 132 return -EINVAL; 133} 134 135/* Parse and set the timing for this device. */ 136static 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 202static 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 263static 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) 304static 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 pr_err("Could not find platform device for '%pOF'\n", 356 rd->dn); 357 358 ret = notifier_from_errno(-EINVAL); 359 } else { 360 of_platform_device_destroy(&pdev->dev, NULL); 361 platform_device_put(pdev); 362 } 363 364 break; 365 default: 366 break; 367 } 368 369 return ret; 370} 371 372static struct notifier_block weim_of_notifier = { 373 .notifier_call = of_weim_notify, 374}; 375#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 376 377static struct platform_driver weim_driver = { 378 .driver = { 379 .name = "imx-weim", 380 .of_match_table = weim_id_table, 381 }, 382 .probe = weim_probe, 383}; 384 385static int __init weim_init(void) 386{ 387#if IS_ENABLED(CONFIG_OF_DYNAMIC) 388 WARN_ON(of_reconfig_notifier_register(&weim_of_notifier)); 389#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 390 391 return platform_driver_register(&weim_driver); 392} 393module_init(weim_init); 394 395static void __exit weim_exit(void) 396{ 397#if IS_ENABLED(CONFIG_OF_DYNAMIC) 398 of_reconfig_notifier_unregister(&weim_of_notifier); 399#endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 400 401 return platform_driver_unregister(&weim_driver); 402 403} 404module_exit(weim_exit); 405 406MODULE_AUTHOR("Freescale Semiconductor Inc."); 407MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 408MODULE_LICENSE("GPL");