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 48*d8dfa59fSKees Cook #define MAX_CS_REGS_COUNT 6 49*d8dfa59fSKees Cook 5085bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = { 513f98b6baSAlexander Shiyan /* i.MX1/21 */ 523f98b6baSAlexander Shiyan { .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, }, 533f98b6baSAlexander Shiyan /* i.MX25/27/31/35 */ 543f98b6baSAlexander Shiyan { .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, }, 553f98b6baSAlexander Shiyan /* i.MX50/53/6Q */ 563f98b6baSAlexander Shiyan { .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, }, 573f98b6baSAlexander Shiyan { .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, }, 583f98b6baSAlexander Shiyan /* i.MX51 */ 593f98b6baSAlexander Shiyan { .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, }, 6085bf6d4eSHuang Shijie { } 6185bf6d4eSHuang Shijie }; 6285bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table); 6385bf6d4eSHuang Shijie 648d9ee21eSShawn Guo static int __init imx_weim_gpr_setup(struct platform_device *pdev) 658d9ee21eSShawn Guo { 668d9ee21eSShawn Guo struct device_node *np = pdev->dev.of_node; 678d9ee21eSShawn Guo struct property *prop; 688d9ee21eSShawn Guo const __be32 *p; 698d9ee21eSShawn Guo struct regmap *gpr; 708d9ee21eSShawn Guo u32 gprvals[4] = { 718d9ee21eSShawn Guo 05, /* CS0(128M) CS1(0M) CS2(0M) CS3(0M) */ 728d9ee21eSShawn Guo 033, /* CS0(64M) CS1(64M) CS2(0M) CS3(0M) */ 738d9ee21eSShawn Guo 0113, /* CS0(64M) CS1(32M) CS2(32M) CS3(0M) */ 748d9ee21eSShawn Guo 01111, /* CS0(32M) CS1(32M) CS2(32M) CS3(32M) */ 758d9ee21eSShawn Guo }; 768d9ee21eSShawn Guo u32 gprval = 0; 778d9ee21eSShawn Guo u32 val; 788d9ee21eSShawn Guo int cs = 0; 798d9ee21eSShawn Guo int i = 0; 808d9ee21eSShawn Guo 818d9ee21eSShawn Guo gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr"); 828d9ee21eSShawn Guo if (IS_ERR(gpr)) { 838d9ee21eSShawn Guo dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n"); 848d9ee21eSShawn Guo return 0; 858d9ee21eSShawn Guo } 868d9ee21eSShawn Guo 878d9ee21eSShawn Guo of_property_for_each_u32(np, "ranges", prop, p, val) { 888d9ee21eSShawn Guo if (i % 4 == 0) { 898d9ee21eSShawn Guo cs = val; 908d9ee21eSShawn Guo } else if (i % 4 == 3 && val) { 918d9ee21eSShawn Guo val = (val / SZ_32M) | 1; 928d9ee21eSShawn Guo gprval |= val << cs * 3; 938d9ee21eSShawn Guo } 948d9ee21eSShawn Guo i++; 958d9ee21eSShawn Guo } 968d9ee21eSShawn Guo 978d9ee21eSShawn Guo if (i == 0 || i % 4) 988d9ee21eSShawn Guo goto err; 998d9ee21eSShawn Guo 1008d9ee21eSShawn Guo for (i = 0; i < ARRAY_SIZE(gprvals); i++) { 1018d9ee21eSShawn Guo if (gprval == gprvals[i]) { 1028d9ee21eSShawn Guo /* Found it. Set up IOMUXC_GPR1[11:0] with it. */ 1038d9ee21eSShawn Guo regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval); 1048d9ee21eSShawn Guo return 0; 1058d9ee21eSShawn Guo } 1068d9ee21eSShawn Guo } 1078d9ee21eSShawn Guo 1088d9ee21eSShawn Guo err: 1098d9ee21eSShawn Guo dev_err(&pdev->dev, "Invalid 'ranges' configuration\n"); 1108d9ee21eSShawn Guo return -EINVAL; 1118d9ee21eSShawn Guo } 1128d9ee21eSShawn Guo 11385bf6d4eSHuang Shijie /* Parse and set the timing for this device. */ 1143f98b6baSAlexander Shiyan static int __init weim_timing_setup(struct device_node *np, void __iomem *base, 1153f98b6baSAlexander Shiyan const struct imx_weim_devtype *devtype) 11685bf6d4eSHuang Shijie { 117*d8dfa59fSKees Cook u32 cs_idx, value[MAX_CS_REGS_COUNT]; 1183f98b6baSAlexander Shiyan int i, ret; 11985bf6d4eSHuang Shijie 120*d8dfa59fSKees Cook if (WARN_ON(devtype->cs_regs_count > MAX_CS_REGS_COUNT)) 121*d8dfa59fSKees Cook return -EINVAL; 122*d8dfa59fSKees Cook 12385bf6d4eSHuang Shijie /* get the CS index from this child node's "reg" property. */ 12485bf6d4eSHuang Shijie ret = of_property_read_u32(np, "reg", &cs_idx); 12585bf6d4eSHuang Shijie if (ret) 12685bf6d4eSHuang Shijie return ret; 12785bf6d4eSHuang Shijie 1283f98b6baSAlexander Shiyan if (cs_idx >= devtype->cs_count) 12985bf6d4eSHuang Shijie return -EINVAL; 13085bf6d4eSHuang Shijie 13185bf6d4eSHuang Shijie ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", 1323f98b6baSAlexander Shiyan value, devtype->cs_regs_count); 13385bf6d4eSHuang Shijie if (ret) 13485bf6d4eSHuang Shijie return ret; 13585bf6d4eSHuang Shijie 13685bf6d4eSHuang Shijie /* set the timing for WEIM */ 1373f98b6baSAlexander Shiyan for (i = 0; i < devtype->cs_regs_count; i++) 1383f98b6baSAlexander Shiyan writel(value[i], base + cs_idx * devtype->cs_stride + i * 4); 1393f98b6baSAlexander Shiyan 14085bf6d4eSHuang Shijie return 0; 14185bf6d4eSHuang Shijie } 14285bf6d4eSHuang Shijie 14329e54970SAlexander Shiyan static int __init weim_parse_dt(struct platform_device *pdev, 14429e54970SAlexander Shiyan void __iomem *base) 14585bf6d4eSHuang Shijie { 1463f98b6baSAlexander Shiyan const struct of_device_id *of_id = of_match_device(weim_id_table, 1473f98b6baSAlexander Shiyan &pdev->dev); 1483f98b6baSAlexander Shiyan const struct imx_weim_devtype *devtype = of_id->data; 14985bf6d4eSHuang Shijie struct device_node *child; 15052c47b63SAlison Chaiken int ret, have_child = 0; 15185bf6d4eSHuang Shijie 1528d9ee21eSShawn Guo if (devtype == &imx50_weim_devtype) { 1538d9ee21eSShawn Guo ret = imx_weim_gpr_setup(pdev); 1548d9ee21eSShawn Guo if (ret) 1558d9ee21eSShawn Guo return ret; 1568d9ee21eSShawn Guo } 1578d9ee21eSShawn Guo 15833b96d2cSFabio Estevam for_each_available_child_of_node(pdev->dev.of_node, child) { 15985bf6d4eSHuang Shijie if (!child->name) 16085bf6d4eSHuang Shijie continue; 16185bf6d4eSHuang Shijie 1623f98b6baSAlexander Shiyan ret = weim_timing_setup(child, base, devtype); 16352c47b63SAlison Chaiken if (ret) 1649c0982d8SRob Herring dev_warn(&pdev->dev, "%pOF set timing failed.\n", 1659c0982d8SRob Herring child); 16652c47b63SAlison Chaiken else 16752c47b63SAlison Chaiken have_child = 1; 16885bf6d4eSHuang Shijie } 16985bf6d4eSHuang Shijie 17052c47b63SAlison Chaiken if (have_child) 17139ec8d38SKefeng Wang ret = of_platform_default_populate(pdev->dev.of_node, 17226651c43SLiu Ying NULL, &pdev->dev); 17385bf6d4eSHuang Shijie if (ret) 1749c0982d8SRob Herring dev_err(&pdev->dev, "%pOF fail to create devices.\n", 1759c0982d8SRob Herring pdev->dev.of_node); 17685bf6d4eSHuang Shijie return ret; 17785bf6d4eSHuang Shijie } 17885bf6d4eSHuang Shijie 17929e54970SAlexander Shiyan static int __init weim_probe(struct platform_device *pdev) 18085bf6d4eSHuang Shijie { 18185bf6d4eSHuang Shijie struct resource *res; 18270ac98daSAlexander Shiyan struct clk *clk; 18370ac98daSAlexander Shiyan void __iomem *base; 184b2d1fb73SAlexander Shiyan int ret; 18585bf6d4eSHuang Shijie 18685bf6d4eSHuang Shijie /* get the resource */ 18785bf6d4eSHuang Shijie res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 18870ac98daSAlexander Shiyan base = devm_ioremap_resource(&pdev->dev, res); 189b2d1fb73SAlexander Shiyan if (IS_ERR(base)) 190b2d1fb73SAlexander Shiyan return PTR_ERR(base); 19185bf6d4eSHuang Shijie 19285bf6d4eSHuang Shijie /* get the clock */ 19370ac98daSAlexander Shiyan clk = devm_clk_get(&pdev->dev, NULL); 19470ac98daSAlexander Shiyan if (IS_ERR(clk)) 195b2d1fb73SAlexander Shiyan return PTR_ERR(clk); 19685bf6d4eSHuang Shijie 19770ac98daSAlexander Shiyan ret = clk_prepare_enable(clk); 19885bf6d4eSHuang Shijie if (ret) 199b2d1fb73SAlexander Shiyan return ret; 20085bf6d4eSHuang Shijie 20185bf6d4eSHuang Shijie /* parse the device node */ 20270ac98daSAlexander Shiyan ret = weim_parse_dt(pdev, base); 203b2d1fb73SAlexander Shiyan if (ret) 20470ac98daSAlexander Shiyan clk_disable_unprepare(clk); 205b2d1fb73SAlexander Shiyan else 206b2d1fb73SAlexander Shiyan dev_info(&pdev->dev, "Driver registered.\n"); 20785bf6d4eSHuang Shijie 20885bf6d4eSHuang Shijie return ret; 20985bf6d4eSHuang Shijie } 21085bf6d4eSHuang Shijie 21185bf6d4eSHuang Shijie static struct platform_driver weim_driver = { 21285bf6d4eSHuang Shijie .driver = { 21385bf6d4eSHuang Shijie .name = "imx-weim", 21485bf6d4eSHuang Shijie .of_match_table = weim_id_table, 21585bf6d4eSHuang Shijie }, 21685bf6d4eSHuang Shijie }; 21729e54970SAlexander Shiyan module_platform_driver_probe(weim_driver, weim_probe); 21885bf6d4eSHuang Shijie 21985bf6d4eSHuang Shijie MODULE_AUTHOR("Freescale Semiconductor Inc."); 22085bf6d4eSHuang Shijie MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 22185bf6d4eSHuang Shijie MODULE_LICENSE("GPL"); 222