| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | *	driver/usb/gadget/imx_udc.c | 
|  | 3 | * | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 4 | *	Copyright (C) 2005 Mike Lee <eemike@gmail.com> | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 5 | *	Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com> | 
|  | 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; either version 2 of the License, or | 
|  | 10 | *	(at your option) any later version. | 
|  | 11 | * | 
|  | 12 | *	This program is distributed in the hope that it will be useful, | 
|  | 13 | *	but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 14 | *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 15 | *	GNU General Public License for more details. | 
|  | 16 | */ | 
|  | 17 |  | 
|  | 18 | #include <linux/init.h> | 
|  | 19 | #include <linux/kernel.h> | 
|  | 20 | #include <linux/platform_device.h> | 
|  | 21 | #include <linux/module.h> | 
|  | 22 | #include <linux/errno.h> | 
|  | 23 | #include <linux/list.h> | 
|  | 24 | #include <linux/interrupt.h> | 
|  | 25 | #include <linux/io.h> | 
|  | 26 | #include <linux/irq.h> | 
|  | 27 | #include <linux/device.h> | 
|  | 28 | #include <linux/dma-mapping.h> | 
|  | 29 | #include <linux/clk.h> | 
|  | 30 | #include <linux/delay.h> | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 31 | #include <linux/timer.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 32 | #include <linux/slab.h> | 
| Linus Torvalds | 268bb0c | 2011-05-20 12:50:29 -0700 | [diff] [blame] | 33 | #include <linux/prefetch.h> | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 34 |  | 
|  | 35 | #include <linux/usb/ch9.h> | 
|  | 36 | #include <linux/usb/gadget.h> | 
|  | 37 |  | 
|  | 38 | #include <mach/usb.h> | 
|  | 39 | #include <mach/hardware.h> | 
|  | 40 |  | 
|  | 41 | #include "imx_udc.h" | 
|  | 42 |  | 
|  | 43 | static const char driver_name[] = "imx_udc"; | 
|  | 44 | static const char ep0name[] = "ep0"; | 
|  | 45 |  | 
|  | 46 | void ep0_chg_stat(const char *label, struct imx_udc_struct *imx_usb, | 
|  | 47 | enum ep0_state stat); | 
|  | 48 |  | 
|  | 49 | /******************************************************************************* | 
|  | 50 | * IMX UDC hardware related functions | 
|  | 51 | ******************************************************************************* | 
|  | 52 | */ | 
|  | 53 |  | 
|  | 54 | void imx_udc_enable(struct imx_udc_struct *imx_usb) | 
|  | 55 | { | 
|  | 56 | int temp = __raw_readl(imx_usb->base + USB_CTRL); | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 57 | __raw_writel(temp | CTRL_FE_ENA | CTRL_AFE_ENA, | 
|  | 58 | imx_usb->base + USB_CTRL); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 59 | imx_usb->gadget.speed = USB_SPEED_FULL; | 
|  | 60 | } | 
|  | 61 |  | 
|  | 62 | void imx_udc_disable(struct imx_udc_struct *imx_usb) | 
|  | 63 | { | 
|  | 64 | int temp = __raw_readl(imx_usb->base + USB_CTRL); | 
|  | 65 |  | 
|  | 66 | __raw_writel(temp & ~(CTRL_FE_ENA | CTRL_AFE_ENA), | 
|  | 67 | imx_usb->base + USB_CTRL); | 
|  | 68 |  | 
|  | 69 | ep0_chg_stat(__func__, imx_usb, EP0_IDLE); | 
|  | 70 | imx_usb->gadget.speed = USB_SPEED_UNKNOWN; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | void imx_udc_reset(struct imx_udc_struct *imx_usb) | 
|  | 74 | { | 
|  | 75 | int temp = __raw_readl(imx_usb->base + USB_ENAB); | 
|  | 76 |  | 
|  | 77 | /* set RST bit */ | 
|  | 78 | __raw_writel(temp | ENAB_RST, imx_usb->base + USB_ENAB); | 
|  | 79 |  | 
|  | 80 | /* wait RST bit to clear */ | 
|  | 81 | do {} while (__raw_readl(imx_usb->base + USB_ENAB) & ENAB_RST); | 
|  | 82 |  | 
|  | 83 | /* wait CFG bit to assert */ | 
|  | 84 | do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); | 
|  | 85 |  | 
|  | 86 | /* udc module is now ready */ | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | void imx_udc_config(struct imx_udc_struct *imx_usb) | 
|  | 90 | { | 
|  | 91 | u8 ep_conf[5]; | 
|  | 92 | u8 i, j, cfg; | 
|  | 93 | struct imx_ep_struct *imx_ep; | 
|  | 94 |  | 
|  | 95 | /* wait CFG bit to assert */ | 
|  | 96 | do {} while (!(__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG)); | 
|  | 97 |  | 
|  | 98 | /* Download the endpoint buffer for endpoint 0. */ | 
|  | 99 | for (j = 0; j < 5; j++) { | 
|  | 100 | i = (j == 2 ? imx_usb->imx_ep[0].fifosize : 0x00); | 
|  | 101 | __raw_writeb(i, imx_usb->base + USB_DDAT); | 
|  | 102 | do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_BSY); | 
|  | 103 | } | 
|  | 104 |  | 
|  | 105 | /* Download the endpoint buffers for endpoints 1-5. | 
|  | 106 | * We specify two configurations, one interface | 
|  | 107 | */ | 
|  | 108 | for (cfg = 1; cfg < 3; cfg++) { | 
|  | 109 | for (i = 1; i < IMX_USB_NB_EP; i++) { | 
|  | 110 | imx_ep = &imx_usb->imx_ep[i]; | 
|  | 111 | /* EP no | Config no */ | 
|  | 112 | ep_conf[0] = (i << 4) | (cfg << 2); | 
|  | 113 | /* Type | Direction */ | 
|  | 114 | ep_conf[1] = (imx_ep->bmAttributes << 3) | | 
|  | 115 | (EP_DIR(imx_ep) << 2); | 
|  | 116 | /* Max packet size */ | 
|  | 117 | ep_conf[2] = imx_ep->fifosize; | 
|  | 118 | /* TRXTYP */ | 
|  | 119 | ep_conf[3] = 0xC0; | 
|  | 120 | /* FIFO no */ | 
|  | 121 | ep_conf[4] = i; | 
|  | 122 |  | 
|  | 123 | D_INI(imx_usb->dev, | 
|  | 124 | "<%s> ep%d_conf[%d]:" | 
|  | 125 | "[%02x-%02x-%02x-%02x-%02x]\n", | 
|  | 126 | __func__, i, cfg, | 
|  | 127 | ep_conf[0], ep_conf[1], ep_conf[2], | 
|  | 128 | ep_conf[3], ep_conf[4]); | 
|  | 129 |  | 
|  | 130 | for (j = 0; j < 5; j++) { | 
|  | 131 | __raw_writeb(ep_conf[j], | 
|  | 132 | imx_usb->base + USB_DDAT); | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 133 | do {} while (__raw_readl(imx_usb->base | 
|  | 134 | + USB_DADR) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 135 | & DADR_BSY); | 
|  | 136 | } | 
|  | 137 | } | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | /* wait CFG bit to clear */ | 
|  | 141 | do {} while (__raw_readl(imx_usb->base + USB_DADR) & DADR_CFG); | 
|  | 142 | } | 
|  | 143 |  | 
|  | 144 | void imx_udc_init_irq(struct imx_udc_struct *imx_usb) | 
|  | 145 | { | 
|  | 146 | int i; | 
|  | 147 |  | 
|  | 148 | /* Mask and clear all irqs */ | 
|  | 149 | __raw_writel(0xFFFFFFFF, imx_usb->base + USB_MASK); | 
|  | 150 | __raw_writel(0xFFFFFFFF, imx_usb->base + USB_INTR); | 
|  | 151 | for (i = 0; i < IMX_USB_NB_EP; i++) { | 
|  | 152 | __raw_writel(0x1FF, imx_usb->base + USB_EP_MASK(i)); | 
|  | 153 | __raw_writel(0x1FF, imx_usb->base + USB_EP_INTR(i)); | 
|  | 154 | } | 
|  | 155 |  | 
|  | 156 | /* Enable USB irqs */ | 
|  | 157 | __raw_writel(INTR_MSOF | INTR_FRAME_MATCH, imx_usb->base + USB_MASK); | 
|  | 158 |  | 
|  | 159 | /* Enable EP0 irqs */ | 
|  | 160 | __raw_writel(0x1FF & ~(EPINTR_DEVREQ | EPINTR_MDEVREQ | EPINTR_EOT | 
|  | 161 | | EPINTR_EOF | EPINTR_FIFO_EMPTY | EPINTR_FIFO_FULL), | 
|  | 162 | imx_usb->base + USB_EP_MASK(0)); | 
|  | 163 | } | 
|  | 164 |  | 
|  | 165 | void imx_udc_init_ep(struct imx_udc_struct *imx_usb) | 
|  | 166 | { | 
|  | 167 | int i, max, temp; | 
|  | 168 | struct imx_ep_struct *imx_ep; | 
|  | 169 | for (i = 0; i < IMX_USB_NB_EP; i++) { | 
|  | 170 | imx_ep = &imx_usb->imx_ep[i]; | 
|  | 171 | switch (imx_ep->fifosize) { | 
|  | 172 | case 8: | 
|  | 173 | max = 0; | 
|  | 174 | break; | 
|  | 175 | case 16: | 
|  | 176 | max = 1; | 
|  | 177 | break; | 
|  | 178 | case 32: | 
|  | 179 | max = 2; | 
|  | 180 | break; | 
|  | 181 | case 64: | 
|  | 182 | max = 3; | 
|  | 183 | break; | 
|  | 184 | default: | 
|  | 185 | max = 1; | 
|  | 186 | break; | 
|  | 187 | } | 
|  | 188 | temp = (EP_DIR(imx_ep) << 7) | (max << 5) | 
|  | 189 | | (imx_ep->bmAttributes << 3); | 
|  | 190 | __raw_writel(temp, imx_usb->base + USB_EP_STAT(i)); | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 191 | __raw_writel(temp | EPSTAT_FLUSH, | 
|  | 192 | imx_usb->base + USB_EP_STAT(i)); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 193 | D_INI(imx_usb->dev, "<%s> ep%d_stat %08x\n", __func__, i, | 
|  | 194 | __raw_readl(imx_usb->base + USB_EP_STAT(i))); | 
|  | 195 | } | 
|  | 196 | } | 
|  | 197 |  | 
|  | 198 | void imx_udc_init_fifo(struct imx_udc_struct *imx_usb) | 
|  | 199 | { | 
|  | 200 | int i, temp; | 
|  | 201 | struct imx_ep_struct *imx_ep; | 
|  | 202 | for (i = 0; i < IMX_USB_NB_EP; i++) { | 
|  | 203 | imx_ep = &imx_usb->imx_ep[i]; | 
|  | 204 |  | 
|  | 205 | /* Fifo control */ | 
|  | 206 | temp = EP_DIR(imx_ep) ? 0x0B000000 : 0x0F000000; | 
|  | 207 | __raw_writel(temp, imx_usb->base + USB_EP_FCTRL(i)); | 
|  | 208 | D_INI(imx_usb->dev, "<%s> ep%d_fctrl %08x\n", __func__, i, | 
|  | 209 | __raw_readl(imx_usb->base + USB_EP_FCTRL(i))); | 
|  | 210 |  | 
|  | 211 | /* Fifo alarm */ | 
|  | 212 | temp = (i ? imx_ep->fifosize / 2 : 0); | 
|  | 213 | __raw_writel(temp, imx_usb->base + USB_EP_FALRM(i)); | 
|  | 214 | D_INI(imx_usb->dev, "<%s> ep%d_falrm %08x\n", __func__, i, | 
|  | 215 | __raw_readl(imx_usb->base + USB_EP_FALRM(i))); | 
|  | 216 | } | 
|  | 217 | } | 
|  | 218 |  | 
|  | 219 | static void imx_udc_init(struct imx_udc_struct *imx_usb) | 
|  | 220 | { | 
|  | 221 | /* Reset UDC */ | 
|  | 222 | imx_udc_reset(imx_usb); | 
|  | 223 |  | 
|  | 224 | /* Download config to enpoint buffer */ | 
|  | 225 | imx_udc_config(imx_usb); | 
|  | 226 |  | 
|  | 227 | /* Setup interrups */ | 
|  | 228 | imx_udc_init_irq(imx_usb); | 
|  | 229 |  | 
|  | 230 | /* Setup endpoints */ | 
|  | 231 | imx_udc_init_ep(imx_usb); | 
|  | 232 |  | 
|  | 233 | /* Setup fifos */ | 
|  | 234 | imx_udc_init_fifo(imx_usb); | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | void imx_ep_irq_enable(struct imx_ep_struct *imx_ep) | 
|  | 238 | { | 
|  | 239 |  | 
|  | 240 | int i = EP_NO(imx_ep); | 
|  | 241 |  | 
|  | 242 | __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); | 
|  | 243 | __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); | 
|  | 244 | __raw_writel(0x1FF & ~(EPINTR_EOT | EPINTR_EOF), | 
|  | 245 | imx_ep->imx_usb->base + USB_EP_MASK(i)); | 
|  | 246 | } | 
|  | 247 |  | 
|  | 248 | void imx_ep_irq_disable(struct imx_ep_struct *imx_ep) | 
|  | 249 | { | 
|  | 250 |  | 
|  | 251 | int i = EP_NO(imx_ep); | 
|  | 252 |  | 
|  | 253 | __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_MASK(i)); | 
|  | 254 | __raw_writel(0x1FF, imx_ep->imx_usb->base + USB_EP_INTR(i)); | 
|  | 255 | } | 
|  | 256 |  | 
|  | 257 | int imx_ep_empty(struct imx_ep_struct *imx_ep) | 
|  | 258 | { | 
|  | 259 | struct imx_udc_struct *imx_usb = imx_ep->imx_usb; | 
|  | 260 |  | 
|  | 261 | return __raw_readl(imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) | 
|  | 262 | & FSTAT_EMPTY; | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | unsigned imx_fifo_bcount(struct imx_ep_struct *imx_ep) | 
|  | 266 | { | 
|  | 267 | struct imx_udc_struct *imx_usb = imx_ep->imx_usb; | 
|  | 268 |  | 
|  | 269 | return (__raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))) | 
|  | 270 | & EPSTAT_BCOUNT) >> 16; | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | void imx_flush(struct imx_ep_struct *imx_ep) | 
|  | 274 | { | 
|  | 275 | struct imx_udc_struct *imx_usb = imx_ep->imx_usb; | 
|  | 276 |  | 
|  | 277 | int temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); | 
|  | 278 | __raw_writel(temp | EPSTAT_FLUSH, | 
|  | 279 | imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); | 
|  | 280 | } | 
|  | 281 |  | 
|  | 282 | void imx_ep_stall(struct imx_ep_struct *imx_ep) | 
|  | 283 | { | 
|  | 284 | struct imx_udc_struct *imx_usb = imx_ep->imx_usb; | 
|  | 285 | int temp, i; | 
|  | 286 |  | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 287 | D_ERR(imx_usb->dev, | 
|  | 288 | "<%s> Forced stall on %s\n", __func__, imx_ep->ep.name); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 289 |  | 
|  | 290 | imx_flush(imx_ep); | 
|  | 291 |  | 
|  | 292 | /* Special care for ep0 */ | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 293 | if (!EP_NO(imx_ep)) { | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 294 | temp = __raw_readl(imx_usb->base + USB_CTRL); | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 295 | __raw_writel(temp | CTRL_CMDOVER | CTRL_CMDERROR, | 
|  | 296 | imx_usb->base + USB_CTRL); | 
|  | 297 | do { } while (__raw_readl(imx_usb->base + USB_CTRL) | 
|  | 298 | & CTRL_CMDOVER); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 299 | temp = __raw_readl(imx_usb->base + USB_CTRL); | 
|  | 300 | __raw_writel(temp & ~CTRL_CMDERROR, imx_usb->base + USB_CTRL); | 
|  | 301 | } | 
|  | 302 | else { | 
|  | 303 | temp = __raw_readl(imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); | 
|  | 304 | __raw_writel(temp | EPSTAT_STALL, | 
|  | 305 | imx_usb->base + USB_EP_STAT(EP_NO(imx_ep))); | 
|  | 306 |  | 
|  | 307 | for (i = 0; i < 100; i ++) { | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 308 | temp = __raw_readl(imx_usb->base | 
|  | 309 | + USB_EP_STAT(EP_NO(imx_ep))); | 
| Roel Kluin | 0df2479 | 2009-01-17 16:52:17 +0100 | [diff] [blame] | 310 | if (!(temp & EPSTAT_STALL)) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 311 | break; | 
|  | 312 | udelay(20); | 
|  | 313 | } | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 314 | if (i == 100) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 315 | D_ERR(imx_usb->dev, "<%s> Non finished stall on %s\n", | 
|  | 316 | __func__, imx_ep->ep.name); | 
|  | 317 | } | 
|  | 318 | } | 
|  | 319 |  | 
|  | 320 | static int imx_udc_get_frame(struct usb_gadget *_gadget) | 
|  | 321 | { | 
|  | 322 | struct imx_udc_struct *imx_usb = container_of(_gadget, | 
|  | 323 | struct imx_udc_struct, gadget); | 
|  | 324 |  | 
|  | 325 | return __raw_readl(imx_usb->base + USB_FRAME) & 0x7FF; | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | static int imx_udc_wakeup(struct usb_gadget *_gadget) | 
|  | 329 | { | 
|  | 330 | return 0; | 
|  | 331 | } | 
|  | 332 |  | 
|  | 333 | /******************************************************************************* | 
|  | 334 | * USB request control functions | 
|  | 335 | ******************************************************************************* | 
|  | 336 | */ | 
|  | 337 |  | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 338 | static void ep_add_request(struct imx_ep_struct *imx_ep, | 
|  | 339 | struct imx_request *req) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 340 | { | 
|  | 341 | if (unlikely(!req)) | 
|  | 342 | return; | 
|  | 343 |  | 
|  | 344 | req->in_use = 1; | 
|  | 345 | list_add_tail(&req->queue, &imx_ep->queue); | 
|  | 346 | } | 
|  | 347 |  | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 348 | static void ep_del_request(struct imx_ep_struct *imx_ep, | 
|  | 349 | struct imx_request *req) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 350 | { | 
|  | 351 | if (unlikely(!req)) | 
|  | 352 | return; | 
|  | 353 |  | 
|  | 354 | list_del_init(&req->queue); | 
|  | 355 | req->in_use = 0; | 
|  | 356 | } | 
|  | 357 |  | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 358 | static void done(struct imx_ep_struct *imx_ep, | 
|  | 359 | struct imx_request *req, int status) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 360 | { | 
|  | 361 | ep_del_request(imx_ep, req); | 
|  | 362 |  | 
|  | 363 | if (likely(req->req.status == -EINPROGRESS)) | 
|  | 364 | req->req.status = status; | 
|  | 365 | else | 
|  | 366 | status = req->req.status; | 
|  | 367 |  | 
|  | 368 | if (status && status != -ESHUTDOWN) | 
|  | 369 | D_ERR(imx_ep->imx_usb->dev, | 
|  | 370 | "<%s> complete %s req %p stat %d len %u/%u\n", __func__, | 
|  | 371 | imx_ep->ep.name, &req->req, status, | 
|  | 372 | req->req.actual, req->req.length); | 
|  | 373 |  | 
|  | 374 | req->req.complete(&imx_ep->ep, &req->req); | 
|  | 375 | } | 
|  | 376 |  | 
|  | 377 | static void nuke(struct imx_ep_struct *imx_ep, int status) | 
|  | 378 | { | 
|  | 379 | struct imx_request *req; | 
|  | 380 |  | 
|  | 381 | while (!list_empty(&imx_ep->queue)) { | 
|  | 382 | req = list_entry(imx_ep->queue.next, struct imx_request, queue); | 
|  | 383 | done(imx_ep, req, status); | 
|  | 384 | } | 
|  | 385 | } | 
|  | 386 |  | 
|  | 387 | /******************************************************************************* | 
|  | 388 | * Data tansfer over USB functions | 
|  | 389 | ******************************************************************************* | 
|  | 390 | */ | 
|  | 391 | static int read_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) | 
|  | 392 | { | 
|  | 393 | u8	*buf; | 
|  | 394 | int	bytes_ep, bufferspace, count, i; | 
|  | 395 |  | 
|  | 396 | bytes_ep = imx_fifo_bcount(imx_ep); | 
|  | 397 | bufferspace = req->req.length - req->req.actual; | 
|  | 398 |  | 
|  | 399 | buf = req->req.buf + req->req.actual; | 
|  | 400 | prefetchw(buf); | 
|  | 401 |  | 
|  | 402 | if (unlikely(imx_ep_empty(imx_ep))) | 
|  | 403 | count = 0;	/* zlp */ | 
|  | 404 | else | 
|  | 405 | count = min(bytes_ep, bufferspace); | 
|  | 406 |  | 
|  | 407 | for (i = count; i > 0; i--) | 
|  | 408 | *buf++ = __raw_readb(imx_ep->imx_usb->base | 
|  | 409 | + USB_EP_FDAT0(EP_NO(imx_ep))); | 
|  | 410 | req->req.actual += count; | 
|  | 411 |  | 
|  | 412 | return count; | 
|  | 413 | } | 
|  | 414 |  | 
|  | 415 | static int write_packet(struct imx_ep_struct *imx_ep, struct imx_request *req) | 
|  | 416 | { | 
|  | 417 | u8	*buf; | 
|  | 418 | int	length, count, temp; | 
|  | 419 |  | 
| Daniel Glöckner | 680cc64 | 2009-05-28 13:00:14 +0200 | [diff] [blame] | 420 | if (unlikely(__raw_readl(imx_ep->imx_usb->base + | 
|  | 421 | USB_EP_STAT(EP_NO(imx_ep))) & EPSTAT_ZLPS)) { | 
|  | 422 | D_TRX(imx_ep->imx_usb->dev, "<%s> zlp still queued in EP %s\n", | 
|  | 423 | __func__, imx_ep->ep.name); | 
|  | 424 | return -1; | 
|  | 425 | } | 
|  | 426 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 427 | buf = req->req.buf + req->req.actual; | 
|  | 428 | prefetch(buf); | 
|  | 429 |  | 
|  | 430 | length = min(req->req.length - req->req.actual, (u32)imx_ep->fifosize); | 
|  | 431 |  | 
|  | 432 | if (imx_fifo_bcount(imx_ep) + length > imx_ep->fifosize) { | 
|  | 433 | D_TRX(imx_ep->imx_usb->dev, "<%s> packet overfill %s fifo\n", | 
|  | 434 | __func__, imx_ep->ep.name); | 
|  | 435 | return -1; | 
|  | 436 | } | 
|  | 437 |  | 
|  | 438 | req->req.actual += length; | 
|  | 439 | count = length; | 
|  | 440 |  | 
|  | 441 | if (!count && req->req.zero) {	/* zlp */ | 
|  | 442 | temp = __raw_readl(imx_ep->imx_usb->base | 
|  | 443 | + USB_EP_STAT(EP_NO(imx_ep))); | 
|  | 444 | __raw_writel(temp | EPSTAT_ZLPS, imx_ep->imx_usb->base | 
|  | 445 | + USB_EP_STAT(EP_NO(imx_ep))); | 
|  | 446 | D_TRX(imx_ep->imx_usb->dev, "<%s> zero packet\n", __func__); | 
|  | 447 | return 0; | 
|  | 448 | } | 
|  | 449 |  | 
|  | 450 | while (count--) { | 
|  | 451 | if (count == 0) {	/* last byte */ | 
|  | 452 | temp = __raw_readl(imx_ep->imx_usb->base | 
|  | 453 | + USB_EP_FCTRL(EP_NO(imx_ep))); | 
|  | 454 | __raw_writel(temp | FCTRL_WFR, imx_ep->imx_usb->base | 
|  | 455 | + USB_EP_FCTRL(EP_NO(imx_ep))); | 
|  | 456 | } | 
|  | 457 | __raw_writeb(*buf++, | 
|  | 458 | imx_ep->imx_usb->base + USB_EP_FDAT0(EP_NO(imx_ep))); | 
|  | 459 | } | 
|  | 460 |  | 
|  | 461 | return length; | 
|  | 462 | } | 
|  | 463 |  | 
|  | 464 | static int read_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) | 
|  | 465 | { | 
|  | 466 | int 	bytes = 0, | 
|  | 467 | count, | 
|  | 468 | completed = 0; | 
|  | 469 |  | 
|  | 470 | while (__raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep))) | 
|  | 471 | & FSTAT_FR) { | 
|  | 472 | count = read_packet(imx_ep, req); | 
|  | 473 | bytes += count; | 
|  | 474 |  | 
|  | 475 | completed = (count != imx_ep->fifosize); | 
|  | 476 | if (completed || req->req.actual == req->req.length) { | 
|  | 477 | completed = 1; | 
|  | 478 | break; | 
|  | 479 | } | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | if (completed || !req->req.length) { | 
|  | 483 | done(imx_ep, req, 0); | 
|  | 484 | D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", | 
|  | 485 | __func__, imx_ep->ep.name, req, | 
|  | 486 | completed ? "completed" : "not completed"); | 
|  | 487 | if (!EP_NO(imx_ep)) | 
|  | 488 | ep0_chg_stat(__func__, imx_ep->imx_usb, EP0_IDLE); | 
|  | 489 | } | 
|  | 490 |  | 
|  | 491 | D_TRX(imx_ep->imx_usb->dev, "<%s> bytes read: %d\n", __func__, bytes); | 
|  | 492 |  | 
|  | 493 | return completed; | 
|  | 494 | } | 
|  | 495 |  | 
|  | 496 | static int write_fifo(struct imx_ep_struct *imx_ep, struct imx_request *req) | 
|  | 497 | { | 
|  | 498 | int	bytes = 0, | 
|  | 499 | count, | 
|  | 500 | completed = 0; | 
|  | 501 |  | 
|  | 502 | while (!completed) { | 
|  | 503 | count = write_packet(imx_ep, req); | 
|  | 504 | if (count < 0) | 
|  | 505 | break; /* busy */ | 
|  | 506 | bytes += count; | 
|  | 507 |  | 
|  | 508 | /* last packet "must be" short (or a zlp) */ | 
|  | 509 | completed = (count != imx_ep->fifosize); | 
|  | 510 |  | 
|  | 511 | if (unlikely(completed)) { | 
|  | 512 | done(imx_ep, req, 0); | 
|  | 513 | D_REQ(imx_ep->imx_usb->dev, "<%s> %s req<%p> %s\n", | 
|  | 514 | __func__, imx_ep->ep.name, req, | 
|  | 515 | completed ? "completed" : "not completed"); | 
|  | 516 | if (!EP_NO(imx_ep)) | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 517 | ep0_chg_stat(__func__, | 
|  | 518 | imx_ep->imx_usb, EP0_IDLE); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 519 | } | 
|  | 520 | } | 
|  | 521 |  | 
|  | 522 | D_TRX(imx_ep->imx_usb->dev, "<%s> bytes sent: %d\n", __func__, bytes); | 
|  | 523 |  | 
|  | 524 | return completed; | 
|  | 525 | } | 
|  | 526 |  | 
|  | 527 | /******************************************************************************* | 
|  | 528 | * Endpoint handlers | 
|  | 529 | ******************************************************************************* | 
|  | 530 | */ | 
|  | 531 | static int handle_ep(struct imx_ep_struct *imx_ep) | 
|  | 532 | { | 
|  | 533 | struct imx_request *req; | 
|  | 534 | int completed = 0; | 
|  | 535 |  | 
|  | 536 | do { | 
|  | 537 | if (!list_empty(&imx_ep->queue)) | 
|  | 538 | req = list_entry(imx_ep->queue.next, | 
|  | 539 | struct imx_request, queue); | 
|  | 540 | else { | 
|  | 541 | D_REQ(imx_ep->imx_usb->dev, "<%s> no request on %s\n", | 
|  | 542 | __func__, imx_ep->ep.name); | 
|  | 543 | return 0; | 
|  | 544 | } | 
|  | 545 |  | 
|  | 546 | if (EP_DIR(imx_ep))	/* to host */ | 
|  | 547 | completed = write_fifo(imx_ep, req); | 
|  | 548 | else			/* to device */ | 
|  | 549 | completed = read_fifo(imx_ep, req); | 
|  | 550 |  | 
|  | 551 | dump_ep_stat(__func__, imx_ep); | 
|  | 552 |  | 
|  | 553 | } while (completed); | 
|  | 554 |  | 
|  | 555 | return 0; | 
|  | 556 | } | 
|  | 557 |  | 
|  | 558 | static int handle_ep0(struct imx_ep_struct *imx_ep) | 
|  | 559 | { | 
|  | 560 | struct imx_request *req = NULL; | 
|  | 561 | int ret = 0; | 
|  | 562 |  | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 563 | if (!list_empty(&imx_ep->queue)) { | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 564 | req = list_entry(imx_ep->queue.next, struct imx_request, queue); | 
|  | 565 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 566 | switch (imx_ep->imx_usb->ep0state) { | 
|  | 567 |  | 
|  | 568 | case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR */ | 
|  | 569 | write_fifo(imx_ep, req); | 
|  | 570 | break; | 
|  | 571 | case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR */ | 
|  | 572 | read_fifo(imx_ep, req); | 
|  | 573 | break; | 
|  | 574 | default: | 
|  | 575 | D_EP0(imx_ep->imx_usb->dev, | 
|  | 576 | "<%s> ep0 i/o, odd state %d\n", | 
|  | 577 | __func__, imx_ep->imx_usb->ep0state); | 
|  | 578 | ep_del_request(imx_ep, req); | 
|  | 579 | ret = -EL2HLT; | 
|  | 580 | break; | 
|  | 581 | } | 
|  | 582 | } | 
|  | 583 |  | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 584 | else | 
|  | 585 | D_ERR(imx_ep->imx_usb->dev, "<%s> no request on %s\n", | 
|  | 586 | __func__, imx_ep->ep.name); | 
|  | 587 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 588 | return ret; | 
|  | 589 | } | 
|  | 590 |  | 
|  | 591 | static void handle_ep0_devreq(struct imx_udc_struct *imx_usb) | 
|  | 592 | { | 
|  | 593 | struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; | 
|  | 594 | union { | 
|  | 595 | struct usb_ctrlrequest	r; | 
|  | 596 | u8			raw[8]; | 
|  | 597 | u32			word[2]; | 
|  | 598 | } u; | 
|  | 599 | int temp, i; | 
|  | 600 |  | 
|  | 601 | nuke(imx_ep, -EPROTO); | 
|  | 602 |  | 
|  | 603 | /* read SETUP packet */ | 
|  | 604 | for (i = 0; i < 2; i++) { | 
|  | 605 | if (imx_ep_empty(imx_ep)) { | 
|  | 606 | D_ERR(imx_usb->dev, | 
|  | 607 | "<%s> no setup packet received\n", __func__); | 
|  | 608 | goto stall; | 
|  | 609 | } | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 610 | u.word[i] = __raw_readl(imx_usb->base | 
|  | 611 | + USB_EP_FDAT(EP_NO(imx_ep))); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 612 | } | 
|  | 613 |  | 
|  | 614 | temp = imx_ep_empty(imx_ep); | 
|  | 615 | while (!imx_ep_empty(imx_ep)) { | 
|  | 616 | i = __raw_readl(imx_usb->base +	USB_EP_FDAT(EP_NO(imx_ep))); | 
|  | 617 | D_ERR(imx_usb->dev, | 
|  | 618 | "<%s> wrong to have extra bytes for setup : 0x%08x\n", | 
|  | 619 | __func__, i); | 
|  | 620 | } | 
|  | 621 | if (!temp) | 
|  | 622 | goto stall; | 
|  | 623 |  | 
|  | 624 | le16_to_cpus(&u.r.wValue); | 
|  | 625 | le16_to_cpus(&u.r.wIndex); | 
|  | 626 | le16_to_cpus(&u.r.wLength); | 
|  | 627 |  | 
|  | 628 | D_REQ(imx_usb->dev, "<%s> SETUP %02x.%02x v%04x i%04x l%04x\n", | 
|  | 629 | __func__, u.r.bRequestType, u.r.bRequest, | 
|  | 630 | u.r.wValue, u.r.wIndex, u.r.wLength); | 
|  | 631 |  | 
|  | 632 | if (imx_usb->set_config) { | 
|  | 633 | /* NACK the host by using CMDOVER */ | 
|  | 634 | temp = __raw_readl(imx_usb->base + USB_CTRL); | 
|  | 635 | __raw_writel(temp | CTRL_CMDOVER, imx_usb->base + USB_CTRL); | 
|  | 636 |  | 
|  | 637 | D_ERR(imx_usb->dev, | 
|  | 638 | "<%s> set config req is pending, NACK the host\n", | 
|  | 639 | __func__); | 
|  | 640 | return; | 
|  | 641 | } | 
|  | 642 |  | 
|  | 643 | if (u.r.bRequestType & USB_DIR_IN) | 
|  | 644 | ep0_chg_stat(__func__, imx_usb, EP0_IN_DATA_PHASE); | 
|  | 645 | else | 
|  | 646 | ep0_chg_stat(__func__, imx_usb, EP0_OUT_DATA_PHASE); | 
|  | 647 |  | 
|  | 648 | i = imx_usb->driver->setup(&imx_usb->gadget, &u.r); | 
|  | 649 | if (i < 0) { | 
|  | 650 | D_ERR(imx_usb->dev, "<%s> device setup error %d\n", | 
|  | 651 | __func__, i); | 
|  | 652 | goto stall; | 
|  | 653 | } | 
|  | 654 |  | 
|  | 655 | return; | 
|  | 656 | stall: | 
|  | 657 | D_ERR(imx_usb->dev, "<%s> protocol STALL\n", __func__); | 
|  | 658 | imx_ep_stall(imx_ep); | 
|  | 659 | ep0_chg_stat(__func__, imx_usb, EP0_STALL); | 
|  | 660 | return; | 
|  | 661 | } | 
|  | 662 |  | 
|  | 663 | /******************************************************************************* | 
|  | 664 | * USB gadget callback functions | 
|  | 665 | ******************************************************************************* | 
|  | 666 | */ | 
|  | 667 |  | 
|  | 668 | static int imx_ep_enable(struct usb_ep *usb_ep, | 
|  | 669 | const struct usb_endpoint_descriptor *desc) | 
|  | 670 | { | 
|  | 671 | struct imx_ep_struct *imx_ep = container_of(usb_ep, | 
|  | 672 | struct imx_ep_struct, ep); | 
|  | 673 | struct imx_udc_struct *imx_usb = imx_ep->imx_usb; | 
|  | 674 | unsigned long flags; | 
|  | 675 |  | 
|  | 676 | if (!usb_ep | 
|  | 677 | || !desc | 
|  | 678 | || !EP_NO(imx_ep) | 
|  | 679 | || desc->bDescriptorType != USB_DT_ENDPOINT | 
|  | 680 | || imx_ep->bEndpointAddress != desc->bEndpointAddress) { | 
|  | 681 | D_ERR(imx_usb->dev, | 
|  | 682 | "<%s> bad ep or descriptor\n", __func__); | 
|  | 683 | return -EINVAL; | 
|  | 684 | } | 
|  | 685 |  | 
|  | 686 | if (imx_ep->bmAttributes != desc->bmAttributes) { | 
|  | 687 | D_ERR(imx_usb->dev, | 
|  | 688 | "<%s> %s type mismatch\n", __func__, usb_ep->name); | 
|  | 689 | return -EINVAL; | 
|  | 690 | } | 
|  | 691 |  | 
| Kuninori Morimoto | 29cc889 | 2011-08-23 03:12:03 -0700 | [diff] [blame] | 692 | if (imx_ep->fifosize < usb_endpoint_maxp(desc)) { | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 693 | D_ERR(imx_usb->dev, | 
|  | 694 | "<%s> bad %s maxpacket\n", __func__, usb_ep->name); | 
|  | 695 | return -ERANGE; | 
|  | 696 | } | 
|  | 697 |  | 
|  | 698 | if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { | 
|  | 699 | D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); | 
|  | 700 | return -ESHUTDOWN; | 
|  | 701 | } | 
|  | 702 |  | 
|  | 703 | local_irq_save(flags); | 
|  | 704 |  | 
|  | 705 | imx_ep->stopped = 0; | 
|  | 706 | imx_flush(imx_ep); | 
|  | 707 | imx_ep_irq_enable(imx_ep); | 
|  | 708 |  | 
|  | 709 | local_irq_restore(flags); | 
|  | 710 |  | 
|  | 711 | D_EPX(imx_usb->dev, "<%s> ENABLED %s\n", __func__, usb_ep->name); | 
|  | 712 | return 0; | 
|  | 713 | } | 
|  | 714 |  | 
|  | 715 | static int imx_ep_disable(struct usb_ep *usb_ep) | 
|  | 716 | { | 
|  | 717 | struct imx_ep_struct *imx_ep = container_of(usb_ep, | 
|  | 718 | struct imx_ep_struct, ep); | 
|  | 719 | unsigned long flags; | 
|  | 720 |  | 
|  | 721 | if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { | 
|  | 722 | D_ERR(imx_ep->imx_usb->dev, "<%s> %s can not be disabled\n", | 
|  | 723 | __func__, usb_ep ? imx_ep->ep.name : NULL); | 
|  | 724 | return -EINVAL; | 
|  | 725 | } | 
|  | 726 |  | 
|  | 727 | local_irq_save(flags); | 
|  | 728 |  | 
|  | 729 | imx_ep->stopped = 1; | 
|  | 730 | nuke(imx_ep, -ESHUTDOWN); | 
|  | 731 | imx_flush(imx_ep); | 
|  | 732 | imx_ep_irq_disable(imx_ep); | 
|  | 733 |  | 
|  | 734 | local_irq_restore(flags); | 
|  | 735 |  | 
|  | 736 | D_EPX(imx_ep->imx_usb->dev, | 
|  | 737 | "<%s> DISABLED %s\n", __func__, usb_ep->name); | 
|  | 738 | return 0; | 
|  | 739 | } | 
|  | 740 |  | 
|  | 741 | static struct usb_request *imx_ep_alloc_request | 
|  | 742 | (struct usb_ep *usb_ep, gfp_t gfp_flags) | 
|  | 743 | { | 
|  | 744 | struct imx_request *req; | 
|  | 745 |  | 
| Daniel Mack | 1e0abb7 | 2009-05-12 13:50:34 -0700 | [diff] [blame] | 746 | if (!usb_ep) | 
|  | 747 | return NULL; | 
|  | 748 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 749 | req = kzalloc(sizeof *req, gfp_flags); | 
| Daniel Mack | 1e0abb7 | 2009-05-12 13:50:34 -0700 | [diff] [blame] | 750 | if (!req) | 
|  | 751 | return NULL; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 752 |  | 
|  | 753 | INIT_LIST_HEAD(&req->queue); | 
|  | 754 | req->in_use = 0; | 
|  | 755 |  | 
|  | 756 | return &req->req; | 
|  | 757 | } | 
|  | 758 |  | 
|  | 759 | static void imx_ep_free_request | 
|  | 760 | (struct usb_ep *usb_ep, struct usb_request *usb_req) | 
|  | 761 | { | 
|  | 762 | struct imx_request *req; | 
|  | 763 |  | 
|  | 764 | req = container_of(usb_req, struct imx_request, req); | 
|  | 765 | WARN_ON(!list_empty(&req->queue)); | 
|  | 766 | kfree(req); | 
|  | 767 | } | 
|  | 768 |  | 
|  | 769 | static int imx_ep_queue | 
|  | 770 | (struct usb_ep *usb_ep, struct usb_request *usb_req, gfp_t gfp_flags) | 
|  | 771 | { | 
|  | 772 | struct imx_ep_struct	*imx_ep; | 
|  | 773 | struct imx_udc_struct	*imx_usb; | 
|  | 774 | struct imx_request	*req; | 
|  | 775 | unsigned long		flags; | 
|  | 776 | int			ret = 0; | 
|  | 777 |  | 
|  | 778 | imx_ep = container_of(usb_ep, struct imx_ep_struct, ep); | 
|  | 779 | imx_usb = imx_ep->imx_usb; | 
|  | 780 | req = container_of(usb_req, struct imx_request, req); | 
|  | 781 |  | 
|  | 782 | /* | 
|  | 783 | Special care on IMX udc. | 
|  | 784 | Ignore enqueue when after set configuration from the | 
|  | 785 | host. This assume all gadget drivers reply set | 
|  | 786 | configuration with the next ep0 req enqueue. | 
|  | 787 | */ | 
|  | 788 | if (imx_usb->set_config && !EP_NO(imx_ep)) { | 
|  | 789 | imx_usb->set_config = 0; | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 790 | D_ERR(imx_usb->dev, | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 791 | "<%s> gadget reply set config\n", __func__); | 
|  | 792 | return 0; | 
|  | 793 | } | 
|  | 794 |  | 
|  | 795 | if (unlikely(!usb_req || !req || !usb_req->complete || !usb_req->buf)) { | 
|  | 796 | D_ERR(imx_usb->dev, "<%s> bad params\n", __func__); | 
|  | 797 | return -EINVAL; | 
|  | 798 | } | 
|  | 799 |  | 
|  | 800 | if (unlikely(!usb_ep || !imx_ep)) { | 
|  | 801 | D_ERR(imx_usb->dev, "<%s> bad ep\n", __func__); | 
|  | 802 | return -EINVAL; | 
|  | 803 | } | 
|  | 804 |  | 
|  | 805 | if (!imx_usb->driver || imx_usb->gadget.speed == USB_SPEED_UNKNOWN) { | 
|  | 806 | D_ERR(imx_usb->dev, "<%s> bogus device state\n", __func__); | 
|  | 807 | return -ESHUTDOWN; | 
|  | 808 | } | 
|  | 809 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 810 | /* Debug */ | 
|  | 811 | D_REQ(imx_usb->dev, "<%s> ep%d %s request for [%d] bytes\n", | 
|  | 812 | __func__, EP_NO(imx_ep), | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 813 | ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state | 
|  | 814 | == EP0_IN_DATA_PHASE) | 
|  | 815 | || (EP_NO(imx_ep) && EP_DIR(imx_ep))) | 
|  | 816 | ? "IN" : "OUT", usb_req->length); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 817 | dump_req(__func__, imx_ep, usb_req); | 
|  | 818 |  | 
|  | 819 | if (imx_ep->stopped) { | 
|  | 820 | usb_req->status = -ESHUTDOWN; | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 821 | return -ESHUTDOWN; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 822 | } | 
|  | 823 |  | 
|  | 824 | if (req->in_use) { | 
|  | 825 | D_ERR(imx_usb->dev, | 
|  | 826 | "<%s> refusing to queue req %p (already queued)\n", | 
|  | 827 | __func__, req); | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 828 | return 0; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 829 | } | 
|  | 830 |  | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 831 | local_irq_save(flags); | 
|  | 832 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 833 | usb_req->status = -EINPROGRESS; | 
|  | 834 | usb_req->actual = 0; | 
|  | 835 |  | 
|  | 836 | ep_add_request(imx_ep, req); | 
|  | 837 |  | 
|  | 838 | if (!EP_NO(imx_ep)) | 
|  | 839 | ret = handle_ep0(imx_ep); | 
|  | 840 | else | 
|  | 841 | ret = handle_ep(imx_ep); | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 842 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 843 | local_irq_restore(flags); | 
|  | 844 | return ret; | 
|  | 845 | } | 
|  | 846 |  | 
|  | 847 | static int imx_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) | 
|  | 848 | { | 
|  | 849 |  | 
|  | 850 | struct imx_ep_struct *imx_ep = container_of | 
|  | 851 | (usb_ep, struct imx_ep_struct, ep); | 
|  | 852 | struct imx_request *req; | 
|  | 853 | unsigned long flags; | 
|  | 854 |  | 
|  | 855 | if (unlikely(!usb_ep || !EP_NO(imx_ep))) { | 
|  | 856 | D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); | 
|  | 857 | return -EINVAL; | 
|  | 858 | } | 
|  | 859 |  | 
|  | 860 | local_irq_save(flags); | 
|  | 861 |  | 
|  | 862 | /* make sure it's actually queued on this endpoint */ | 
|  | 863 | list_for_each_entry(req, &imx_ep->queue, queue) { | 
|  | 864 | if (&req->req == usb_req) | 
|  | 865 | break; | 
|  | 866 | } | 
|  | 867 | if (&req->req != usb_req) { | 
|  | 868 | local_irq_restore(flags); | 
|  | 869 | return -EINVAL; | 
|  | 870 | } | 
|  | 871 |  | 
|  | 872 | done(imx_ep, req, -ECONNRESET); | 
|  | 873 |  | 
|  | 874 | local_irq_restore(flags); | 
|  | 875 | return 0; | 
|  | 876 | } | 
|  | 877 |  | 
|  | 878 | static int imx_ep_set_halt(struct usb_ep *usb_ep, int value) | 
|  | 879 | { | 
|  | 880 | struct imx_ep_struct *imx_ep = container_of | 
|  | 881 | (usb_ep, struct imx_ep_struct, ep); | 
|  | 882 | unsigned long flags; | 
|  | 883 |  | 
|  | 884 | if (unlikely(!usb_ep || !EP_NO(imx_ep))) { | 
|  | 885 | D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); | 
|  | 886 | return -EINVAL; | 
|  | 887 | } | 
|  | 888 |  | 
|  | 889 | local_irq_save(flags); | 
|  | 890 |  | 
|  | 891 | if ((imx_ep->bEndpointAddress & USB_DIR_IN) | 
|  | 892 | && !list_empty(&imx_ep->queue)) { | 
|  | 893 | local_irq_restore(flags); | 
|  | 894 | return -EAGAIN; | 
|  | 895 | } | 
|  | 896 |  | 
|  | 897 | imx_ep_stall(imx_ep); | 
|  | 898 |  | 
|  | 899 | local_irq_restore(flags); | 
|  | 900 |  | 
|  | 901 | D_EPX(imx_ep->imx_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name); | 
|  | 902 | return 0; | 
|  | 903 | } | 
|  | 904 |  | 
|  | 905 | static int imx_ep_fifo_status(struct usb_ep *usb_ep) | 
|  | 906 | { | 
|  | 907 | struct imx_ep_struct *imx_ep = container_of | 
|  | 908 | (usb_ep, struct imx_ep_struct, ep); | 
|  | 909 |  | 
|  | 910 | if (!usb_ep) { | 
|  | 911 | D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); | 
|  | 912 | return -ENODEV; | 
|  | 913 | } | 
|  | 914 |  | 
|  | 915 | if (imx_ep->imx_usb->gadget.speed == USB_SPEED_UNKNOWN) | 
|  | 916 | return 0; | 
|  | 917 | else | 
|  | 918 | return imx_fifo_bcount(imx_ep); | 
|  | 919 | } | 
|  | 920 |  | 
|  | 921 | static void imx_ep_fifo_flush(struct usb_ep *usb_ep) | 
|  | 922 | { | 
|  | 923 | struct imx_ep_struct *imx_ep = container_of | 
|  | 924 | (usb_ep, struct imx_ep_struct, ep); | 
|  | 925 | unsigned long flags; | 
|  | 926 |  | 
|  | 927 | local_irq_save(flags); | 
|  | 928 |  | 
|  | 929 | if (!usb_ep || !EP_NO(imx_ep) || !list_empty(&imx_ep->queue)) { | 
|  | 930 | D_ERR(imx_ep->imx_usb->dev, "<%s> bad ep\n", __func__); | 
|  | 931 | local_irq_restore(flags); | 
|  | 932 | return; | 
|  | 933 | } | 
|  | 934 |  | 
|  | 935 | /* toggle and halt bits stay unchanged */ | 
|  | 936 | imx_flush(imx_ep); | 
|  | 937 |  | 
|  | 938 | local_irq_restore(flags); | 
|  | 939 | } | 
|  | 940 |  | 
|  | 941 | static struct usb_ep_ops imx_ep_ops = { | 
|  | 942 | .enable		= imx_ep_enable, | 
|  | 943 | .disable	= imx_ep_disable, | 
|  | 944 |  | 
|  | 945 | .alloc_request	= imx_ep_alloc_request, | 
|  | 946 | .free_request	= imx_ep_free_request, | 
|  | 947 |  | 
|  | 948 | .queue		= imx_ep_queue, | 
|  | 949 | .dequeue	= imx_ep_dequeue, | 
|  | 950 |  | 
|  | 951 | .set_halt	= imx_ep_set_halt, | 
|  | 952 | .fifo_status	= imx_ep_fifo_status, | 
|  | 953 | .fifo_flush	= imx_ep_fifo_flush, | 
|  | 954 | }; | 
|  | 955 |  | 
|  | 956 | /******************************************************************************* | 
|  | 957 | * USB endpoint control functions | 
|  | 958 | ******************************************************************************* | 
|  | 959 | */ | 
|  | 960 |  | 
|  | 961 | void ep0_chg_stat(const char *label, | 
|  | 962 | struct imx_udc_struct *imx_usb, enum ep0_state stat) | 
|  | 963 | { | 
|  | 964 | D_EP0(imx_usb->dev, "<%s> from %15s to %15s\n", | 
|  | 965 | label, state_name[imx_usb->ep0state], state_name[stat]); | 
|  | 966 |  | 
|  | 967 | if (imx_usb->ep0state == stat) | 
|  | 968 | return; | 
|  | 969 |  | 
|  | 970 | imx_usb->ep0state = stat; | 
|  | 971 | } | 
|  | 972 |  | 
|  | 973 | static void usb_init_data(struct imx_udc_struct *imx_usb) | 
|  | 974 | { | 
|  | 975 | struct imx_ep_struct *imx_ep; | 
|  | 976 | u8 i; | 
|  | 977 |  | 
|  | 978 | /* device/ep0 records init */ | 
|  | 979 | INIT_LIST_HEAD(&imx_usb->gadget.ep_list); | 
|  | 980 | INIT_LIST_HEAD(&imx_usb->gadget.ep0->ep_list); | 
|  | 981 | ep0_chg_stat(__func__, imx_usb, EP0_IDLE); | 
|  | 982 |  | 
|  | 983 | /* basic endpoint records init */ | 
|  | 984 | for (i = 0; i < IMX_USB_NB_EP; i++) { | 
|  | 985 | imx_ep = &imx_usb->imx_ep[i]; | 
|  | 986 |  | 
|  | 987 | if (i) { | 
|  | 988 | list_add_tail(&imx_ep->ep.ep_list, | 
|  | 989 | &imx_usb->gadget.ep_list); | 
|  | 990 | imx_ep->stopped = 1; | 
|  | 991 | } else | 
|  | 992 | imx_ep->stopped = 0; | 
|  | 993 |  | 
|  | 994 | INIT_LIST_HEAD(&imx_ep->queue); | 
|  | 995 | } | 
|  | 996 | } | 
|  | 997 |  | 
|  | 998 | static void udc_stop_activity(struct imx_udc_struct *imx_usb, | 
|  | 999 | struct usb_gadget_driver *driver) | 
|  | 1000 | { | 
|  | 1001 | struct imx_ep_struct *imx_ep; | 
|  | 1002 | int i; | 
|  | 1003 |  | 
|  | 1004 | if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN) | 
|  | 1005 | driver = NULL; | 
|  | 1006 |  | 
|  | 1007 | /* prevent new request submissions, kill any outstanding requests  */ | 
|  | 1008 | for (i = 1; i < IMX_USB_NB_EP; i++) { | 
|  | 1009 | imx_ep = &imx_usb->imx_ep[i]; | 
|  | 1010 | imx_flush(imx_ep); | 
|  | 1011 | imx_ep->stopped = 1; | 
|  | 1012 | imx_ep_irq_disable(imx_ep); | 
|  | 1013 | nuke(imx_ep, -ESHUTDOWN); | 
|  | 1014 | } | 
|  | 1015 |  | 
|  | 1016 | imx_usb->cfg = 0; | 
|  | 1017 | imx_usb->intf = 0; | 
|  | 1018 | imx_usb->alt = 0; | 
|  | 1019 |  | 
|  | 1020 | if (driver) | 
|  | 1021 | driver->disconnect(&imx_usb->gadget); | 
|  | 1022 | } | 
|  | 1023 |  | 
|  | 1024 | /******************************************************************************* | 
|  | 1025 | * Interrupt handlers | 
|  | 1026 | ******************************************************************************* | 
|  | 1027 | */ | 
|  | 1028 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1029 | /* | 
|  | 1030 | * Called when timer expires. | 
|  | 1031 | * Timer is started when CFG_CHG is received. | 
|  | 1032 | */ | 
|  | 1033 | static void handle_config(unsigned long data) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1034 | { | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1035 | struct imx_udc_struct *imx_usb = (void *)data; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1036 | struct usb_ctrlrequest u; | 
|  | 1037 | int temp, cfg, intf, alt; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1038 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1039 | local_irq_disable(); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1040 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1041 | temp = __raw_readl(imx_usb->base + USB_STAT); | 
|  | 1042 | cfg  = (temp & STAT_CFG) >> 5; | 
|  | 1043 | intf = (temp & STAT_INTF) >> 3; | 
|  | 1044 | alt  =  temp & STAT_ALTSET; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1045 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1046 | D_REQ(imx_usb->dev, | 
|  | 1047 | "<%s> orig config C=%d, I=%d, A=%d / " | 
|  | 1048 | "req config C=%d, I=%d, A=%d\n", | 
|  | 1049 | __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, | 
|  | 1050 | cfg, intf, alt); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1051 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1052 | if (cfg == 1 || cfg == 2) { | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1053 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1054 | if (imx_usb->cfg != cfg) { | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1055 | u.bRequest = USB_REQ_SET_CONFIGURATION; | 
|  | 1056 | u.bRequestType = USB_DIR_OUT | | 
|  | 1057 | USB_TYPE_STANDARD | | 
|  | 1058 | USB_RECIP_DEVICE; | 
|  | 1059 | u.wValue = cfg; | 
|  | 1060 | u.wIndex = 0; | 
|  | 1061 | u.wLength = 0; | 
|  | 1062 | imx_usb->cfg = cfg; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1063 | imx_usb->driver->setup(&imx_usb->gadget, &u); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1064 |  | 
|  | 1065 | } | 
|  | 1066 | if (imx_usb->intf != intf || imx_usb->alt != alt) { | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1067 | u.bRequest = USB_REQ_SET_INTERFACE; | 
|  | 1068 | u.bRequestType = USB_DIR_OUT | | 
|  | 1069 | USB_TYPE_STANDARD | | 
|  | 1070 | USB_RECIP_INTERFACE; | 
|  | 1071 | u.wValue = alt; | 
|  | 1072 | u.wIndex = intf; | 
|  | 1073 | u.wLength = 0; | 
|  | 1074 | imx_usb->intf = intf; | 
|  | 1075 | imx_usb->alt = alt; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1076 | imx_usb->driver->setup(&imx_usb->gadget, &u); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1077 | } | 
|  | 1078 | } | 
|  | 1079 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1080 | imx_usb->set_config = 0; | 
|  | 1081 |  | 
|  | 1082 | local_irq_enable(); | 
|  | 1083 | } | 
|  | 1084 |  | 
|  | 1085 | static irqreturn_t imx_udc_irq(int irq, void *dev) | 
|  | 1086 | { | 
|  | 1087 | struct imx_udc_struct *imx_usb = dev; | 
|  | 1088 | int intr = __raw_readl(imx_usb->base + USB_INTR); | 
|  | 1089 | int temp; | 
|  | 1090 |  | 
|  | 1091 | if (intr & (INTR_WAKEUP | INTR_SUSPEND | INTR_RESUME | INTR_RESET_START | 
|  | 1092 | | INTR_RESET_STOP | INTR_CFG_CHG)) { | 
|  | 1093 | dump_intr(__func__, intr, imx_usb->dev); | 
|  | 1094 | dump_usb_stat(__func__, imx_usb); | 
|  | 1095 | } | 
|  | 1096 |  | 
|  | 1097 | if (!imx_usb->driver) | 
|  | 1098 | goto end_irq; | 
|  | 1099 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1100 | if (intr & INTR_SOF) { | 
| Darius Augulis | 8f182e5 | 2009-01-21 15:17:25 +0200 | [diff] [blame] | 1101 | /* Copy from Freescale BSP. | 
|  | 1102 | We must enable SOF intr and set CMDOVER. | 
|  | 1103 | Datasheet don't specifiy this action, but it | 
|  | 1104 | is done in Freescale BSP, so just copy it. | 
|  | 1105 | */ | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1106 | if (imx_usb->ep0state == EP0_IDLE) { | 
|  | 1107 | temp = __raw_readl(imx_usb->base + USB_CTRL); | 
| Darius Augulis | 593bef6 | 2009-01-21 15:17:55 +0200 | [diff] [blame] | 1108 | __raw_writel(temp | CTRL_CMDOVER, | 
|  | 1109 | imx_usb->base + USB_CTRL); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1110 | } | 
|  | 1111 | } | 
|  | 1112 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1113 | if (intr & INTR_CFG_CHG) { | 
|  | 1114 | /* A workaround of serious IMX UDC bug. | 
|  | 1115 | Handling of CFG_CHG should be delayed for some time, because | 
|  | 1116 | IMX does not NACK the host when CFG_CHG interrupt is pending. | 
|  | 1117 | There is no time to handle current CFG_CHG | 
|  | 1118 | if next CFG_CHG or SETUP packed is send immediately. | 
|  | 1119 | We have to clear CFG_CHG, start the timer and | 
|  | 1120 | NACK the host by setting CTRL_CMDOVER | 
|  | 1121 | if it sends any SETUP packet. | 
|  | 1122 | When timer expires, handler is called to handle configuration | 
|  | 1123 | changes. While CFG_CHG is not handled (set_config=1), | 
|  | 1124 | we must NACK the host to every SETUP packed. | 
|  | 1125 | This delay prevents from going out of sync with host. | 
|  | 1126 | */ | 
|  | 1127 | __raw_writel(INTR_CFG_CHG, imx_usb->base + USB_INTR); | 
|  | 1128 | imx_usb->set_config = 1; | 
|  | 1129 | mod_timer(&imx_usb->timer, jiffies + 5); | 
|  | 1130 | goto end_irq; | 
|  | 1131 | } | 
|  | 1132 |  | 
|  | 1133 | if (intr & INTR_WAKEUP) { | 
|  | 1134 | if (imx_usb->gadget.speed == USB_SPEED_UNKNOWN | 
|  | 1135 | && imx_usb->driver && imx_usb->driver->resume) | 
|  | 1136 | imx_usb->driver->resume(&imx_usb->gadget); | 
|  | 1137 | imx_usb->set_config = 0; | 
|  | 1138 | del_timer(&imx_usb->timer); | 
|  | 1139 | imx_usb->gadget.speed = USB_SPEED_FULL; | 
|  | 1140 | } | 
|  | 1141 |  | 
|  | 1142 | if (intr & INTR_SUSPEND) { | 
|  | 1143 | if (imx_usb->gadget.speed != USB_SPEED_UNKNOWN | 
|  | 1144 | && imx_usb->driver && imx_usb->driver->suspend) | 
|  | 1145 | imx_usb->driver->suspend(&imx_usb->gadget); | 
|  | 1146 | imx_usb->set_config = 0; | 
|  | 1147 | del_timer(&imx_usb->timer); | 
|  | 1148 | imx_usb->gadget.speed = USB_SPEED_UNKNOWN; | 
|  | 1149 | } | 
|  | 1150 |  | 
|  | 1151 | if (intr & INTR_RESET_START) { | 
|  | 1152 | __raw_writel(intr, imx_usb->base + USB_INTR); | 
|  | 1153 | udc_stop_activity(imx_usb, imx_usb->driver); | 
|  | 1154 | imx_usb->set_config = 0; | 
|  | 1155 | del_timer(&imx_usb->timer); | 
|  | 1156 | imx_usb->gadget.speed = USB_SPEED_UNKNOWN; | 
|  | 1157 | } | 
|  | 1158 |  | 
|  | 1159 | if (intr & INTR_RESET_STOP) | 
|  | 1160 | imx_usb->gadget.speed = USB_SPEED_FULL; | 
|  | 1161 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1162 | end_irq: | 
|  | 1163 | __raw_writel(intr, imx_usb->base + USB_INTR); | 
|  | 1164 | return IRQ_HANDLED; | 
|  | 1165 | } | 
|  | 1166 |  | 
|  | 1167 | static irqreturn_t imx_udc_ctrl_irq(int irq, void *dev) | 
|  | 1168 | { | 
|  | 1169 | struct imx_udc_struct *imx_usb = dev; | 
| Darius Augulis | d24921a | 2009-01-21 15:18:33 +0200 | [diff] [blame] | 1170 | struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[0]; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1171 | int intr = __raw_readl(imx_usb->base + USB_EP_INTR(0)); | 
|  | 1172 |  | 
|  | 1173 | dump_ep_intr(__func__, 0, intr, imx_usb->dev); | 
|  | 1174 |  | 
|  | 1175 | if (!imx_usb->driver) { | 
|  | 1176 | __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); | 
|  | 1177 | return IRQ_HANDLED; | 
|  | 1178 | } | 
|  | 1179 |  | 
| Darius Augulis | d24921a | 2009-01-21 15:18:33 +0200 | [diff] [blame] | 1180 | /* DEVREQ has highest priority */ | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1181 | if (intr & (EPINTR_DEVREQ | EPINTR_MDEVREQ)) | 
|  | 1182 | handle_ep0_devreq(imx_usb); | 
|  | 1183 | /* Seem i.MX is missing EOF interrupt sometimes. | 
| Darius Augulis | d24921a | 2009-01-21 15:18:33 +0200 | [diff] [blame] | 1184 | * Therefore we don't monitor EOF. | 
|  | 1185 | * We call handle_ep0() only if a request is queued for ep0. | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1186 | */ | 
| Darius Augulis | d24921a | 2009-01-21 15:18:33 +0200 | [diff] [blame] | 1187 | else if (!list_empty(&imx_ep->queue)) | 
|  | 1188 | handle_ep0(imx_ep); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1189 |  | 
|  | 1190 | __raw_writel(intr, imx_usb->base + USB_EP_INTR(0)); | 
|  | 1191 |  | 
|  | 1192 | return IRQ_HANDLED; | 
|  | 1193 | } | 
|  | 1194 |  | 
| Uwe Kleine-König | 3440408 | 2010-12-06 17:38:24 +0100 | [diff] [blame] | 1195 | #ifndef MX1_INT_USBD0 | 
|  | 1196 | #define MX1_INT_USBD0 MX1_USBD_INT0 | 
|  | 1197 | #endif | 
|  | 1198 |  | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1199 | static irqreturn_t imx_udc_bulk_irq(int irq, void *dev) | 
|  | 1200 | { | 
|  | 1201 | struct imx_udc_struct *imx_usb = dev; | 
| Uwe Kleine-König | 3440408 | 2010-12-06 17:38:24 +0100 | [diff] [blame] | 1202 | struct imx_ep_struct *imx_ep = &imx_usb->imx_ep[irq - MX1_INT_USBD0]; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1203 | int intr = __raw_readl(imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); | 
|  | 1204 |  | 
| Uwe Kleine-König | 3440408 | 2010-12-06 17:38:24 +0100 | [diff] [blame] | 1205 | dump_ep_intr(__func__, irq - MX1_INT_USBD0, intr, imx_usb->dev); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1206 |  | 
|  | 1207 | if (!imx_usb->driver) { | 
|  | 1208 | __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); | 
|  | 1209 | return IRQ_HANDLED; | 
|  | 1210 | } | 
|  | 1211 |  | 
|  | 1212 | handle_ep(imx_ep); | 
|  | 1213 |  | 
|  | 1214 | __raw_writel(intr, imx_usb->base + USB_EP_INTR(EP_NO(imx_ep))); | 
|  | 1215 |  | 
|  | 1216 | return IRQ_HANDLED; | 
|  | 1217 | } | 
|  | 1218 |  | 
|  | 1219 | irq_handler_t intr_handler(int i) | 
|  | 1220 | { | 
|  | 1221 | switch (i) { | 
|  | 1222 | case 0: | 
|  | 1223 | return imx_udc_ctrl_irq; | 
|  | 1224 | case 1: | 
|  | 1225 | case 2: | 
|  | 1226 | case 3: | 
|  | 1227 | case 4: | 
|  | 1228 | case 5: | 
|  | 1229 | return imx_udc_bulk_irq; | 
|  | 1230 | default: | 
|  | 1231 | return imx_udc_irq; | 
|  | 1232 | } | 
|  | 1233 | } | 
|  | 1234 |  | 
|  | 1235 | /******************************************************************************* | 
|  | 1236 | * Static defined IMX UDC structure | 
|  | 1237 | ******************************************************************************* | 
|  | 1238 | */ | 
|  | 1239 |  | 
| Sebastian Andrzej Siewior | 0f91349 | 2011-06-28 16:33:47 +0300 | [diff] [blame] | 1240 | static int imx_udc_start(struct usb_gadget_driver *driver, | 
|  | 1241 | int (*bind)(struct usb_gadget *)); | 
|  | 1242 | static int imx_udc_stop(struct usb_gadget_driver *driver); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1243 | static const struct usb_gadget_ops imx_udc_ops = { | 
|  | 1244 | .get_frame	 = imx_udc_get_frame, | 
|  | 1245 | .wakeup		 = imx_udc_wakeup, | 
| Sebastian Andrzej Siewior | 0f91349 | 2011-06-28 16:33:47 +0300 | [diff] [blame] | 1246 | .start		= imx_udc_start, | 
|  | 1247 | .stop		= imx_udc_stop, | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1248 | }; | 
|  | 1249 |  | 
|  | 1250 | static struct imx_udc_struct controller = { | 
|  | 1251 | .gadget = { | 
|  | 1252 | .ops		= &imx_udc_ops, | 
|  | 1253 | .ep0		= &controller.imx_ep[0].ep, | 
|  | 1254 | .name		= driver_name, | 
|  | 1255 | .dev = { | 
| Kay Sievers | 5df5852 | 2009-03-24 16:38:23 -0700 | [diff] [blame] | 1256 | .init_name	= "gadget", | 
|  | 1257 | }, | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1258 | }, | 
|  | 1259 |  | 
|  | 1260 | .imx_ep[0] = { | 
|  | 1261 | .ep = { | 
|  | 1262 | .name		= ep0name, | 
|  | 1263 | .ops		= &imx_ep_ops, | 
|  | 1264 | .maxpacket	= 32, | 
|  | 1265 | }, | 
|  | 1266 | .imx_usb		= &controller, | 
|  | 1267 | .fifosize		= 32, | 
|  | 1268 | .bEndpointAddress	= 0, | 
|  | 1269 | .bmAttributes		= USB_ENDPOINT_XFER_CONTROL, | 
|  | 1270 | }, | 
|  | 1271 | .imx_ep[1] = { | 
|  | 1272 | .ep = { | 
|  | 1273 | .name		= "ep1in-bulk", | 
|  | 1274 | .ops		= &imx_ep_ops, | 
|  | 1275 | .maxpacket	= 64, | 
|  | 1276 | }, | 
|  | 1277 | .imx_usb		= &controller, | 
|  | 1278 | .fifosize		= 64, | 
|  | 1279 | .bEndpointAddress	= USB_DIR_IN | 1, | 
|  | 1280 | .bmAttributes		= USB_ENDPOINT_XFER_BULK, | 
|  | 1281 | }, | 
|  | 1282 | .imx_ep[2] = { | 
|  | 1283 | .ep = { | 
|  | 1284 | .name		= "ep2out-bulk", | 
|  | 1285 | .ops		= &imx_ep_ops, | 
|  | 1286 | .maxpacket	= 64, | 
|  | 1287 | }, | 
|  | 1288 | .imx_usb		= &controller, | 
|  | 1289 | .fifosize		= 64, | 
|  | 1290 | .bEndpointAddress	= USB_DIR_OUT | 2, | 
|  | 1291 | .bmAttributes		= USB_ENDPOINT_XFER_BULK, | 
|  | 1292 | }, | 
|  | 1293 | .imx_ep[3] = { | 
|  | 1294 | .ep = { | 
|  | 1295 | .name		= "ep3out-bulk", | 
|  | 1296 | .ops		= &imx_ep_ops, | 
|  | 1297 | .maxpacket	= 32, | 
|  | 1298 | }, | 
|  | 1299 | .imx_usb		= &controller, | 
|  | 1300 | .fifosize		= 32, | 
|  | 1301 | .bEndpointAddress 	= USB_DIR_OUT | 3, | 
|  | 1302 | .bmAttributes		= USB_ENDPOINT_XFER_BULK, | 
|  | 1303 | }, | 
|  | 1304 | .imx_ep[4] = { | 
|  | 1305 | .ep = { | 
|  | 1306 | .name		= "ep4in-int", | 
|  | 1307 | .ops		= &imx_ep_ops, | 
|  | 1308 | .maxpacket	= 32, | 
|  | 1309 | }, | 
|  | 1310 | .imx_usb		= &controller, | 
|  | 1311 | .fifosize		= 32, | 
|  | 1312 | .bEndpointAddress 	= USB_DIR_IN | 4, | 
|  | 1313 | .bmAttributes		= USB_ENDPOINT_XFER_INT, | 
|  | 1314 | }, | 
|  | 1315 | .imx_ep[5] = { | 
|  | 1316 | .ep = { | 
|  | 1317 | .name		= "ep5out-int", | 
|  | 1318 | .ops		= &imx_ep_ops, | 
|  | 1319 | .maxpacket	= 32, | 
|  | 1320 | }, | 
|  | 1321 | .imx_usb		= &controller, | 
|  | 1322 | .fifosize		= 32, | 
|  | 1323 | .bEndpointAddress 	= USB_DIR_OUT | 5, | 
|  | 1324 | .bmAttributes		= USB_ENDPOINT_XFER_INT, | 
|  | 1325 | }, | 
|  | 1326 | }; | 
|  | 1327 |  | 
|  | 1328 | /******************************************************************************* | 
| Uwe Kleine-König | b595076 | 2010-11-01 15:38:34 -0400 | [diff] [blame] | 1329 | * USB gadget driver functions | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1330 | ******************************************************************************* | 
|  | 1331 | */ | 
| Sebastian Andrzej Siewior | 0f91349 | 2011-06-28 16:33:47 +0300 | [diff] [blame] | 1332 | static int imx_udc_start(struct usb_gadget_driver *driver, | 
| Uwe Kleine-König | b0fca50 | 2010-08-12 17:43:53 +0200 | [diff] [blame] | 1333 | int (*bind)(struct usb_gadget *)) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1334 | { | 
|  | 1335 | struct imx_udc_struct *imx_usb = &controller; | 
|  | 1336 | int retval; | 
|  | 1337 |  | 
|  | 1338 | if (!driver | 
|  | 1339 | || driver->speed < USB_SPEED_FULL | 
| Uwe Kleine-König | b0fca50 | 2010-08-12 17:43:53 +0200 | [diff] [blame] | 1340 | || !bind | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1341 | || !driver->disconnect | 
|  | 1342 | || !driver->setup) | 
|  | 1343 | return -EINVAL; | 
|  | 1344 | if (!imx_usb) | 
|  | 1345 | return -ENODEV; | 
|  | 1346 | if (imx_usb->driver) | 
|  | 1347 | return -EBUSY; | 
|  | 1348 |  | 
|  | 1349 | /* first hook up the driver ... */ | 
|  | 1350 | imx_usb->driver = driver; | 
|  | 1351 | imx_usb->gadget.dev.driver = &driver->driver; | 
|  | 1352 |  | 
|  | 1353 | retval = device_add(&imx_usb->gadget.dev); | 
|  | 1354 | if (retval) | 
|  | 1355 | goto fail; | 
| Uwe Kleine-König | b0fca50 | 2010-08-12 17:43:53 +0200 | [diff] [blame] | 1356 | retval = bind(&imx_usb->gadget); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1357 | if (retval) { | 
|  | 1358 | D_ERR(imx_usb->dev, "<%s> bind to driver %s --> error %d\n", | 
|  | 1359 | __func__, driver->driver.name, retval); | 
|  | 1360 | device_del(&imx_usb->gadget.dev); | 
|  | 1361 |  | 
|  | 1362 | goto fail; | 
|  | 1363 | } | 
|  | 1364 |  | 
|  | 1365 | D_INI(imx_usb->dev, "<%s> registered gadget driver '%s'\n", | 
|  | 1366 | __func__, driver->driver.name); | 
|  | 1367 |  | 
|  | 1368 | imx_udc_enable(imx_usb); | 
|  | 1369 |  | 
|  | 1370 | return 0; | 
|  | 1371 | fail: | 
|  | 1372 | imx_usb->driver = NULL; | 
|  | 1373 | imx_usb->gadget.dev.driver = NULL; | 
|  | 1374 | return retval; | 
|  | 1375 | } | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1376 |  | 
| Sebastian Andrzej Siewior | 0f91349 | 2011-06-28 16:33:47 +0300 | [diff] [blame] | 1377 | static int imx_udc_stop(struct usb_gadget_driver *driver) | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1378 | { | 
|  | 1379 | struct imx_udc_struct *imx_usb = &controller; | 
|  | 1380 |  | 
|  | 1381 | if (!imx_usb) | 
|  | 1382 | return -ENODEV; | 
|  | 1383 | if (!driver || driver != imx_usb->driver || !driver->unbind) | 
|  | 1384 | return -EINVAL; | 
|  | 1385 |  | 
|  | 1386 | udc_stop_activity(imx_usb, driver); | 
|  | 1387 | imx_udc_disable(imx_usb); | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1388 | del_timer(&imx_usb->timer); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1389 |  | 
|  | 1390 | driver->unbind(&imx_usb->gadget); | 
|  | 1391 | imx_usb->gadget.dev.driver = NULL; | 
|  | 1392 | imx_usb->driver = NULL; | 
|  | 1393 |  | 
|  | 1394 | device_del(&imx_usb->gadget.dev); | 
|  | 1395 |  | 
|  | 1396 | D_INI(imx_usb->dev, "<%s> unregistered gadget driver '%s'\n", | 
|  | 1397 | __func__, driver->driver.name); | 
|  | 1398 |  | 
|  | 1399 | return 0; | 
|  | 1400 | } | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1401 |  | 
|  | 1402 | /******************************************************************************* | 
|  | 1403 | * Module functions | 
|  | 1404 | ******************************************************************************* | 
|  | 1405 | */ | 
|  | 1406 |  | 
|  | 1407 | static int __init imx_udc_probe(struct platform_device *pdev) | 
|  | 1408 | { | 
|  | 1409 | struct imx_udc_struct *imx_usb = &controller; | 
|  | 1410 | struct resource *res; | 
|  | 1411 | struct imxusb_platform_data *pdata; | 
|  | 1412 | struct clk *clk; | 
|  | 1413 | void __iomem *base; | 
|  | 1414 | int ret = 0; | 
| Tobias Klauser | d86a83f | 2009-09-18 09:14:46 +0200 | [diff] [blame] | 1415 | int i; | 
|  | 1416 | resource_size_t res_size; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1417 |  | 
|  | 1418 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 1419 | if (!res) { | 
|  | 1420 | dev_err(&pdev->dev, "can't get device resources\n"); | 
|  | 1421 | return -ENODEV; | 
|  | 1422 | } | 
|  | 1423 |  | 
|  | 1424 | pdata = pdev->dev.platform_data; | 
|  | 1425 | if (!pdata) { | 
|  | 1426 | dev_err(&pdev->dev, "driver needs platform data\n"); | 
|  | 1427 | return -ENODEV; | 
|  | 1428 | } | 
|  | 1429 |  | 
| Tobias Klauser | d86a83f | 2009-09-18 09:14:46 +0200 | [diff] [blame] | 1430 | res_size = resource_size(res); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1431 | if (!request_mem_region(res->start, res_size, res->name)) { | 
|  | 1432 | dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", | 
|  | 1433 | res_size, res->start); | 
|  | 1434 | return -ENOMEM; | 
|  | 1435 | } | 
|  | 1436 |  | 
|  | 1437 | if (pdata->init) { | 
|  | 1438 | ret = pdata->init(&pdev->dev); | 
|  | 1439 | if (ret) | 
|  | 1440 | goto fail0; | 
|  | 1441 | } | 
|  | 1442 |  | 
|  | 1443 | base = ioremap(res->start, res_size); | 
|  | 1444 | if (!base) { | 
|  | 1445 | dev_err(&pdev->dev, "ioremap failed\n"); | 
|  | 1446 | ret = -EIO; | 
|  | 1447 | goto fail1; | 
|  | 1448 | } | 
|  | 1449 |  | 
|  | 1450 | clk = clk_get(NULL, "usbd_clk"); | 
|  | 1451 | if (IS_ERR(clk)) { | 
|  | 1452 | ret = PTR_ERR(clk); | 
|  | 1453 | dev_err(&pdev->dev, "can't get USB clock\n"); | 
|  | 1454 | goto fail2; | 
|  | 1455 | } | 
|  | 1456 | clk_enable(clk); | 
|  | 1457 |  | 
|  | 1458 | if (clk_get_rate(clk) != 48000000) { | 
|  | 1459 | D_INI(&pdev->dev, | 
|  | 1460 | "Bad USB clock (%d Hz), changing to 48000000 Hz\n", | 
|  | 1461 | (int)clk_get_rate(clk)); | 
|  | 1462 | if (clk_set_rate(clk, 48000000)) { | 
|  | 1463 | dev_err(&pdev->dev, | 
|  | 1464 | "Unable to set correct USB clock (48MHz)\n"); | 
|  | 1465 | ret = -EIO; | 
|  | 1466 | goto fail3; | 
|  | 1467 | } | 
|  | 1468 | } | 
|  | 1469 |  | 
|  | 1470 | for (i = 0; i < IMX_USB_NB_EP + 1; i++) { | 
|  | 1471 | imx_usb->usbd_int[i] = platform_get_irq(pdev, i); | 
|  | 1472 | if (imx_usb->usbd_int[i] < 0) { | 
|  | 1473 | dev_err(&pdev->dev, "can't get irq number\n"); | 
|  | 1474 | ret = -ENODEV; | 
|  | 1475 | goto fail3; | 
|  | 1476 | } | 
|  | 1477 | } | 
|  | 1478 |  | 
|  | 1479 | for (i = 0; i < IMX_USB_NB_EP + 1; i++) { | 
|  | 1480 | ret = request_irq(imx_usb->usbd_int[i], intr_handler(i), | 
| Yong Zhang | b5dd18d | 2011-09-07 16:10:52 +0800 | [diff] [blame] | 1481 | 0, driver_name, imx_usb); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1482 | if (ret) { | 
|  | 1483 | dev_err(&pdev->dev, "can't get irq %i, err %d\n", | 
|  | 1484 | imx_usb->usbd_int[i], ret); | 
|  | 1485 | for (--i; i >= 0; i--) | 
|  | 1486 | free_irq(imx_usb->usbd_int[i], imx_usb); | 
|  | 1487 | goto fail3; | 
|  | 1488 | } | 
|  | 1489 | } | 
|  | 1490 |  | 
|  | 1491 | imx_usb->res = res; | 
|  | 1492 | imx_usb->base = base; | 
|  | 1493 | imx_usb->clk = clk; | 
|  | 1494 | imx_usb->dev = &pdev->dev; | 
|  | 1495 |  | 
|  | 1496 | device_initialize(&imx_usb->gadget.dev); | 
|  | 1497 |  | 
|  | 1498 | imx_usb->gadget.dev.parent = &pdev->dev; | 
|  | 1499 | imx_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; | 
|  | 1500 |  | 
|  | 1501 | platform_set_drvdata(pdev, imx_usb); | 
|  | 1502 |  | 
|  | 1503 | usb_init_data(imx_usb); | 
|  | 1504 | imx_udc_init(imx_usb); | 
|  | 1505 |  | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1506 | init_timer(&imx_usb->timer); | 
|  | 1507 | imx_usb->timer.function = handle_config; | 
|  | 1508 | imx_usb->timer.data = (unsigned long)imx_usb; | 
|  | 1509 |  | 
| Sebastian Andrzej Siewior | 0f91349 | 2011-06-28 16:33:47 +0300 | [diff] [blame] | 1510 | ret = usb_add_gadget_udc(&pdev->dev, &imx_usb->gadget); | 
|  | 1511 | if (ret) | 
|  | 1512 | goto fail4; | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1513 |  | 
| Sebastian Andrzej Siewior | 0f91349 | 2011-06-28 16:33:47 +0300 | [diff] [blame] | 1514 | return 0; | 
|  | 1515 | fail4: | 
|  | 1516 | for (i = 0; i < IMX_USB_NB_EP + 1; i++) | 
|  | 1517 | free_irq(imx_usb->usbd_int[i], imx_usb); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1518 | fail3: | 
|  | 1519 | clk_put(clk); | 
|  | 1520 | clk_disable(clk); | 
|  | 1521 | fail2: | 
|  | 1522 | iounmap(base); | 
|  | 1523 | fail1: | 
|  | 1524 | if (pdata->exit) | 
|  | 1525 | pdata->exit(&pdev->dev); | 
|  | 1526 | fail0: | 
|  | 1527 | release_mem_region(res->start, res_size); | 
|  | 1528 | return ret; | 
|  | 1529 | } | 
|  | 1530 |  | 
|  | 1531 | static int __exit imx_udc_remove(struct platform_device *pdev) | 
|  | 1532 | { | 
|  | 1533 | struct imx_udc_struct *imx_usb = platform_get_drvdata(pdev); | 
|  | 1534 | struct imxusb_platform_data *pdata = pdev->dev.platform_data; | 
|  | 1535 | int i; | 
|  | 1536 |  | 
| Sebastian Andrzej Siewior | 0f91349 | 2011-06-28 16:33:47 +0300 | [diff] [blame] | 1537 | usb_del_gadget_udc(&imx_usb->gadget); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1538 | imx_udc_disable(imx_usb); | 
| Darius Augulis | b633d28 | 2009-01-21 15:19:19 +0200 | [diff] [blame] | 1539 | del_timer(&imx_usb->timer); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1540 |  | 
|  | 1541 | for (i = 0; i < IMX_USB_NB_EP + 1; i++) | 
|  | 1542 | free_irq(imx_usb->usbd_int[i], imx_usb); | 
|  | 1543 |  | 
|  | 1544 | clk_put(imx_usb->clk); | 
|  | 1545 | clk_disable(imx_usb->clk); | 
|  | 1546 | iounmap(imx_usb->base); | 
|  | 1547 |  | 
| Tobias Klauser | d86a83f | 2009-09-18 09:14:46 +0200 | [diff] [blame] | 1548 | release_mem_region(imx_usb->res->start, resource_size(imx_usb->res)); | 
| Darius Augulis | 2a4f136 | 2008-11-12 13:38:31 -0800 | [diff] [blame] | 1549 |  | 
|  | 1550 | if (pdata->exit) | 
|  | 1551 | pdata->exit(&pdev->dev); | 
|  | 1552 |  | 
|  | 1553 | platform_set_drvdata(pdev, NULL); | 
|  | 1554 |  | 
|  | 1555 | return 0; | 
|  | 1556 | } | 
|  | 1557 |  | 
|  | 1558 | /*----------------------------------------------------------------------------*/ | 
|  | 1559 |  | 
|  | 1560 | #ifdef	CONFIG_PM | 
|  | 1561 | #define	imx_udc_suspend	NULL | 
|  | 1562 | #define	imx_udc_resume	NULL | 
|  | 1563 | #else | 
|  | 1564 | #define	imx_udc_suspend	NULL | 
|  | 1565 | #define	imx_udc_resume	NULL | 
|  | 1566 | #endif | 
|  | 1567 |  | 
|  | 1568 | /*----------------------------------------------------------------------------*/ | 
|  | 1569 |  | 
|  | 1570 | static struct platform_driver udc_driver = { | 
|  | 1571 | .driver		= { | 
|  | 1572 | .name	= driver_name, | 
|  | 1573 | .owner	= THIS_MODULE, | 
|  | 1574 | }, | 
|  | 1575 | .remove		= __exit_p(imx_udc_remove), | 
|  | 1576 | .suspend	= imx_udc_suspend, | 
|  | 1577 | .resume		= imx_udc_resume, | 
|  | 1578 | }; | 
|  | 1579 |  | 
|  | 1580 | static int __init udc_init(void) | 
|  | 1581 | { | 
|  | 1582 | return platform_driver_probe(&udc_driver, imx_udc_probe); | 
|  | 1583 | } | 
|  | 1584 | module_init(udc_init); | 
|  | 1585 |  | 
|  | 1586 | static void __exit udc_exit(void) | 
|  | 1587 | { | 
|  | 1588 | platform_driver_unregister(&udc_driver); | 
|  | 1589 | } | 
|  | 1590 | module_exit(udc_exit); | 
|  | 1591 |  | 
|  | 1592 | MODULE_DESCRIPTION("IMX USB Device Controller driver"); | 
|  | 1593 | MODULE_AUTHOR("Darius Augulis <augulis.darius@gmail.com>"); | 
|  | 1594 | MODULE_LICENSE("GPL"); | 
|  | 1595 | MODULE_ALIAS("platform:imx_udc"); |