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