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; 223f98b6baSAlexander Shiyan }; 233f98b6baSAlexander Shiyan 243f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx1_weim_devtype = { 253f98b6baSAlexander Shiyan .cs_count = 6, 263f98b6baSAlexander Shiyan .cs_regs_count = 2, 273f98b6baSAlexander Shiyan .cs_stride = 0x08, 283f98b6baSAlexander Shiyan }; 293f98b6baSAlexander Shiyan 303f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx27_weim_devtype = { 313f98b6baSAlexander Shiyan .cs_count = 6, 323f98b6baSAlexander Shiyan .cs_regs_count = 3, 333f98b6baSAlexander Shiyan .cs_stride = 0x10, 343f98b6baSAlexander Shiyan }; 353f98b6baSAlexander Shiyan 363f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx50_weim_devtype = { 373f98b6baSAlexander Shiyan .cs_count = 4, 383f98b6baSAlexander Shiyan .cs_regs_count = 6, 393f98b6baSAlexander Shiyan .cs_stride = 0x18, 403f98b6baSAlexander Shiyan }; 413f98b6baSAlexander Shiyan 423f98b6baSAlexander Shiyan static const struct imx_weim_devtype imx51_weim_devtype = { 433f98b6baSAlexander Shiyan .cs_count = 6, 443f98b6baSAlexander Shiyan .cs_regs_count = 6, 453f98b6baSAlexander Shiyan .cs_stride = 0x18, 463f98b6baSAlexander Shiyan }; 473f98b6baSAlexander Shiyan 48d8dfa59fSKees Cook #define MAX_CS_REGS_COUNT 6 49*8b8cb52aSSven Van Asbroeck #define OF_REG_SIZE 3 50d8dfa59fSKees Cook 5185bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = { 523f98b6baSAlexander Shiyan /* i.MX1/21 */ 533f98b6baSAlexander Shiyan { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, }, 543f98b6baSAlexander Shiyan /* i.MX25/27/31/35 */ 553f98b6baSAlexander Shiyan { .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, }, 563f98b6baSAlexander Shiyan /* i.MX50/53/6Q */ 573f98b6baSAlexander Shiyan { .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, }, 583f98b6baSAlexander Shiyan { .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, }, 593f98b6baSAlexander Shiyan /* i.MX51 */ 603f98b6baSAlexander Shiyan { .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, }, 6185bf6d4eSHuang Shijie { } 6285bf6d4eSHuang Shijie }; 6385bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table); 6485bf6d4eSHuang Shijie 658d9ee21eSShawn Guo static int __init imx_weim_gpr_setup(struct platform_device *pdev) 668d9ee21eSShawn Guo { 678d9ee21eSShawn Guo struct device_node *np = pdev->dev.of_node; 688d9ee21eSShawn Guo struct property *prop; 698d9ee21eSShawn Guo const __be32 *p; 708d9ee21eSShawn Guo struct regmap *gpr; 718d9ee21eSShawn Guo u32 gprvals[4] = { 728d9ee21eSShawn Guo 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */ 738d9ee21eSShawn Guo 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */ 748d9ee21eSShawn Guo 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */ 758d9ee21eSShawn Guo 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */ 768d9ee21eSShawn Guo }; 778d9ee21eSShawn Guo u32 gprval = 0; 788d9ee21eSShawn Guo u32 val; 798d9ee21eSShawn Guo int cs = 0; 808d9ee21eSShawn Guo int i = 0; 818d9ee21eSShawn Guo 828d9ee21eSShawn Guo gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr"); 838d9ee21eSShawn Guo if (IS_ERR(gpr)) { 848d9ee21eSShawn Guo dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n"); 858d9ee21eSShawn Guo return 0; 868d9ee21eSShawn Guo } 878d9ee21eSShawn Guo 888d9ee21eSShawn Guo of_property_for_each_u32(np, "ranges", prop, p, val) { 898d9ee21eSShawn Guo if (i % 4 == 0) { 908d9ee21eSShawn Guo cs = val; 918d9ee21eSShawn Guo } else if (i % 4 == 3 && val) { 928d9ee21eSShawn Guo val = (val / SZ_32M) | 1; 938d9ee21eSShawn Guo gprval |= val << cs * 3; 948d9ee21eSShawn Guo } 958d9ee21eSShawn Guo i++; 968d9ee21eSShawn Guo } 978d9ee21eSShawn Guo 988d9ee21eSShawn Guo if (i == 0 || i % 4) 998d9ee21eSShawn Guo goto err; 1008d9ee21eSShawn Guo 1018d9ee21eSShawn Guo for (i = 0; i < ARRAY_SIZE(gprvals); i++) { 1028d9ee21eSShawn Guo if (gprval == gprvals[i]) { 1038d9ee21eSShawn Guo /* Found it. Set up IOMUXC_GPR1[11:0] with it. */ 1048d9ee21eSShawn Guo regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval); 1058d9ee21eSShawn Guo return 0; 1068d9ee21eSShawn Guo } 1078d9ee21eSShawn Guo } 1088d9ee21eSShawn Guo 1098d9ee21eSShawn Guo err: 1108d9ee21eSShawn Guo dev_err(&pdev->dev, "Invalid 'ranges' configuration\n"); 1118d9ee21eSShawn Guo return -EINVAL; 1128d9ee21eSShawn Guo } 1138d9ee21eSShawn Guo 11485bf6d4eSHuang Shijie /* Parse and set the timing for this device. */ 1153f98b6baSAlexander Shiyan static int __init weim_timing_setup(struct device_node *np, void __iomem *base, 1163f98b6baSAlexander Shiyan const struct imx_weim_devtype *devtype) 11785bf6d4eSHuang Shijie { 118d8dfa59fSKees Cook u32 cs_idx, value[MAX_CS_REGS_COUNT]; 1193f98b6baSAlexander Shiyan int i, ret; 120*8b8cb52aSSven Van Asbroeck int reg_idx, num_regs; 12185bf6d4eSHuang Shijie 122d8dfa59fSKees Cook if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) 123d8dfa59fSKees Cook return -EINVAL; 124d8dfa59fSKees Cook 12585bf6d4eSHuang Shijie ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", 1263f98b6baSAlexander Shiyan value, devtype->cs_regs_count); 12785bf6d4eSHuang Shijie if (ret) 12885bf6d4eSHuang Shijie return ret; 12985bf6d4eSHuang Shijie 130*8b8cb52aSSven Van Asbroeck /* 131*8b8cb52aSSven Van Asbroeck * the child node's "reg" property may contain multiple address ranges, 132*8b8cb52aSSven Van Asbroeck * extract the chip select for each. 133*8b8cb52aSSven Van Asbroeck */ 134*8b8cb52aSSven Van Asbroeck num_regs = of_property_count_elems_of_size(np, "reg", OF_REG_SIZE); 135*8b8cb52aSSven Van Asbroeck if (num_regs < 0) 136*8b8cb52aSSven Van Asbroeck return num_regs; 137*8b8cb52aSSven Van Asbroeck if (!num_regs) 138*8b8cb52aSSven Van Asbroeck return -EINVAL; 139*8b8cb52aSSven Van Asbroeck for (reg_idx = 0; reg_idx < num_regs; reg_idx++) { 140*8b8cb52aSSven Van Asbroeck /* get the CS index from this child node's "reg" property. */ 141*8b8cb52aSSven Van Asbroeck ret = of_property_read_u32_index(np, "reg", 142*8b8cb52aSSven Van Asbroeck reg_idx * OF_REG_SIZE, &cs_idx); 143*8b8cb52aSSven Van Asbroeck if (ret) 144*8b8cb52aSSven Van Asbroeck break; 145*8b8cb52aSSven Van Asbroeck 146*8b8cb52aSSven Van Asbroeck if (cs_idx >= devtype->cs_count) 147*8b8cb52aSSven Van Asbroeck return -EINVAL; 148*8b8cb52aSSven Van Asbroeck 14985bf6d4eSHuang Shijie /* set the timing for WEIM */ 1503f98b6baSAlexander Shiyan for (i = 0; i < devtype->cs_regs_count; i++) 151*8b8cb52aSSven Van Asbroeck writel(value[i], 152*8b8cb52aSSven Van Asbroeck base + cs_idx * devtype->cs_stride + i * 4); 153*8b8cb52aSSven Van Asbroeck } 1543f98b6baSAlexander Shiyan 15585bf6d4eSHuang Shijie return 0; 15685bf6d4eSHuang Shijie } 15785bf6d4eSHuang Shijie 15829e54970SAlexander Shiyan static int __init weim_parse_dt(struct platform_device *pdev, 15929e54970SAlexander Shiyan void __iomem *base) 16085bf6d4eSHuang Shijie { 1613f98b6baSAlexander Shiyan const struct of_device_id *of_id = of_match_device(weim_id_table, 1623f98b6baSAlexander Shiyan &pdev->dev); 1633f98b6baSAlexander Shiyan const struct imx_weim_devtype *devtype = of_id->data; 16485bf6d4eSHuang Shijie struct device_node *child; 16552c47b63SAlison Chaiken int ret, have_child = 0; 16685bf6d4eSHuang Shijie 1678d9ee21eSShawn Guo if (devtype == &imx50_weim_devtype) { 1688d9ee21eSShawn Guo ret = imx_weim_gpr_setup(pdev); 1698d9ee21eSShawn Guo if (ret) 1708d9ee21eSShawn Guo return ret; 1718d9ee21eSShawn Guo } 1728d9ee21eSShawn Guo 17333b96d2cSFabio Estevam for_each_available_child_of_node(pdev->dev.of_node, child) { 1743f98b6baSAlexander Shiyan ret = weim_timing_setup(child, base, devtype); 17552c47b63SAlison Chaiken if (ret) 1769c0982d8SRob Herring dev_warn(&pdev->dev, "%pOF set timing failed.\n", 1779c0982d8SRob Herring child); 17852c47b63SAlison Chaiken else 17952c47b63SAlison Chaiken have_child = 1; 18085bf6d4eSHuang Shijie } 18185bf6d4eSHuang Shijie 18252c47b63SAlison Chaiken if (have_child) 18339ec8d38SKefeng Wang ret = of_platform_default_populate(pdev->dev.of_node, 18426651c43SLiu Ying NULL, &pdev->dev); 18585bf6d4eSHuang Shijie if (ret) 1869c0982d8SRob Herring dev_err(&pdev->dev, "%pOF fail to create devices.\n", 1879c0982d8SRob Herring pdev->dev.of_node); 18885bf6d4eSHuang Shijie return ret; 18985bf6d4eSHuang Shijie } 19085bf6d4eSHuang Shijie 19129e54970SAlexander Shiyan static int __init weim_probe(struct platform_device *pdev) 19285bf6d4eSHuang Shijie { 19385bf6d4eSHuang Shijie struct resource *res; 19470ac98daSAlexander Shiyan struct clk *clk; 19570ac98daSAlexander Shiyan void __iomem *base; 196b2d1fb73SAlexander Shiyan int ret; 19785bf6d4eSHuang Shijie 19885bf6d4eSHuang Shijie /* get the resource */ 19985bf6d4eSHuang Shijie res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 20070ac98daSAlexander Shiyan base = devm_ioremap_resource(&pdev->dev, res); 201b2d1fb73SAlexander Shiyan if (IS_ERR(base)) 202b2d1fb73SAlexander Shiyan return PTR_ERR(base); 20385bf6d4eSHuang Shijie 20485bf6d4eSHuang Shijie /* get the clock */ 20570ac98daSAlexander Shiyan clk = devm_clk_get(&pdev->dev, NULL); 20670ac98daSAlexander Shiyan if (IS_ERR(clk)) 207b2d1fb73SAlexander Shiyan return PTR_ERR(clk); 20885bf6d4eSHuang Shijie 20970ac98daSAlexander Shiyan ret = clk_prepare_enable(clk); 21085bf6d4eSHuang Shijie if (ret) 211b2d1fb73SAlexander Shiyan return ret; 21285bf6d4eSHuang Shijie 21385bf6d4eSHuang Shijie /* parse the device node */ 21470ac98daSAlexander Shiyan ret = weim_parse_dt(pdev, base); 215b2d1fb73SAlexander Shiyan if (ret) 21670ac98daSAlexander Shiyan clk_disable_unprepare(clk); 217b2d1fb73SAlexander Shiyan else 218b2d1fb73SAlexander Shiyan dev_info(&pdev->dev, "Driver registered.\n"); 21985bf6d4eSHuang Shijie 22085bf6d4eSHuang Shijie return ret; 22185bf6d4eSHuang Shijie } 22285bf6d4eSHuang Shijie 22385bf6d4eSHuang Shijie static struct platform_driver weim_driver = { 22485bf6d4eSHuang Shijie .driver = { 22585bf6d4eSHuang Shijie .name = "imx-weim", 22685bf6d4eSHuang Shijie .of_match_table = weim_id_table, 22785bf6d4eSHuang Shijie }, 22885bf6d4eSHuang Shijie }; 22929e54970SAlexander Shiyan module_platform_driver_probe(weim_driver, weim_probe); 23085bf6d4eSHuang Shijie 23185bf6d4eSHuang Shijie MODULE_AUTHOR("Freescale Semiconductor Inc."); 23285bf6d4eSHuang Shijie MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 23385bf6d4eSHuang Shijie MODULE_LICENSE("GPL"); 234