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