diff options
author | wwang <wei_wang@realsil.com.cn> | 2011-01-14 03:53:34 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-01-22 22:40:10 -0500 |
commit | 50a6cb932d5cccc6a165219f137b87ea596b4cd0 (patch) | |
tree | c600f75616d700219ae6727c142c76940fba974d /drivers | |
parent | f6c259a39fd7bb8db6661690976a0f05d12b707d (diff) |
USB: usb_storage: add ums-realtek driver
ums_realtek is used to support the power-saving function
for Realtek RTS51xx USB card readers.
Signed-off-by: wwang <wei_wang@realsil.com.cn>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/storage/Kconfig | 10 | ||||
-rw-r--r-- | drivers/usb/storage/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/storage/realtek_cr.c | 675 | ||||
-rw-r--r-- | drivers/usb/storage/unusual_realtek.h | 41 | ||||
-rw-r--r-- | drivers/usb/storage/usual-tables.c | 1 |
5 files changed, 729 insertions, 0 deletions
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 49a489e03716..353aeb44da6a 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig | |||
@@ -31,6 +31,16 @@ config USB_STORAGE_DEBUG | |||
31 | Say Y here in order to have the USB Mass Storage code generate | 31 | Say Y here in order to have the USB Mass Storage code generate |
32 | verbose debugging messages. | 32 | verbose debugging messages. |
33 | 33 | ||
34 | config USB_STORAGE_REALTEK | ||
35 | tristate "Realtek Card Reader support" | ||
36 | depends on USB_STORAGE | ||
37 | help | ||
38 | Say Y here to include additional code to support the power-saving function | ||
39 | for Realtek RTS51xx USB card readers. | ||
40 | |||
41 | If this driver is compiled as a module, it will be named ums-realtek. | ||
42 | |||
43 | |||
34 | config USB_STORAGE_DATAFAB | 44 | config USB_STORAGE_DATAFAB |
35 | tristate "Datafab Compact Flash Reader support" | 45 | tristate "Datafab Compact Flash Reader support" |
36 | depends on USB_STORAGE | 46 | depends on USB_STORAGE |
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index fcf14cdc4a04..0d2de971bd91 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile | |||
@@ -30,6 +30,7 @@ obj-$(CONFIG_USB_STORAGE_ISD200) += ums-isd200.o | |||
30 | obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += ums-jumpshot.o | 30 | obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += ums-jumpshot.o |
31 | obj-$(CONFIG_USB_STORAGE_KARMA) += ums-karma.o | 31 | obj-$(CONFIG_USB_STORAGE_KARMA) += ums-karma.o |
32 | obj-$(CONFIG_USB_STORAGE_ONETOUCH) += ums-onetouch.o | 32 | obj-$(CONFIG_USB_STORAGE_ONETOUCH) += ums-onetouch.o |
33 | obj-$(CONFIG_USB_STORAGE_REALTEK) += ums-realtek.o | ||
33 | obj-$(CONFIG_USB_STORAGE_SDDR09) += ums-sddr09.o | 34 | obj-$(CONFIG_USB_STORAGE_SDDR09) += ums-sddr09.o |
34 | obj-$(CONFIG_USB_STORAGE_SDDR55) += ums-sddr55.o | 35 | obj-$(CONFIG_USB_STORAGE_SDDR55) += ums-sddr55.o |
35 | obj-$(CONFIG_USB_STORAGE_USBAT) += ums-usbat.o | 36 | obj-$(CONFIG_USB_STORAGE_USBAT) += ums-usbat.o |
@@ -42,6 +43,7 @@ ums-isd200-y := isd200.o | |||
42 | ums-jumpshot-y := jumpshot.o | 43 | ums-jumpshot-y := jumpshot.o |
43 | ums-karma-y := karma.o | 44 | ums-karma-y := karma.o |
44 | ums-onetouch-y := onetouch.o | 45 | ums-onetouch-y := onetouch.o |
46 | ums-realtek-y := realtek_cr.o | ||
45 | ums-sddr09-y := sddr09.o | 47 | ums-sddr09-y := sddr09.o |
46 | ums-sddr55-y := sddr55.o | 48 | ums-sddr55-y := sddr55.o |
47 | ums-usbat-y := shuttle_usbat.o | 49 | ums-usbat-y := shuttle_usbat.o |
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c new file mode 100644 index 000000000000..c2bebb3731d3 --- /dev/null +++ b/drivers/usb/storage/realtek_cr.c | |||
@@ -0,0 +1,675 @@ | |||
1 | /* Driver for Realtek RTS51xx USB card reader | ||
2 | * | ||
3 | * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2, or (at your option) any | ||
8 | * later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, see <http://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | * Author: | ||
19 | * wwang (wei_wang@realsil.com.cn) | ||
20 | * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/blkdev.h> | ||
25 | #include <linux/kthread.h> | ||
26 | #include <linux/sched.h> | ||
27 | #include <linux/workqueue.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/version.h> | ||
30 | |||
31 | #include <scsi/scsi.h> | ||
32 | #include <scsi/scsi_cmnd.h> | ||
33 | #include <scsi/scsi_device.h> | ||
34 | #include <linux/cdrom.h> | ||
35 | |||
36 | #include <linux/usb.h> | ||
37 | #include <linux/slab.h> | ||
38 | #include <linux/usb_usual.h> | ||
39 | |||
40 | #include "usb.h" | ||
41 | #include "transport.h" | ||
42 | #include "protocol.h" | ||
43 | #include "debug.h" | ||
44 | |||
45 | MODULE_DESCRIPTION("Driver for Realtek USB Card Reader"); | ||
46 | MODULE_AUTHOR("wwang <wei_wang@realsil.com.cn>"); | ||
47 | MODULE_LICENSE("GPL"); | ||
48 | MODULE_VERSION("1.03"); | ||
49 | |||
50 | static int auto_delink_en = 1; | ||
51 | module_param(auto_delink_en, int, S_IRUGO | S_IWUSR); | ||
52 | MODULE_PARM_DESC(auto_delink_en, "enable auto delink"); | ||
53 | |||
54 | struct rts51x_status { | ||
55 | u16 vid; | ||
56 | u16 pid; | ||
57 | u8 cur_lun; | ||
58 | u8 card_type; | ||
59 | u8 total_lun; | ||
60 | u16 fw_ver; | ||
61 | u8 phy_exist; | ||
62 | u8 multi_flag; | ||
63 | u8 multi_card; | ||
64 | u8 log_exist; | ||
65 | union { | ||
66 | u8 detailed_type1; | ||
67 | u8 detailed_type2; | ||
68 | } detailed_type; | ||
69 | u8 function[2]; | ||
70 | }; | ||
71 | |||
72 | struct rts51x_chip { | ||
73 | u16 vendor_id; | ||
74 | u16 product_id; | ||
75 | char max_lun; | ||
76 | |||
77 | struct rts51x_status *status; | ||
78 | int status_len; | ||
79 | |||
80 | u32 flag; | ||
81 | }; | ||
82 | |||
83 | /* flag definition */ | ||
84 | #define FLIDX_AUTO_DELINK 0x01 | ||
85 | |||
86 | #define SCSI_LUN(srb) ((srb)->device->lun) | ||
87 | |||
88 | /* Bit Operation */ | ||
89 | #define SET_BIT(data, idx) ((data) |= 1 << (idx)) | ||
90 | #define CLR_BIT(data, idx) ((data) &= ~(1 << (idx))) | ||
91 | #define CHK_BIT(data, idx) ((data) & (1 << (idx))) | ||
92 | |||
93 | #define SET_AUTO_DELINK(chip) ((chip)->flag |= FLIDX_AUTO_DELINK) | ||
94 | #define CLR_AUTO_DELINK(chip) ((chip)->flag &= ~FLIDX_AUTO_DELINK) | ||
95 | #define CHK_AUTO_DELINK(chip) ((chip)->flag & FLIDX_AUTO_DELINK) | ||
96 | |||
97 | #define RTS51X_GET_VID(chip) ((chip)->vendor_id) | ||
98 | #define RTS51X_GET_PID(chip) ((chip)->product_id) | ||
99 | |||
100 | #define FW_VERSION(chip) ((chip)->status[0].fw_ver) | ||
101 | #define STATUS_LEN(chip) ((chip)->status_len) | ||
102 | |||
103 | /* Check card reader function */ | ||
104 | #define SUPPORT_DETAILED_TYPE1(chip) \ | ||
105 | CHK_BIT((chip)->status[0].function[0], 1) | ||
106 | #define SUPPORT_OT(chip) \ | ||
107 | CHK_BIT((chip)->status[0].function[0], 2) | ||
108 | #define SUPPORT_OC(chip) \ | ||
109 | CHK_BIT((chip)->status[0].function[0], 3) | ||
110 | #define SUPPORT_AUTO_DELINK(chip) \ | ||
111 | CHK_BIT((chip)->status[0].function[0], 4) | ||
112 | #define SUPPORT_SDIO(chip) \ | ||
113 | CHK_BIT((chip)->status[0].function[1], 0) | ||
114 | #define SUPPORT_DETAILED_TYPE2(chip) \ | ||
115 | CHK_BIT((chip)->status[0].function[1], 1) | ||
116 | |||
117 | #define CHECK_PID(chip, pid) (RTS51X_GET_PID(chip) == (pid)) | ||
118 | #define CHECK_FW_VER(chip, fw_ver) (FW_VERSION(chip) == (fw_ver)) | ||
119 | #define CHECK_ID(chip, pid, fw_ver) \ | ||
120 | (CHECK_PID((chip), (pid)) && CHECK_FW_VER((chip), (fw_ver))) | ||
121 | |||
122 | #define wait_timeout_x(task_state, msecs) \ | ||
123 | do { \ | ||
124 | set_current_state((task_state)); \ | ||
125 | schedule_timeout((msecs) * HZ / 1000); \ | ||
126 | } while (0) | ||
127 | |||
128 | #define wait_timeout(msecs) \ | ||
129 | wait_timeout_x(TASK_INTERRUPTIBLE, (msecs)) | ||
130 | |||
131 | static int init_realtek_cr(struct us_data *us); | ||
132 | |||
133 | /* | ||
134 | * The table of devices | ||
135 | */ | ||
136 | #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ | ||
137 | vendorName, productName, useProtocol, useTransport, \ | ||
138 | initFunction, flags) \ | ||
139 | {\ | ||
140 | USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ | ||
141 | .driver_info = (flags)|(USB_US_TYPE_STOR<<24)\ | ||
142 | } | ||
143 | |||
144 | struct usb_device_id realtek_cr_ids[] = { | ||
145 | # include "unusual_realtek.h" | ||
146 | { } /* Terminating entry */ | ||
147 | }; | ||
148 | MODULE_DEVICE_TABLE(usb, realtek_cr_ids); | ||
149 | |||
150 | #undef UNUSUAL_DEV | ||
151 | |||
152 | /* | ||
153 | * The flags table | ||
154 | */ | ||
155 | #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ | ||
156 | vendor_name, product_name, use_protocol, use_transport, \ | ||
157 | init_function, Flags) \ | ||
158 | { \ | ||
159 | .vendorName = vendor_name, \ | ||
160 | .productName = product_name, \ | ||
161 | .useProtocol = use_protocol, \ | ||
162 | .useTransport = use_transport, \ | ||
163 | .initFunction = init_function, \ | ||
164 | } | ||
165 | |||
166 | static struct us_unusual_dev realtek_cr_unusual_dev_list[] = { | ||
167 | # include "unusual_realtek.h" | ||
168 | { } /* Terminating entry */ | ||
169 | }; | ||
170 | |||
171 | #undef UNUSUAL_DEV | ||
172 | |||
173 | static int rts51x_bulk_transport(struct us_data *us, u8 lun, | ||
174 | u8 *cmd, int cmd_len, u8 *buf, int buf_len, | ||
175 | enum dma_data_direction dir, int *act_len) | ||
176 | { | ||
177 | struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; | ||
178 | struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; | ||
179 | int result; | ||
180 | unsigned int residue; | ||
181 | unsigned int cswlen; | ||
182 | unsigned int cbwlen = US_BULK_CB_WRAP_LEN; | ||
183 | |||
184 | /* set up the command wrapper */ | ||
185 | bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); | ||
186 | bcb->DataTransferLength = cpu_to_le32(buf_len); | ||
187 | bcb->Flags = (dir == DMA_FROM_DEVICE) ? 1 << 7 : 0; | ||
188 | bcb->Tag = ++us->tag; | ||
189 | bcb->Lun = lun; | ||
190 | bcb->Length = cmd_len; | ||
191 | |||
192 | /* copy the command payload */ | ||
193 | memset(bcb->CDB, 0, sizeof(bcb->CDB)); | ||
194 | memcpy(bcb->CDB, cmd, bcb->Length); | ||
195 | |||
196 | /* send it to out endpoint */ | ||
197 | result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, | ||
198 | bcb, cbwlen, NULL); | ||
199 | if (result != USB_STOR_XFER_GOOD) | ||
200 | return USB_STOR_TRANSPORT_ERROR; | ||
201 | |||
202 | /* DATA STAGE */ | ||
203 | /* send/receive data payload, if there is any */ | ||
204 | |||
205 | if (buf && buf_len) { | ||
206 | unsigned int pipe = (dir == DMA_FROM_DEVICE) ? | ||
207 | us->recv_bulk_pipe : us->send_bulk_pipe; | ||
208 | result = usb_stor_bulk_transfer_buf(us, pipe, | ||
209 | buf, buf_len, NULL); | ||
210 | if (result == USB_STOR_XFER_ERROR) | ||
211 | return USB_STOR_TRANSPORT_ERROR; | ||
212 | } | ||
213 | |||
214 | /* get CSW for device status */ | ||
215 | result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe, | ||
216 | bcs, US_BULK_CS_WRAP_LEN, &cswlen); | ||
217 | if (result != USB_STOR_XFER_GOOD) | ||
218 | return USB_STOR_TRANSPORT_ERROR; | ||
219 | |||
220 | /* check bulk status */ | ||
221 | if (bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN)) { | ||
222 | US_DEBUGP("Signature mismatch: got %08X, expecting %08X\n", | ||
223 | le32_to_cpu(bcs->Signature), | ||
224 | US_BULK_CS_SIGN); | ||
225 | return USB_STOR_TRANSPORT_ERROR; | ||
226 | } | ||
227 | |||
228 | residue = bcs->Residue; | ||
229 | if (bcs->Tag != us->tag) | ||
230 | return USB_STOR_TRANSPORT_ERROR; | ||
231 | |||
232 | /* try to compute the actual residue, based on how much data | ||
233 | * was really transferred and what the device tells us */ | ||
234 | if (residue) | ||
235 | residue = residue < buf_len ? residue : buf_len; | ||
236 | |||
237 | if (act_len) | ||
238 | *act_len = buf_len - residue; | ||
239 | |||
240 | /* based on the status code, we report good or bad */ | ||
241 | switch (bcs->Status) { | ||
242 | case US_BULK_STAT_OK: | ||
243 | /* command good -- note that data could be short */ | ||
244 | return USB_STOR_TRANSPORT_GOOD; | ||
245 | |||
246 | case US_BULK_STAT_FAIL: | ||
247 | /* command failed */ | ||
248 | return USB_STOR_TRANSPORT_FAILED; | ||
249 | |||
250 | case US_BULK_STAT_PHASE: | ||
251 | /* phase error -- note that a transport reset will be | ||
252 | * invoked by the invoke_transport() function | ||
253 | */ | ||
254 | return USB_STOR_TRANSPORT_ERROR; | ||
255 | } | ||
256 | |||
257 | /* we should never get here, but if we do, we're in trouble */ | ||
258 | return USB_STOR_TRANSPORT_ERROR; | ||
259 | } | ||
260 | |||
261 | /* Determine what the maximum LUN supported is */ | ||
262 | static int rts51x_get_max_lun(struct us_data *us) | ||
263 | { | ||
264 | int result; | ||
265 | |||
266 | /* issue the command */ | ||
267 | us->iobuf[0] = 0; | ||
268 | result = usb_stor_control_msg(us, us->recv_ctrl_pipe, | ||
269 | US_BULK_GET_MAX_LUN, | ||
270 | USB_DIR_IN | USB_TYPE_CLASS | | ||
271 | USB_RECIP_INTERFACE, | ||
272 | 0, us->ifnum, us->iobuf, 1, 10*HZ); | ||
273 | |||
274 | US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", | ||
275 | result, us->iobuf[0]); | ||
276 | |||
277 | /* if we have a successful request, return the result */ | ||
278 | if (result > 0) | ||
279 | return us->iobuf[0]; | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int rts51x_read_mem(struct us_data *us, u16 addr, u8 *data, u16 len) | ||
285 | { | ||
286 | int retval; | ||
287 | u8 cmnd[12] = {0}; | ||
288 | |||
289 | US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); | ||
290 | |||
291 | cmnd[0] = 0xF0; | ||
292 | cmnd[1] = 0x0D; | ||
293 | cmnd[2] = (u8)(addr >> 8); | ||
294 | cmnd[3] = (u8)addr; | ||
295 | cmnd[4] = (u8)(len >> 8); | ||
296 | cmnd[5] = (u8)len; | ||
297 | |||
298 | retval = rts51x_bulk_transport(us, 0, cmnd, 12, | ||
299 | data, len, DMA_FROM_DEVICE, NULL); | ||
300 | if (retval != USB_STOR_TRANSPORT_GOOD) | ||
301 | return -EIO; | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | static int rts51x_write_mem(struct us_data *us, u16 addr, u8 *data, u16 len) | ||
307 | { | ||
308 | int retval; | ||
309 | u8 cmnd[12] = {0}; | ||
310 | |||
311 | US_DEBUGP("%s, addr = 0x%x, len = %d\n", __func__, addr, len); | ||
312 | |||
313 | cmnd[0] = 0xF0; | ||
314 | cmnd[1] = 0x0E; | ||
315 | cmnd[2] = (u8)(addr >> 8); | ||
316 | cmnd[3] = (u8)addr; | ||
317 | cmnd[4] = (u8)(len >> 8); | ||
318 | cmnd[5] = (u8)len; | ||
319 | |||
320 | retval = rts51x_bulk_transport(us, 0, cmnd, 12, | ||
321 | data, len, DMA_TO_DEVICE, NULL); | ||
322 | if (retval != USB_STOR_TRANSPORT_GOOD) | ||
323 | return -EIO; | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static int rts51x_read_status(struct us_data *us, | ||
329 | u8 lun, u8 *status, int len, int *actlen) | ||
330 | { | ||
331 | int retval; | ||
332 | u8 cmnd[12] = {0}; | ||
333 | |||
334 | US_DEBUGP("%s, lun = %d\n", __func__, lun); | ||
335 | |||
336 | cmnd[0] = 0xF0; | ||
337 | cmnd[1] = 0x09; | ||
338 | |||
339 | retval = rts51x_bulk_transport(us, lun, cmnd, 12, | ||
340 | status, len, DMA_FROM_DEVICE, actlen); | ||
341 | if (retval != USB_STOR_TRANSPORT_GOOD) | ||
342 | return -EIO; | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static int rts51x_check_status(struct us_data *us, u8 lun) | ||
348 | { | ||
349 | struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); | ||
350 | int retval; | ||
351 | u8 buf[16]; | ||
352 | |||
353 | retval = rts51x_read_status(us, lun, buf, 16, &(chip->status_len)); | ||
354 | if (retval < 0) | ||
355 | return -EIO; | ||
356 | |||
357 | US_DEBUGP("chip->status_len = %d\n", chip->status_len); | ||
358 | |||
359 | chip->status[lun].vid = ((u16)buf[0] << 8) | buf[1]; | ||
360 | chip->status[lun].pid = ((u16)buf[2] << 8) | buf[3]; | ||
361 | chip->status[lun].cur_lun = buf[4]; | ||
362 | chip->status[lun].card_type = buf[5]; | ||
363 | chip->status[lun].total_lun = buf[6]; | ||
364 | chip->status[lun].fw_ver = ((u16)buf[7] << 8) | buf[8]; | ||
365 | chip->status[lun].phy_exist = buf[9]; | ||
366 | chip->status[lun].multi_flag = buf[10]; | ||
367 | chip->status[lun].multi_card = buf[11]; | ||
368 | chip->status[lun].log_exist = buf[12]; | ||
369 | if (chip->status_len == 16) { | ||
370 | chip->status[lun].detailed_type.detailed_type1 = buf[13]; | ||
371 | chip->status[lun].function[0] = buf[14]; | ||
372 | chip->status[lun].function[1] = buf[15]; | ||
373 | } | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | static int enable_oscillator(struct us_data *us) | ||
379 | { | ||
380 | int retval; | ||
381 | u8 value; | ||
382 | |||
383 | retval = rts51x_read_mem(us, 0xFE77, &value, 1); | ||
384 | if (retval < 0) | ||
385 | return -EIO; | ||
386 | |||
387 | value |= 0x04; | ||
388 | retval = rts51x_write_mem(us, 0xFE77, &value, 1); | ||
389 | if (retval < 0) | ||
390 | return -EIO; | ||
391 | |||
392 | retval = rts51x_read_mem(us, 0xFE77, &value, 1); | ||
393 | if (retval < 0) | ||
394 | return -EIO; | ||
395 | |||
396 | if (!(value & 0x04)) | ||
397 | return -EIO; | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int do_config_autodelink(struct us_data *us, int enable, int force) | ||
403 | { | ||
404 | int retval; | ||
405 | u8 value; | ||
406 | |||
407 | retval = rts51x_read_mem(us, 0xFE47, &value, 1); | ||
408 | if (retval < 0) | ||
409 | return -EIO; | ||
410 | |||
411 | if (enable) { | ||
412 | if (force) | ||
413 | value |= 0x03; | ||
414 | else | ||
415 | value |= 0x01; | ||
416 | } else { | ||
417 | value &= ~0x03; | ||
418 | } | ||
419 | |||
420 | US_DEBUGP("In %s,set 0xfe47 to 0x%x\n", __func__, value); | ||
421 | |||
422 | retval = rts51x_write_mem(us, 0xFE47, &value, 1); | ||
423 | if (retval < 0) | ||
424 | return -EIO; | ||
425 | |||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | static int config_autodelink_after_power_on(struct us_data *us) | ||
430 | { | ||
431 | struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); | ||
432 | int retval; | ||
433 | u8 value; | ||
434 | |||
435 | if (!CHK_AUTO_DELINK(chip)) | ||
436 | return 0; | ||
437 | |||
438 | retval = rts51x_read_mem(us, 0xFE47, &value, 1); | ||
439 | if (retval < 0) | ||
440 | return -EIO; | ||
441 | |||
442 | if (auto_delink_en) { | ||
443 | CLR_BIT(value, 0); | ||
444 | CLR_BIT(value, 1); | ||
445 | SET_BIT(value, 2); | ||
446 | |||
447 | if (CHECK_ID(chip, 0x0138, 0x3882)) | ||
448 | CLR_BIT(value, 2); | ||
449 | |||
450 | SET_BIT(value, 7); | ||
451 | |||
452 | retval = rts51x_write_mem(us, 0xFE47, &value, 1); | ||
453 | if (retval < 0) | ||
454 | return -EIO; | ||
455 | |||
456 | retval = enable_oscillator(us); | ||
457 | if (retval == 0) | ||
458 | (void)do_config_autodelink(us, 1, 0); | ||
459 | } else { | ||
460 | /* Autodelink controlled by firmware */ | ||
461 | |||
462 | SET_BIT(value, 2); | ||
463 | |||
464 | if (CHECK_ID(chip, 0x0138, 0x3882)) | ||
465 | CLR_BIT(value, 2); | ||
466 | |||
467 | if (CHECK_ID(chip, 0x0159, 0x5889) || | ||
468 | CHECK_ID(chip, 0x0138, 0x3880)) { | ||
469 | CLR_BIT(value, 0); | ||
470 | CLR_BIT(value, 7); | ||
471 | } | ||
472 | |||
473 | retval = rts51x_write_mem(us, 0xFE47, &value, 1); | ||
474 | if (retval < 0) | ||
475 | return -EIO; | ||
476 | |||
477 | if (CHECK_ID(chip, 0x0159, 0x5888)) { | ||
478 | value = 0xFF; | ||
479 | retval = rts51x_write_mem(us, 0xFE79, &value, 1); | ||
480 | if (retval < 0) | ||
481 | return -EIO; | ||
482 | |||
483 | value = 0x01; | ||
484 | retval = rts51x_write_mem(us, 0x48, &value, 1); | ||
485 | if (retval < 0) | ||
486 | return -EIO; | ||
487 | } | ||
488 | } | ||
489 | |||
490 | return 0; | ||
491 | } | ||
492 | |||
493 | static int config_autodelink_before_power_down(struct us_data *us) | ||
494 | { | ||
495 | struct rts51x_chip *chip = (struct rts51x_chip *)(us->extra); | ||
496 | int retval; | ||
497 | u8 value; | ||
498 | |||
499 | if (!CHK_AUTO_DELINK(chip)) | ||
500 | return 0; | ||
501 | |||
502 | if (auto_delink_en) { | ||
503 | retval = rts51x_read_mem(us, 0xFE77, &value, 1); | ||
504 | if (retval < 0) | ||
505 | return -EIO; | ||
506 | |||
507 | SET_BIT(value, 2); | ||
508 | retval = rts51x_write_mem(us, 0xFE77, &value, 1); | ||
509 | if (retval < 0) | ||
510 | return -EIO; | ||
511 | |||
512 | if (CHECK_ID(chip, 0x0159, 0x5888)) { | ||
513 | value = 0x01; | ||
514 | retval = rts51x_write_mem(us, 0x48, &value, 1); | ||
515 | if (retval < 0) | ||
516 | return -EIO; | ||
517 | } | ||
518 | |||
519 | retval = rts51x_read_mem(us, 0xFE47, &value, 1); | ||
520 | if (retval < 0) | ||
521 | return -EIO; | ||
522 | |||
523 | SET_BIT(value, 0); | ||
524 | if (CHECK_ID(chip, 0x0138, 0x3882)) | ||
525 | SET_BIT(value, 2); | ||
526 | retval = rts51x_write_mem(us, 0xFE77, &value, 1); | ||
527 | if (retval < 0) | ||
528 | return -EIO; | ||
529 | } else { | ||
530 | if (CHECK_ID(chip, 0x0159, 0x5889) || | ||
531 | CHECK_ID(chip, 0x0138, 0x3880) || | ||
532 | CHECK_ID(chip, 0x0138, 0x3882)) { | ||
533 | retval = rts51x_read_mem(us, 0xFE47, &value, 1); | ||
534 | if (retval < 0) | ||
535 | return -EIO; | ||
536 | |||
537 | if (CHECK_ID(chip, 0x0159, 0x5889) || | ||
538 | CHECK_ID(chip, 0x0138, 0x3880)) { | ||
539 | SET_BIT(value, 0); | ||
540 | SET_BIT(value, 7); | ||
541 | } | ||
542 | |||
543 | if (CHECK_ID(chip, 0x0138, 0x3882)) | ||
544 | SET_BIT(value, 2); | ||
545 | |||
546 | retval = rts51x_write_mem(us, 0xFE47, &value, 1); | ||
547 | if (retval < 0) | ||
548 | return -EIO; | ||
549 | } | ||
550 | |||
551 | if (CHECK_ID(chip, 0x0159, 0x5888)) { | ||
552 | value = 0x01; | ||
553 | retval = rts51x_write_mem(us, 0x48, &value, 1); | ||
554 | if (retval < 0) | ||
555 | return -EIO; | ||
556 | } | ||
557 | } | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | static void realtek_cr_destructor(void *extra) | ||
563 | { | ||
564 | struct rts51x_chip *chip = (struct rts51x_chip *)extra; | ||
565 | |||
566 | if (!chip) | ||
567 | return; | ||
568 | |||
569 | kfree(chip->status); | ||
570 | } | ||
571 | |||
572 | #ifdef CONFIG_PM | ||
573 | void realtek_pm_hook(struct us_data *us, int pm_state) | ||
574 | { | ||
575 | if (pm_state == US_SUSPEND) | ||
576 | (void)config_autodelink_before_power_down(us); | ||
577 | } | ||
578 | #endif | ||
579 | |||
580 | static int init_realtek_cr(struct us_data *us) | ||
581 | { | ||
582 | struct rts51x_chip *chip; | ||
583 | int size, i, retval; | ||
584 | |||
585 | chip = kzalloc(sizeof(struct rts51x_chip), GFP_KERNEL); | ||
586 | if (!chip) | ||
587 | return -ENOMEM; | ||
588 | |||
589 | us->extra = chip; | ||
590 | us->extra_destructor = realtek_cr_destructor; | ||
591 | #ifdef CONFIG_PM | ||
592 | us->suspend_resume_hook = realtek_pm_hook; | ||
593 | #endif | ||
594 | |||
595 | us->max_lun = chip->max_lun = rts51x_get_max_lun(us); | ||
596 | |||
597 | US_DEBUGP("chip->max_lun = %d\n", chip->max_lun); | ||
598 | |||
599 | size = (chip->max_lun + 1) * sizeof(struct rts51x_status); | ||
600 | chip->status = kzalloc(size, GFP_KERNEL); | ||
601 | if (!chip->status) | ||
602 | goto INIT_FAIL; | ||
603 | |||
604 | for (i = 0; i <= (int)(chip->max_lun); i++) { | ||
605 | retval = rts51x_check_status(us, (u8)i); | ||
606 | if (retval < 0) | ||
607 | goto INIT_FAIL; | ||
608 | } | ||
609 | |||
610 | if (CHECK_FW_VER(chip, 0x5888) || CHECK_FW_VER(chip, 0x5889) || | ||
611 | CHECK_FW_VER(chip, 0x5901)) | ||
612 | SET_AUTO_DELINK(chip); | ||
613 | if (STATUS_LEN(chip) == 16) { | ||
614 | if (SUPPORT_AUTO_DELINK(chip)) | ||
615 | SET_AUTO_DELINK(chip); | ||
616 | } | ||
617 | |||
618 | US_DEBUGP("chip->flag = 0x%x\n", chip->flag); | ||
619 | |||
620 | (void)config_autodelink_after_power_on(us); | ||
621 | |||
622 | return 0; | ||
623 | |||
624 | INIT_FAIL: | ||
625 | if (us->extra) { | ||
626 | kfree(chip->status); | ||
627 | kfree(us->extra); | ||
628 | us->extra = NULL; | ||
629 | } | ||
630 | |||
631 | return -EIO; | ||
632 | } | ||
633 | |||
634 | static int realtek_cr_probe(struct usb_interface *intf, | ||
635 | const struct usb_device_id *id) | ||
636 | { | ||
637 | struct us_data *us; | ||
638 | int result; | ||
639 | |||
640 | US_DEBUGP("Probe Realtek Card Reader!\n"); | ||
641 | |||
642 | result = usb_stor_probe1(&us, intf, id, | ||
643 | (id - realtek_cr_ids) + realtek_cr_unusual_dev_list); | ||
644 | if (result) | ||
645 | return result; | ||
646 | |||
647 | result = usb_stor_probe2(us); | ||
648 | return result; | ||
649 | } | ||
650 | |||
651 | static struct usb_driver realtek_cr_driver = { | ||
652 | .name = "ums-realtek", | ||
653 | .probe = realtek_cr_probe, | ||
654 | .disconnect = usb_stor_disconnect, | ||
655 | .suspend = usb_stor_suspend, | ||
656 | .resume = usb_stor_resume, | ||
657 | .reset_resume = usb_stor_reset_resume, | ||
658 | .pre_reset = usb_stor_pre_reset, | ||
659 | .post_reset = usb_stor_post_reset, | ||
660 | .id_table = realtek_cr_ids, | ||
661 | .soft_unbind = 1, | ||
662 | }; | ||
663 | |||
664 | static int __init realtek_cr_init(void) | ||
665 | { | ||
666 | return usb_register(&realtek_cr_driver); | ||
667 | } | ||
668 | |||
669 | static void __exit realtek_cr_exit(void) | ||
670 | { | ||
671 | usb_deregister(&realtek_cr_driver); | ||
672 | } | ||
673 | |||
674 | module_init(realtek_cr_init); | ||
675 | module_exit(realtek_cr_exit); | ||
diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h new file mode 100644 index 000000000000..3236e0328516 --- /dev/null +++ b/drivers/usb/storage/unusual_realtek.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* Driver for Realtek RTS51xx USB card reader | ||
2 | * | ||
3 | * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License as published by the | ||
7 | * Free Software Foundation; either version 2, or (at your option) any | ||
8 | * later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | * General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along | ||
16 | * with this program; if not, see <http://www.gnu.org/licenses/>. | ||
17 | * | ||
18 | * Author: | ||
19 | * wwang (wei_wang@realsil.com.cn) | ||
20 | * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China | ||
21 | */ | ||
22 | |||
23 | #if defined(CONFIG_USB_STORAGE_REALTEK) || \ | ||
24 | defined(CONFIG_USB_STORAGE_REALTEK_MODULE) | ||
25 | |||
26 | UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999, | ||
27 | "Realtek", | ||
28 | "USB Card Reader", | ||
29 | USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), | ||
30 | |||
31 | UNUSUAL_DEV(0x0bda, 0x0158, 0x0000, 0x9999, | ||
32 | "Realtek", | ||
33 | "USB Card Reader", | ||
34 | USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), | ||
35 | |||
36 | UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, | ||
37 | "Realtek", | ||
38 | "USB Card Reader", | ||
39 | USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), | ||
40 | |||
41 | #endif /* defined(CONFIG_USB_STORAGE_REALTEK) || ... */ | ||
diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index 468bde7d1971..4293077c01aa 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c | |||
@@ -85,6 +85,7 @@ static struct ignore_entry ignore_ids[] = { | |||
85 | # include "unusual_jumpshot.h" | 85 | # include "unusual_jumpshot.h" |
86 | # include "unusual_karma.h" | 86 | # include "unusual_karma.h" |
87 | # include "unusual_onetouch.h" | 87 | # include "unusual_onetouch.h" |
88 | # include "unusual_realtek.h" | ||
88 | # include "unusual_sddr09.h" | 89 | # include "unusual_sddr09.h" |
89 | # include "unusual_sddr55.h" | 90 | # include "unusual_sddr55.h" |
90 | # include "unusual_usbat.h" | 91 | # include "unusual_usbat.h" |