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> 132a88e479SRob Herring #include <linux/of_address.h> 14fcefbb49SRob Herring #include <linux/of.h> 15fcefbb49SRob Herring #include <linux/of_platform.h> 16fcefbb49SRob Herring #include <linux/platform_device.h> 17fcefbb49SRob Herring #include <linux/property.h> 188d9ee21eSShawn Guo #include <linux/mfd/syscon.h> 198d9ee21eSShawn Guo #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> 208d9ee21eSShawn Guo #include <linux/regmap.h> 2185bf6d4eSHuang Shijie 223f98b6baSAlexander Shiyan struct imx_weim_devtype { 233f98b6baSAlexander Shiyan unsigned int cs_count; 243f98b6baSAlexander Shiyan unsigned int cs_regs_count; 253f98b6baSAlexander Shiyan unsigned int cs_stride; 2677266e72SSven Van Asbroeck unsigned int wcr_offset; 2777266e72SSven Van Asbroeck unsigned int wcr_bcm; 287b983da3SIvan Bornyakov unsigned int wcr_cont_bclk; 293f98b6baSAlexander Shiyan }; 303f98b6baSAlexander Shiyan 313f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx1_weim_devtype = { 323f98b6baSAlexander Shiyan .cs_count = 6, 333f98b6baSAlexander Shiyan .cs_regs_count = 2, 343f98b6baSAlexander Shiyan .cs_stride = 0x08, 353f98b6baSAlexander Shiyan }; 363f98b6baSAlexander Shiyan 373f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx27_weim_devtype = { 383f98b6baSAlexander Shiyan .cs_count = 6, 393f98b6baSAlexander Shiyan .cs_regs_count = 3, 403f98b6baSAlexander Shiyan .cs_stride = 0x10, 413f98b6baSAlexander Shiyan }; 423f98b6baSAlexander Shiyan 433f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx50_weim_devtype = { 443f98b6baSAlexander Shiyan .cs_count = 4, 453f98b6baSAlexander Shiyan .cs_regs_count = 6, 463f98b6baSAlexander Shiyan .cs_stride = 0x18, 4777266e72SSven Van Asbroeck .wcr_offset = 0x90, 4877266e72SSven Van Asbroeck .wcr_bcm = BIT(0), 497b983da3SIvan Bornyakov .wcr_cont_bclk = BIT(3), 503f98b6baSAlexander Shiyan }; 513f98b6baSAlexander Shiyan 523f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx51_weim_devtype = { 533f98b6baSAlexander Shiyan .cs_count = 6, 543f98b6baSAlexander Shiyan .cs_regs_count = 6, 553f98b6baSAlexander Shiyan .cs_stride = 0x18, 563f98b6baSAlexander Shiyan }; 573f98b6baSAlexander Shiyan 58d8dfa59fSKees Cook #define MAX_CS_REGS_COUNT 6 59c7995bcbSSven Van Asbroeck #define MAX_CS_COUNT 6 608b8cb52aSSven Van Asbroeck #define OF_REG_SIZE 3 61d8dfa59fSKees Cook 62c7995bcbSSven Van Asbroeck struct cs_timing { 63c7995bcbSSven Van Asbroeck bool is_applied; 64c7995bcbSSven Van Asbroeck u32 regs[MAX_CS_REGS_COUNT]; 65c7995bcbSSven Van Asbroeck }; 66c7995bcbSSven Van Asbroeck 67c7995bcbSSven Van Asbroeck struct cs_timing_state { 68c7995bcbSSven Van Asbroeck struct cs_timing cs[MAX_CS_COUNT]; 69c7995bcbSSven Van Asbroeck }; 70c7995bcbSSven Van Asbroeck 71e6cb5408SIvan Bornyakov struct weim_priv { 72e6cb5408SIvan Bornyakov void __iomem *base; 73e6cb5408SIvan Bornyakov struct cs_timing_state timing_state; 74e6cb5408SIvan Bornyakov }; 75e6cb5408SIvan Bornyakov 7685bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = { 773f98b6baSAlexander Shiyan /* i.MX1/21 */ 783f98b6baSAlexander Shiyan { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, }, 793f98b6baSAlexander Shiyan /* i.MX25/27/31/35 */ 803f98b6baSAlexander Shiyan { .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, }, 813f98b6baSAlexander Shiyan /* i.MX50/53/6Q */ 823f98b6baSAlexander Shiyan { .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, }, 833f98b6baSAlexander Shiyan { .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, }, 843f98b6baSAlexander Shiyan /* i.MX51 */ 853f98b6baSAlexander Shiyan { .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, }, 8685bf6d4eSHuang Shijie { } 8785bf6d4eSHuang Shijie }; 8885bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table); 8985bf6d4eSHuang Shijie 903b1261fbSArnd Bergmann static int imx_weim_gpr_setup(struct platform_device *pdev) 918d9ee21eSShawn Guo { 928d9ee21eSShawn Guo struct device_node *np = pdev->dev.of_node; 932a88e479SRob Herring struct of_range_parser parser; 942a88e479SRob Herring struct of_range range; 958d9ee21eSShawn Guo struct regmap *gpr; 968d9ee21eSShawn Guo u32 gprvals[4] = { 978d9ee21eSShawn Guo 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */ 988d9ee21eSShawn Guo 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */ 998d9ee21eSShawn Guo 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */ 1008d9ee21eSShawn Guo 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */ 1018d9ee21eSShawn Guo }; 1028d9ee21eSShawn Guo u32 gprval = 0; 1038d9ee21eSShawn Guo u32 val; 1048d9ee21eSShawn Guo int cs = 0; 1058d9ee21eSShawn Guo int i = 0; 1068d9ee21eSShawn Guo 1078d9ee21eSShawn Guo gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr"); 1088d9ee21eSShawn Guo if (IS_ERR(gpr)) { 1098d9ee21eSShawn Guo dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n"); 1108d9ee21eSShawn Guo return 0; 1118d9ee21eSShawn Guo } 1128d9ee21eSShawn Guo 1132a88e479SRob Herring if (of_range_parser_init(&parser, np)) 1142a88e479SRob Herring goto err; 1152a88e479SRob Herring 1162a88e479SRob Herring for_each_of_range(&parser, &range) { 1172a88e479SRob Herring cs = range.bus_addr >> 32; 1182a88e479SRob Herring val = (range.size / SZ_32M) | 1; 1198d9ee21eSShawn Guo gprval |= val << cs * 3; 1208d9ee21eSShawn Guo i++; 1218d9ee21eSShawn Guo } 1228d9ee21eSShawn Guo 123*7bca405cSLucas Stach if (i == 0) 1248d9ee21eSShawn Guo goto err; 1258d9ee21eSShawn Guo 1268d9ee21eSShawn Guo for (i = 0; i < ARRAY_SIZE(gprvals); i++) { 1278d9ee21eSShawn Guo if (gprval == gprvals[i]) { 1288d9ee21eSShawn Guo /* Found it. Set up IOMUXC_GPR1[11:0] with it. */ 1298d9ee21eSShawn Guo regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval); 1308d9ee21eSShawn Guo return 0; 1318d9ee21eSShawn Guo } 1328d9ee21eSShawn Guo } 1338d9ee21eSShawn Guo 1348d9ee21eSShawn Guo err: 1358d9ee21eSShawn Guo dev_err(&pdev->dev, "Invalid 'ranges' configuration\n"); 1368d9ee21eSShawn Guo return -EINVAL; 1378d9ee21eSShawn Guo } 1388d9ee21eSShawn Guo 13985bf6d4eSHuang Shijie /* Parse and set the timing for this device. */ 140e6cb5408SIvan Bornyakov static int weim_timing_setup(struct device *dev, struct device_node *np, 141e6cb5408SIvan Bornyakov const struct imx_weim_devtype *devtype) 14285bf6d4eSHuang Shijie { 143d8dfa59fSKees Cook u32 cs_idx, value[MAX_CS_REGS_COUNT]; 1443f98b6baSAlexander Shiyan int i, ret; 1458b8cb52aSSven Van Asbroeck int reg_idx, num_regs; 146c7995bcbSSven Van Asbroeck struct cs_timing *cst; 147e6cb5408SIvan Bornyakov struct weim_priv *priv; 148e6cb5408SIvan Bornyakov struct cs_timing_state *ts; 149e6cb5408SIvan Bornyakov void __iomem *base; 15085bf6d4eSHuang Shijie 151d8dfa59fSKees Cook if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) 152d8dfa59fSKees Cook return -EINVAL; 153c7995bcbSSven Van Asbroeck if (WARN_ON(devtype->cs_count > MAX_CS_COUNT)) 154c7995bcbSSven Van Asbroeck return -EINVAL; 155d8dfa59fSKees Cook 156e6cb5408SIvan Bornyakov priv = dev_get_drvdata(dev); 157e6cb5408SIvan Bornyakov base = priv->base; 158e6cb5408SIvan Bornyakov ts = &priv->timing_state; 159e6cb5408SIvan Bornyakov 16085bf6d4eSHuang Shijie ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", 1613f98b6baSAlexander Shiyan value, devtype->cs_regs_count); 16285bf6d4eSHuang Shijie if (ret) 16385bf6d4eSHuang Shijie return ret; 16485bf6d4eSHuang Shijie 1658b8cb52aSSven Van Asbroeck /* 1668b8cb52aSSven Van Asbroeck * the child node's "reg" property may contain multiple address ranges, 1678b8cb52aSSven Van Asbroeck * extract the chip select for each. 1688b8cb52aSSven Van Asbroeck */ 1698b8cb52aSSven Van Asbroeck num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE); 1708b8cb52aSSven Van Asbroeck if (num_regs < 0) 1718b8cb52aSSven Van Asbroeck return num_regs; 1728b8cb52aSSven Van Asbroeck if (!num_regs) 1738b8cb52aSSven Van Asbroeck return -EINVAL; 1748b8cb52aSSven Van Asbroeck for (reg_idx = 0; reg_idx < num_regs; reg_idx++) { 1758b8cb52aSSven Van Asbroeck /* get the CS index from this child node's "reg" property. */ 1768b8cb52aSSven Van Asbroeck ret = of_property_read_u32_index(np, "reg", 1778b8cb52aSSven Van Asbroeck reg_idx * OF_REG_SIZE, &cs_idx); 1788b8cb52aSSven Van Asbroeck if (ret) 1798b8cb52aSSven Van Asbroeck break; 1808b8cb52aSSven Van Asbroeck 1818b8cb52aSSven Van Asbroeck if (cs_idx >= devtype->cs_count) 1828b8cb52aSSven Van Asbroeck return -EINVAL; 1838b8cb52aSSven Van Asbroeck 184c7995bcbSSven Van Asbroeck /* prevent re-configuring a CS that's already been configured */ 185c7995bcbSSven Van Asbroeck cst = &ts->cs[cs_idx]; 186c7995bcbSSven Van Asbroeck if (cst->is_applied && memcmp(value, cst->regs, 187c7995bcbSSven Van Asbroeck devtype->cs_regs_count * sizeof(u32))) { 188c7995bcbSSven Van Asbroeck dev_err(dev, "fsl,weim-cs-timing conflict on %pOF", np); 189c7995bcbSSven Van Asbroeck return -EINVAL; 190c7995bcbSSven Van Asbroeck } 191c7995bcbSSven Van Asbroeck 19285bf6d4eSHuang Shijie /* set the timing for WEIM */ 1933f98b6baSAlexander Shiyan for (i = 0; i < devtype->cs_regs_count; i++) 1948b8cb52aSSven Van Asbroeck writel(value[i], 1958b8cb52aSSven Van Asbroeck base + cs_idx * devtype->cs_stride + i * 4); 196c7995bcbSSven Van Asbroeck if (!cst->is_applied) { 197c7995bcbSSven Van Asbroeck cst->is_applied = true; 198c7995bcbSSven Van Asbroeck memcpy(cst->regs, value, 199c7995bcbSSven Van Asbroeck devtype->cs_regs_count * sizeof(u32)); 200c7995bcbSSven Van Asbroeck } 2018b8cb52aSSven Van Asbroeck } 2023f98b6baSAlexander Shiyan 20385bf6d4eSHuang Shijie return 0; 20485bf6d4eSHuang Shijie } 20585bf6d4eSHuang Shijie 206e6cb5408SIvan Bornyakov static int weim_parse_dt(struct platform_device *pdev) 20785bf6d4eSHuang Shijie { 208fcefbb49SRob Herring const struct imx_weim_devtype *devtype = device_get_match_data(&pdev->dev); 2091adab292SIvan Bornyakov int ret = 0, have_child = 0; 21085bf6d4eSHuang Shijie struct device_node *child; 211e6cb5408SIvan Bornyakov struct weim_priv *priv; 212e6cb5408SIvan Bornyakov void __iomem *base; 21377266e72SSven Van Asbroeck u32 reg; 21485bf6d4eSHuang Shijie 2158d9ee21eSShawn Guo if (devtype == &imx50_weim_devtype) { 2168d9ee21eSShawn Guo ret = imx_weim_gpr_setup(pdev); 2178d9ee21eSShawn Guo if (ret) 2188d9ee21eSShawn Guo return ret; 2198d9ee21eSShawn Guo } 2208d9ee21eSShawn Guo 221e6cb5408SIvan Bornyakov priv = dev_get_drvdata(&pdev->dev); 222e6cb5408SIvan Bornyakov base = priv->base; 223e6cb5408SIvan Bornyakov 22477266e72SSven Van Asbroeck if (of_property_read_bool(pdev->dev.of_node, "fsl,burst-clk-enable")) { 22577266e72SSven Van Asbroeck if (devtype->wcr_bcm) { 22677266e72SSven Van Asbroeck reg = readl(base + devtype->wcr_offset); 2277b983da3SIvan Bornyakov reg |= devtype->wcr_bcm; 2287b983da3SIvan Bornyakov 2297b983da3SIvan Bornyakov if (of_property_read_bool(pdev->dev.of_node, 2307b983da3SIvan Bornyakov "fsl,continuous-burst-clk")) { 2317b983da3SIvan Bornyakov if (devtype->wcr_cont_bclk) { 2327b983da3SIvan Bornyakov reg |= devtype->wcr_cont_bclk; 2337b983da3SIvan Bornyakov } else { 2347b983da3SIvan Bornyakov dev_err(&pdev->dev, 2357b983da3SIvan Bornyakov "continuous burst clk not supported.\n"); 2367b983da3SIvan Bornyakov return -EINVAL; 2377b983da3SIvan Bornyakov } 2387b983da3SIvan Bornyakov } 2397b983da3SIvan Bornyakov 2407b983da3SIvan Bornyakov writel(reg, base + devtype->wcr_offset); 24177266e72SSven Van Asbroeck } else { 24277266e72SSven Van Asbroeck dev_err(&pdev->dev, "burst clk mode not supported.\n"); 24377266e72SSven Van Asbroeck return -EINVAL; 24477266e72SSven Van Asbroeck } 24577266e72SSven Van Asbroeck } 24677266e72SSven Van Asbroeck 24733b96d2cSFabio Estevam for_each_available_child_of_node(pdev->dev.of_node, child) { 248e6cb5408SIvan Bornyakov ret = weim_timing_setup(&pdev->dev, child, devtype); 24952c47b63SAlison Chaiken if (ret) 2509c0982d8SRob Herring dev_warn(&pdev->dev, "%pOF set timing failed.\n", 2519c0982d8SRob Herring child); 25252c47b63SAlison Chaiken else 25352c47b63SAlison Chaiken have_child = 1; 25485bf6d4eSHuang Shijie } 25585bf6d4eSHuang Shijie 25652c47b63SAlison Chaiken if (have_child) 25739ec8d38SKefeng Wang ret = of_platform_default_populate(pdev->dev.of_node, 25826651c43SLiu Ying NULL, &pdev->dev); 25985bf6d4eSHuang Shijie if (ret) 2609c0982d8SRob Herring dev_err(&pdev->dev, "%pOF fail to create devices.\n", 2619c0982d8SRob Herring pdev->dev.of_node); 26285bf6d4eSHuang Shijie return ret; 26385bf6d4eSHuang Shijie } 26485bf6d4eSHuang Shijie 2654a92f078SSascha Hauer static int weim_probe(struct platform_device *pdev) 26685bf6d4eSHuang Shijie { 267e6cb5408SIvan Bornyakov struct weim_priv *priv; 26870ac98daSAlexander Shiyan struct clk *clk; 26970ac98daSAlexander Shiyan void __iomem *base; 270b2d1fb73SAlexander Shiyan int ret; 27185bf6d4eSHuang Shijie 272e6cb5408SIvan Bornyakov priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 273e6cb5408SIvan Bornyakov if (!priv) 274e6cb5408SIvan Bornyakov return -ENOMEM; 275e6cb5408SIvan Bornyakov 27685bf6d4eSHuang Shijie /* get the resource */ 2770e40e5feSMartin Kaiser base = devm_platform_ioremap_resource(pdev, 0); 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)) { 3341a50d940SGeert Uytterhoeven /* 3351a50d940SGeert Uytterhoeven * Clear the flag before adding the device so that 3361a50d940SGeert Uytterhoeven * fw_devlink doesn't skip adding consumers to this 3371a50d940SGeert Uytterhoeven * device. 3381a50d940SGeert Uytterhoeven */ 3391a50d940SGeert 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