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 | ||
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 | } |