1*85bf6d4eSHuang Shijie /* 2*85bf6d4eSHuang Shijie * EIM driver for Freescale's i.MX chips 3*85bf6d4eSHuang Shijie * 4*85bf6d4eSHuang Shijie * Copyright (C) 2013 Freescale Semiconductor, Inc. 5*85bf6d4eSHuang Shijie * 6*85bf6d4eSHuang Shijie * This file is licensed under the terms of the GNU General Public 7*85bf6d4eSHuang Shijie * License version 2. This program is licensed "as is" without any 8*85bf6d4eSHuang Shijie * warranty of any kind, whether express or implied. 9*85bf6d4eSHuang Shijie */ 10*85bf6d4eSHuang Shijie #include <linux/module.h> 11*85bf6d4eSHuang Shijie #include <linux/clk.h> 12*85bf6d4eSHuang Shijie #include <linux/io.h> 13*85bf6d4eSHuang Shijie #include <linux/of_device.h> 14*85bf6d4eSHuang Shijie 15*85bf6d4eSHuang Shijie struct imx_weim { 16*85bf6d4eSHuang Shijie void __iomem *base; 17*85bf6d4eSHuang Shijie struct clk *clk; 18*85bf6d4eSHuang Shijie }; 19*85bf6d4eSHuang Shijie 20*85bf6d4eSHuang Shijie static const struct of_device_id weim_id_table[] = { 21*85bf6d4eSHuang Shijie { .compatible = "fsl,imx6q-weim", }, 22*85bf6d4eSHuang Shijie {} 23*85bf6d4eSHuang Shijie }; 24*85bf6d4eSHuang Shijie MODULE_DEVICE_TABLE(of, weim_id_table); 25*85bf6d4eSHuang Shijie 26*85bf6d4eSHuang Shijie #define CS_TIMING_LEN 6 27*85bf6d4eSHuang Shijie #define CS_REG_RANGE 0x18 28*85bf6d4eSHuang Shijie 29*85bf6d4eSHuang Shijie /* Parse and set the timing for this device. */ 30*85bf6d4eSHuang Shijie static int 31*85bf6d4eSHuang Shijie weim_timing_setup(struct platform_device *pdev, struct device_node *np) 32*85bf6d4eSHuang Shijie { 33*85bf6d4eSHuang Shijie struct imx_weim *weim = platform_get_drvdata(pdev); 34*85bf6d4eSHuang Shijie u32 value[CS_TIMING_LEN]; 35*85bf6d4eSHuang Shijie u32 cs_idx; 36*85bf6d4eSHuang Shijie int ret; 37*85bf6d4eSHuang Shijie int i; 38*85bf6d4eSHuang Shijie 39*85bf6d4eSHuang Shijie /* get the CS index from this child node's "reg" property. */ 40*85bf6d4eSHuang Shijie ret = of_property_read_u32(np, "reg", &cs_idx); 41*85bf6d4eSHuang Shijie if (ret) 42*85bf6d4eSHuang Shijie return ret; 43*85bf6d4eSHuang Shijie 44*85bf6d4eSHuang Shijie /* The weim has four chip selects. */ 45*85bf6d4eSHuang Shijie if (cs_idx > 3) 46*85bf6d4eSHuang Shijie return -EINVAL; 47*85bf6d4eSHuang Shijie 48*85bf6d4eSHuang Shijie ret = of_property_read_u32_array(np, "fsl,weim-cs-timing", 49*85bf6d4eSHuang Shijie value, CS_TIMING_LEN); 50*85bf6d4eSHuang Shijie if (ret) 51*85bf6d4eSHuang Shijie return ret; 52*85bf6d4eSHuang Shijie 53*85bf6d4eSHuang Shijie /* set the timing for WEIM */ 54*85bf6d4eSHuang Shijie for (i = 0; i < CS_TIMING_LEN; i++) 55*85bf6d4eSHuang Shijie writel(value[i], weim->base + cs_idx * CS_REG_RANGE + i * 4); 56*85bf6d4eSHuang Shijie return 0; 57*85bf6d4eSHuang Shijie } 58*85bf6d4eSHuang Shijie 59*85bf6d4eSHuang Shijie static int weim_parse_dt(struct platform_device *pdev) 60*85bf6d4eSHuang Shijie { 61*85bf6d4eSHuang Shijie struct device_node *child; 62*85bf6d4eSHuang Shijie int ret; 63*85bf6d4eSHuang Shijie 64*85bf6d4eSHuang Shijie for_each_child_of_node(pdev->dev.of_node, child) { 65*85bf6d4eSHuang Shijie if (!child->name) 66*85bf6d4eSHuang Shijie continue; 67*85bf6d4eSHuang Shijie 68*85bf6d4eSHuang Shijie ret = weim_timing_setup(pdev, child); 69*85bf6d4eSHuang Shijie if (ret) { 70*85bf6d4eSHuang Shijie dev_err(&pdev->dev, "%s set timing failed.\n", 71*85bf6d4eSHuang Shijie child->full_name); 72*85bf6d4eSHuang Shijie return ret; 73*85bf6d4eSHuang Shijie } 74*85bf6d4eSHuang Shijie } 75*85bf6d4eSHuang Shijie 76*85bf6d4eSHuang Shijie ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); 77*85bf6d4eSHuang Shijie if (ret) 78*85bf6d4eSHuang Shijie dev_err(&pdev->dev, "%s fail to create devices.\n", 79*85bf6d4eSHuang Shijie pdev->dev.of_node->full_name); 80*85bf6d4eSHuang Shijie return ret; 81*85bf6d4eSHuang Shijie } 82*85bf6d4eSHuang Shijie 83*85bf6d4eSHuang Shijie static int weim_probe(struct platform_device *pdev) 84*85bf6d4eSHuang Shijie { 85*85bf6d4eSHuang Shijie struct imx_weim *weim; 86*85bf6d4eSHuang Shijie struct resource *res; 87*85bf6d4eSHuang Shijie int ret = -EINVAL; 88*85bf6d4eSHuang Shijie 89*85bf6d4eSHuang Shijie weim = devm_kzalloc(&pdev->dev, sizeof(*weim), GFP_KERNEL); 90*85bf6d4eSHuang Shijie if (!weim) { 91*85bf6d4eSHuang Shijie ret = -ENOMEM; 92*85bf6d4eSHuang Shijie goto weim_err; 93*85bf6d4eSHuang Shijie } 94*85bf6d4eSHuang Shijie platform_set_drvdata(pdev, weim); 95*85bf6d4eSHuang Shijie 96*85bf6d4eSHuang Shijie /* get the resource */ 97*85bf6d4eSHuang Shijie res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 98*85bf6d4eSHuang Shijie weim->base = devm_ioremap_resource(&pdev->dev, res); 99*85bf6d4eSHuang Shijie if (IS_ERR(weim->base)) { 100*85bf6d4eSHuang Shijie ret = PTR_ERR(weim->base); 101*85bf6d4eSHuang Shijie goto weim_err; 102*85bf6d4eSHuang Shijie } 103*85bf6d4eSHuang Shijie 104*85bf6d4eSHuang Shijie /* get the clock */ 105*85bf6d4eSHuang Shijie weim->clk = devm_clk_get(&pdev->dev, NULL); 106*85bf6d4eSHuang Shijie if (IS_ERR(weim->clk)) 107*85bf6d4eSHuang Shijie goto weim_err; 108*85bf6d4eSHuang Shijie 109*85bf6d4eSHuang Shijie ret = clk_prepare_enable(weim->clk); 110*85bf6d4eSHuang Shijie if (ret) 111*85bf6d4eSHuang Shijie goto weim_err; 112*85bf6d4eSHuang Shijie 113*85bf6d4eSHuang Shijie /* parse the device node */ 114*85bf6d4eSHuang Shijie ret = weim_parse_dt(pdev); 115*85bf6d4eSHuang Shijie if (ret) { 116*85bf6d4eSHuang Shijie clk_disable_unprepare(weim->clk); 117*85bf6d4eSHuang Shijie goto weim_err; 118*85bf6d4eSHuang Shijie } 119*85bf6d4eSHuang Shijie 120*85bf6d4eSHuang Shijie dev_info(&pdev->dev, "WEIM driver registered.\n"); 121*85bf6d4eSHuang Shijie return 0; 122*85bf6d4eSHuang Shijie 123*85bf6d4eSHuang Shijie weim_err: 124*85bf6d4eSHuang Shijie return ret; 125*85bf6d4eSHuang Shijie } 126*85bf6d4eSHuang Shijie 127*85bf6d4eSHuang Shijie static struct platform_driver weim_driver = { 128*85bf6d4eSHuang Shijie .driver = { 129*85bf6d4eSHuang Shijie .name = "imx-weim", 130*85bf6d4eSHuang Shijie .of_match_table = weim_id_table, 131*85bf6d4eSHuang Shijie }, 132*85bf6d4eSHuang Shijie .probe = weim_probe, 133*85bf6d4eSHuang Shijie }; 134*85bf6d4eSHuang Shijie 135*85bf6d4eSHuang Shijie module_platform_driver(weim_driver); 136*85bf6d4eSHuang Shijie MODULE_AUTHOR("Freescale Semiconductor Inc."); 137*85bf6d4eSHuang Shijie MODULE_DESCRIPTION("i.MX EIM Controller Driver"); 138*85bf6d4eSHuang Shijie MODULE_LICENSE("GPL"); 139