185bf6d4eSHuang Shijie /* 285bf6d4eSHuang Shijie * EIM driver for Freescale's i.MX chips 385bf6d4eSHuang Shijie * 485bf6d4eSHuang Shijie * Copyright (C) 2013 Freescale Semiconductor, Inc. 585bf6d4eSHuang Shijie * 685bf6d4eSHuang Shijie * This file is licensed under the terms of the GNU General Public 785bf6d4eSHuang Shijie * License version 2. This program is licensed "as is" without any 885bf6d4eSHuang Shijie * warranty of any kind, whether express or implied. 985bf6d4eSHuang Shijie */ 1085bf6d4eSHuang Shijie #include <linux/module.h> 1185bf6d4eSHuang Shijie #include <linux/clk.h> 1285bf6d4eSHuang Shijie #include <linux/io.h> 1385bf6d4eSHuang Shijie #include <linux/of_device.h> 148d9ee21eSShawn Guo #include <linux/mfd/syscon.h> 158d9ee21eSShawn Guo #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 168d9ee21eSShawn Guo #include <linux/regmap.h> 1785bf6d4eSHuang Shijie 183f98b6baSAlexander Shiyan struct imx_weim_devtype { 193f98b6baSAlexander Shiyan unsigned int cs_count; 203f98b6baSAlexander Shiyan unsigned int cs_regs_count; 213f98b6baSAlexander Shiyan unsigned int cs_stride; 2277266e72SSven Van Asbroeck unsigned int wcr_offset; 2377266e72SSven Van Asbroeck unsigned int wcr_bcm; 247b983da3SIvan Bornyakov unsigned int wcr_cont_bclk; 253f98b6baSAlexander Shiyan }; 263f98b6baSAlexander Shiyan 273f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx1_weim_devtype = { 283f98b6baSAlexander Shiyan .cs_count = 6, 293f98b6baSAlexander Shiyan .cs_regs_count = 2, 303f98b6baSAlexander Shiyan .cs_stride = 0x08, 313f98b6baSAlexander Shiyan }; 323f98b6baSAlexander Shiyan 333f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx27_weim_devtype = { 343f98b6baSAlexander Shiyan .cs_count = 6, 353f98b6baSAlexander Shiyan .cs_regs_count = 3, 363f98b6baSAlexander Shiyan .cs_stride = 0x10, 373f98b6baSAlexander Shiyan }; 383f98b6baSAlexander Shiyan 393f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx50_weim_devtype = { 403f98b6baSAlexander Shiyan .cs_count = 4, 413f98b6baSAlexander Shiyan .cs_regs_count = 6, 423f98b6baSAlexander Shiyan .cs_stride = 0x18, 4377266e72SSven Van Asbroeck .wcr_offset = 0x90, 4477266e72SSven Van Asbroeck .wcr_bcm = BIT(0), 457b983da3SIvan Bornyakov .wcr_cont_bclk = BIT(3), 463f98b6baSAlexander Shiyan }; 473f98b6baSAlexander Shiyan 483f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx51_weim_devtype = { 493f98b6baSAlexander Shiyan .cs_count = 6, 503f98b6baSAlexander Shiyan .cs_regs_count = 6, 513f98b6baSAlexander Shiyan .cs_stride = 0x18, 523f98b6baSAlexander Shiyan }; 533f98b6baSAlexander Shiyan 54d8dfa59fSKees Cook #define MAX_CS_REGS_COUNT 6 55c7995bcbSSven Van Asbroeck #define MAX_CS_COUNT 6 568b8cb52aSSven Van Asbroeck #define OF_REG_SIZE 3 57d8dfa59fSKees Cook 58c7995bcbSSven Van Asbroeck struct cs_timing { 59c7995bcbSSven Van Asbroeck bool is_applied; 60c7995bcbSSven Van Asbroeck u32 regs[MAX_CS_REGS_COUNT]; 61c7995bcbSSven Van Asbroeck }; 62c7995bcbSSven Van Asbroeck 63c7995bcbSSven Van Asbroeck struct cs_timing_state { 64c7995bcbSSven Van Asbroeck struct cs_timing cs[MAX_CS_COUNT]; 65c7995bcbSSven Van Asbroeck }; 66c7995bcbSSven Van Asbroeck 67e6cb5408SIvan Bornyakov struct weim_priv { 68e6cb5408SIvan Bornyakov void __iomem *base; 69e6cb5408SIvan Bornyakov struct cs_timing_state timing_state; 70e6cb5408SIvan Bornyakov }; 71e6cb5408SIvan Bornyakov 7285bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = { 733f98b6baSAlexander Shiyan /* i.MX1/21 */ 743f98b6baSAlexander Shiyan { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, }, 753f98b6baSAlexander Shiyan /* i.MX25/27/31/35 */ 763f98b6baSAlexander Shiyan { .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, }, 773f98b6baSAlexander Shiyan /* i.MX50/53/6Q */ 783f98b6baSAlexander Shiyan { .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, }, 793f98b6baSAlexander Shiyan { .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, }, 803f98b6baSAlexander Shiyan /* i.MX51 */ 813f98b6baSAlexander Shiyan { .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, }, 8285bf6d4eSHuang Shijie { } 8385bf6d4eSHuang Shijie }; 8485bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table); 8585bf6d4eSHuang Shijie 863b1261fbSArnd Bergmann static int imx_weim_gpr_setup(struct platform_device *pdev) 878d9ee21eSShawn Guo { 888d9ee21eSShawn Guo struct device_node *np = pdev->dev.of_node; 898d9ee21eSShawn Guo struct property *prop; 908d9ee21eSShawn Guo const __be32 *p; 918d9ee21eSShawn Guo struct regmap *gpr; 928d9ee21eSShawn Guo u32 gprvals[4] = { 938d9ee21eSShawn Guo 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */ 948d9ee21eSShawn Guo 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */ 958d9ee21eSShawn Guo 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */ 968d9ee21eSShawn Guo 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */ 978d9ee21eSShawn Guo }; 988d9ee21eSShawn Guo u32 gprval = 0; 998d9ee21eSShawn Guo u32 val; 1008d9ee21eSShawn Guo int cs = 0; 1018d9ee21eSShawn Guo int i = 0; 1028d9ee21eSShawn Guo 1038d9ee21eSShawn Guo gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr"); 1048d9ee21eSShawn Guo if (IS_ERR(gpr)) { 1058d9ee21eSShawn Guo dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n"); 1068d9ee21eSShawn Guo return 0; 1078d9ee21eSShawn Guo } 1088d9ee21eSShawn Guo 1098d9ee21eSShawn Guo of_property_for_each_u32(np, "ranges", prop, p, val) { 1108d9ee21eSShawn Guo if (i % 4 == 0) { 1118d9ee21eSShawn Guo cs = val; 1128d9ee21eSShawn Guo } else if (i % 4 == 3 && val) { 1138d9ee21eSShawn Guo val = (val / SZ_32M) | 1; 1148d9ee21eSShawn Guo gprval |= val << cs * 3; 1158d9ee21eSShawn Guo } 1168d9ee21eSShawn Guo i++; 1178d9ee21eSShawn Guo } 1188d9ee21eSShawn Guo 1198d9ee21eSShawn Guo if (i == 0 || i % 4) 1208d9ee21eSShawn Guo goto err; 1218d9ee21eSShawn Guo 1228d9ee21eSShawn Guo for (i = 0; i < ARRAY_SIZE(gprvals); i++) { 1238d9ee21eSShawn Guo if (gprval == gprvals[i]) { 1248d9ee21eSShawn Guo /* Found it. Set up IOMUXC_GPR1[11:0] with it. */ 1258d9ee21eSShawn Guo regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval); 1268d9ee21eSShawn Guo return 0; 1278d9ee21eSShawn Guo } 1288d9ee21eSShawn Guo } 1298d9ee21eSShawn Guo 1308d9ee21eSShawn Guo err: 1318d9ee21eSShawn Guo dev_err(&pdev->dev, "Invalid 'ranges' configuration\n"); 1328d9ee21eSShawn Guo return -EINVAL; 1338d9ee21eSShawn Guo } 1348d9ee21eSShawn Guo 13585bf6d4eSHuang Shijie /* Parse and set the timing for this device. */ 136e6cb5408SIvan Bornyakov static int weim_timing_setup(struct device *dev, struct device_node *np, 137e6cb5408SIvan Bornyakov const struct imx_weim_devtype *devtype) 13885bf6d4eSHuang Shijie { 139d8dfa59fSKees Cook u32 cs_idx, value[MAX_CS_REGS_COUNT]; 1403f98b6baSAlexander Shiyan int i, ret; 1418b8cb52aSSven Van Asbroeck int reg_idx, num_regs; 142c7995bcbSSven Van Asbroeck struct cs_timing *cst; 143e6cb5408SIvan Bornyakov struct weim_priv *priv; 144e6cb5408SIvan Bornyakov struct cs_timing_state *ts; 145e6cb5408SIvan Bornyakov void __iomem *base; 14685bf6d4eSHuang Shijie 147d8dfa59fSKees Cook if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) 148d8dfa59fSKees Cook return -EINVAL; 149c7995bcbSSven Van Asbroeck if (WARN_ON(devtype->cs_count > MAX_CS_COUNT)) 150c7995bcbSSven Van Asbroeck return -EINVAL; 151d8dfa59fSKees Cook 152e6cb5408SIvan Bornyakov priv = dev_get_drvdata(dev); 153e6cb5408SIvan Bornyakov base = priv->base; 154e6cb5408SIvan Bornyakov ts = &priv->timing_state; 155e6cb5408SIvan Bornyakov 15685bf6d4eSHuang Shijie ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", 1573f98b6baSAlexander Shiyan value, devtype->cs_regs_count); 15885bf6d4eSHuang Shijie if (ret) 15985bf6d4eSHuang Shijie return ret; 16085bf6d4eSHuang Shijie 1618b8cb52aSSven Van Asbroeck /* 1628b8cb52aSSven Van Asbroeck * the child node's "reg" property may contain multiple address ranges, 1638b8cb52aSSven Van Asbroeck * extract the chip select for each. 1648b8cb52aSSven Van Asbroeck */ 1658b8cb52aSSven Van Asbroeck num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE); 1668b8cb52aSSven Van Asbroeck if (num_regs < 0) 1678b8cb52aSSven Van Asbroeck return num_regs; 1688b8cb52aSSven Van Asbroeck if (!num_regs) 1698b8cb52aSSven Van Asbroeck return -EINVAL; 1708b8cb52aSSven Van Asbroeck for (reg_idx = 0; reg_idx < num_regs; reg_idx++) { 1718b8cb52aSSven Van Asbroeck /* get the CS index from this child node's "reg" property. */ 1728b8cb52aSSven Van Asbroeck ret = of_property_read_u32_index(np, "reg", 1738b8cb52aSSven Van Asbroeck reg_idx * OF_REG_SIZE, &cs_idx); 1748b8cb52aSSven Van Asbroeck if (ret) 1758b8cb52aSSven Van Asbroeck break; 1768b8cb52aSSven Van Asbroeck 1778b8cb52aSSven Van Asbroeck if (cs_idx >= devtype->cs_count) 1788b8cb52aSSven Van Asbroeck return -EINVAL; 1798b8cb52aSSven Van Asbroeck 180c7995bcbSSven Van Asbroeck /* prevent re-configuring a CS that's already been configured */ 181c7995bcbSSven Van Asbroeck cst = &ts->cs[cs_idx]; 182c7995bcbSSven Van Asbroeck if (cst->is_applied && memcmp(value, cst->regs, 183c7995bcbSSven Van Asbroeck devtype->cs_regs_count * sizeof(u32))) { 184c7995bcbSSven Van Asbroeck dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np); 185c7995bcbSSven Van Asbroeck return -EINVAL; 186c7995bcbSSven Van Asbroeck } 187c7995bcbSSven Van Asbroeck 18885bf6d4eSHuang Shijie /* set the timing for WEIM */ 1893f98b6baSAlexander Shiyan for (i = 0; i < devtype->cs_regs_count; i++) 1908b8cb52aSSven Van Asbroeck writel(value[i], 1918b8cb52aSSven Van Asbroeck base + cs_idx * devtype->cs_stride + i * 4); 192c7995bcbSSven Van Asbroeck if (!cst->is_applied) { 193c7995bcbSSven Van Asbroeck cst->is_applied = true; 194c7995bcbSSven Van Asbroeck memcpy(cst->regs, value, 195c7995bcbSSven Van Asbroeck devtype->cs_regs_count * sizeof(u32)); 196c7995bcbSSven Van Asbroeck } 1978b8cb52aSSven Van Asbroeck } 1983f98b6baSAlexander Shiyan 19985bf6d4eSHuang Shijie return 0; 20085bf6d4eSHuang Shijie } 20185bf6d4eSHuang Shijie 202e6cb5408SIvan Bornyakov static int weim_parse_dt(struct platform_device *pdev) 20385bf6d4eSHuang Shijie { 2043f98b6baSAlexander Shiyan const struct of_device_id *of_id = of_match_device(weim_id_table, 2053f98b6baSAlexander Shiyan &pdev->dev); 2063f98b6baSAlexander Shiyan const struct imx_weim_devtype *devtype = of_id->data; 20785bf6d4eSHuang Shijie struct device_node *child; 20852c47b63SAlison Chaiken int ret, have_child = 0; 209e6cb5408SIvan Bornyakov struct weim_priv *priv; 210e6cb5408SIvan Bornyakov void __iomem *base; 21177266e72SSven Van Asbroeck u32 reg; 21285bf6d4eSHuang Shijie 2138d9ee21eSShawn Guo if (devtype == &imx50_weim_devtype) { 2148d9ee21eSShawn Guo ret = imx_weim_gpr_setup(pdev); 2158d9ee21eSShawn Guo if (ret) 2168d9ee21eSShawn Guo return ret; 2178d9ee21eSShawn Guo } 2188d9ee21eSShawn Guo 219e6cb5408SIvan Bornyakov priv = dev_get_drvdata(&pdev->dev); 220e6cb5408SIvan Bornyakov base = priv->base; 221e6cb5408SIvan Bornyakov 22277266e72SSven Van Asbroeck if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) { 22377266e72SSven Van Asbroeck if (devtype->wcr_bcm) { 22477266e72SSven Van Asbroeck reg = readl(base + devtype->wcr_offset); 2257b983da3SIvan Bornyakov reg |= devtype->wcr_bcm; 2267b983da3SIvan Bornyakov 2277b983da3SIvan Bornyakov if (of_property_read_bool(pdev->dev.of_node, 2287b983da3SIvan Bornyakov "fsl,continuous-burst-clk")) { 2297b983da3SIvan Bornyakov if (devtype->wcr_cont_bclk) { 2307b983da3SIvan Bornyakov reg |= devtype->wcr_cont_bclk; 2317b983da3SIvan Bornyakov } else { 2327b983da3SIvan Bornyakov dev_err(&pdev->dev, 2337b983da3SIvan Bornyakov "continuous burst clk not supported.\n"); 2347b983da3SIvan Bornyakov return -EINVAL; 2357b983da3SIvan Bornyakov } 2367b983da3SIvan Bornyakov } 2377b983da3SIvan Bornyakov 2387b983da3SIvan Bornyakov writel(reg, base + devtype->wcr_offset); 23977266e72SSven Van Asbroeck } else { 24077266e72SSven Van Asbroeck dev_err(&pdev->dev, "burst clk mode not supported.\n"); 24177266e72SSven Van Asbroeck return -EINVAL; 24277266e72SSven Van Asbroeck } 24377266e72SSven Van Asbroeck } 24477266e72SSven Van Asbroeck 24533b96d2cSFabio Estevam for_each_available_child_of_node(pdev->dev.of_node, child) { 246e6cb5408SIvan Bornyakov ret = weim_timing_setup(&pdev->dev, child, devtype); 24752c47b63SAlison Chaiken if (ret) 2489c0982d8SRob Herring dev_warn(&pdev->dev, "%pOF set timing failed.\n", 2499c0982d8SRob Herring child); 25052c47b63SAlison Chaiken else 25152c47b63SAlison Chaiken have_child = 1; 25285bf6d4eSHuang Shijie } 25385bf6d4eSHuang Shijie 25452c47b63SAlison Chaiken if (have_child) 25539ec8d38SKefeng Wang ret = of_platform_default_populate(pdev->dev.of_node, 25626651c43SLiu Ying NULL, &pdev->dev); 25785bf6d4eSHuang Shijie if (ret) 2589c0982d8SRob Herring dev_err(&pdev->dev, "%pOF fail to create devices.\n", 2599c0982d8SRob Herring pdev->dev.of_node); 26085bf6d4eSHuang Shijie return ret; 26185bf6d4eSHuang Shijie } 26285bf6d4eSHuang Shijie 2634a92f078SSascha Hauer static int weim_probe(struct platform_device *pdev) 26485bf6d4eSHuang Shijie { 265e6cb5408SIvan Bornyakov struct weim_priv *priv; 26685bf6d4eSHuang Shijie struct resource *res; 26770ac98daSAlexander Shiyan struct clk *clk; 26870ac98daSAlexander Shiyan void __iomem *base; 269b2d1fb73SAlexander Shiyan int ret; 27085bf6d4eSHuang Shijie 271e6cb5408SIvan Bornyakov priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 272e6cb5408SIvan Bornyakov if (!priv) 273e6cb5408SIvan Bornyakov return -ENOMEM; 274e6cb5408SIvan Bornyakov 27585bf6d4eSHuang Shijie /* get the resource */ 27685bf6d4eSHuang Shijie res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 27770ac98daSAlexander Shiyan base = devm_ioremap_resource(&pdev->dev, res); 278b2d1fb73SAlexander Shiyan if (IS_ERR(base)) 279b2d1fb73SAlexander Shiyan return PTR_ERR(base); 28085bf6d4eSHuang Shijie 281e6cb5408SIvan Bornyakov priv->base = base; 282e6cb5408SIvan Bornyakov dev_set_drvdata(&pdev->dev, priv); 283e6cb5408SIvan Bornyakov 28485bf6d4eSHuang Shijie /* get the clock */ 28570ac98daSAlexander Shiyan clk = devm_clk_get(&pdev->dev, NULL); 28670ac98daSAlexander Shiyan if (IS_ERR(clk)) 287b2d1fb73SAlexander Shiyan return PTR_ERR(clk); 28885bf6d4eSHuang Shijie 28970ac98daSAlexander Shiyan ret = clk_prepare_enable(clk); 29085bf6d4eSHuang Shijie if (ret) 291b2d1fb73SAlexander Shiyan return ret; 29285bf6d4eSHuang Shijie 29385bf6d4eSHuang Shijie /* parse the device node */ 294e6cb5408SIvan Bornyakov ret = weim_parse_dt(pdev); 295b2d1fb73SAlexander Shiyan if (ret) 29670ac98daSAlexander Shiyan clk_disable_unprepare(clk); 297b2d1fb73SAlexander Shiyan else 298b2d1fb73SAlexander Shiyan dev_info(&pdev->dev, "Driver registered.\n"); 29985bf6d4eSHuang Shijie 30085bf6d4eSHuang Shijie return ret; 30185bf6d4eSHuang Shijie } 30285bf6d4eSHuang Shijie 303e6cb5408SIvan Bornyakov #if IS_ENABLED(CONFIG_OF_DYNAMIC) 304e6cb5408SIvan Bornyakov static int of_weim_notify(struct notifier_block *nb, unsigned long action, 305e6cb5408SIvan Bornyakov void *arg) 306e6cb5408SIvan Bornyakov { 307e6cb5408SIvan Bornyakov const struct imx_weim_devtype *devtype; 308e6cb5408SIvan Bornyakov struct of_reconfig_data *rd = arg; 309e6cb5408SIvan Bornyakov const struct of_device_id *of_id; 310e6cb5408SIvan Bornyakov struct platform_device *pdev; 311e6cb5408SIvan Bornyakov int ret = NOTIFY_OK; 312e6cb5408SIvan Bornyakov 313e6cb5408SIvan Bornyakov switch (of_reconfig_get_state_change(action, rd)) { 314e6cb5408SIvan Bornyakov case OF_RECONFIG_CHANGE_ADD: 315e6cb5408SIvan Bornyakov of_id = of_match_node(weim_id_table, rd->dn->parent); 316e6cb5408SIvan Bornyakov if (!of_id) 317e6cb5408SIvan Bornyakov return NOTIFY_OK; /* not for us */ 318e6cb5408SIvan Bornyakov 319e6cb5408SIvan Bornyakov devtype = of_id->data; 320e6cb5408SIvan Bornyakov 321e6cb5408SIvan Bornyakov pdev = of_find_device_by_node(rd->dn->parent); 322e6cb5408SIvan Bornyakov if (!pdev) { 323e6cb5408SIvan Bornyakov pr_err("%s: could not find platform device for '%pOF'\n", 324e6cb5408SIvan Bornyakov __func__, rd->dn->parent); 325e6cb5408SIvan Bornyakov 326e6cb5408SIvan Bornyakov return notifier_from_errno(-EINVAL); 327e6cb5408SIvan Bornyakov } 328e6cb5408SIvan Bornyakov 329e6cb5408SIvan Bornyakov if (weim_timing_setup(&pdev->dev, rd->dn, devtype)) 330e6cb5408SIvan Bornyakov dev_warn(&pdev->dev, 331e6cb5408SIvan Bornyakov "Failed to setup timing for '%pOF'\n", rd->dn); 332e6cb5408SIvan Bornyakov 333e6cb5408SIvan Bornyakov if (!of_node_check_flag(rd->dn, OF_POPULATED)) { 334*1a50d940SGeert Uytterhoeven /* 335*1a50d940SGeert Uytterhoeven * Clear the flag before adding the device so that 336*1a50d940SGeert Uytterhoeven * fw_devlink doesn't skip adding consumers to this 337*1a50d940SGeert Uytterhoeven * device. 338*1a50d940SGeert Uytterhoeven */ 339*1a50d940SGeert Uytterhoeven rd->dn->fwnode.flags &= ~FWNODE_FLAG_NOT_DEVICE; 340e6cb5408SIvan Bornyakov if (!of_platform_device_create(rd->dn, NULL, &pdev->dev)) { 341e6cb5408SIvan Bornyakov dev_err(&pdev->dev, 342e6cb5408SIvan Bornyakov "Failed to create child device '%pOF'\n", 343e6cb5408SIvan Bornyakov rd->dn); 344e6cb5408SIvan Bornyakov ret = notifier_from_errno(-EINVAL); 345e6cb5408SIvan Bornyakov } 346e6cb5408SIvan Bornyakov } 347e6cb5408SIvan Bornyakov 348e6cb5408SIvan Bornyakov platform_device_put(pdev); 349e6cb5408SIvan Bornyakov 350e6cb5408SIvan Bornyakov break; 351e6cb5408SIvan Bornyakov case OF_RECONFIG_CHANGE_REMOVE: 352e6cb5408SIvan Bornyakov if (!of_node_check_flag(rd->dn, OF_POPULATED)) 353e6cb5408SIvan Bornyakov return NOTIFY_OK; /* device already destroyed */ 354e6cb5408SIvan Bornyakov 355e6cb5408SIvan Bornyakov of_id = of_match_node(weim_id_table, rd->dn->parent); 356e6cb5408SIvan Bornyakov if (!of_id) 357e6cb5408SIvan Bornyakov return NOTIFY_OK; /* not for us */ 358e6cb5408SIvan Bornyakov 359e6cb5408SIvan Bornyakov pdev = of_find_device_by_node(rd->dn); 360e6cb5408SIvan Bornyakov if (!pdev) { 3619b6d368bSWan Jiabing pr_err("Could not find platform device for '%pOF'\n", 362e6cb5408SIvan Bornyakov rd->dn); 363e6cb5408SIvan Bornyakov 364e6cb5408SIvan Bornyakov ret = notifier_from_errno(-EINVAL); 365e6cb5408SIvan Bornyakov } else { 366e6cb5408SIvan Bornyakov of_platform_device_destroy(&pdev->dev, NULL); 367e6cb5408SIvan Bornyakov platform_device_put(pdev); 368e6cb5408SIvan Bornyakov } 369e6cb5408SIvan Bornyakov 370e6cb5408SIvan Bornyakov break; 371e6cb5408SIvan Bornyakov default: 372e6cb5408SIvan Bornyakov break; 373e6cb5408SIvan Bornyakov } 374e6cb5408SIvan Bornyakov 375e6cb5408SIvan Bornyakov return ret; 376e6cb5408SIvan Bornyakov } 377e6cb5408SIvan Bornyakov 3788be9cdc6SWei Yongjun static struct notifier_block weim_of_notifier = { 379e6cb5408SIvan Bornyakov .notifier_call = of_weim_notify, 380e6cb5408SIvan Bornyakov }; 381e6cb5408SIvan Bornyakov #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 382e6cb5408SIvan Bornyakov 38385bf6d4eSHuang Shijie static struct platform_driver weim_driver = { 38485bf6d4eSHuang Shijie .driver = { 38585bf6d4eSHuang Shijie .name = "imx-weim", 38685bf6d4eSHuang Shijie .of_match_table = weim_id_table, 38785bf6d4eSHuang Shijie }, 3884a92f078SSascha Hauer .probe = weim_probe, 38985bf6d4eSHuang Shijie }; 390e6cb5408SIvan Bornyakov 391e6cb5408SIvan Bornyakov static int __init weim_init(void) 392e6cb5408SIvan Bornyakov { 393e6cb5408SIvan Bornyakov #if IS_ENABLED(CONFIG_OF_DYNAMIC) 394e6cb5408SIvan Bornyakov WARN_ON(of_reconfig_notifier_register(&weim_of_notifier)); 395e6cb5408SIvan Bornyakov #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 396e6cb5408SIvan Bornyakov 397e6cb5408SIvan Bornyakov return platform_driver_register(&weim_driver); 398e6cb5408SIvan Bornyakov } 399e6cb5408SIvan Bornyakov module_init(weim_init); 400e6cb5408SIvan Bornyakov 401e6cb5408SIvan Bornyakov static void __exit weim_exit(void) 402e6cb5408SIvan Bornyakov { 403e6cb5408SIvan Bornyakov #if IS_ENABLED(CONFIG_OF_DYNAMIC) 404e6cb5408SIvan Bornyakov of_reconfig_notifier_unregister(&weim_of_notifier); 405e6cb5408SIvan Bornyakov #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */ 406e6cb5408SIvan Bornyakov 407e6cb5408SIvan Bornyakov return platform_driver_unregister(&weim_driver); 408e6cb5408SIvan Bornyakov 409e6cb5408SIvan Bornyakov } 410e6cb5408SIvan Bornyakov module_exit(weim_exit); 41185bf6d4eSHuang Shijie 41285bf6d4eSHuang Shijie MODULE_AUTHOR("Freescale Semiconductor Inc."); 41385bf6d4eSHuang Shijie MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 41485bf6d4eSHuang Shijie MODULE_LICENSE("GPL"); 415