Blame view

drivers/bus/imx-weim.c 5.11 KB
85bf6d4e4   Huang Shijie   drivers: bus: add...
1
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * EIM driver for Freescale's i.MX chips
   *
   * Copyright (C) 2013 Freescale Semiconductor, Inc.
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2. This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
  #include <linux/module.h>
  #include <linux/clk.h>
  #include <linux/io.h>
  #include <linux/of_device.h>
8d9ee21e9   Shawn Guo   bus: imx-weim: su...
14
15
16
  #include <linux/mfd/syscon.h>
  #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
  #include <linux/regmap.h>
85bf6d4e4   Huang Shijie   drivers: bus: add...
17

3f98b6baa   Alexander Shiyan   drivers: bus: imx...
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  struct imx_weim_devtype {
  	unsigned int	cs_count;
  	unsigned int	cs_regs_count;
  	unsigned int	cs_stride;
  };
  
  static const struct imx_weim_devtype imx1_weim_devtype = {
  	.cs_count	= 6,
  	.cs_regs_count	= 2,
  	.cs_stride	= 0x08,
  };
  
  static const struct imx_weim_devtype imx27_weim_devtype = {
  	.cs_count	= 6,
  	.cs_regs_count	= 3,
  	.cs_stride	= 0x10,
  };
  
  static const struct imx_weim_devtype imx50_weim_devtype = {
  	.cs_count	= 4,
  	.cs_regs_count	= 6,
  	.cs_stride	= 0x18,
  };
  
  static const struct imx_weim_devtype imx51_weim_devtype = {
  	.cs_count	= 6,
  	.cs_regs_count	= 6,
  	.cs_stride	= 0x18,
  };
85bf6d4e4   Huang Shijie   drivers: bus: add...
47
  static const struct of_device_id weim_id_table[] = {
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
48
49
50
51
52
53
54
55
56
57
  	/* i.MX1/21 */
  	{ .compatible = "fsl,imx1-weim", .data = &imx1_weim_devtype, },
  	/* i.MX25/27/31/35 */
  	{ .compatible = "fsl,imx27-weim", .data = &imx27_weim_devtype, },
  	/* i.MX50/53/6Q */
  	{ .compatible = "fsl,imx50-weim", .data = &imx50_weim_devtype, },
  	{ .compatible = "fsl,imx6q-weim", .data = &imx50_weim_devtype, },
  	/* i.MX51 */
  	{ .compatible = "fsl,imx51-weim", .data = &imx51_weim_devtype, },
  	{ }
85bf6d4e4   Huang Shijie   drivers: bus: add...
58
59
  };
  MODULE_DEVICE_TABLE(of, weim_id_table);
8d9ee21e9   Shawn Guo   bus: imx-weim: su...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  static int __init imx_weim_gpr_setup(struct platform_device *pdev)
  {
  	struct device_node *np = pdev->dev.of_node;
  	struct property *prop;
  	const __be32 *p;
  	struct regmap *gpr;
  	u32 gprvals[4] = {
  		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M)  */
  		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M)  */
  		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M)  */
  		01111,	/* CS0(32M)  CS1(32M) CS2(32M) CS3(32M) */
  	};
  	u32 gprval = 0;
  	u32 val;
  	int cs = 0;
  	int i = 0;
  
  	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
  	if (IS_ERR(gpr)) {
  		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr
  ");
  		return 0;
  	}
  
  	of_property_for_each_u32(np, "ranges", prop, p, val) {
  		if (i % 4 == 0) {
  			cs = val;
  		} else if (i % 4 == 3 && val) {
  			val = (val / SZ_32M) | 1;
  			gprval |= val << cs * 3;
  		}
  		i++;
  	}
  
  	if (i == 0 || i % 4)
  		goto err;
  
  	for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
  		if (gprval == gprvals[i]) {
  			/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
  			regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
  			return 0;
  		}
  	}
  
  err:
  	dev_err(&pdev->dev, "Invalid 'ranges' configuration
  ");
  	return -EINVAL;
  }
85bf6d4e4   Huang Shijie   drivers: bus: add...
110
  /* Parse and set the timing for this device. */
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
111
112
  static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
  				    const struct imx_weim_devtype *devtype)
85bf6d4e4   Huang Shijie   drivers: bus: add...
113
  {
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
114
115
  	u32 cs_idx, value[devtype->cs_regs_count];
  	int i, ret;
85bf6d4e4   Huang Shijie   drivers: bus: add...
116
117
118
119
120
  
  	/* get the CS index from this child node's "reg" property. */
  	ret = of_property_read_u32(np, "reg", &cs_idx);
  	if (ret)
  		return ret;
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
121
  	if (cs_idx >= devtype->cs_count)
85bf6d4e4   Huang Shijie   drivers: bus: add...
122
123
124
  		return -EINVAL;
  
  	ret = of_property_read_u32_array(np, "fsl,weim-cs-timing",
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
125
  					 value, devtype->cs_regs_count);
85bf6d4e4   Huang Shijie   drivers: bus: add...
126
127
128
129
  	if (ret)
  		return ret;
  
  	/* set the timing for WEIM */
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
130
131
  	for (i = 0; i < devtype->cs_regs_count; i++)
  		writel(value[i], base + cs_idx * devtype->cs_stride + i * 4);
85bf6d4e4   Huang Shijie   drivers: bus: add...
132
133
  	return 0;
  }
29e54970c   Alexander Shiyan   drivers: bus: imx...
134
135
  static int __init weim_parse_dt(struct platform_device *pdev,
  				void __iomem *base)
85bf6d4e4   Huang Shijie   drivers: bus: add...
136
  {
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
137
138
139
  	const struct of_device_id *of_id = of_match_device(weim_id_table,
  							   &pdev->dev);
  	const struct imx_weim_devtype *devtype = of_id->data;
85bf6d4e4   Huang Shijie   drivers: bus: add...
140
  	struct device_node *child;
52c47b634   Alison Chaiken   bus: imx-weim: im...
141
  	int ret, have_child = 0;
85bf6d4e4   Huang Shijie   drivers: bus: add...
142

8d9ee21e9   Shawn Guo   bus: imx-weim: su...
143
144
145
146
147
  	if (devtype == &imx50_weim_devtype) {
  		ret = imx_weim_gpr_setup(pdev);
  		if (ret)
  			return ret;
  	}
33b96d2c9   Fabio Estevam   bus: imx-weim: Ta...
148
  	for_each_available_child_of_node(pdev->dev.of_node, child) {
85bf6d4e4   Huang Shijie   drivers: bus: add...
149
150
  		if (!child->name)
  			continue;
3f98b6baa   Alexander Shiyan   drivers: bus: imx...
151
  		ret = weim_timing_setup(child, base, devtype);
52c47b634   Alison Chaiken   bus: imx-weim: im...
152
153
154
  		if (ret)
  			dev_warn(&pdev->dev, "%s set timing failed.
  ",
85bf6d4e4   Huang Shijie   drivers: bus: add...
155
  				child->full_name);
52c47b634   Alison Chaiken   bus: imx-weim: im...
156
157
  		else
  			have_child = 1;
85bf6d4e4   Huang Shijie   drivers: bus: add...
158
  	}
52c47b634   Alison Chaiken   bus: imx-weim: im...
159
  	if (have_child)
39ec8d380   Kefeng Wang   bus: imx-weim: us...
160
161
  		ret = of_platform_default_populate(pdev->dev.of_node,
  						   NULL, &pdev->dev);
85bf6d4e4   Huang Shijie   drivers: bus: add...
162
163
164
165
166
167
  	if (ret)
  		dev_err(&pdev->dev, "%s fail to create devices.
  ",
  			pdev->dev.of_node->full_name);
  	return ret;
  }
29e54970c   Alexander Shiyan   drivers: bus: imx...
168
  static int __init weim_probe(struct platform_device *pdev)
85bf6d4e4   Huang Shijie   drivers: bus: add...
169
  {
85bf6d4e4   Huang Shijie   drivers: bus: add...
170
  	struct resource *res;
70ac98da7   Alexander Shiyan   drivers: bus: imx...
171
172
  	struct clk *clk;
  	void __iomem *base;
b2d1fb733   Alexander Shiyan   drivers: bus: imx...
173
  	int ret;
85bf6d4e4   Huang Shijie   drivers: bus: add...
174

85bf6d4e4   Huang Shijie   drivers: bus: add...
175
176
  	/* get the resource */
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
70ac98da7   Alexander Shiyan   drivers: bus: imx...
177
  	base = devm_ioremap_resource(&pdev->dev, res);
b2d1fb733   Alexander Shiyan   drivers: bus: imx...
178
179
  	if (IS_ERR(base))
  		return PTR_ERR(base);
85bf6d4e4   Huang Shijie   drivers: bus: add...
180
181
  
  	/* get the clock */
70ac98da7   Alexander Shiyan   drivers: bus: imx...
182
183
  	clk = devm_clk_get(&pdev->dev, NULL);
  	if (IS_ERR(clk))
b2d1fb733   Alexander Shiyan   drivers: bus: imx...
184
  		return PTR_ERR(clk);
85bf6d4e4   Huang Shijie   drivers: bus: add...
185

70ac98da7   Alexander Shiyan   drivers: bus: imx...
186
  	ret = clk_prepare_enable(clk);
85bf6d4e4   Huang Shijie   drivers: bus: add...
187
  	if (ret)
b2d1fb733   Alexander Shiyan   drivers: bus: imx...
188
  		return ret;
85bf6d4e4   Huang Shijie   drivers: bus: add...
189
190
  
  	/* parse the device node */
70ac98da7   Alexander Shiyan   drivers: bus: imx...
191
  	ret = weim_parse_dt(pdev, base);
b2d1fb733   Alexander Shiyan   drivers: bus: imx...
192
  	if (ret)
70ac98da7   Alexander Shiyan   drivers: bus: imx...
193
  		clk_disable_unprepare(clk);
b2d1fb733   Alexander Shiyan   drivers: bus: imx...
194
195
196
  	else
  		dev_info(&pdev->dev, "Driver registered.
  ");
85bf6d4e4   Huang Shijie   drivers: bus: add...
197

85bf6d4e4   Huang Shijie   drivers: bus: add...
198
199
200
201
202
  	return ret;
  }
  
  static struct platform_driver weim_driver = {
  	.driver = {
fc608c745   Alexander Shiyan   drivers: bus: imx...
203
  		.name		= "imx-weim",
fc608c745   Alexander Shiyan   drivers: bus: imx...
204
  		.of_match_table	= weim_id_table,
85bf6d4e4   Huang Shijie   drivers: bus: add...
205
  	},
85bf6d4e4   Huang Shijie   drivers: bus: add...
206
  };
29e54970c   Alexander Shiyan   drivers: bus: imx...
207
  module_platform_driver_probe(weim_driver, weim_probe);
85bf6d4e4   Huang Shijie   drivers: bus: add...
208

85bf6d4e4   Huang Shijie   drivers: bus: add...
209
210
211
  MODULE_AUTHOR("Freescale Semiconductor Inc.");
  MODULE_DESCRIPTION("i.MX EIM Controller Driver");
  MODULE_LICENSE("GPL");