]>
Commit | Line | Data |
---|---|---|
3edad321 EG |
1 | /* |
2 | * Marvell EBU SoC Device Bus Controller | |
3 | * (memory controller for NOR/NAND/SRAM/FPGA devices) | |
4 | * | |
5 | * Copyright (C) 2013 Marvell | |
6 | * | |
7 | * This program is free software: you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation version 2 of the License. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | * | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/slab.h> | |
24 | #include <linux/err.h> | |
25 | #include <linux/io.h> | |
26 | #include <linux/clk.h> | |
27 | #include <linux/mbus.h> | |
28 | #include <linux/of_platform.h> | |
29 | #include <linux/of_address.h> | |
30 | #include <linux/platform_device.h> | |
31 | ||
32 | /* Register definitions */ | |
33 | #define DEV_WIDTH_BIT 30 | |
34 | #define BADR_SKEW_BIT 28 | |
35 | #define RD_HOLD_BIT 23 | |
36 | #define ACC_NEXT_BIT 17 | |
37 | #define RD_SETUP_BIT 12 | |
38 | #define ACC_FIRST_BIT 6 | |
39 | ||
40 | #define SYNC_ENABLE_BIT 24 | |
41 | #define WR_HIGH_BIT 16 | |
42 | #define WR_LOW_BIT 8 | |
43 | ||
44 | #define READ_PARAM_OFFSET 0x0 | |
45 | #define WRITE_PARAM_OFFSET 0x4 | |
46 | ||
47 | static const char * const devbus_wins[] = { | |
48 | "devbus-boot", | |
49 | "devbus-cs0", | |
50 | "devbus-cs1", | |
51 | "devbus-cs2", | |
52 | "devbus-cs3", | |
53 | }; | |
54 | ||
55 | struct devbus_read_params { | |
56 | u32 bus_width; | |
57 | u32 badr_skew; | |
58 | u32 turn_off; | |
59 | u32 acc_first; | |
60 | u32 acc_next; | |
61 | u32 rd_setup; | |
62 | u32 rd_hold; | |
63 | }; | |
64 | ||
65 | struct devbus_write_params { | |
66 | u32 sync_enable; | |
67 | u32 wr_high; | |
68 | u32 wr_low; | |
69 | u32 ale_wr; | |
70 | }; | |
71 | ||
72 | struct devbus { | |
73 | struct device *dev; | |
74 | void __iomem *base; | |
75 | unsigned long tick_ps; | |
76 | }; | |
77 | ||
78 | static int get_timing_param_ps(struct devbus *devbus, | |
79 | struct device_node *node, | |
80 | const char *name, | |
81 | u32 *ticks) | |
82 | { | |
83 | u32 time_ps; | |
84 | int err; | |
85 | ||
86 | err = of_property_read_u32(node, name, &time_ps); | |
87 | if (err < 0) { | |
88 | dev_err(devbus->dev, "%s has no '%s' property\n", | |
89 | name, node->full_name); | |
90 | return err; | |
91 | } | |
92 | ||
93 | *ticks = (time_ps + devbus->tick_ps - 1) / devbus->tick_ps; | |
94 | ||
95 | dev_dbg(devbus->dev, "%s: %u ps -> 0x%x\n", | |
96 | name, time_ps, *ticks); | |
97 | return 0; | |
98 | } | |
99 | ||
100 | static int devbus_set_timing_params(struct devbus *devbus, | |
101 | struct device_node *node) | |
102 | { | |
103 | struct devbus_read_params r; | |
104 | struct devbus_write_params w; | |
105 | u32 value; | |
106 | int err; | |
107 | ||
108 | dev_dbg(devbus->dev, "Setting timing parameter, tick is %lu ps\n", | |
109 | devbus->tick_ps); | |
110 | ||
111 | /* Get read timings */ | |
112 | err = of_property_read_u32(node, "devbus,bus-width", &r.bus_width); | |
113 | if (err < 0) { | |
114 | dev_err(devbus->dev, | |
115 | "%s has no 'devbus,bus-width' property\n", | |
116 | node->full_name); | |
117 | return err; | |
118 | } | |
119 | /* Convert bit width to byte width */ | |
120 | r.bus_width /= 8; | |
121 | ||
122 | err = get_timing_param_ps(devbus, node, "devbus,badr-skew-ps", | |
123 | &r.badr_skew); | |
124 | if (err < 0) | |
125 | return err; | |
126 | ||
127 | err = get_timing_param_ps(devbus, node, "devbus,turn-off-ps", | |
128 | &r.turn_off); | |
129 | if (err < 0) | |
130 | return err; | |
131 | ||
132 | err = get_timing_param_ps(devbus, node, "devbus,acc-first-ps", | |
133 | &r.acc_first); | |
134 | if (err < 0) | |
135 | return err; | |
136 | ||
137 | err = get_timing_param_ps(devbus, node, "devbus,acc-next-ps", | |
138 | &r.acc_next); | |
139 | if (err < 0) | |
140 | return err; | |
141 | ||
142 | err = get_timing_param_ps(devbus, node, "devbus,rd-setup-ps", | |
143 | &r.rd_setup); | |
144 | if (err < 0) | |
145 | return err; | |
146 | ||
147 | err = get_timing_param_ps(devbus, node, "devbus,rd-hold-ps", | |
148 | &r.rd_hold); | |
149 | if (err < 0) | |
150 | return err; | |
151 | ||
152 | /* Get write timings */ | |
153 | err = of_property_read_u32(node, "devbus,sync-enable", | |
154 | &w.sync_enable); | |
155 | if (err < 0) { | |
156 | dev_err(devbus->dev, | |
157 | "%s has no 'devbus,sync-enable' property\n", | |
158 | node->full_name); | |
159 | return err; | |
160 | } | |
161 | ||
162 | err = get_timing_param_ps(devbus, node, "devbus,ale-wr-ps", | |
163 | &w.ale_wr); | |
164 | if (err < 0) | |
165 | return err; | |
166 | ||
167 | err = get_timing_param_ps(devbus, node, "devbus,wr-low-ps", | |
168 | &w.wr_low); | |
169 | if (err < 0) | |
170 | return err; | |
171 | ||
172 | err = get_timing_param_ps(devbus, node, "devbus,wr-high-ps", | |
173 | &w.wr_high); | |
174 | if (err < 0) | |
175 | return err; | |
176 | ||
177 | /* Set read timings */ | |
178 | value = r.bus_width << DEV_WIDTH_BIT | | |
179 | r.badr_skew << BADR_SKEW_BIT | | |
180 | r.rd_hold << RD_HOLD_BIT | | |
181 | r.acc_next << ACC_NEXT_BIT | | |
182 | r.rd_setup << RD_SETUP_BIT | | |
183 | r.acc_first << ACC_FIRST_BIT | | |
184 | r.turn_off; | |
185 | ||
186 | dev_dbg(devbus->dev, "read parameters register 0x%p = 0x%x\n", | |
187 | devbus->base + READ_PARAM_OFFSET, | |
188 | value); | |
189 | ||
190 | writel(value, devbus->base + READ_PARAM_OFFSET); | |
191 | ||
192 | /* Set write timings */ | |
193 | value = w.sync_enable << SYNC_ENABLE_BIT | | |
194 | w.wr_low << WR_LOW_BIT | | |
195 | w.wr_high << WR_HIGH_BIT | | |
196 | w.ale_wr; | |
197 | ||
198 | dev_dbg(devbus->dev, "write parameters register: 0x%p = 0x%x\n", | |
199 | devbus->base + WRITE_PARAM_OFFSET, | |
200 | value); | |
201 | ||
202 | writel(value, devbus->base + WRITE_PARAM_OFFSET); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static int mvebu_devbus_probe(struct platform_device *pdev) | |
208 | { | |
209 | struct device *dev = &pdev->dev; | |
210 | struct device_node *node = pdev->dev.of_node; | |
211 | struct device_node *parent; | |
212 | struct devbus *devbus; | |
213 | struct resource *res; | |
214 | struct clk *clk; | |
215 | unsigned long rate; | |
216 | const __be32 *ranges; | |
217 | int err, cs; | |
218 | int addr_cells, p_addr_cells, size_cells; | |
219 | int ranges_len, tuple_len; | |
220 | u32 base, size; | |
221 | ||
222 | devbus = devm_kzalloc(&pdev->dev, sizeof(struct devbus), GFP_KERNEL); | |
223 | if (!devbus) | |
224 | return -ENOMEM; | |
225 | ||
226 | devbus->dev = dev; | |
227 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
228 | devbus->base = devm_ioremap_resource(&pdev->dev, res); | |
229 | if (IS_ERR(devbus->base)) | |
230 | return PTR_ERR(devbus->base); | |
231 | ||
232 | clk = devm_clk_get(&pdev->dev, NULL); | |
233 | if (IS_ERR(clk)) | |
234 | return PTR_ERR(clk); | |
235 | clk_prepare_enable(clk); | |
236 | ||
237 | /* | |
238 | * Obtain clock period in picoseconds, | |
239 | * we need this in order to convert timing | |
240 | * parameters from cycles to picoseconds. | |
241 | */ | |
242 | rate = clk_get_rate(clk) / 1000; | |
243 | devbus->tick_ps = 1000000000 / rate; | |
244 | ||
245 | /* Read the device tree node and set the new timing parameters */ | |
246 | err = devbus_set_timing_params(devbus, node); | |
247 | if (err < 0) | |
248 | return err; | |
249 | ||
250 | /* | |
251 | * Allocate an address window for this device. | |
252 | * If the device probing fails, then we won't be able to | |
253 | * remove the allocated address decoding window. | |
254 | * | |
255 | * FIXME: This is only a temporary hack! We need to do this here | |
256 | * because we still don't have device tree bindings for mbus. | |
257 | * Once that support is added, we will declare these address windows | |
258 | * statically in the device tree, and remove the window configuration | |
259 | * from here. | |
260 | */ | |
261 | ||
262 | /* | |
263 | * Get the CS to choose the window string. | |
264 | * This is a bit hacky, but it will be removed once the | |
265 | * address windows are declared in the device tree. | |
266 | */ | |
267 | cs = (((unsigned long)devbus->base) % 0x400) / 8; | |
268 | ||
269 | /* | |
270 | * Parse 'ranges' property to obtain a (base,size) window tuple. | |
271 | * This will be removed once the address windows | |
272 | * are declared in the device tree. | |
273 | */ | |
274 | parent = of_get_parent(node); | |
275 | if (!parent) | |
276 | return -EINVAL; | |
277 | ||
278 | p_addr_cells = of_n_addr_cells(parent); | |
279 | of_node_put(parent); | |
280 | ||
281 | addr_cells = of_n_addr_cells(node); | |
282 | size_cells = of_n_size_cells(node); | |
283 | tuple_len = (p_addr_cells + addr_cells + size_cells) * sizeof(__be32); | |
284 | ||
285 | ranges = of_get_property(node, "ranges", &ranges_len); | |
286 | if (ranges == NULL || ranges_len != tuple_len) | |
287 | return -EINVAL; | |
288 | ||
289 | base = of_translate_address(node, ranges + addr_cells); | |
290 | if (base == OF_BAD_ADDR) | |
291 | return -EINVAL; | |
292 | size = of_read_number(ranges + addr_cells + p_addr_cells, size_cells); | |
293 | ||
294 | /* | |
295 | * Create an mbus address windows. | |
296 | * FIXME: Remove this, together with the above code, once the | |
297 | * address windows are declared in the device tree. | |
298 | */ | |
299 | err = mvebu_mbus_add_window(devbus_wins[cs], base, size); | |
300 | if (err < 0) | |
301 | return err; | |
302 | ||
303 | /* | |
304 | * We need to create a child device explicitly from here to | |
305 | * guarantee that the child will be probed after the timing | |
306 | * parameters for the bus are written. | |
307 | */ | |
308 | err = of_platform_populate(node, NULL, NULL, dev); | |
309 | if (err < 0) { | |
310 | mvebu_mbus_del_window(base, size); | |
311 | return err; | |
312 | } | |
313 | ||
314 | return 0; | |
315 | } | |
316 | ||
317 | static const struct of_device_id mvebu_devbus_of_match[] = { | |
318 | { .compatible = "marvell,mvebu-devbus" }, | |
319 | {}, | |
320 | }; | |
321 | MODULE_DEVICE_TABLE(of, mvebu_devbus_of_match); | |
322 | ||
323 | static struct platform_driver mvebu_devbus_driver = { | |
324 | .probe = mvebu_devbus_probe, | |
325 | .driver = { | |
326 | .name = "mvebu-devbus", | |
327 | .owner = THIS_MODULE, | |
328 | .of_match_table = mvebu_devbus_of_match, | |
329 | }, | |
330 | }; | |
331 | ||
332 | static int __init mvebu_devbus_init(void) | |
333 | { | |
334 | return platform_driver_register(&mvebu_devbus_driver); | |
335 | } | |
336 | module_init(mvebu_devbus_init); | |
337 | ||
338 | MODULE_LICENSE("GPL v2"); | |
339 | MODULE_AUTHOR("Ezequiel Garcia <[email protected]>"); | |
340 | MODULE_DESCRIPTION("Marvell EBU SoC Device Bus controller"); |