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