Merge tag 'dt-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[linux-drm-fsl-dcu.git] / drivers / bus / imx-weim.c
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");