diff options
author | Thomas Abraham <thomas.ab@samsung.com> | 2011-05-07 16:28:04 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-05-10 17:16:55 -0400 |
commit | a9df304cf78d76108196da1ff1dad4d9a5737c2e (patch) | |
tree | 9c9ed7a7a7e93263192e990ad002b85d3f89a810 /drivers/usb/gadget | |
parent | f65680455def9eea074fce58b76006a5ce60e28e (diff) |
USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
device controller module. This driver enables support for USB high-speed
gadget functionality for the Samsung S3C24xx SoC's that include this
controller.
Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Alexander Neumann <alexander@bumpern.de>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/Kconfig | 17 | ||||
-rw-r--r-- | drivers/usb/gadget/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/gadget/gadget_chips.h | 8 | ||||
-rw-r--r-- | drivers/usb/gadget/s3c-hsudc.c | 1349 |
4 files changed, 1375 insertions, 0 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 4c02b9f1597e..58456d1aec21 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig | |||
@@ -356,6 +356,23 @@ config USB_S3C2410_DEBUG | |||
356 | boolean "S3C2410 udc debug messages" | 356 | boolean "S3C2410 udc debug messages" |
357 | depends on USB_GADGET_S3C2410 | 357 | depends on USB_GADGET_S3C2410 |
358 | 358 | ||
359 | config USB_GADGET_S3C_HSUDC | ||
360 | boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller" | ||
361 | depends on ARCH_S3C2410 | ||
362 | select USB_GADGET_DUALSPEED | ||
363 | help | ||
364 | Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC | ||
365 | integrated with dual speed USB 2.0 device controller. It has | ||
366 | 8 endpoints, as well as endpoint zero. | ||
367 | |||
368 | This driver has been tested on S3C2416 and S3C2450 processors. | ||
369 | |||
370 | config USB_S3C_HSUDC | ||
371 | tristate | ||
372 | depends on USB_GADGET_S3C_HSUDC | ||
373 | default USB_GADGET | ||
374 | select USB_GADGET_SELECTED | ||
375 | |||
359 | config USB_GADGET_PXA_U2O | 376 | config USB_GADGET_PXA_U2O |
360 | boolean "PXA9xx Processor USB2.0 controller" | 377 | boolean "PXA9xx Processor USB2.0 controller" |
361 | select USB_GADGET_DUALSPEED | 378 | select USB_GADGET_DUALSPEED |
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1ea15ee74fd3..4fe92b18a055 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile | |||
@@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o | |||
22 | obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o | 22 | obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o |
23 | obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o | 23 | obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o |
24 | obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o | 24 | obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o |
25 | obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o | ||
25 | obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o | 26 | obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o |
26 | obj-$(CONFIG_USB_EG20T) += pch_udc.o | 27 | obj-$(CONFIG_USB_EG20T) += pch_udc.o |
27 | obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o | 28 | obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o |
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index ec3fd979c71d..bcdac7c73e89 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h | |||
@@ -136,6 +136,12 @@ | |||
136 | #define gadget_is_s3c_hsotg(g) 0 | 136 | #define gadget_is_s3c_hsotg(g) 0 |
137 | #endif | 137 | #endif |
138 | 138 | ||
139 | #ifdef CONFIG_USB_S3C_HSUDC | ||
140 | #define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) | ||
141 | #else | ||
142 | #define gadget_is_s3c_hsudc(g) 0 | ||
143 | #endif | ||
144 | |||
139 | #ifdef CONFIG_USB_GADGET_EG20T | 145 | #ifdef CONFIG_USB_GADGET_EG20T |
140 | #define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name)) | 146 | #define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name)) |
141 | #else | 147 | #else |
@@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) | |||
215 | return 0x28; | 221 | return 0x28; |
216 | else if (gadget_is_renesas_usbhs(gadget)) | 222 | else if (gadget_is_renesas_usbhs(gadget)) |
217 | return 0x29; | 223 | return 0x29; |
224 | else if (gadget_is_s3c_hsudc(gadget)) | ||
225 | return 0x30; | ||
218 | 226 | ||
219 | return -ENOENT; | 227 | return -ENOENT; |
220 | } | 228 | } |
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c new file mode 100644 index 000000000000..d26901932a5c --- /dev/null +++ b/drivers/usb/gadget/s3c-hsudc.c | |||
@@ -0,0 +1,1349 @@ | |||
1 | /* linux/drivers/usb/gadget/s3c-hsudc.c | ||
2 | * | ||
3 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. | ||
4 | * http://www.samsung.com/ | ||
5 | * | ||
6 | * S3C24XX USB 2.0 High-speed USB controller gadget driver | ||
7 | * | ||
8 | * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints. | ||
9 | * Each endpoint can be configured as either in or out endpoint. Endpoints | ||
10 | * can be configured for Bulk or Interrupt transfer mode. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/dma-mapping.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/io.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/clk.h> | ||
27 | #include <linux/usb/ch9.h> | ||
28 | #include <linux/usb/gadget.h> | ||
29 | |||
30 | #include <mach/regs-s3c2443-clock.h> | ||
31 | #include <plat/udc.h> | ||
32 | |||
33 | #define S3C_HSUDC_REG(x) (x) | ||
34 | |||
35 | /* Non-Indexed Registers */ | ||
36 | #define S3C_IR S3C_HSUDC_REG(0x00) /* Index Register */ | ||
37 | #define S3C_EIR S3C_HSUDC_REG(0x04) /* EP Intr Status */ | ||
38 | #define S3C_EIR_EP0 (1<<0) | ||
39 | #define S3C_EIER S3C_HSUDC_REG(0x08) /* EP Intr Enable */ | ||
40 | #define S3C_FAR S3C_HSUDC_REG(0x0c) /* Gadget Address */ | ||
41 | #define S3C_FNR S3C_HSUDC_REG(0x10) /* Frame Number */ | ||
42 | #define S3C_EDR S3C_HSUDC_REG(0x14) /* EP Direction */ | ||
43 | #define S3C_TR S3C_HSUDC_REG(0x18) /* Test Register */ | ||
44 | #define S3C_SSR S3C_HSUDC_REG(0x1c) /* System Status */ | ||
45 | #define S3C_SSR_DTZIEN_EN (0xff8f) | ||
46 | #define S3C_SSR_ERR (0xff80) | ||
47 | #define S3C_SSR_VBUSON (1 << 8) | ||
48 | #define S3C_SSR_HSP (1 << 4) | ||
49 | #define S3C_SSR_SDE (1 << 3) | ||
50 | #define S3C_SSR_RESUME (1 << 2) | ||
51 | #define S3C_SSR_SUSPEND (1 << 1) | ||
52 | #define S3C_SSR_RESET (1 << 0) | ||
53 | #define S3C_SCR S3C_HSUDC_REG(0x20) /* System Control */ | ||
54 | #define S3C_SCR_DTZIEN_EN (1 << 14) | ||
55 | #define S3C_SCR_RRD_EN (1 << 5) | ||
56 | #define S3C_SCR_SUS_EN (1 << 1) | ||
57 | #define S3C_SCR_RST_EN (1 << 0) | ||
58 | #define S3C_EP0SR S3C_HSUDC_REG(0x24) /* EP0 Status */ | ||
59 | #define S3C_EP0SR_EP0_LWO (1 << 6) | ||
60 | #define S3C_EP0SR_STALL (1 << 4) | ||
61 | #define S3C_EP0SR_TX_SUCCESS (1 << 1) | ||
62 | #define S3C_EP0SR_RX_SUCCESS (1 << 0) | ||
63 | #define S3C_EP0CR S3C_HSUDC_REG(0x28) /* EP0 Control */ | ||
64 | #define S3C_BR(_x) S3C_HSUDC_REG(0x60 + (_x * 4)) | ||
65 | |||
66 | /* Indexed Registers */ | ||
67 | #define S3C_ESR S3C_HSUDC_REG(0x2c) /* EPn Status */ | ||
68 | #define S3C_ESR_FLUSH (1 << 6) | ||
69 | #define S3C_ESR_STALL (1 << 5) | ||
70 | #define S3C_ESR_LWO (1 << 4) | ||
71 | #define S3C_ESR_PSIF_ONE (1 << 2) | ||
72 | #define S3C_ESR_PSIF_TWO (2 << 2) | ||
73 | #define S3C_ESR_TX_SUCCESS (1 << 1) | ||
74 | #define S3C_ESR_RX_SUCCESS (1 << 0) | ||
75 | #define S3C_ECR S3C_HSUDC_REG(0x30) /* EPn Control */ | ||
76 | #define S3C_ECR_DUEN (1 << 7) | ||
77 | #define S3C_ECR_FLUSH (1 << 6) | ||
78 | #define S3C_ECR_STALL (1 << 1) | ||
79 | #define S3C_ECR_IEMS (1 << 0) | ||
80 | #define S3C_BRCR S3C_HSUDC_REG(0x34) /* Read Count */ | ||
81 | #define S3C_BWCR S3C_HSUDC_REG(0x38) /* Write Count */ | ||
82 | #define S3C_MPR S3C_HSUDC_REG(0x3c) /* Max Pkt Size */ | ||
83 | |||
84 | #define WAIT_FOR_SETUP (0) | ||
85 | #define DATA_STATE_XMIT (1) | ||
86 | #define DATA_STATE_RECV (2) | ||
87 | |||
88 | /** | ||
89 | * struct s3c_hsudc_ep - Endpoint representation used by driver. | ||
90 | * @ep: USB gadget layer representation of device endpoint. | ||
91 | * @name: Endpoint name (as required by ep autoconfiguration). | ||
92 | * @dev: Reference to the device controller to which this EP belongs. | ||
93 | * @desc: Endpoint descriptor obtained from the gadget driver. | ||
94 | * @queue: Transfer request queue for the endpoint. | ||
95 | * @stopped: Maintains state of endpoint, set if EP is halted. | ||
96 | * @bEndpointAddress: EP address (including direction bit). | ||
97 | * @fifo: Base address of EP FIFO. | ||
98 | */ | ||
99 | struct s3c_hsudc_ep { | ||
100 | struct usb_ep ep; | ||
101 | char name[20]; | ||
102 | struct s3c_hsudc *dev; | ||
103 | const struct usb_endpoint_descriptor *desc; | ||
104 | struct list_head queue; | ||
105 | u8 stopped; | ||
106 | u8 wedge; | ||
107 | u8 bEndpointAddress; | ||
108 | void __iomem *fifo; | ||
109 | }; | ||
110 | |||
111 | /** | ||
112 | * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request. | ||
113 | * @req: Reference to USB gadget transfer request. | ||
114 | * @queue: Used for inserting this request to the endpoint request queue. | ||
115 | */ | ||
116 | struct s3c_hsudc_req { | ||
117 | struct usb_request req; | ||
118 | struct list_head queue; | ||
119 | }; | ||
120 | |||
121 | /** | ||
122 | * struct s3c_hsudc - Driver's abstraction of the device controller. | ||
123 | * @gadget: Instance of usb_gadget which is referenced by gadget driver. | ||
124 | * @driver: Reference to currenty active gadget driver. | ||
125 | * @dev: The device reference used by probe function. | ||
126 | * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed). | ||
127 | * @regs: Remapped base address of controller's register space. | ||
128 | * @mem_rsrc: Device memory resource used for remapping device register space. | ||
129 | * irq: IRQ number used by the controller. | ||
130 | * uclk: Reference to the controller clock. | ||
131 | * ep0state: Current state of EP0. | ||
132 | * ep: List of endpoints supported by the controller. | ||
133 | */ | ||
134 | struct s3c_hsudc { | ||
135 | struct usb_gadget gadget; | ||
136 | struct usb_gadget_driver *driver; | ||
137 | struct device *dev; | ||
138 | struct s3c24xx_hsudc_platdata *pd; | ||
139 | spinlock_t lock; | ||
140 | void __iomem *regs; | ||
141 | struct resource *mem_rsrc; | ||
142 | int irq; | ||
143 | struct clk *uclk; | ||
144 | int ep0state; | ||
145 | struct s3c_hsudc_ep ep[]; | ||
146 | }; | ||
147 | |||
148 | #define ep_maxpacket(_ep) ((_ep)->ep.maxpacket) | ||
149 | #define ep_is_in(_ep) ((_ep)->bEndpointAddress & USB_DIR_IN) | ||
150 | #define ep_index(_ep) ((_ep)->bEndpointAddress & \ | ||
151 | USB_ENDPOINT_NUMBER_MASK) | ||
152 | |||
153 | static struct s3c_hsudc *the_controller; | ||
154 | static const char driver_name[] = "s3c-udc"; | ||
155 | static const char ep0name[] = "ep0-control"; | ||
156 | |||
157 | static inline struct s3c_hsudc_req *our_req(struct usb_request *req) | ||
158 | { | ||
159 | return container_of(req, struct s3c_hsudc_req, req); | ||
160 | } | ||
161 | |||
162 | static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep) | ||
163 | { | ||
164 | return container_of(ep, struct s3c_hsudc_ep, ep); | ||
165 | } | ||
166 | |||
167 | static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget) | ||
168 | { | ||
169 | return container_of(gadget, struct s3c_hsudc, gadget); | ||
170 | } | ||
171 | |||
172 | static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr) | ||
173 | { | ||
174 | ep_addr &= USB_ENDPOINT_NUMBER_MASK; | ||
175 | writel(ep_addr, hsudc->regs + S3C_IR); | ||
176 | } | ||
177 | |||
178 | static inline void __orr32(void __iomem *ptr, u32 val) | ||
179 | { | ||
180 | writel(readl(ptr) | val, ptr); | ||
181 | } | ||
182 | |||
183 | static void s3c_hsudc_init_phy(void) | ||
184 | { | ||
185 | u32 cfg; | ||
186 | |||
187 | cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; | ||
188 | writel(cfg, S3C2443_PWRCFG); | ||
189 | |||
190 | cfg = readl(S3C2443_URSTCON); | ||
191 | cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); | ||
192 | writel(cfg, S3C2443_URSTCON); | ||
193 | mdelay(1); | ||
194 | |||
195 | cfg = readl(S3C2443_URSTCON); | ||
196 | cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); | ||
197 | writel(cfg, S3C2443_URSTCON); | ||
198 | |||
199 | cfg = readl(S3C2443_PHYCTRL); | ||
200 | cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); | ||
201 | cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); | ||
202 | writel(cfg, S3C2443_PHYCTRL); | ||
203 | |||
204 | cfg = readl(S3C2443_PHYPWR); | ||
205 | cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN | | ||
206 | S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK | | ||
207 | S3C2443_PHYPWR_ANALOG_PD); | ||
208 | cfg |= S3C2443_PHYPWR_COMMON_ON; | ||
209 | writel(cfg, S3C2443_PHYPWR); | ||
210 | |||
211 | cfg = readl(S3C2443_UCLKCON); | ||
212 | cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | | ||
213 | S3C2443_UCLKCON_TCLKEN); | ||
214 | writel(cfg, S3C2443_UCLKCON); | ||
215 | } | ||
216 | |||
217 | static void s3c_hsudc_uninit_phy(void) | ||
218 | { | ||
219 | u32 cfg; | ||
220 | |||
221 | cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; | ||
222 | writel(cfg, S3C2443_PWRCFG); | ||
223 | |||
224 | writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); | ||
225 | |||
226 | cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; | ||
227 | writel(cfg, S3C2443_UCLKCON); | ||
228 | } | ||
229 | |||
230 | /** | ||
231 | * s3c_hsudc_complete_request - Complete a transfer request. | ||
232 | * @hsep: Endpoint to which the request belongs. | ||
233 | * @hsreq: Transfer request to be completed. | ||
234 | * @status: Transfer completion status for the transfer request. | ||
235 | */ | ||
236 | static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep, | ||
237 | struct s3c_hsudc_req *hsreq, int status) | ||
238 | { | ||
239 | unsigned int stopped = hsep->stopped; | ||
240 | struct s3c_hsudc *hsudc = hsep->dev; | ||
241 | |||
242 | list_del_init(&hsreq->queue); | ||
243 | hsreq->req.status = status; | ||
244 | |||
245 | if (!ep_index(hsep)) { | ||
246 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
247 | hsep->bEndpointAddress &= ~USB_DIR_IN; | ||
248 | } | ||
249 | |||
250 | hsep->stopped = 1; | ||
251 | spin_unlock(&hsudc->lock); | ||
252 | if (hsreq->req.complete != NULL) | ||
253 | hsreq->req.complete(&hsep->ep, &hsreq->req); | ||
254 | spin_lock(&hsudc->lock); | ||
255 | hsep->stopped = stopped; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint. | ||
260 | * @hsep: Endpoint for which queued requests have to be terminated. | ||
261 | * @status: Transfer completion status for the transfer request. | ||
262 | */ | ||
263 | static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status) | ||
264 | { | ||
265 | struct s3c_hsudc_req *hsreq; | ||
266 | |||
267 | while (!list_empty(&hsep->queue)) { | ||
268 | hsreq = list_entry(hsep->queue.next, | ||
269 | struct s3c_hsudc_req, queue); | ||
270 | s3c_hsudc_complete_request(hsep, hsreq, status); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /** | ||
275 | * s3c_hsudc_stop_activity - Stop activity on all endpoints. | ||
276 | * @hsudc: Device controller for which EP activity is to be stopped. | ||
277 | * @driver: Reference to the gadget driver which is currently active. | ||
278 | * | ||
279 | * All the endpoints are stopped and any pending transfer requests if any on | ||
280 | * the endpoint are terminated. | ||
281 | */ | ||
282 | static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc, | ||
283 | struct usb_gadget_driver *driver) | ||
284 | { | ||
285 | struct s3c_hsudc_ep *hsep; | ||
286 | int epnum; | ||
287 | |||
288 | hsudc->gadget.speed = USB_SPEED_UNKNOWN; | ||
289 | |||
290 | for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) { | ||
291 | hsep = &hsudc->ep[epnum]; | ||
292 | hsep->stopped = 1; | ||
293 | s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); | ||
294 | } | ||
295 | |||
296 | spin_unlock(&hsudc->lock); | ||
297 | driver->disconnect(&hsudc->gadget); | ||
298 | spin_lock(&hsudc->lock); | ||
299 | } | ||
300 | |||
301 | /** | ||
302 | * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo. | ||
303 | * @hsudc: Device controller from which setup packet is to be read. | ||
304 | * @buf: The buffer into which the setup packet is read. | ||
305 | * | ||
306 | * The setup packet received in the EP0 fifo is read and stored into a | ||
307 | * given buffer address. | ||
308 | */ | ||
309 | |||
310 | static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf) | ||
311 | { | ||
312 | int count; | ||
313 | |||
314 | count = readl(hsudc->regs + S3C_BRCR); | ||
315 | while (count--) | ||
316 | *buf++ = (u16)readl(hsudc->regs + S3C_BR(0)); | ||
317 | |||
318 | writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR); | ||
319 | } | ||
320 | |||
321 | /** | ||
322 | * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo. | ||
323 | * @hsep: Endpoint to which the data is to be written. | ||
324 | * @hsreq: Transfer request from which the next chunk of data is written. | ||
325 | * | ||
326 | * Write the next chunk of data from a transfer request to the endpoint FIFO. | ||
327 | * If the transfer request completes, 1 is returned, otherwise 0 is returned. | ||
328 | */ | ||
329 | static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep, | ||
330 | struct s3c_hsudc_req *hsreq) | ||
331 | { | ||
332 | u16 *buf; | ||
333 | u32 max = ep_maxpacket(hsep); | ||
334 | u32 count, length; | ||
335 | bool is_last; | ||
336 | void __iomem *fifo = hsep->fifo; | ||
337 | |||
338 | buf = hsreq->req.buf + hsreq->req.actual; | ||
339 | prefetch(buf); | ||
340 | |||
341 | length = hsreq->req.length - hsreq->req.actual; | ||
342 | length = min(length, max); | ||
343 | hsreq->req.actual += length; | ||
344 | |||
345 | writel(length, hsep->dev->regs + S3C_BWCR); | ||
346 | for (count = 0; count < length; count += 2) | ||
347 | writel(*buf++, fifo); | ||
348 | |||
349 | if (count != max) { | ||
350 | is_last = true; | ||
351 | } else { | ||
352 | if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) | ||
353 | is_last = false; | ||
354 | else | ||
355 | is_last = true; | ||
356 | } | ||
357 | |||
358 | if (is_last) { | ||
359 | s3c_hsudc_complete_request(hsep, hsreq, 0); | ||
360 | return 1; | ||
361 | } | ||
362 | |||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | /** | ||
367 | * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo. | ||
368 | * @hsep: Endpoint from which the data is to be read. | ||
369 | * @hsreq: Transfer request to which the next chunk of data read is written. | ||
370 | * | ||
371 | * Read the next chunk of data from the endpoint FIFO and a write it to the | ||
372 | * transfer request buffer. If the transfer request completes, 1 is returned, | ||
373 | * otherwise 0 is returned. | ||
374 | */ | ||
375 | static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep, | ||
376 | struct s3c_hsudc_req *hsreq) | ||
377 | { | ||
378 | struct s3c_hsudc *hsudc = hsep->dev; | ||
379 | u32 csr, offset; | ||
380 | u16 *buf, word; | ||
381 | u32 buflen, rcnt, rlen; | ||
382 | void __iomem *fifo = hsep->fifo; | ||
383 | u32 is_short = 0; | ||
384 | |||
385 | offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; | ||
386 | csr = readl(hsudc->regs + offset); | ||
387 | if (!(csr & S3C_ESR_RX_SUCCESS)) | ||
388 | return -EINVAL; | ||
389 | |||
390 | buf = hsreq->req.buf + hsreq->req.actual; | ||
391 | prefetchw(buf); | ||
392 | buflen = hsreq->req.length - hsreq->req.actual; | ||
393 | |||
394 | rcnt = readl(hsudc->regs + S3C_BRCR); | ||
395 | rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2); | ||
396 | |||
397 | hsreq->req.actual += min(rlen, buflen); | ||
398 | is_short = (rlen < hsep->ep.maxpacket); | ||
399 | |||
400 | while (rcnt-- != 0) { | ||
401 | word = (u16)readl(fifo); | ||
402 | if (buflen) { | ||
403 | *buf++ = word; | ||
404 | buflen--; | ||
405 | } else { | ||
406 | hsreq->req.status = -EOVERFLOW; | ||
407 | } | ||
408 | } | ||
409 | |||
410 | writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset); | ||
411 | |||
412 | if (is_short || hsreq->req.actual == hsreq->req.length) { | ||
413 | s3c_hsudc_complete_request(hsep, hsreq, 0); | ||
414 | return 1; | ||
415 | } | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | /** | ||
421 | * s3c_hsudc_epin_intr - Handle in-endpoint interrupt. | ||
422 | * @hsudc - Device controller for which the interrupt is to be handled. | ||
423 | * @ep_idx - Endpoint number on which an interrupt is pending. | ||
424 | * | ||
425 | * Handles interrupt for a in-endpoint. The interrupts that are handled are | ||
426 | * stall and data transmit complete interrupt. | ||
427 | */ | ||
428 | static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx) | ||
429 | { | ||
430 | struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; | ||
431 | struct s3c_hsudc_req *hsreq; | ||
432 | u32 csr; | ||
433 | |||
434 | csr = readl((u32)hsudc->regs + S3C_ESR); | ||
435 | if (csr & S3C_ESR_STALL) { | ||
436 | writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); | ||
437 | return; | ||
438 | } | ||
439 | |||
440 | if (csr & S3C_ESR_TX_SUCCESS) { | ||
441 | writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR); | ||
442 | if (list_empty(&hsep->queue)) | ||
443 | return; | ||
444 | |||
445 | hsreq = list_entry(hsep->queue.next, | ||
446 | struct s3c_hsudc_req, queue); | ||
447 | if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) && | ||
448 | (csr & S3C_ESR_PSIF_TWO)) | ||
449 | s3c_hsudc_write_fifo(hsep, hsreq); | ||
450 | } | ||
451 | } | ||
452 | |||
453 | /** | ||
454 | * s3c_hsudc_epout_intr - Handle out-endpoint interrupt. | ||
455 | * @hsudc - Device controller for which the interrupt is to be handled. | ||
456 | * @ep_idx - Endpoint number on which an interrupt is pending. | ||
457 | * | ||
458 | * Handles interrupt for a out-endpoint. The interrupts that are handled are | ||
459 | * stall, flush and data ready interrupt. | ||
460 | */ | ||
461 | static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx) | ||
462 | { | ||
463 | struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; | ||
464 | struct s3c_hsudc_req *hsreq; | ||
465 | u32 csr; | ||
466 | |||
467 | csr = readl((u32)hsudc->regs + S3C_ESR); | ||
468 | if (csr & S3C_ESR_STALL) { | ||
469 | writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); | ||
470 | return; | ||
471 | } | ||
472 | |||
473 | if (csr & S3C_ESR_FLUSH) { | ||
474 | __orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH); | ||
475 | return; | ||
476 | } | ||
477 | |||
478 | if (csr & S3C_ESR_RX_SUCCESS) { | ||
479 | if (list_empty(&hsep->queue)) | ||
480 | return; | ||
481 | |||
482 | hsreq = list_entry(hsep->queue.next, | ||
483 | struct s3c_hsudc_req, queue); | ||
484 | if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) && | ||
485 | (csr & S3C_ESR_PSIF_TWO)) | ||
486 | s3c_hsudc_read_fifo(hsep, hsreq); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | /** s3c_hsudc_set_halt - Set or clear a endpoint halt. | ||
491 | * @_ep: Endpoint on which halt has to be set or cleared. | ||
492 | * @value: 1 for setting halt on endpoint, 0 to clear halt. | ||
493 | * | ||
494 | * Set or clear endpoint halt. If halt is set, the endpoint is stopped. | ||
495 | * If halt is cleared, for in-endpoints, if there are any pending | ||
496 | * transfer requests, transfers are started. | ||
497 | */ | ||
498 | static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value) | ||
499 | { | ||
500 | struct s3c_hsudc_ep *hsep = our_ep(_ep); | ||
501 | struct s3c_hsudc *hsudc = hsep->dev; | ||
502 | struct s3c_hsudc_req *hsreq; | ||
503 | unsigned long irqflags; | ||
504 | u32 ecr; | ||
505 | u32 offset; | ||
506 | |||
507 | if (value && ep_is_in(hsep) && !list_empty(&hsep->queue)) | ||
508 | return -EAGAIN; | ||
509 | |||
510 | spin_lock_irqsave(&hsudc->lock, irqflags); | ||
511 | set_index(hsudc, ep_index(hsep)); | ||
512 | offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR; | ||
513 | ecr = readl(hsudc->regs + offset); | ||
514 | |||
515 | if (value) { | ||
516 | ecr |= S3C_ECR_STALL; | ||
517 | if (ep_index(hsep)) | ||
518 | ecr |= S3C_ECR_FLUSH; | ||
519 | hsep->stopped = 1; | ||
520 | } else { | ||
521 | ecr &= ~S3C_ECR_STALL; | ||
522 | hsep->stopped = hsep->wedge = 0; | ||
523 | } | ||
524 | writel(ecr, hsudc->regs + offset); | ||
525 | |||
526 | if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) { | ||
527 | hsreq = list_entry(hsep->queue.next, | ||
528 | struct s3c_hsudc_req, queue); | ||
529 | if (hsreq) | ||
530 | s3c_hsudc_write_fifo(hsep, hsreq); | ||
531 | } | ||
532 | |||
533 | spin_unlock_irqrestore(&hsudc->lock, irqflags); | ||
534 | return 0; | ||
535 | } | ||
536 | |||
537 | /** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored | ||
538 | * @_ep: Endpoint on which wedge has to be set. | ||
539 | * | ||
540 | * Sets the halt feature with the clear requests ignored. | ||
541 | */ | ||
542 | static int s3c_hsudc_set_wedge(struct usb_ep *_ep) | ||
543 | { | ||
544 | struct s3c_hsudc_ep *hsep = our_ep(_ep); | ||
545 | |||
546 | if (!hsep) | ||
547 | return -EINVAL; | ||
548 | |||
549 | hsep->wedge = 1; | ||
550 | return usb_ep_set_halt(_ep); | ||
551 | } | ||
552 | |||
553 | /** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests. | ||
554 | * @_ep: Device controller on which the set/clear feature needs to be handled. | ||
555 | * @ctrl: Control request as received on the endpoint 0. | ||
556 | * | ||
557 | * Handle set feature or clear feature control requests on the control endpoint. | ||
558 | */ | ||
559 | static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc, | ||
560 | struct usb_ctrlrequest *ctrl) | ||
561 | { | ||
562 | struct s3c_hsudc_ep *hsep; | ||
563 | bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); | ||
564 | u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; | ||
565 | |||
566 | if (ctrl->bRequestType == USB_RECIP_ENDPOINT) { | ||
567 | hsep = &hsudc->ep[ep_num]; | ||
568 | switch (le16_to_cpu(ctrl->wValue)) { | ||
569 | case USB_ENDPOINT_HALT: | ||
570 | if (set || (!set && !hsep->wedge)) | ||
571 | s3c_hsudc_set_halt(&hsep->ep, set); | ||
572 | return 0; | ||
573 | } | ||
574 | } | ||
575 | |||
576 | return -ENOENT; | ||
577 | } | ||
578 | |||
579 | /** | ||
580 | * s3c_hsudc_process_req_status - Handle get status control request. | ||
581 | * @hsudc: Device controller on which get status request has be handled. | ||
582 | * @ctrl: Control request as received on the endpoint 0. | ||
583 | * | ||
584 | * Handle get status control request received on control endpoint. | ||
585 | */ | ||
586 | static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc, | ||
587 | struct usb_ctrlrequest *ctrl) | ||
588 | { | ||
589 | struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0]; | ||
590 | struct s3c_hsudc_req hsreq; | ||
591 | struct s3c_hsudc_ep *hsep; | ||
592 | __le16 reply; | ||
593 | u8 epnum; | ||
594 | |||
595 | switch (ctrl->bRequestType & USB_RECIP_MASK) { | ||
596 | case USB_RECIP_DEVICE: | ||
597 | reply = cpu_to_le16(0); | ||
598 | break; | ||
599 | |||
600 | case USB_RECIP_INTERFACE: | ||
601 | reply = cpu_to_le16(0); | ||
602 | break; | ||
603 | |||
604 | case USB_RECIP_ENDPOINT: | ||
605 | epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; | ||
606 | hsep = &hsudc->ep[epnum]; | ||
607 | reply = cpu_to_le16(hsep->stopped ? 1 : 0); | ||
608 | break; | ||
609 | } | ||
610 | |||
611 | INIT_LIST_HEAD(&hsreq.queue); | ||
612 | hsreq.req.length = 2; | ||
613 | hsreq.req.buf = &reply; | ||
614 | hsreq.req.actual = 0; | ||
615 | hsreq.req.complete = NULL; | ||
616 | s3c_hsudc_write_fifo(hsep0, &hsreq); | ||
617 | } | ||
618 | |||
619 | /** | ||
620 | * s3c_hsudc_process_setup - Process control request received on endpoint 0. | ||
621 | * @hsudc: Device controller on which control request has been received. | ||
622 | * | ||
623 | * Read the control request received on endpoint 0, decode it and handle | ||
624 | * the request. | ||
625 | */ | ||
626 | static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc) | ||
627 | { | ||
628 | struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; | ||
629 | struct usb_ctrlrequest ctrl = {0}; | ||
630 | int ret; | ||
631 | |||
632 | s3c_hsudc_nuke_ep(hsep, -EPROTO); | ||
633 | s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl); | ||
634 | |||
635 | if (ctrl.bRequestType & USB_DIR_IN) { | ||
636 | hsep->bEndpointAddress |= USB_DIR_IN; | ||
637 | hsudc->ep0state = DATA_STATE_XMIT; | ||
638 | } else { | ||
639 | hsep->bEndpointAddress &= ~USB_DIR_IN; | ||
640 | hsudc->ep0state = DATA_STATE_RECV; | ||
641 | } | ||
642 | |||
643 | switch (ctrl.bRequest) { | ||
644 | case USB_REQ_SET_ADDRESS: | ||
645 | if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) | ||
646 | break; | ||
647 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
648 | return; | ||
649 | |||
650 | case USB_REQ_GET_STATUS: | ||
651 | if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) | ||
652 | break; | ||
653 | s3c_hsudc_process_req_status(hsudc, &ctrl); | ||
654 | return; | ||
655 | |||
656 | case USB_REQ_SET_FEATURE: | ||
657 | case USB_REQ_CLEAR_FEATURE: | ||
658 | if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) | ||
659 | break; | ||
660 | s3c_hsudc_handle_reqfeat(hsudc, &ctrl); | ||
661 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
662 | return; | ||
663 | } | ||
664 | |||
665 | if (hsudc->driver) { | ||
666 | spin_unlock(&hsudc->lock); | ||
667 | ret = hsudc->driver->setup(&hsudc->gadget, &ctrl); | ||
668 | spin_lock(&hsudc->lock); | ||
669 | |||
670 | if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) { | ||
671 | hsep->bEndpointAddress &= ~USB_DIR_IN; | ||
672 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
673 | } | ||
674 | |||
675 | if (ret < 0) { | ||
676 | dev_err(hsudc->dev, "setup failed, returned %d\n", | ||
677 | ret); | ||
678 | s3c_hsudc_set_halt(&hsep->ep, 1); | ||
679 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
680 | hsep->bEndpointAddress &= ~USB_DIR_IN; | ||
681 | } | ||
682 | } | ||
683 | } | ||
684 | |||
685 | /** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt. | ||
686 | * @hsudc: Device controller on which endpoint 0 interrupt has occured. | ||
687 | * | ||
688 | * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur | ||
689 | * when a stall handshake is sent to host or data is sent/received on | ||
690 | * endpoint 0. | ||
691 | */ | ||
692 | static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc) | ||
693 | { | ||
694 | struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; | ||
695 | struct s3c_hsudc_req *hsreq; | ||
696 | u32 csr = readl(hsudc->regs + S3C_EP0SR); | ||
697 | u32 ecr; | ||
698 | |||
699 | if (csr & S3C_EP0SR_STALL) { | ||
700 | ecr = readl(hsudc->regs + S3C_EP0CR); | ||
701 | ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH); | ||
702 | writel(ecr, hsudc->regs + S3C_EP0CR); | ||
703 | |||
704 | writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR); | ||
705 | hsep->stopped = 0; | ||
706 | |||
707 | s3c_hsudc_nuke_ep(hsep, -ECONNABORTED); | ||
708 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
709 | hsep->bEndpointAddress &= ~USB_DIR_IN; | ||
710 | return; | ||
711 | } | ||
712 | |||
713 | if (csr & S3C_EP0SR_TX_SUCCESS) { | ||
714 | writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR); | ||
715 | if (ep_is_in(hsep)) { | ||
716 | if (list_empty(&hsep->queue)) | ||
717 | return; | ||
718 | |||
719 | hsreq = list_entry(hsep->queue.next, | ||
720 | struct s3c_hsudc_req, queue); | ||
721 | s3c_hsudc_write_fifo(hsep, hsreq); | ||
722 | } | ||
723 | } | ||
724 | |||
725 | if (csr & S3C_EP0SR_RX_SUCCESS) { | ||
726 | if (hsudc->ep0state == WAIT_FOR_SETUP) | ||
727 | s3c_hsudc_process_setup(hsudc); | ||
728 | else { | ||
729 | if (!ep_is_in(hsep)) { | ||
730 | if (list_empty(&hsep->queue)) | ||
731 | return; | ||
732 | hsreq = list_entry(hsep->queue.next, | ||
733 | struct s3c_hsudc_req, queue); | ||
734 | s3c_hsudc_read_fifo(hsep, hsreq); | ||
735 | } | ||
736 | } | ||
737 | } | ||
738 | } | ||
739 | |||
740 | /** | ||
741 | * s3c_hsudc_ep_enable - Enable a endpoint. | ||
742 | * @_ep: The endpoint to be enabled. | ||
743 | * @desc: Endpoint descriptor. | ||
744 | * | ||
745 | * Enables a endpoint when called from the gadget driver. Endpoint stall if | ||
746 | * any is cleared, transfer type is configured and endpoint interrupt is | ||
747 | * enabled. | ||
748 | */ | ||
749 | static int s3c_hsudc_ep_enable(struct usb_ep *_ep, | ||
750 | const struct usb_endpoint_descriptor *desc) | ||
751 | { | ||
752 | struct s3c_hsudc_ep *hsep; | ||
753 | struct s3c_hsudc *hsudc; | ||
754 | unsigned long flags; | ||
755 | u32 ecr = 0; | ||
756 | |||
757 | hsep = container_of(_ep, struct s3c_hsudc_ep, ep); | ||
758 | if (!_ep || !desc || hsep->desc || _ep->name == ep0name | ||
759 | || desc->bDescriptorType != USB_DT_ENDPOINT | ||
760 | || hsep->bEndpointAddress != desc->bEndpointAddress | ||
761 | || ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize)) | ||
762 | return -EINVAL; | ||
763 | |||
764 | if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK | ||
765 | && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep)) | ||
766 | || !desc->wMaxPacketSize) | ||
767 | return -ERANGE; | ||
768 | |||
769 | hsudc = hsep->dev; | ||
770 | if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) | ||
771 | return -ESHUTDOWN; | ||
772 | |||
773 | spin_lock_irqsave(&hsudc->lock, flags); | ||
774 | |||
775 | set_index(hsudc, hsep->bEndpointAddress); | ||
776 | ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN); | ||
777 | writel(ecr, hsudc->regs + S3C_ECR); | ||
778 | |||
779 | hsep->stopped = hsep->wedge = 0; | ||
780 | hsep->desc = desc; | ||
781 | hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); | ||
782 | |||
783 | s3c_hsudc_set_halt(_ep, 0); | ||
784 | __set_bit(ep_index(hsep), hsudc->regs + S3C_EIER); | ||
785 | |||
786 | spin_unlock_irqrestore(&hsudc->lock, flags); | ||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | /** | ||
791 | * s3c_hsudc_ep_disable - Disable a endpoint. | ||
792 | * @_ep: The endpoint to be disabled. | ||
793 | * @desc: Endpoint descriptor. | ||
794 | * | ||
795 | * Disables a endpoint when called from the gadget driver. | ||
796 | */ | ||
797 | static int s3c_hsudc_ep_disable(struct usb_ep *_ep) | ||
798 | { | ||
799 | struct s3c_hsudc_ep *hsep = our_ep(_ep); | ||
800 | struct s3c_hsudc *hsudc = hsep->dev; | ||
801 | unsigned long flags; | ||
802 | |||
803 | if (!_ep || !hsep->desc) | ||
804 | return -EINVAL; | ||
805 | |||
806 | spin_lock_irqsave(&hsudc->lock, flags); | ||
807 | |||
808 | set_index(hsudc, hsep->bEndpointAddress); | ||
809 | __clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER); | ||
810 | |||
811 | s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); | ||
812 | |||
813 | hsep->desc = 0; | ||
814 | hsep->stopped = 1; | ||
815 | |||
816 | spin_unlock_irqrestore(&hsudc->lock, flags); | ||
817 | return 0; | ||
818 | } | ||
819 | |||
820 | /** | ||
821 | * s3c_hsudc_alloc_request - Allocate a new request. | ||
822 | * @_ep: Endpoint for which request is allocated (not used). | ||
823 | * @gfp_flags: Flags used for the allocation. | ||
824 | * | ||
825 | * Allocates a single transfer request structure when called from gadget driver. | ||
826 | */ | ||
827 | static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep, | ||
828 | gfp_t gfp_flags) | ||
829 | { | ||
830 | struct s3c_hsudc_req *hsreq; | ||
831 | |||
832 | hsreq = kzalloc(sizeof *hsreq, gfp_flags); | ||
833 | if (!hsreq) | ||
834 | return 0; | ||
835 | |||
836 | INIT_LIST_HEAD(&hsreq->queue); | ||
837 | return &hsreq->req; | ||
838 | } | ||
839 | |||
840 | /** | ||
841 | * s3c_hsudc_free_request - Deallocate a request. | ||
842 | * @ep: Endpoint for which request is deallocated (not used). | ||
843 | * @_req: Request to be deallocated. | ||
844 | * | ||
845 | * Allocates a single transfer request structure when called from gadget driver. | ||
846 | */ | ||
847 | static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req) | ||
848 | { | ||
849 | struct s3c_hsudc_req *hsreq; | ||
850 | |||
851 | hsreq = container_of(_req, struct s3c_hsudc_req, req); | ||
852 | WARN_ON(!list_empty(&hsreq->queue)); | ||
853 | kfree(hsreq); | ||
854 | } | ||
855 | |||
856 | /** | ||
857 | * s3c_hsudc_queue - Queue a transfer request for the endpoint. | ||
858 | * @_ep: Endpoint for which the request is queued. | ||
859 | * @_req: Request to be queued. | ||
860 | * @gfp_flags: Not used. | ||
861 | * | ||
862 | * Start or enqueue a request for a endpoint when called from gadget driver. | ||
863 | */ | ||
864 | static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req, | ||
865 | gfp_t gfp_flags) | ||
866 | { | ||
867 | struct s3c_hsudc_req *hsreq; | ||
868 | struct s3c_hsudc_ep *hsep; | ||
869 | struct s3c_hsudc *hsudc; | ||
870 | unsigned long flags; | ||
871 | u32 offset; | ||
872 | u32 csr; | ||
873 | |||
874 | hsreq = container_of(_req, struct s3c_hsudc_req, req); | ||
875 | if ((!_req || !_req->complete || !_req->buf || | ||
876 | !list_empty(&hsreq->queue))) | ||
877 | return -EINVAL; | ||
878 | |||
879 | hsep = container_of(_ep, struct s3c_hsudc_ep, ep); | ||
880 | hsudc = hsep->dev; | ||
881 | if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) | ||
882 | return -ESHUTDOWN; | ||
883 | |||
884 | spin_lock_irqsave(&hsudc->lock, flags); | ||
885 | set_index(hsudc, hsep->bEndpointAddress); | ||
886 | |||
887 | _req->status = -EINPROGRESS; | ||
888 | _req->actual = 0; | ||
889 | |||
890 | if (!ep_index(hsep) && _req->length == 0) { | ||
891 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
892 | s3c_hsudc_complete_request(hsep, hsreq, 0); | ||
893 | spin_unlock_irqrestore(&hsudc->lock, flags); | ||
894 | return 0; | ||
895 | } | ||
896 | |||
897 | if (list_empty(&hsep->queue) && !hsep->stopped) { | ||
898 | offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; | ||
899 | if (ep_is_in(hsep)) { | ||
900 | csr = readl((u32)hsudc->regs + offset); | ||
901 | if (!(csr & S3C_ESR_TX_SUCCESS) && | ||
902 | (s3c_hsudc_write_fifo(hsep, hsreq) == 1)) | ||
903 | hsreq = 0; | ||
904 | } else { | ||
905 | csr = readl((u32)hsudc->regs + offset); | ||
906 | if ((csr & S3C_ESR_RX_SUCCESS) | ||
907 | && (s3c_hsudc_read_fifo(hsep, hsreq) == 1)) | ||
908 | hsreq = 0; | ||
909 | } | ||
910 | } | ||
911 | |||
912 | if (hsreq != 0) | ||
913 | list_add_tail(&hsreq->queue, &hsep->queue); | ||
914 | |||
915 | spin_unlock_irqrestore(&hsudc->lock, flags); | ||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | /** | ||
920 | * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint. | ||
921 | * @_ep: Endpoint from which the request is dequeued. | ||
922 | * @_req: Request to be dequeued. | ||
923 | * | ||
924 | * Dequeue a request from a endpoint when called from gadget driver. | ||
925 | */ | ||
926 | static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req) | ||
927 | { | ||
928 | struct s3c_hsudc_ep *hsep = our_ep(_ep); | ||
929 | struct s3c_hsudc *hsudc = hsep->dev; | ||
930 | struct s3c_hsudc_req *hsreq; | ||
931 | unsigned long flags; | ||
932 | |||
933 | hsep = container_of(_ep, struct s3c_hsudc_ep, ep); | ||
934 | if (!_ep || hsep->ep.name == ep0name) | ||
935 | return -EINVAL; | ||
936 | |||
937 | spin_lock_irqsave(&hsudc->lock, flags); | ||
938 | |||
939 | list_for_each_entry(hsreq, &hsep->queue, queue) { | ||
940 | if (&hsreq->req == _req) | ||
941 | break; | ||
942 | } | ||
943 | if (&hsreq->req != _req) { | ||
944 | spin_unlock_irqrestore(&hsudc->lock, flags); | ||
945 | return -EINVAL; | ||
946 | } | ||
947 | |||
948 | set_index(hsudc, hsep->bEndpointAddress); | ||
949 | s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET); | ||
950 | |||
951 | spin_unlock_irqrestore(&hsudc->lock, flags); | ||
952 | return 0; | ||
953 | } | ||
954 | |||
955 | static struct usb_ep_ops s3c_hsudc_ep_ops = { | ||
956 | .enable = s3c_hsudc_ep_enable, | ||
957 | .disable = s3c_hsudc_ep_disable, | ||
958 | .alloc_request = s3c_hsudc_alloc_request, | ||
959 | .free_request = s3c_hsudc_free_request, | ||
960 | .queue = s3c_hsudc_queue, | ||
961 | .dequeue = s3c_hsudc_dequeue, | ||
962 | .set_halt = s3c_hsudc_set_halt, | ||
963 | .set_wedge = s3c_hsudc_set_wedge, | ||
964 | }; | ||
965 | |||
966 | /** | ||
967 | * s3c_hsudc_initep - Initialize a endpoint to default state. | ||
968 | * @hsudc - Reference to the device controller. | ||
969 | * @hsep - Endpoint to be initialized. | ||
970 | * @epnum - Address to be assigned to the endpoint. | ||
971 | * | ||
972 | * Initialize a endpoint with default configuration. | ||
973 | */ | ||
974 | static void s3c_hsudc_initep(struct s3c_hsudc *hsudc, | ||
975 | struct s3c_hsudc_ep *hsep, int epnum) | ||
976 | { | ||
977 | char *dir; | ||
978 | |||
979 | if ((epnum % 2) == 0) { | ||
980 | dir = "out"; | ||
981 | } else { | ||
982 | dir = "in"; | ||
983 | hsep->bEndpointAddress = USB_DIR_IN; | ||
984 | } | ||
985 | |||
986 | hsep->bEndpointAddress |= epnum; | ||
987 | if (epnum) | ||
988 | snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir); | ||
989 | else | ||
990 | snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name); | ||
991 | |||
992 | INIT_LIST_HEAD(&hsep->queue); | ||
993 | INIT_LIST_HEAD(&hsep->ep.ep_list); | ||
994 | if (epnum) | ||
995 | list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list); | ||
996 | |||
997 | hsep->dev = hsudc; | ||
998 | hsep->ep.name = hsep->name; | ||
999 | hsep->ep.maxpacket = epnum ? 512 : 64; | ||
1000 | hsep->ep.ops = &s3c_hsudc_ep_ops; | ||
1001 | hsep->fifo = hsudc->regs + S3C_BR(epnum); | ||
1002 | hsep->desc = 0; | ||
1003 | hsep->stopped = 0; | ||
1004 | hsep->wedge = 0; | ||
1005 | |||
1006 | set_index(hsudc, epnum); | ||
1007 | writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR); | ||
1008 | } | ||
1009 | |||
1010 | /** | ||
1011 | * s3c_hsudc_setup_ep - Configure all endpoints to default state. | ||
1012 | * @hsudc: Reference to device controller. | ||
1013 | * | ||
1014 | * Configures all endpoints to default state. | ||
1015 | */ | ||
1016 | static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc) | ||
1017 | { | ||
1018 | int epnum; | ||
1019 | |||
1020 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
1021 | INIT_LIST_HEAD(&hsudc->gadget.ep_list); | ||
1022 | for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) | ||
1023 | s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum); | ||
1024 | } | ||
1025 | |||
1026 | /** | ||
1027 | * s3c_hsudc_reconfig - Reconfigure the device controller to default state. | ||
1028 | * @hsudc: Reference to device controller. | ||
1029 | * | ||
1030 | * Reconfigures the device controller registers to a default state. | ||
1031 | */ | ||
1032 | static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc) | ||
1033 | { | ||
1034 | writel(0xAA, hsudc->regs + S3C_EDR); | ||
1035 | writel(1, hsudc->regs + S3C_EIER); | ||
1036 | writel(0, hsudc->regs + S3C_TR); | ||
1037 | writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN | | ||
1038 | S3C_SCR_RST_EN, hsudc->regs + S3C_SCR); | ||
1039 | writel(0, hsudc->regs + S3C_EP0CR); | ||
1040 | |||
1041 | s3c_hsudc_setup_ep(hsudc); | ||
1042 | } | ||
1043 | |||
1044 | /** | ||
1045 | * s3c_hsudc_irq - Interrupt handler for device controller. | ||
1046 | * @irq: Not used. | ||
1047 | * @_dev: Reference to the device controller. | ||
1048 | * | ||
1049 | * Interrupt handler for the device controller. This handler handles controller | ||
1050 | * interrupts and endpoint interrupts. | ||
1051 | */ | ||
1052 | static irqreturn_t s3c_hsudc_irq(int irq, void *_dev) | ||
1053 | { | ||
1054 | struct s3c_hsudc *hsudc = _dev; | ||
1055 | struct s3c_hsudc_ep *hsep; | ||
1056 | u32 ep_intr; | ||
1057 | u32 sys_status; | ||
1058 | u32 ep_idx; | ||
1059 | |||
1060 | spin_lock(&hsudc->lock); | ||
1061 | |||
1062 | sys_status = readl(hsudc->regs + S3C_SSR); | ||
1063 | ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF; | ||
1064 | |||
1065 | if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) { | ||
1066 | spin_unlock(&hsudc->lock); | ||
1067 | return IRQ_HANDLED; | ||
1068 | } | ||
1069 | |||
1070 | if (sys_status) { | ||
1071 | if (sys_status & S3C_SSR_VBUSON) | ||
1072 | writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR); | ||
1073 | |||
1074 | if (sys_status & S3C_SSR_ERR) | ||
1075 | writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR); | ||
1076 | |||
1077 | if (sys_status & S3C_SSR_SDE) { | ||
1078 | writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR); | ||
1079 | hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ? | ||
1080 | USB_SPEED_HIGH : USB_SPEED_FULL; | ||
1081 | } | ||
1082 | |||
1083 | if (sys_status & S3C_SSR_SUSPEND) { | ||
1084 | writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR); | ||
1085 | if (hsudc->gadget.speed != USB_SPEED_UNKNOWN | ||
1086 | && hsudc->driver && hsudc->driver->suspend) | ||
1087 | hsudc->driver->suspend(&hsudc->gadget); | ||
1088 | } | ||
1089 | |||
1090 | if (sys_status & S3C_SSR_RESUME) { | ||
1091 | writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR); | ||
1092 | if (hsudc->gadget.speed != USB_SPEED_UNKNOWN | ||
1093 | && hsudc->driver && hsudc->driver->resume) | ||
1094 | hsudc->driver->resume(&hsudc->gadget); | ||
1095 | } | ||
1096 | |||
1097 | if (sys_status & S3C_SSR_RESET) { | ||
1098 | writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR); | ||
1099 | for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) { | ||
1100 | hsep = &hsudc->ep[ep_idx]; | ||
1101 | hsep->stopped = 1; | ||
1102 | s3c_hsudc_nuke_ep(hsep, -ECONNRESET); | ||
1103 | } | ||
1104 | s3c_hsudc_reconfig(hsudc); | ||
1105 | hsudc->ep0state = WAIT_FOR_SETUP; | ||
1106 | } | ||
1107 | } | ||
1108 | |||
1109 | if (ep_intr & S3C_EIR_EP0) { | ||
1110 | writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR); | ||
1111 | set_index(hsudc, 0); | ||
1112 | s3c_hsudc_handle_ep0_intr(hsudc); | ||
1113 | } | ||
1114 | |||
1115 | ep_intr >>= 1; | ||
1116 | ep_idx = 1; | ||
1117 | while (ep_intr) { | ||
1118 | if (ep_intr & 1) { | ||
1119 | hsep = &hsudc->ep[ep_idx]; | ||
1120 | set_index(hsudc, ep_idx); | ||
1121 | writel(1 << ep_idx, hsudc->regs + S3C_EIR); | ||
1122 | if (ep_is_in(hsep)) | ||
1123 | s3c_hsudc_epin_intr(hsudc, ep_idx); | ||
1124 | else | ||
1125 | s3c_hsudc_epout_intr(hsudc, ep_idx); | ||
1126 | } | ||
1127 | ep_intr >>= 1; | ||
1128 | ep_idx++; | ||
1129 | } | ||
1130 | |||
1131 | spin_unlock(&hsudc->lock); | ||
1132 | return IRQ_HANDLED; | ||
1133 | } | ||
1134 | |||
1135 | int usb_gadget_probe_driver(struct usb_gadget_driver *driver, | ||
1136 | int (*bind)(struct usb_gadget *)) | ||
1137 | { | ||
1138 | struct s3c_hsudc *hsudc = the_controller; | ||
1139 | int ret; | ||
1140 | |||
1141 | if (!driver | ||
1142 | || (driver->speed != USB_SPEED_FULL && | ||
1143 | driver->speed != USB_SPEED_HIGH) | ||
1144 | || !bind | ||
1145 | || !driver->unbind || !driver->disconnect || !driver->setup) | ||
1146 | return -EINVAL; | ||
1147 | |||
1148 | if (!hsudc) | ||
1149 | return -ENODEV; | ||
1150 | |||
1151 | if (hsudc->driver) | ||
1152 | return -EBUSY; | ||
1153 | |||
1154 | hsudc->driver = driver; | ||
1155 | hsudc->gadget.dev.driver = &driver->driver; | ||
1156 | hsudc->gadget.speed = USB_SPEED_UNKNOWN; | ||
1157 | ret = device_add(&hsudc->gadget.dev); | ||
1158 | if (ret) { | ||
1159 | dev_err(hsudc->dev, "failed to probe gadget device"); | ||
1160 | return ret; | ||
1161 | } | ||
1162 | |||
1163 | ret = bind(&hsudc->gadget); | ||
1164 | if (ret) { | ||
1165 | dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name); | ||
1166 | device_del(&hsudc->gadget.dev); | ||
1167 | |||
1168 | hsudc->driver = NULL; | ||
1169 | hsudc->gadget.dev.driver = NULL; | ||
1170 | return ret; | ||
1171 | } | ||
1172 | |||
1173 | enable_irq(hsudc->irq); | ||
1174 | dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); | ||
1175 | |||
1176 | s3c_hsudc_reconfig(hsudc); | ||
1177 | s3c_hsudc_init_phy(); | ||
1178 | if (hsudc->pd->gpio_init) | ||
1179 | hsudc->pd->gpio_init(); | ||
1180 | |||
1181 | return 0; | ||
1182 | } | ||
1183 | EXPORT_SYMBOL(usb_gadget_probe_driver); | ||
1184 | |||
1185 | int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) | ||
1186 | { | ||
1187 | struct s3c_hsudc *hsudc = the_controller; | ||
1188 | unsigned long flags; | ||
1189 | |||
1190 | if (!hsudc) | ||
1191 | return -ENODEV; | ||
1192 | |||
1193 | if (!driver || driver != hsudc->driver || !driver->unbind) | ||
1194 | return -EINVAL; | ||
1195 | |||
1196 | spin_lock_irqsave(&hsudc->lock, flags); | ||
1197 | hsudc->driver = 0; | ||
1198 | s3c_hsudc_uninit_phy(); | ||
1199 | if (hsudc->pd->gpio_uninit) | ||
1200 | hsudc->pd->gpio_uninit(); | ||
1201 | s3c_hsudc_stop_activity(hsudc, driver); | ||
1202 | spin_unlock_irqrestore(&hsudc->lock, flags); | ||
1203 | |||
1204 | driver->unbind(&hsudc->gadget); | ||
1205 | device_del(&hsudc->gadget.dev); | ||
1206 | disable_irq(hsudc->irq); | ||
1207 | |||
1208 | dev_info(hsudc->dev, "unregistered gadget driver '%s'\n", | ||
1209 | driver->driver.name); | ||
1210 | return 0; | ||
1211 | } | ||
1212 | EXPORT_SYMBOL(usb_gadget_unregister_driver); | ||
1213 | |||
1214 | static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc) | ||
1215 | { | ||
1216 | return readl(hsudc->regs + S3C_FNR) & 0x3FF; | ||
1217 | } | ||
1218 | |||
1219 | static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) | ||
1220 | { | ||
1221 | return s3c_hsudc_read_frameno(to_hsudc(gadget)); | ||
1222 | } | ||
1223 | |||
1224 | static struct usb_gadget_ops s3c_hsudc_gadget_ops = { | ||
1225 | .get_frame = s3c_hsudc_gadget_getframe, | ||
1226 | }; | ||
1227 | |||
1228 | static int s3c_hsudc_probe(struct platform_device *pdev) | ||
1229 | { | ||
1230 | struct device *dev = &pdev->dev; | ||
1231 | struct resource *res; | ||
1232 | struct s3c_hsudc *hsudc; | ||
1233 | struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data; | ||
1234 | int ret; | ||
1235 | |||
1236 | hsudc = kzalloc(sizeof(struct s3c_hsudc) + | ||
1237 | sizeof(struct s3c_hsudc_ep) * pd->epnum, | ||
1238 | GFP_KERNEL); | ||
1239 | if (!hsudc) { | ||
1240 | dev_err(dev, "cannot allocate memory\n"); | ||
1241 | return -ENOMEM; | ||
1242 | } | ||
1243 | |||
1244 | the_controller = hsudc; | ||
1245 | platform_set_drvdata(pdev, dev); | ||
1246 | hsudc->dev = dev; | ||
1247 | hsudc->pd = pdev->dev.platform_data; | ||
1248 | |||
1249 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1250 | if (!res) { | ||
1251 | dev_err(dev, "unable to obtain driver resource data\n"); | ||
1252 | ret = -ENODEV; | ||
1253 | goto err_res; | ||
1254 | } | ||
1255 | |||
1256 | hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res), | ||
1257 | dev_name(&pdev->dev)); | ||
1258 | if (!hsudc->mem_rsrc) { | ||
1259 | dev_err(dev, "failed to reserve register area\n"); | ||
1260 | ret = -ENODEV; | ||
1261 | goto err_res; | ||
1262 | } | ||
1263 | |||
1264 | hsudc->regs = ioremap(res->start, resource_size(res)); | ||
1265 | if (!hsudc->regs) { | ||
1266 | dev_err(dev, "error mapping device register area\n"); | ||
1267 | ret = -EBUSY; | ||
1268 | goto err_remap; | ||
1269 | } | ||
1270 | |||
1271 | ret = platform_get_irq(pdev, 0); | ||
1272 | if (ret < 0) { | ||
1273 | dev_err(dev, "unable to obtain IRQ number\n"); | ||
1274 | goto err_irq; | ||
1275 | } | ||
1276 | hsudc->irq = ret; | ||
1277 | |||
1278 | ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); | ||
1279 | if (ret < 0) { | ||
1280 | dev_err(dev, "irq request failed\n"); | ||
1281 | goto err_irq; | ||
1282 | } | ||
1283 | |||
1284 | spin_lock_init(&hsudc->lock); | ||
1285 | |||
1286 | device_initialize(&hsudc->gadget.dev); | ||
1287 | dev_set_name(&hsudc->gadget.dev, "gadget"); | ||
1288 | |||
1289 | hsudc->gadget.is_dualspeed = 1; | ||
1290 | hsudc->gadget.ops = &s3c_hsudc_gadget_ops; | ||
1291 | hsudc->gadget.name = dev_name(dev); | ||
1292 | hsudc->gadget.dev.parent = dev; | ||
1293 | hsudc->gadget.dev.dma_mask = dev->dma_mask; | ||
1294 | hsudc->gadget.ep0 = &hsudc->ep[0].ep; | ||
1295 | |||
1296 | hsudc->gadget.is_otg = 0; | ||
1297 | hsudc->gadget.is_a_peripheral = 0; | ||
1298 | |||
1299 | s3c_hsudc_setup_ep(hsudc); | ||
1300 | |||
1301 | hsudc->uclk = clk_get(&pdev->dev, "usb-device"); | ||
1302 | if (hsudc->uclk == NULL) { | ||
1303 | dev_err(dev, "failed to find usb-device clock source\n"); | ||
1304 | return -ENOENT; | ||
1305 | } | ||
1306 | clk_enable(hsudc->uclk); | ||
1307 | |||
1308 | local_irq_disable(); | ||
1309 | |||
1310 | disable_irq(hsudc->irq); | ||
1311 | local_irq_enable(); | ||
1312 | return 0; | ||
1313 | |||
1314 | err_irq: | ||
1315 | iounmap(hsudc->regs); | ||
1316 | |||
1317 | err_remap: | ||
1318 | release_resource(hsudc->mem_rsrc); | ||
1319 | kfree(hsudc->mem_rsrc); | ||
1320 | |||
1321 | err_res: | ||
1322 | kfree(hsudc); | ||
1323 | return ret; | ||
1324 | } | ||
1325 | |||
1326 | static struct platform_driver s3c_hsudc_driver = { | ||
1327 | .driver = { | ||
1328 | .owner = THIS_MODULE, | ||
1329 | .name = "s3c-hsudc", | ||
1330 | }, | ||
1331 | .probe = s3c_hsudc_probe, | ||
1332 | }; | ||
1333 | |||
1334 | static int __init s3c_hsudc_modinit(void) | ||
1335 | { | ||
1336 | return platform_driver_register(&s3c_hsudc_driver); | ||
1337 | } | ||
1338 | |||
1339 | static void __exit s3c_hsudc_modexit(void) | ||
1340 | { | ||
1341 | platform_driver_unregister(&s3c_hsudc_driver); | ||
1342 | } | ||
1343 | |||
1344 | module_init(s3c_hsudc_modinit); | ||
1345 | module_exit(s3c_hsudc_modexit); | ||
1346 | |||
1347 | MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver"); | ||
1348 | MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); | ||
1349 | MODULE_LICENSE("GPL"); | ||