Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
53f3a9e2 DV |
2 | /* |
3 | * USB SD Host Controller (USHC) controller driver. | |
4 | * | |
5 | * Copyright (C) 2010 Cambridge Silicon Radio Ltd. | |
6 | * | |
53f3a9e2 DV |
7 | * Notes: |
8 | * - Only version 2 devices are supported. | |
9 | * - Version 2 devices only support SDIO cards/devices (R2 response is | |
10 | * unsupported). | |
11 | * | |
12 | * References: | |
13 | * [USHC] USB SD Host Controller specification (CS-118793-SP) | |
14 | */ | |
15 | #include <linux/module.h> | |
16 | #include <linux/usb.h> | |
17 | #include <linux/kernel.h> | |
53f3a9e2 DV |
18 | #include <linux/slab.h> |
19 | #include <linux/dma-mapping.h> | |
20 | #include <linux/mmc/host.h> | |
21 | ||
22 | enum ushc_request { | |
23 | USHC_GET_CAPS = 0x00, | |
24 | USHC_HOST_CTRL = 0x01, | |
25 | USHC_PWR_CTRL = 0x02, | |
26 | USHC_CLK_FREQ = 0x03, | |
27 | USHC_EXEC_CMD = 0x04, | |
28 | USHC_READ_RESP = 0x05, | |
29 | USHC_RESET = 0x06, | |
30 | }; | |
31 | ||
32 | enum ushc_request_type { | |
33 | USHC_GET_CAPS_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
34 | USHC_HOST_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
35 | USHC_PWR_CTRL_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
36 | USHC_CLK_FREQ_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
37 | USHC_EXEC_CMD_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
38 | USHC_READ_RESP_TYPE = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
39 | USHC_RESET_TYPE = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
40 | }; | |
41 | ||
42 | #define USHC_GET_CAPS_VERSION_MASK 0xff | |
43 | #define USHC_GET_CAPS_3V3 (1 << 8) | |
44 | #define USHC_GET_CAPS_3V0 (1 << 9) | |
45 | #define USHC_GET_CAPS_1V8 (1 << 10) | |
46 | #define USHC_GET_CAPS_HIGH_SPD (1 << 16) | |
47 | ||
48 | #define USHC_HOST_CTRL_4BIT (1 << 1) | |
49 | #define USHC_HOST_CTRL_HIGH_SPD (1 << 0) | |
50 | ||
51 | #define USHC_PWR_CTRL_OFF 0x00 | |
52 | #define USHC_PWR_CTRL_3V3 0x01 | |
53 | #define USHC_PWR_CTRL_3V0 0x02 | |
54 | #define USHC_PWR_CTRL_1V8 0x03 | |
55 | ||
56 | #define USHC_READ_RESP_BUSY (1 << 4) | |
57 | #define USHC_READ_RESP_ERR_TIMEOUT (1 << 3) | |
58 | #define USHC_READ_RESP_ERR_CRC (1 << 2) | |
59 | #define USHC_READ_RESP_ERR_DAT (1 << 1) | |
60 | #define USHC_READ_RESP_ERR_CMD (1 << 0) | |
61 | #define USHC_READ_RESP_ERR_MASK 0x0f | |
62 | ||
63 | struct ushc_cbw { | |
64 | __u8 signature; | |
65 | __u8 cmd_idx; | |
66 | __le16 block_size; | |
67 | __le32 arg; | |
68 | } __attribute__((packed)); | |
69 | ||
70 | #define USHC_CBW_SIGNATURE 'C' | |
71 | ||
72 | struct ushc_csw { | |
73 | __u8 signature; | |
74 | __u8 status; | |
75 | __le32 response; | |
76 | } __attribute__((packed)); | |
77 | ||
78 | #define USHC_CSW_SIGNATURE 'S' | |
79 | ||
80 | struct ushc_int_data { | |
81 | u8 status; | |
82 | u8 reserved[3]; | |
83 | }; | |
84 | ||
85 | #define USHC_INT_STATUS_SDIO_INT (1 << 1) | |
86 | #define USHC_INT_STATUS_CARD_PRESENT (1 << 0) | |
87 | ||
88 | ||
89 | struct ushc_data { | |
90 | struct usb_device *usb_dev; | |
91 | struct mmc_host *mmc; | |
92 | ||
93 | struct urb *int_urb; | |
94 | struct ushc_int_data *int_data; | |
95 | ||
96 | struct urb *cbw_urb; | |
97 | struct ushc_cbw *cbw; | |
98 | ||
99 | struct urb *data_urb; | |
100 | ||
101 | struct urb *csw_urb; | |
102 | struct ushc_csw *csw; | |
103 | ||
104 | spinlock_t lock; | |
105 | struct mmc_request *current_req; | |
106 | u32 caps; | |
107 | u16 host_ctrl; | |
108 | unsigned long flags; | |
109 | u8 last_status; | |
110 | int clock_freq; | |
111 | }; | |
112 | ||
113 | #define DISCONNECTED 0 | |
114 | #define INT_EN 1 | |
115 | #define IGNORE_NEXT_INT 2 | |
116 | ||
117 | static void data_callback(struct urb *urb); | |
118 | ||
119 | static int ushc_hw_reset(struct ushc_data *ushc) | |
120 | { | |
121 | return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), | |
122 | USHC_RESET, USHC_RESET_TYPE, | |
123 | 0, 0, NULL, 0, 100); | |
124 | } | |
125 | ||
126 | static int ushc_hw_get_caps(struct ushc_data *ushc) | |
127 | { | |
128 | int ret; | |
129 | int version; | |
130 | ||
131 | ret = usb_control_msg(ushc->usb_dev, usb_rcvctrlpipe(ushc->usb_dev, 0), | |
132 | USHC_GET_CAPS, USHC_GET_CAPS_TYPE, | |
133 | 0, 0, &ushc->caps, sizeof(ushc->caps), 100); | |
134 | if (ret < 0) | |
135 | return ret; | |
136 | ||
137 | ushc->caps = le32_to_cpu(ushc->caps); | |
138 | ||
139 | version = ushc->caps & USHC_GET_CAPS_VERSION_MASK; | |
140 | if (version != 0x02) { | |
141 | dev_err(&ushc->usb_dev->dev, "controller version %d is not supported\n", version); | |
142 | return -EINVAL; | |
143 | } | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | static int ushc_hw_set_host_ctrl(struct ushc_data *ushc, u16 mask, u16 val) | |
149 | { | |
150 | u16 host_ctrl; | |
151 | int ret; | |
152 | ||
153 | host_ctrl = (ushc->host_ctrl & ~mask) | val; | |
154 | ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), | |
155 | USHC_HOST_CTRL, USHC_HOST_CTRL_TYPE, | |
156 | host_ctrl, 0, NULL, 0, 100); | |
157 | if (ret < 0) | |
158 | return ret; | |
159 | ushc->host_ctrl = host_ctrl; | |
160 | return 0; | |
161 | } | |
162 | ||
163 | static void int_callback(struct urb *urb) | |
164 | { | |
165 | struct ushc_data *ushc = urb->context; | |
166 | u8 status, last_status; | |
167 | ||
168 | if (urb->status < 0) | |
169 | return; | |
170 | ||
171 | status = ushc->int_data->status; | |
172 | last_status = ushc->last_status; | |
173 | ushc->last_status = status; | |
174 | ||
175 | /* | |
176 | * Ignore the card interrupt status on interrupt transfers that | |
177 | * were submitted while card interrupts where disabled. | |
178 | * | |
179 | * This avoid occasional spurious interrupts when enabling | |
180 | * interrupts immediately after clearing the source on the card. | |
181 | */ | |
182 | ||
183 | if (!test_and_clear_bit(IGNORE_NEXT_INT, &ushc->flags) | |
184 | && test_bit(INT_EN, &ushc->flags) | |
185 | && status & USHC_INT_STATUS_SDIO_INT) { | |
186 | mmc_signal_sdio_irq(ushc->mmc); | |
187 | } | |
188 | ||
189 | if ((status ^ last_status) & USHC_INT_STATUS_CARD_PRESENT) | |
190 | mmc_detect_change(ushc->mmc, msecs_to_jiffies(100)); | |
191 | ||
192 | if (!test_bit(INT_EN, &ushc->flags)) | |
193 | set_bit(IGNORE_NEXT_INT, &ushc->flags); | |
194 | usb_submit_urb(ushc->int_urb, GFP_ATOMIC); | |
195 | } | |
196 | ||
197 | static void cbw_callback(struct urb *urb) | |
198 | { | |
199 | struct ushc_data *ushc = urb->context; | |
200 | ||
201 | if (urb->status != 0) { | |
202 | usb_unlink_urb(ushc->data_urb); | |
203 | usb_unlink_urb(ushc->csw_urb); | |
204 | } | |
205 | } | |
206 | ||
207 | static void data_callback(struct urb *urb) | |
208 | { | |
209 | struct ushc_data *ushc = urb->context; | |
210 | ||
211 | if (urb->status != 0) | |
212 | usb_unlink_urb(ushc->csw_urb); | |
213 | } | |
214 | ||
215 | static void csw_callback(struct urb *urb) | |
216 | { | |
217 | struct ushc_data *ushc = urb->context; | |
218 | struct mmc_request *req = ushc->current_req; | |
219 | int status; | |
220 | ||
221 | status = ushc->csw->status; | |
222 | ||
223 | if (urb->status != 0) { | |
224 | req->cmd->error = urb->status; | |
225 | } else if (status & USHC_READ_RESP_ERR_CMD) { | |
226 | if (status & USHC_READ_RESP_ERR_CRC) | |
227 | req->cmd->error = -EIO; | |
228 | else | |
229 | req->cmd->error = -ETIMEDOUT; | |
230 | } | |
231 | if (req->data) { | |
232 | if (status & USHC_READ_RESP_ERR_DAT) { | |
233 | if (status & USHC_READ_RESP_ERR_CRC) | |
234 | req->data->error = -EIO; | |
235 | else | |
236 | req->data->error = -ETIMEDOUT; | |
237 | req->data->bytes_xfered = 0; | |
238 | } else { | |
239 | req->data->bytes_xfered = req->data->blksz * req->data->blocks; | |
240 | } | |
241 | } | |
242 | ||
243 | req->cmd->resp[0] = le32_to_cpu(ushc->csw->response); | |
244 | ||
245 | mmc_request_done(ushc->mmc, req); | |
246 | } | |
247 | ||
248 | static void ushc_request(struct mmc_host *mmc, struct mmc_request *req) | |
249 | { | |
250 | struct ushc_data *ushc = mmc_priv(mmc); | |
251 | int ret; | |
252 | unsigned long flags; | |
253 | ||
254 | spin_lock_irqsave(&ushc->lock, flags); | |
255 | ||
256 | if (test_bit(DISCONNECTED, &ushc->flags)) { | |
257 | ret = -ENODEV; | |
258 | goto out; | |
259 | } | |
260 | ||
261 | /* Version 2 firmware doesn't support the R2 response format. */ | |
262 | if (req->cmd->flags & MMC_RSP_136) { | |
263 | ret = -EINVAL; | |
264 | goto out; | |
265 | } | |
266 | ||
267 | /* The Astoria's data FIFOs don't work with clock speeds < 5MHz so | |
268 | limit commands with data to 6MHz or more. */ | |
269 | if (req->data && ushc->clock_freq < 6000000) { | |
270 | ret = -EINVAL; | |
271 | goto out; | |
272 | } | |
273 | ||
274 | ushc->current_req = req; | |
275 | ||
276 | /* Start cmd with CBW. */ | |
277 | ushc->cbw->cmd_idx = cpu_to_le16(req->cmd->opcode); | |
278 | if (req->data) | |
279 | ushc->cbw->block_size = cpu_to_le16(req->data->blksz); | |
280 | else | |
281 | ushc->cbw->block_size = 0; | |
282 | ushc->cbw->arg = cpu_to_le32(req->cmd->arg); | |
283 | ||
284 | ret = usb_submit_urb(ushc->cbw_urb, GFP_ATOMIC); | |
285 | if (ret < 0) | |
286 | goto out; | |
287 | ||
288 | /* Submit data (if any). */ | |
289 | if (req->data) { | |
290 | struct mmc_data *data = req->data; | |
291 | int pipe; | |
292 | ||
293 | if (data->flags & MMC_DATA_READ) | |
294 | pipe = usb_rcvbulkpipe(ushc->usb_dev, 6); | |
295 | else | |
296 | pipe = usb_sndbulkpipe(ushc->usb_dev, 2); | |
297 | ||
298 | usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe, | |
a4034173 | 299 | NULL, data->sg->length, |
53f3a9e2 | 300 | data_callback, ushc); |
a4034173 CH |
301 | ushc->data_urb->num_sgs = 1; |
302 | ushc->data_urb->sg = data->sg; | |
53f3a9e2 DV |
303 | ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC); |
304 | if (ret < 0) | |
305 | goto out; | |
306 | } | |
307 | ||
308 | /* Submit CSW. */ | |
309 | ret = usb_submit_urb(ushc->csw_urb, GFP_ATOMIC); | |
53f3a9e2 DV |
310 | |
311 | out: | |
312 | spin_unlock_irqrestore(&ushc->lock, flags); | |
313 | if (ret < 0) { | |
314 | usb_unlink_urb(ushc->cbw_urb); | |
315 | usb_unlink_urb(ushc->data_urb); | |
316 | req->cmd->error = ret; | |
317 | mmc_request_done(mmc, req); | |
318 | } | |
319 | } | |
320 | ||
321 | static int ushc_set_power(struct ushc_data *ushc, unsigned char power_mode) | |
322 | { | |
323 | u16 voltage; | |
324 | ||
325 | switch (power_mode) { | |
326 | case MMC_POWER_OFF: | |
327 | voltage = USHC_PWR_CTRL_OFF; | |
328 | break; | |
329 | case MMC_POWER_UP: | |
330 | case MMC_POWER_ON: | |
331 | voltage = USHC_PWR_CTRL_3V3; | |
332 | break; | |
333 | default: | |
334 | return -EINVAL; | |
335 | } | |
336 | ||
337 | return usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), | |
338 | USHC_PWR_CTRL, USHC_PWR_CTRL_TYPE, | |
339 | voltage, 0, NULL, 0, 100); | |
340 | } | |
341 | ||
342 | static int ushc_set_bus_width(struct ushc_data *ushc, int bus_width) | |
343 | { | |
344 | return ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_4BIT, | |
345 | bus_width == 4 ? USHC_HOST_CTRL_4BIT : 0); | |
346 | } | |
347 | ||
348 | static int ushc_set_bus_freq(struct ushc_data *ushc, int clk, bool enable_hs) | |
349 | { | |
350 | int ret; | |
351 | ||
352 | /* Hardware can't detect interrupts while the clock is off. */ | |
353 | if (clk == 0) | |
354 | clk = 400000; | |
355 | ||
356 | ret = ushc_hw_set_host_ctrl(ushc, USHC_HOST_CTRL_HIGH_SPD, | |
357 | enable_hs ? USHC_HOST_CTRL_HIGH_SPD : 0); | |
358 | if (ret < 0) | |
359 | return ret; | |
360 | ||
361 | ret = usb_control_msg(ushc->usb_dev, usb_sndctrlpipe(ushc->usb_dev, 0), | |
362 | USHC_CLK_FREQ, USHC_CLK_FREQ_TYPE, | |
363 | clk & 0xffff, (clk >> 16) & 0xffff, NULL, 0, 100); | |
364 | if (ret < 0) | |
365 | return ret; | |
366 | ||
367 | ushc->clock_freq = clk; | |
368 | return 0; | |
369 | } | |
370 | ||
371 | static void ushc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |
372 | { | |
373 | struct ushc_data *ushc = mmc_priv(mmc); | |
374 | ||
375 | ushc_set_power(ushc, ios->power_mode); | |
376 | ushc_set_bus_width(ushc, 1 << ios->bus_width); | |
377 | ushc_set_bus_freq(ushc, ios->clock, ios->timing == MMC_TIMING_SD_HS); | |
378 | } | |
379 | ||
380 | static int ushc_get_cd(struct mmc_host *mmc) | |
381 | { | |
382 | struct ushc_data *ushc = mmc_priv(mmc); | |
383 | ||
384 | return !!(ushc->last_status & USHC_INT_STATUS_CARD_PRESENT); | |
385 | } | |
386 | ||
387 | static void ushc_enable_sdio_irq(struct mmc_host *mmc, int enable) | |
388 | { | |
389 | struct ushc_data *ushc = mmc_priv(mmc); | |
390 | ||
391 | if (enable) | |
392 | set_bit(INT_EN, &ushc->flags); | |
393 | else | |
394 | clear_bit(INT_EN, &ushc->flags); | |
395 | } | |
396 | ||
397 | static void ushc_clean_up(struct ushc_data *ushc) | |
398 | { | |
399 | usb_free_urb(ushc->int_urb); | |
400 | usb_free_urb(ushc->csw_urb); | |
401 | usb_free_urb(ushc->data_urb); | |
402 | usb_free_urb(ushc->cbw_urb); | |
403 | ||
404 | kfree(ushc->int_data); | |
405 | kfree(ushc->cbw); | |
406 | kfree(ushc->csw); | |
407 | ||
408 | mmc_free_host(ushc->mmc); | |
409 | } | |
410 | ||
411 | static const struct mmc_host_ops ushc_ops = { | |
412 | .request = ushc_request, | |
413 | .set_ios = ushc_set_ios, | |
414 | .get_cd = ushc_get_cd, | |
415 | .enable_sdio_irq = ushc_enable_sdio_irq, | |
416 | }; | |
417 | ||
418 | static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id) | |
419 | { | |
420 | struct usb_device *usb_dev = interface_to_usbdev(intf); | |
421 | struct mmc_host *mmc; | |
422 | struct ushc_data *ushc; | |
14d4031d | 423 | int ret; |
53f3a9e2 | 424 | |
181302dc JH |
425 | if (intf->cur_altsetting->desc.bNumEndpoints < 1) |
426 | return -ENODEV; | |
427 | ||
53f3a9e2 DV |
428 | mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); |
429 | if (mmc == NULL) | |
430 | return -ENOMEM; | |
431 | ushc = mmc_priv(mmc); | |
432 | usb_set_intfdata(intf, ushc); | |
433 | ||
434 | ushc->usb_dev = usb_dev; | |
435 | ushc->mmc = mmc; | |
436 | ||
437 | spin_lock_init(&ushc->lock); | |
438 | ||
439 | ret = ushc_hw_reset(ushc); | |
440 | if (ret < 0) | |
441 | goto err; | |
442 | ||
443 | /* Read capabilities. */ | |
444 | ret = ushc_hw_get_caps(ushc); | |
445 | if (ret < 0) | |
446 | goto err; | |
447 | ||
448 | mmc->ops = &ushc_ops; | |
449 | ||
450 | mmc->f_min = 400000; | |
451 | mmc->f_max = 50000000; | |
452 | mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; | |
453 | mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; | |
454 | mmc->caps |= (ushc->caps & USHC_GET_CAPS_HIGH_SPD) ? MMC_CAP_SD_HIGHSPEED : 0; | |
455 | ||
456 | mmc->max_seg_size = 512*511; | |
457 | mmc->max_segs = 1; | |
458 | mmc->max_req_size = 512*511; | |
459 | mmc->max_blk_size = 512; | |
460 | mmc->max_blk_count = 511; | |
461 | ||
462 | ushc->int_urb = usb_alloc_urb(0, GFP_KERNEL); | |
14d4031d AL |
463 | if (ushc->int_urb == NULL) { |
464 | ret = -ENOMEM; | |
53f3a9e2 | 465 | goto err; |
14d4031d | 466 | } |
53f3a9e2 | 467 | ushc->int_data = kzalloc(sizeof(struct ushc_int_data), GFP_KERNEL); |
14d4031d AL |
468 | if (ushc->int_data == NULL) { |
469 | ret = -ENOMEM; | |
53f3a9e2 | 470 | goto err; |
14d4031d | 471 | } |
53f3a9e2 DV |
472 | usb_fill_int_urb(ushc->int_urb, ushc->usb_dev, |
473 | usb_rcvintpipe(usb_dev, | |
474 | intf->cur_altsetting->endpoint[0].desc.bEndpointAddress), | |
475 | ushc->int_data, sizeof(struct ushc_int_data), | |
476 | int_callback, ushc, | |
477 | intf->cur_altsetting->endpoint[0].desc.bInterval); | |
478 | ||
479 | ushc->cbw_urb = usb_alloc_urb(0, GFP_KERNEL); | |
14d4031d AL |
480 | if (ushc->cbw_urb == NULL) { |
481 | ret = -ENOMEM; | |
53f3a9e2 | 482 | goto err; |
14d4031d | 483 | } |
53f3a9e2 | 484 | ushc->cbw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); |
14d4031d AL |
485 | if (ushc->cbw == NULL) { |
486 | ret = -ENOMEM; | |
53f3a9e2 | 487 | goto err; |
14d4031d | 488 | } |
53f3a9e2 DV |
489 | ushc->cbw->signature = USHC_CBW_SIGNATURE; |
490 | ||
491 | usb_fill_bulk_urb(ushc->cbw_urb, ushc->usb_dev, usb_sndbulkpipe(usb_dev, 2), | |
492 | ushc->cbw, sizeof(struct ushc_cbw), | |
493 | cbw_callback, ushc); | |
494 | ||
495 | ushc->data_urb = usb_alloc_urb(0, GFP_KERNEL); | |
14d4031d AL |
496 | if (ushc->data_urb == NULL) { |
497 | ret = -ENOMEM; | |
53f3a9e2 | 498 | goto err; |
14d4031d | 499 | } |
53f3a9e2 DV |
500 | |
501 | ushc->csw_urb = usb_alloc_urb(0, GFP_KERNEL); | |
14d4031d AL |
502 | if (ushc->csw_urb == NULL) { |
503 | ret = -ENOMEM; | |
53f3a9e2 | 504 | goto err; |
14d4031d | 505 | } |
c4c7fb19 | 506 | ushc->csw = kzalloc(sizeof(struct ushc_csw), GFP_KERNEL); |
14d4031d AL |
507 | if (ushc->csw == NULL) { |
508 | ret = -ENOMEM; | |
53f3a9e2 | 509 | goto err; |
14d4031d | 510 | } |
53f3a9e2 DV |
511 | usb_fill_bulk_urb(ushc->csw_urb, ushc->usb_dev, usb_rcvbulkpipe(usb_dev, 6), |
512 | ushc->csw, sizeof(struct ushc_csw), | |
513 | csw_callback, ushc); | |
514 | ||
515 | ret = mmc_add_host(ushc->mmc); | |
516 | if (ret) | |
517 | goto err; | |
518 | ||
519 | ret = usb_submit_urb(ushc->int_urb, GFP_KERNEL); | |
520 | if (ret < 0) { | |
521 | mmc_remove_host(ushc->mmc); | |
522 | goto err; | |
523 | } | |
524 | ||
525 | return 0; | |
526 | ||
527 | err: | |
528 | ushc_clean_up(ushc); | |
529 | return ret; | |
530 | } | |
531 | ||
532 | static void ushc_disconnect(struct usb_interface *intf) | |
533 | { | |
534 | struct ushc_data *ushc = usb_get_intfdata(intf); | |
535 | ||
536 | spin_lock_irq(&ushc->lock); | |
537 | set_bit(DISCONNECTED, &ushc->flags); | |
538 | spin_unlock_irq(&ushc->lock); | |
539 | ||
540 | usb_kill_urb(ushc->int_urb); | |
541 | usb_kill_urb(ushc->cbw_urb); | |
542 | usb_kill_urb(ushc->data_urb); | |
543 | usb_kill_urb(ushc->csw_urb); | |
544 | ||
545 | mmc_remove_host(ushc->mmc); | |
546 | ||
547 | ushc_clean_up(ushc); | |
548 | } | |
549 | ||
550 | static struct usb_device_id ushc_id_table[] = { | |
551 | /* CSR USB SD Host Controller */ | |
552 | { USB_DEVICE(0x0a12, 0x5d10) }, | |
553 | { }, | |
554 | }; | |
555 | MODULE_DEVICE_TABLE(usb, ushc_id_table); | |
556 | ||
557 | static struct usb_driver ushc_driver = { | |
558 | .name = "ushc", | |
559 | .id_table = ushc_id_table, | |
560 | .probe = ushc_probe, | |
561 | .disconnect = ushc_disconnect, | |
562 | }; | |
563 | ||
fe748483 | 564 | module_usb_driver(ushc_driver); |
53f3a9e2 DV |
565 | |
566 | MODULE_DESCRIPTION("USB SD Host Controller driver"); | |
567 | MODULE_AUTHOR("David Vrabel <david.vrabel@csr.com>"); | |
568 | MODULE_LICENSE("GPL"); |