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