xref: /linux/drivers/bus/imx-weim.c (revision 4ba25a496f62129a2ad8c2436ab2b402752dc66c)
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