Commit | Line | Data |
---|---|---|
c1ed6407 DB |
1 | /* |
2 | * linux/arch/arm/mach-omap2/usb-tusb6010.c | |
3 | * | |
4 | * Copyright (C) 2006 Nokia Corporation | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | */ | |
10 | ||
d44b28c4 | 11 | #include <linux/string.h> |
c1ed6407 DB |
12 | #include <linux/types.h> |
13 | #include <linux/errno.h> | |
14 | #include <linux/delay.h> | |
15 | #include <linux/platform_device.h> | |
f2d18fea | 16 | #include <linux/gpio.h> |
dc28094b | 17 | #include <linux/export.h> |
c1ed6407 DB |
18 | |
19 | #include <linux/usb/musb.h> | |
20 | ||
ce491cf8 | 21 | #include <plat/gpmc.h> |
c1ed6407 | 22 | |
f99bf16d | 23 | #include "mux.h" |
c1ed6407 DB |
24 | |
25 | static u8 async_cs, sync_cs; | |
26 | static unsigned refclk_psec; | |
27 | ||
28 | ||
29 | /* t2_ps, when quantized to fclk units, must happen no earlier than | |
30 | * the clock after after t1_NS. | |
31 | * | |
32 | * Return a possibly updated value of t2_ps, converted to nsec. | |
33 | */ | |
34 | static unsigned | |
35 | next_clk(unsigned t1_NS, unsigned t2_ps, unsigned fclk_ps) | |
36 | { | |
37 | unsigned t1_ps = t1_NS * 1000; | |
38 | unsigned t1_f, t2_f; | |
39 | ||
40 | if ((t1_ps + fclk_ps) < t2_ps) | |
41 | return t2_ps / 1000; | |
42 | ||
43 | t1_f = (t1_ps + fclk_ps - 1) / fclk_ps; | |
44 | t2_f = (t2_ps + fclk_ps - 1) / fclk_ps; | |
45 | ||
46 | if (t1_f >= t2_f) | |
47 | t2_f = t1_f + 1; | |
48 | ||
49 | return (t2_f * fclk_ps) / 1000; | |
50 | } | |
51 | ||
52 | /* NOTE: timings are from tusb 6010 datasheet Rev 1.8, 12-Sept 2006 */ | |
53 | ||
54 | static int tusb_set_async_mode(unsigned sysclk_ps, unsigned fclk_ps) | |
55 | { | |
56 | struct gpmc_timings t; | |
57 | unsigned t_acsnh_advnh = sysclk_ps + 3000; | |
58 | unsigned tmp; | |
59 | ||
60 | memset(&t, 0, sizeof(t)); | |
61 | ||
62 | /* CS_ON = t_acsnh_acsnl */ | |
63 | t.cs_on = 8; | |
64 | /* ADV_ON = t_acsnh_advnh - t_advn */ | |
65 | t.adv_on = next_clk(t.cs_on, t_acsnh_advnh - 7000, fclk_ps); | |
66 | ||
67 | /* | |
68 | * READ ... from omap2420 TRM fig 12-13 | |
69 | */ | |
70 | ||
71 | /* ADV_RD_OFF = t_acsnh_advnh */ | |
72 | t.adv_rd_off = next_clk(t.adv_on, t_acsnh_advnh, fclk_ps); | |
73 | ||
74 | /* OE_ON = t_acsnh_advnh + t_advn_oen (then wait for nRDY) */ | |
75 | t.oe_on = next_clk(t.adv_on, t_acsnh_advnh + 1000, fclk_ps); | |
76 | ||
77 | /* ACCESS = counters continue only after nRDY */ | |
78 | tmp = t.oe_on * 1000 + 300; | |
79 | t.access = next_clk(t.oe_on, tmp, fclk_ps); | |
80 | ||
81 | /* OE_OFF = after data gets sampled */ | |
82 | tmp = t.access * 1000; | |
83 | t.oe_off = next_clk(t.access, tmp, fclk_ps); | |
84 | ||
85 | t.cs_rd_off = t.oe_off; | |
86 | ||
87 | tmp = t.cs_rd_off * 1000 + 7000 /* t_acsn_rdy_z */; | |
88 | t.rd_cycle = next_clk(t.cs_rd_off, tmp, fclk_ps); | |
89 | ||
90 | /* | |
91 | * WRITE ... from omap2420 TRM fig 12-15 | |
92 | */ | |
93 | ||
94 | /* ADV_WR_OFF = t_acsnh_advnh */ | |
95 | t.adv_wr_off = t.adv_rd_off; | |
96 | ||
97 | /* WE_ON = t_acsnh_advnh + t_advn_wen (then wait for nRDY) */ | |
98 | t.we_on = next_clk(t.adv_wr_off, t_acsnh_advnh + 1000, fclk_ps); | |
99 | ||
100 | /* WE_OFF = after data gets sampled */ | |
101 | tmp = t.we_on * 1000 + 300; | |
102 | t.we_off = next_clk(t.we_on, tmp, fclk_ps); | |
103 | ||
104 | t.cs_wr_off = t.we_off; | |
105 | ||
106 | tmp = t.cs_wr_off * 1000 + 7000 /* t_acsn_rdy_z */; | |
107 | t.wr_cycle = next_clk(t.cs_wr_off, tmp, fclk_ps); | |
108 | ||
109 | return gpmc_cs_set_timings(async_cs, &t); | |
110 | } | |
111 | ||
112 | static int tusb_set_sync_mode(unsigned sysclk_ps, unsigned fclk_ps) | |
113 | { | |
114 | struct gpmc_timings t; | |
115 | unsigned t_scsnh_advnh = sysclk_ps + 3000; | |
116 | unsigned tmp; | |
117 | ||
118 | memset(&t, 0, sizeof(t)); | |
119 | t.cs_on = 8; | |
120 | ||
121 | /* ADV_ON = t_acsnh_advnh - t_advn */ | |
122 | t.adv_on = next_clk(t.cs_on, t_scsnh_advnh - 7000, fclk_ps); | |
123 | ||
124 | /* GPMC_CLK rate = fclk rate / div */ | |
a3551f5b AH |
125 | t.sync_clk = 11100 /* 11.1 nsec */; |
126 | tmp = (t.sync_clk + fclk_ps - 1) / fclk_ps; | |
c1ed6407 DB |
127 | if (tmp > 4) |
128 | return -ERANGE; | |
eeb3711b | 129 | if (tmp == 0) |
c1ed6407 DB |
130 | tmp = 1; |
131 | t.page_burst_access = (fclk_ps * tmp) / 1000; | |
132 | ||
133 | /* | |
134 | * READ ... based on omap2420 TRM fig 12-19, 12-20 | |
135 | */ | |
136 | ||
137 | /* ADV_RD_OFF = t_scsnh_advnh */ | |
138 | t.adv_rd_off = next_clk(t.adv_on, t_scsnh_advnh, fclk_ps); | |
139 | ||
140 | /* OE_ON = t_scsnh_advnh + t_advn_oen * fclk_ps (then wait for nRDY) */ | |
141 | tmp = (t.adv_rd_off * 1000) + (3 * fclk_ps); | |
142 | t.oe_on = next_clk(t.adv_on, tmp, fclk_ps); | |
143 | ||
144 | /* ACCESS = number of clock cycles after t_adv_eon */ | |
145 | tmp = (t.oe_on * 1000) + (5 * fclk_ps); | |
146 | t.access = next_clk(t.oe_on, tmp, fclk_ps); | |
147 | ||
148 | /* OE_OFF = after data gets sampled */ | |
149 | tmp = (t.access * 1000) + (1 * fclk_ps); | |
150 | t.oe_off = next_clk(t.access, tmp, fclk_ps); | |
151 | ||
152 | t.cs_rd_off = t.oe_off; | |
153 | ||
154 | tmp = t.cs_rd_off * 1000 + 7000 /* t_scsn_rdy_z */; | |
155 | t.rd_cycle = next_clk(t.cs_rd_off, tmp, fclk_ps); | |
156 | ||
157 | /* | |
158 | * WRITE ... based on omap2420 TRM fig 12-21 | |
159 | */ | |
160 | ||
161 | /* ADV_WR_OFF = t_scsnh_advnh */ | |
162 | t.adv_wr_off = t.adv_rd_off; | |
163 | ||
164 | /* WE_ON = t_scsnh_advnh + t_advn_wen * fclk_ps (then wait for nRDY) */ | |
165 | tmp = (t.adv_wr_off * 1000) + (3 * fclk_ps); | |
166 | t.we_on = next_clk(t.adv_wr_off, tmp, fclk_ps); | |
167 | ||
168 | /* WE_OFF = number of clock cycles after t_adv_wen */ | |
169 | tmp = (t.we_on * 1000) + (6 * fclk_ps); | |
170 | t.we_off = next_clk(t.we_on, tmp, fclk_ps); | |
171 | ||
172 | t.cs_wr_off = t.we_off; | |
173 | ||
174 | tmp = t.cs_wr_off * 1000 + 7000 /* t_scsn_rdy_z */; | |
175 | t.wr_cycle = next_clk(t.cs_wr_off, tmp, fclk_ps); | |
176 | ||
177 | return gpmc_cs_set_timings(sync_cs, &t); | |
178 | } | |
179 | ||
180 | extern unsigned long gpmc_get_fclk_period(void); | |
181 | ||
182 | /* tusb driver calls this when it changes the chip's clocking */ | |
183 | int tusb6010_platform_retime(unsigned is_refclk) | |
184 | { | |
185 | static const char error[] = | |
186 | KERN_ERR "tusb6010 %s retime error %d\n"; | |
187 | ||
188 | unsigned fclk_ps = gpmc_get_fclk_period(); | |
189 | unsigned sysclk_ps; | |
190 | int status; | |
191 | ||
eaf9393b | 192 | if (!refclk_psec || fclk_ps == 0) |
c1ed6407 DB |
193 | return -ENODEV; |
194 | ||
195 | sysclk_ps = is_refclk ? refclk_psec : TUSB6010_OSCCLK_60; | |
196 | ||
197 | status = tusb_set_async_mode(sysclk_ps, fclk_ps); | |
198 | if (status < 0) { | |
199 | printk(error, "async", status); | |
200 | goto done; | |
201 | } | |
202 | status = tusb_set_sync_mode(sysclk_ps, fclk_ps); | |
203 | if (status < 0) | |
204 | printk(error, "sync", status); | |
205 | done: | |
206 | return status; | |
207 | } | |
208 | EXPORT_SYMBOL_GPL(tusb6010_platform_retime); | |
209 | ||
210 | static struct resource tusb_resources[] = { | |
211 | /* Order is significant! The start/end fields | |
212 | * are updated during setup.. | |
213 | */ | |
214 | { /* Asynchronous access */ | |
215 | .flags = IORESOURCE_MEM, | |
216 | }, | |
217 | { /* Synchronous access */ | |
218 | .flags = IORESOURCE_MEM, | |
219 | }, | |
220 | { /* IRQ */ | |
6ec1e077 | 221 | .name = "mc", |
c1ed6407 DB |
222 | .flags = IORESOURCE_IRQ, |
223 | }, | |
224 | }; | |
225 | ||
226 | static u64 tusb_dmamask = ~(u32)0; | |
227 | ||
228 | static struct platform_device tusb_device = { | |
18688fbe | 229 | .name = "musb-tusb", |
c1ed6407 DB |
230 | .id = -1, |
231 | .dev = { | |
232 | .dma_mask = &tusb_dmamask, | |
233 | .coherent_dma_mask = 0xffffffff, | |
234 | }, | |
235 | .num_resources = ARRAY_SIZE(tusb_resources), | |
236 | .resource = tusb_resources, | |
237 | }; | |
238 | ||
239 | ||
240 | /* this may be called only from board-*.c setup code */ | |
241 | int __init | |
242 | tusb6010_setup_interface(struct musb_hdrc_platform_data *data, | |
243 | unsigned ps_refclk, unsigned waitpin, | |
244 | unsigned async, unsigned sync, | |
245 | unsigned irq, unsigned dmachan) | |
246 | { | |
247 | int status; | |
248 | static char error[] __initdata = | |
249 | KERN_ERR "tusb6010 init error %d, %d\n"; | |
250 | ||
251 | /* ASYNC region, primarily for PIO */ | |
252 | status = gpmc_cs_request(async, SZ_16M, (unsigned long *) | |
253 | &tusb_resources[0].start); | |
254 | if (status < 0) { | |
255 | printk(error, 1, status); | |
256 | return status; | |
257 | } | |
258 | tusb_resources[0].end = tusb_resources[0].start + 0x9ff; | |
259 | async_cs = async; | |
260 | gpmc_cs_write_reg(async, GPMC_CS_CONFIG1, | |
261 | GPMC_CONFIG1_PAGE_LEN(2) | |
262 | | GPMC_CONFIG1_WAIT_READ_MON | |
263 | | GPMC_CONFIG1_WAIT_WRITE_MON | |
264 | | GPMC_CONFIG1_WAIT_PIN_SEL(waitpin) | |
265 | | GPMC_CONFIG1_READTYPE_ASYNC | |
266 | | GPMC_CONFIG1_WRITETYPE_ASYNC | |
267 | | GPMC_CONFIG1_DEVICESIZE_16 | |
268 | | GPMC_CONFIG1_DEVICETYPE_NOR | |
269 | | GPMC_CONFIG1_MUXADDDATA); | |
270 | ||
271 | ||
272 | /* SYNC region, primarily for DMA */ | |
273 | status = gpmc_cs_request(sync, SZ_16M, (unsigned long *) | |
274 | &tusb_resources[1].start); | |
275 | if (status < 0) { | |
276 | printk(error, 2, status); | |
277 | return status; | |
278 | } | |
279 | tusb_resources[1].end = tusb_resources[1].start + 0x9ff; | |
280 | sync_cs = sync; | |
281 | gpmc_cs_write_reg(sync, GPMC_CS_CONFIG1, | |
282 | GPMC_CONFIG1_READMULTIPLE_SUPP | |
283 | | GPMC_CONFIG1_READTYPE_SYNC | |
284 | | GPMC_CONFIG1_WRITEMULTIPLE_SUPP | |
285 | | GPMC_CONFIG1_WRITETYPE_SYNC | |
286 | | GPMC_CONFIG1_CLKACTIVATIONTIME(1) | |
287 | | GPMC_CONFIG1_PAGE_LEN(2) | |
288 | | GPMC_CONFIG1_WAIT_READ_MON | |
289 | | GPMC_CONFIG1_WAIT_WRITE_MON | |
290 | | GPMC_CONFIG1_WAIT_PIN_SEL(waitpin) | |
291 | | GPMC_CONFIG1_DEVICESIZE_16 | |
292 | | GPMC_CONFIG1_DEVICETYPE_NOR | |
293 | | GPMC_CONFIG1_MUXADDDATA | |
294 | /* fclk divider gets set later */ | |
295 | ); | |
296 | ||
297 | /* IRQ */ | |
bc593f5d | 298 | status = gpio_request_one(irq, GPIOF_IN, "TUSB6010 irq"); |
c1ed6407 DB |
299 | if (status < 0) { |
300 | printk(error, 3, status); | |
301 | return status; | |
302 | } | |
3d09b33f | 303 | tusb_resources[2].start = gpio_to_irq(irq); |
c1ed6407 DB |
304 | |
305 | /* set up memory timings ... can speed them up later */ | |
306 | if (!ps_refclk) { | |
307 | printk(error, 4, status); | |
308 | return -ENODEV; | |
309 | } | |
310 | refclk_psec = ps_refclk; | |
311 | status = tusb6010_platform_retime(1); | |
312 | if (status < 0) { | |
313 | printk(error, 5, status); | |
314 | return status; | |
315 | } | |
316 | ||
317 | /* finish device setup ... */ | |
318 | if (!data) { | |
319 | printk(error, 6, status); | |
320 | return -ENODEV; | |
321 | } | |
c1ed6407 DB |
322 | tusb_device.dev.platform_data = data; |
323 | ||
324 | /* REVISIT let the driver know what DMA channels work */ | |
325 | if (!dmachan) | |
326 | tusb_device.dev.dma_mask = NULL; | |
327 | else { | |
328 | /* assume OMAP 2420 ES2.0 and later */ | |
329 | if (dmachan & (1 << 0)) | |
f99bf16d | 330 | omap_mux_init_signal("sys_ndmareq0", 0); |
c1ed6407 | 331 | if (dmachan & (1 << 1)) |
f99bf16d | 332 | omap_mux_init_signal("sys_ndmareq1", 0); |
c1ed6407 | 333 | if (dmachan & (1 << 2)) |
f99bf16d | 334 | omap_mux_init_signal("sys_ndmareq2", 0); |
c1ed6407 | 335 | if (dmachan & (1 << 3)) |
f99bf16d | 336 | omap_mux_init_signal("sys_ndmareq3", 0); |
c1ed6407 | 337 | if (dmachan & (1 << 4)) |
f99bf16d | 338 | omap_mux_init_signal("sys_ndmareq4", 0); |
c1ed6407 | 339 | if (dmachan & (1 << 5)) |
f99bf16d | 340 | omap_mux_init_signal("sys_ndmareq5", 0); |
c1ed6407 DB |
341 | } |
342 | ||
343 | /* so far so good ... register the device */ | |
344 | status = platform_device_register(&tusb_device); | |
345 | if (status < 0) { | |
346 | printk(error, 7, status); | |
347 | return status; | |
348 | } | |
349 | return 0; | |
350 | } |