1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Realtek USB Memstick Card Interface driver |
3 | * |
4 | * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. |
5 | * |
6 | * Author: |
7 | * Roger Tseng <rogerable@realtek.com> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/highmem.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/workqueue.h> |
15 | #include <linux/memstick.h> |
16 | #include <linux/kthread.h> |
17 | #include <linux/rtsx_usb.h> |
18 | #include <linux/pm_runtime.h> |
19 | #include <linux/mutex.h> |
20 | #include <linux/sched.h> |
21 | #include <linux/completion.h> |
22 | #include <asm/unaligned.h> |
23 | |
24 | struct rtsx_usb_ms { |
25 | struct platform_device *pdev; |
26 | struct rtsx_ucr *ucr; |
27 | struct memstick_host *msh; |
28 | struct memstick_request *req; |
29 | |
30 | struct mutex host_mutex; |
31 | struct work_struct handle_req; |
32 | struct delayed_work poll_card; |
33 | |
34 | u8 ssc_depth; |
35 | unsigned int clock; |
36 | int power_mode; |
37 | unsigned char ifmode; |
38 | bool eject; |
39 | bool system_suspending; |
40 | }; |
41 | |
42 | static inline struct device *ms_dev(struct rtsx_usb_ms *host) |
43 | { |
44 | return &(host->pdev->dev); |
45 | } |
46 | |
47 | static inline void ms_clear_error(struct rtsx_usb_ms *host) |
48 | { |
49 | struct rtsx_ucr *ucr = host->ucr; |
50 | rtsx_usb_ep0_write_register(ucr, CARD_STOP, |
51 | MS_STOP | MS_CLR_ERR, |
52 | MS_STOP | MS_CLR_ERR); |
53 | |
54 | rtsx_usb_clear_dma_err(ucr); |
55 | rtsx_usb_clear_fsm_err(ucr); |
56 | } |
57 | |
58 | #ifdef DEBUG |
59 | |
60 | static void ms_print_debug_regs(struct rtsx_usb_ms *host) |
61 | { |
62 | struct rtsx_ucr *ucr = host->ucr; |
63 | u16 i; |
64 | u8 *ptr; |
65 | |
66 | /* Print MS host internal registers */ |
67 | rtsx_usb_init_cmd(ucr); |
68 | |
69 | /* MS_CFG to MS_INT_REG */ |
70 | for (i = 0xFD40; i <= 0xFD44; i++) |
71 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, reg_addr: i, mask: 0, data: 0); |
72 | |
73 | /* CARD_SHARE_MODE to CARD_GPIO */ |
74 | for (i = 0xFD51; i <= 0xFD56; i++) |
75 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, reg_addr: i, mask: 0, data: 0); |
76 | |
77 | /* CARD_PULL_CTLx */ |
78 | for (i = 0xFD60; i <= 0xFD65; i++) |
79 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, reg_addr: i, mask: 0, data: 0); |
80 | |
81 | /* CARD_DATA_SOURCE, CARD_SELECT, CARD_CLK_EN, CARD_PWR_CTL */ |
82 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_DATA_SOURCE, mask: 0, data: 0); |
83 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_SELECT, mask: 0, data: 0); |
84 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_CLK_EN, mask: 0, data: 0); |
85 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_PWR_CTL, mask: 0, data: 0); |
86 | |
87 | rtsx_usb_send_cmd(ucr, MODE_CR, timeout: 100); |
88 | rtsx_usb_get_rsp(ucr, rsp_len: 21, timeout: 100); |
89 | |
90 | ptr = ucr->rsp_buf; |
91 | for (i = 0xFD40; i <= 0xFD44; i++) |
92 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , i, *(ptr++)); |
93 | for (i = 0xFD51; i <= 0xFD56; i++) |
94 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , i, *(ptr++)); |
95 | for (i = 0xFD60; i <= 0xFD65; i++) |
96 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , i, *(ptr++)); |
97 | |
98 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , CARD_DATA_SOURCE, *(ptr++)); |
99 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , CARD_SELECT, *(ptr++)); |
100 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , CARD_CLK_EN, *(ptr++)); |
101 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , CARD_PWR_CTL, *(ptr++)); |
102 | } |
103 | |
104 | #else |
105 | |
106 | static void ms_print_debug_regs(struct rtsx_usb_ms *host) |
107 | { |
108 | } |
109 | |
110 | #endif |
111 | |
112 | static int ms_pull_ctl_disable_lqfp48(struct rtsx_ucr *ucr) |
113 | { |
114 | rtsx_usb_init_cmd(ucr); |
115 | |
116 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, mask: 0xFF, data: 0x55); |
117 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, mask: 0xFF, data: 0x55); |
118 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, mask: 0xFF, data: 0x95); |
119 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, mask: 0xFF, data: 0x55); |
120 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, mask: 0xFF, data: 0x55); |
121 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, mask: 0xFF, data: 0xA5); |
122 | |
123 | return rtsx_usb_send_cmd(ucr, MODE_C, timeout: 100); |
124 | } |
125 | |
126 | static int ms_pull_ctl_disable_qfn24(struct rtsx_ucr *ucr) |
127 | { |
128 | rtsx_usb_init_cmd(ucr); |
129 | |
130 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, mask: 0xFF, data: 0x65); |
131 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, mask: 0xFF, data: 0x55); |
132 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, mask: 0xFF, data: 0x95); |
133 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, mask: 0xFF, data: 0x55); |
134 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, mask: 0xFF, data: 0x56); |
135 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, mask: 0xFF, data: 0x59); |
136 | |
137 | return rtsx_usb_send_cmd(ucr, MODE_C, timeout: 100); |
138 | } |
139 | |
140 | static int ms_pull_ctl_enable_lqfp48(struct rtsx_ucr *ucr) |
141 | { |
142 | rtsx_usb_init_cmd(ucr); |
143 | |
144 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, mask: 0xFF, data: 0x55); |
145 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, mask: 0xFF, data: 0x55); |
146 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, mask: 0xFF, data: 0x95); |
147 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, mask: 0xFF, data: 0x55); |
148 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, mask: 0xFF, data: 0x55); |
149 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, mask: 0xFF, data: 0xA5); |
150 | |
151 | return rtsx_usb_send_cmd(ucr, MODE_C, timeout: 100); |
152 | } |
153 | |
154 | static int ms_pull_ctl_enable_qfn24(struct rtsx_ucr *ucr) |
155 | { |
156 | rtsx_usb_init_cmd(ucr); |
157 | |
158 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL1, mask: 0xFF, data: 0x65); |
159 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, mask: 0xFF, data: 0x55); |
160 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, mask: 0xFF, data: 0x95); |
161 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, mask: 0xFF, data: 0x55); |
162 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, mask: 0xFF, data: 0x55); |
163 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, mask: 0xFF, data: 0x59); |
164 | |
165 | return rtsx_usb_send_cmd(ucr, MODE_C, timeout: 100); |
166 | } |
167 | |
168 | static int ms_power_on(struct rtsx_usb_ms *host) |
169 | { |
170 | struct rtsx_ucr *ucr = host->ucr; |
171 | int err; |
172 | |
173 | dev_dbg(ms_dev(host), "%s\n" , __func__); |
174 | |
175 | rtsx_usb_init_cmd(ucr); |
176 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, mask: 0x07, MS_MOD_SEL); |
177 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE, |
178 | CARD_SHARE_MASK, CARD_SHARE_MS); |
179 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, |
180 | MS_CLK_EN, MS_CLK_EN); |
181 | err = rtsx_usb_send_cmd(ucr, MODE_C, timeout: 100); |
182 | if (err < 0) |
183 | return err; |
184 | |
185 | if (CHECK_PKG(ucr, LQFP48)) |
186 | err = ms_pull_ctl_enable_lqfp48(ucr); |
187 | else |
188 | err = ms_pull_ctl_enable_qfn24(ucr); |
189 | if (err < 0) |
190 | return err; |
191 | |
192 | err = rtsx_usb_write_register(ucr, CARD_PWR_CTL, |
193 | POWER_MASK, PARTIAL_POWER_ON); |
194 | if (err) |
195 | return err; |
196 | |
197 | usleep_range(min: 800, max: 1000); |
198 | |
199 | rtsx_usb_init_cmd(ucr); |
200 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, |
201 | POWER_MASK, POWER_ON); |
202 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, |
203 | MS_OUTPUT_EN, MS_OUTPUT_EN); |
204 | |
205 | return rtsx_usb_send_cmd(ucr, MODE_C, timeout: 100); |
206 | } |
207 | |
208 | static int ms_power_off(struct rtsx_usb_ms *host) |
209 | { |
210 | struct rtsx_ucr *ucr = host->ucr; |
211 | int err; |
212 | |
213 | dev_dbg(ms_dev(host), "%s\n" , __func__); |
214 | |
215 | rtsx_usb_init_cmd(ucr); |
216 | |
217 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, data: 0); |
218 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, data: 0); |
219 | |
220 | err = rtsx_usb_send_cmd(ucr, MODE_C, timeout: 100); |
221 | if (err < 0) |
222 | return err; |
223 | |
224 | if (CHECK_PKG(ucr, LQFP48)) |
225 | return ms_pull_ctl_disable_lqfp48(ucr); |
226 | |
227 | return ms_pull_ctl_disable_qfn24(ucr); |
228 | } |
229 | |
230 | static int ms_transfer_data(struct rtsx_usb_ms *host, unsigned char data_dir, |
231 | u8 tpc, u8 cfg, struct scatterlist *sg) |
232 | { |
233 | struct rtsx_ucr *ucr = host->ucr; |
234 | int err; |
235 | unsigned int length = sg->length; |
236 | u16 sec_cnt = (u16)(length / 512); |
237 | u8 trans_mode, dma_dir, flag; |
238 | unsigned int pipe; |
239 | struct memstick_dev *card = host->msh->card; |
240 | |
241 | dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n" , |
242 | __func__, tpc, (data_dir == READ) ? "READ" : "WRITE" , |
243 | length); |
244 | |
245 | if (data_dir == READ) { |
246 | flag = MODE_CDIR; |
247 | dma_dir = DMA_DIR_FROM_CARD; |
248 | if (card->id.type != MEMSTICK_TYPE_PRO) |
249 | trans_mode = MS_TM_NORMAL_READ; |
250 | else |
251 | trans_mode = MS_TM_AUTO_READ; |
252 | pipe = usb_rcvbulkpipe(ucr->pusb_dev, EP_BULK_IN); |
253 | } else { |
254 | flag = MODE_CDOR; |
255 | dma_dir = DMA_DIR_TO_CARD; |
256 | if (card->id.type != MEMSTICK_TYPE_PRO) |
257 | trans_mode = MS_TM_NORMAL_WRITE; |
258 | else |
259 | trans_mode = MS_TM_AUTO_WRITE; |
260 | pipe = usb_sndbulkpipe(ucr->pusb_dev, EP_BULK_OUT); |
261 | } |
262 | |
263 | rtsx_usb_init_cmd(ucr); |
264 | |
265 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, mask: 0xFF, data: tpc); |
266 | if (card->id.type == MEMSTICK_TYPE_PRO) { |
267 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_H, |
268 | mask: 0xFF, data: (u8)(sec_cnt >> 8)); |
269 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_L, |
270 | mask: 0xFF, data: (u8)sec_cnt); |
271 | } |
272 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, mask: 0xFF, data: cfg); |
273 | |
274 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3, |
275 | mask: 0xFF, data: (u8)(length >> 24)); |
276 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2, |
277 | mask: 0xFF, data: (u8)(length >> 16)); |
278 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1, |
279 | mask: 0xFF, data: (u8)(length >> 8)); |
280 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0, mask: 0xFF, |
281 | data: (u8)length); |
282 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL, |
283 | mask: 0x03 | DMA_PACK_SIZE_MASK, data: dma_dir | DMA_EN | DMA_512); |
284 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, |
285 | mask: 0x01, RING_BUFFER); |
286 | |
287 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, |
288 | mask: 0xFF, MS_TRANSFER_START | trans_mode); |
289 | rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER, |
290 | MS_TRANSFER_END, MS_TRANSFER_END); |
291 | |
292 | err = rtsx_usb_send_cmd(ucr, flag: flag | STAGE_MS_STATUS, timeout: 100); |
293 | if (err) |
294 | return err; |
295 | |
296 | err = rtsx_usb_transfer_data(ucr, pipe, buf: sg, len: length, |
297 | use_sg: 1, NULL, timeout: 10000); |
298 | if (err) |
299 | goto err_out; |
300 | |
301 | err = rtsx_usb_get_rsp(ucr, rsp_len: 3, timeout: 15000); |
302 | if (err) |
303 | goto err_out; |
304 | |
305 | if (ucr->rsp_buf[0] & MS_TRANSFER_ERR || |
306 | ucr->rsp_buf[1] & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) { |
307 | err = -EIO; |
308 | goto err_out; |
309 | } |
310 | return 0; |
311 | err_out: |
312 | ms_clear_error(host); |
313 | return err; |
314 | } |
315 | |
316 | static int ms_write_bytes(struct rtsx_usb_ms *host, u8 tpc, |
317 | u8 cfg, u8 cnt, u8 *data, u8 *int_reg) |
318 | { |
319 | struct rtsx_ucr *ucr = host->ucr; |
320 | int err, i; |
321 | |
322 | dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n" , __func__, tpc); |
323 | |
324 | rtsx_usb_init_cmd(ucr); |
325 | |
326 | for (i = 0; i < cnt; i++) |
327 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, |
328 | PPBUF_BASE2 + i, mask: 0xFF, data: data[i]); |
329 | |
330 | if (cnt % 2) |
331 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, |
332 | PPBUF_BASE2 + i, mask: 0xFF, data: 0xFF); |
333 | |
334 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, mask: 0xFF, data: tpc); |
335 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, mask: 0xFF, data: cnt); |
336 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, mask: 0xFF, data: cfg); |
337 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, |
338 | mask: 0x01, PINGPONG_BUFFER); |
339 | |
340 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, |
341 | mask: 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES); |
342 | rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER, |
343 | MS_TRANSFER_END, MS_TRANSFER_END); |
344 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, mask: 0, data: 0); |
345 | |
346 | err = rtsx_usb_send_cmd(ucr, MODE_CR, timeout: 100); |
347 | if (err) |
348 | return err; |
349 | |
350 | err = rtsx_usb_get_rsp(ucr, rsp_len: 2, timeout: 5000); |
351 | if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) { |
352 | u8 val; |
353 | |
354 | rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, data: &val); |
355 | dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n" , val); |
356 | |
357 | if (int_reg) |
358 | *int_reg = val & 0x0F; |
359 | |
360 | ms_print_debug_regs(host); |
361 | |
362 | ms_clear_error(host); |
363 | |
364 | if (!(tpc & 0x08)) { |
365 | if (val & MS_CRC16_ERR) |
366 | return -EIO; |
367 | } else { |
368 | if (!(val & 0x80)) { |
369 | if (val & (MS_INT_ERR | MS_INT_CMDNK)) |
370 | return -EIO; |
371 | } |
372 | } |
373 | |
374 | return -ETIMEDOUT; |
375 | } |
376 | |
377 | if (int_reg) |
378 | *int_reg = ucr->rsp_buf[1] & 0x0F; |
379 | |
380 | return 0; |
381 | } |
382 | |
383 | static int ms_read_bytes(struct rtsx_usb_ms *host, u8 tpc, |
384 | u8 cfg, u8 cnt, u8 *data, u8 *int_reg) |
385 | { |
386 | struct rtsx_ucr *ucr = host->ucr; |
387 | int err, i; |
388 | u8 *ptr; |
389 | |
390 | dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n" , __func__, tpc); |
391 | |
392 | rtsx_usb_init_cmd(ucr); |
393 | |
394 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, mask: 0xFF, data: tpc); |
395 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, mask: 0xFF, data: cnt); |
396 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, mask: 0xFF, data: cfg); |
397 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, |
398 | mask: 0x01, PINGPONG_BUFFER); |
399 | |
400 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, |
401 | mask: 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES); |
402 | rtsx_usb_add_cmd(ucr, CHECK_REG_CMD, MS_TRANSFER, |
403 | MS_TRANSFER_END, MS_TRANSFER_END); |
404 | for (i = 0; i < cnt - 1; i++) |
405 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + i, mask: 0, data: 0); |
406 | if (cnt % 2) |
407 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + cnt, mask: 0, data: 0); |
408 | else |
409 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, |
410 | PPBUF_BASE2 + cnt - 1, mask: 0, data: 0); |
411 | |
412 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, mask: 0, data: 0); |
413 | |
414 | err = rtsx_usb_send_cmd(ucr, MODE_CR, timeout: 100); |
415 | if (err) |
416 | return err; |
417 | |
418 | err = rtsx_usb_get_rsp(ucr, rsp_len: cnt + 2, timeout: 5000); |
419 | if (err || (ucr->rsp_buf[0] & MS_TRANSFER_ERR)) { |
420 | u8 val; |
421 | |
422 | rtsx_usb_ep0_read_register(ucr, MS_TRANS_CFG, data: &val); |
423 | dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n" , val); |
424 | |
425 | if (int_reg && (host->ifmode != MEMSTICK_SERIAL)) |
426 | *int_reg = val & 0x0F; |
427 | |
428 | ms_print_debug_regs(host); |
429 | |
430 | ms_clear_error(host); |
431 | |
432 | if (!(tpc & 0x08)) { |
433 | if (val & MS_CRC16_ERR) |
434 | return -EIO; |
435 | } else { |
436 | if (!(val & 0x80)) { |
437 | if (val & (MS_INT_ERR | MS_INT_CMDNK)) |
438 | return -EIO; |
439 | } |
440 | } |
441 | |
442 | return -ETIMEDOUT; |
443 | } |
444 | |
445 | ptr = ucr->rsp_buf + 1; |
446 | for (i = 0; i < cnt; i++) |
447 | data[i] = *ptr++; |
448 | |
449 | |
450 | if (int_reg && (host->ifmode != MEMSTICK_SERIAL)) |
451 | *int_reg = *ptr & 0x0F; |
452 | |
453 | return 0; |
454 | } |
455 | |
456 | static int rtsx_usb_ms_issue_cmd(struct rtsx_usb_ms *host) |
457 | { |
458 | struct memstick_request *req = host->req; |
459 | int err = 0; |
460 | u8 cfg = 0, int_reg; |
461 | |
462 | dev_dbg(ms_dev(host), "%s\n" , __func__); |
463 | |
464 | if (req->need_card_int) { |
465 | if (host->ifmode != MEMSTICK_SERIAL) |
466 | cfg = WAIT_INT; |
467 | } |
468 | |
469 | if (req->long_data) { |
470 | err = ms_transfer_data(host, data_dir: req->data_dir, |
471 | tpc: req->tpc, cfg, sg: &(req->sg)); |
472 | } else { |
473 | if (req->data_dir == READ) |
474 | err = ms_read_bytes(host, tpc: req->tpc, cfg, |
475 | cnt: req->data_len, data: req->data, int_reg: &int_reg); |
476 | else |
477 | err = ms_write_bytes(host, tpc: req->tpc, cfg, |
478 | cnt: req->data_len, data: req->data, int_reg: &int_reg); |
479 | } |
480 | if (err < 0) |
481 | return err; |
482 | |
483 | if (req->need_card_int) { |
484 | if (host->ifmode == MEMSTICK_SERIAL) { |
485 | err = ms_read_bytes(host, tpc: MS_TPC_GET_INT, |
486 | NO_WAIT_INT, cnt: 1, data: &req->int_reg, NULL); |
487 | if (err < 0) |
488 | return err; |
489 | } else { |
490 | |
491 | if (int_reg & MS_INT_CMDNK) |
492 | req->int_reg |= MEMSTICK_INT_CMDNAK; |
493 | if (int_reg & MS_INT_BREQ) |
494 | req->int_reg |= MEMSTICK_INT_BREQ; |
495 | if (int_reg & MS_INT_ERR) |
496 | req->int_reg |= MEMSTICK_INT_ERR; |
497 | if (int_reg & MS_INT_CED) |
498 | req->int_reg |= MEMSTICK_INT_CED; |
499 | } |
500 | dev_dbg(ms_dev(host), "int_reg: 0x%02x\n" , req->int_reg); |
501 | } |
502 | |
503 | return 0; |
504 | } |
505 | |
506 | static void rtsx_usb_ms_handle_req(struct work_struct *work) |
507 | { |
508 | struct rtsx_usb_ms *host = container_of(work, |
509 | struct rtsx_usb_ms, handle_req); |
510 | struct rtsx_ucr *ucr = host->ucr; |
511 | struct memstick_host *msh = host->msh; |
512 | int rc; |
513 | |
514 | if (!host->req) { |
515 | pm_runtime_get_sync(dev: ms_dev(host)); |
516 | do { |
517 | rc = memstick_next_req(host: msh, mrq: &host->req); |
518 | dev_dbg(ms_dev(host), "next req %d\n" , rc); |
519 | |
520 | if (!rc) { |
521 | mutex_lock(&ucr->dev_mutex); |
522 | |
523 | if (rtsx_usb_card_exclusive_check(ucr, |
524 | RTSX_USB_MS_CARD)) |
525 | host->req->error = -EIO; |
526 | else |
527 | host->req->error = |
528 | rtsx_usb_ms_issue_cmd(host); |
529 | |
530 | mutex_unlock(lock: &ucr->dev_mutex); |
531 | |
532 | dev_dbg(ms_dev(host), "req result %d\n" , |
533 | host->req->error); |
534 | } |
535 | } while (!rc); |
536 | pm_runtime_put_sync(dev: ms_dev(host)); |
537 | } |
538 | |
539 | } |
540 | |
541 | static void rtsx_usb_ms_request(struct memstick_host *msh) |
542 | { |
543 | struct rtsx_usb_ms *host = memstick_priv(host: msh); |
544 | |
545 | dev_dbg(ms_dev(host), "--> %s\n" , __func__); |
546 | |
547 | if (!host->eject) |
548 | schedule_work(work: &host->handle_req); |
549 | } |
550 | |
551 | static int rtsx_usb_ms_set_param(struct memstick_host *msh, |
552 | enum memstick_param param, int value) |
553 | { |
554 | struct rtsx_usb_ms *host = memstick_priv(host: msh); |
555 | struct rtsx_ucr *ucr = host->ucr; |
556 | unsigned int clock = 0; |
557 | u8 ssc_depth = 0; |
558 | int err; |
559 | |
560 | dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n" , |
561 | __func__, param, value); |
562 | |
563 | pm_runtime_get_sync(dev: ms_dev(host)); |
564 | mutex_lock(&ucr->dev_mutex); |
565 | |
566 | err = rtsx_usb_card_exclusive_check(ucr, RTSX_USB_MS_CARD); |
567 | if (err) |
568 | goto out; |
569 | |
570 | switch (param) { |
571 | case MEMSTICK_POWER: |
572 | if (value == host->power_mode) |
573 | break; |
574 | |
575 | if (value == MEMSTICK_POWER_ON) { |
576 | pm_runtime_get_noresume(dev: ms_dev(host)); |
577 | err = ms_power_on(host); |
578 | if (err) |
579 | pm_runtime_put_noidle(dev: ms_dev(host)); |
580 | } else if (value == MEMSTICK_POWER_OFF) { |
581 | err = ms_power_off(host); |
582 | if (!err) |
583 | pm_runtime_put_noidle(dev: ms_dev(host)); |
584 | } else |
585 | err = -EINVAL; |
586 | if (!err) |
587 | host->power_mode = value; |
588 | break; |
589 | |
590 | case MEMSTICK_INTERFACE: |
591 | if (value == MEMSTICK_SERIAL) { |
592 | clock = 19000000; |
593 | ssc_depth = SSC_DEPTH_512K; |
594 | err = rtsx_usb_write_register(ucr, MS_CFG, mask: 0x5A, |
595 | MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT); |
596 | if (err < 0) |
597 | break; |
598 | } else if (value == MEMSTICK_PAR4) { |
599 | clock = 39000000; |
600 | ssc_depth = SSC_DEPTH_1M; |
601 | |
602 | err = rtsx_usb_write_register(ucr, MS_CFG, mask: 0x5A, |
603 | MS_BUS_WIDTH_4 | PUSH_TIME_ODD | |
604 | MS_NO_CHECK_INT); |
605 | if (err < 0) |
606 | break; |
607 | } else { |
608 | err = -EINVAL; |
609 | break; |
610 | } |
611 | |
612 | err = rtsx_usb_switch_clock(ucr, card_clock: clock, |
613 | ssc_depth, initial_mode: false, double_clk: true, vpclk: false); |
614 | if (err < 0) { |
615 | dev_dbg(ms_dev(host), "switch clock failed\n" ); |
616 | break; |
617 | } |
618 | |
619 | host->ssc_depth = ssc_depth; |
620 | host->clock = clock; |
621 | host->ifmode = value; |
622 | break; |
623 | default: |
624 | err = -EINVAL; |
625 | break; |
626 | } |
627 | out: |
628 | mutex_unlock(lock: &ucr->dev_mutex); |
629 | pm_runtime_put_sync(dev: ms_dev(host)); |
630 | |
631 | /* power-on delay */ |
632 | if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) { |
633 | usleep_range(min: 10000, max: 12000); |
634 | |
635 | if (!host->eject) |
636 | schedule_delayed_work(dwork: &host->poll_card, delay: 100); |
637 | } |
638 | |
639 | dev_dbg(ms_dev(host), "%s: return = %d\n" , __func__, err); |
640 | return err; |
641 | } |
642 | |
643 | #ifdef CONFIG_PM_SLEEP |
644 | static int rtsx_usb_ms_suspend(struct device *dev) |
645 | { |
646 | struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
647 | struct memstick_host *msh = host->msh; |
648 | |
649 | /* Since we use rtsx_usb's resume callback to runtime resume its |
650 | * children to implement remote wakeup signaling, this causes |
651 | * rtsx_usb_ms' runtime resume callback runs after its suspend |
652 | * callback: |
653 | * rtsx_usb_ms_suspend() |
654 | * rtsx_usb_resume() |
655 | * -> rtsx_usb_ms_runtime_resume() |
656 | * -> memstick_detect_change() |
657 | * |
658 | * rtsx_usb_suspend() |
659 | * |
660 | * To avoid this, skip runtime resume/suspend if system suspend is |
661 | * underway. |
662 | */ |
663 | |
664 | host->system_suspending = true; |
665 | memstick_suspend_host(host: msh); |
666 | |
667 | return 0; |
668 | } |
669 | |
670 | static int rtsx_usb_ms_resume(struct device *dev) |
671 | { |
672 | struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
673 | struct memstick_host *msh = host->msh; |
674 | |
675 | memstick_resume_host(host: msh); |
676 | host->system_suspending = false; |
677 | |
678 | return 0; |
679 | } |
680 | #endif /* CONFIG_PM_SLEEP */ |
681 | |
682 | #ifdef CONFIG_PM |
683 | static int rtsx_usb_ms_runtime_suspend(struct device *dev) |
684 | { |
685 | struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
686 | |
687 | if (host->system_suspending) |
688 | return 0; |
689 | |
690 | if (host->msh->card || host->power_mode != MEMSTICK_POWER_OFF) |
691 | return -EAGAIN; |
692 | |
693 | return 0; |
694 | } |
695 | |
696 | static int rtsx_usb_ms_runtime_resume(struct device *dev) |
697 | { |
698 | struct rtsx_usb_ms *host = dev_get_drvdata(dev); |
699 | |
700 | |
701 | if (host->system_suspending) |
702 | return 0; |
703 | |
704 | memstick_detect_change(host: host->msh); |
705 | |
706 | return 0; |
707 | } |
708 | #endif /* CONFIG_PM */ |
709 | |
710 | static const struct dev_pm_ops rtsx_usb_ms_pm_ops = { |
711 | SET_SYSTEM_SLEEP_PM_OPS(rtsx_usb_ms_suspend, rtsx_usb_ms_resume) |
712 | SET_RUNTIME_PM_OPS(rtsx_usb_ms_runtime_suspend, rtsx_usb_ms_runtime_resume, NULL) |
713 | }; |
714 | |
715 | |
716 | static void rtsx_usb_ms_poll_card(struct work_struct *work) |
717 | { |
718 | struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms, |
719 | poll_card.work); |
720 | struct rtsx_ucr *ucr = host->ucr; |
721 | int err; |
722 | u8 val; |
723 | |
724 | if (host->eject || host->power_mode != MEMSTICK_POWER_ON) |
725 | return; |
726 | |
727 | pm_runtime_get_sync(dev: ms_dev(host)); |
728 | mutex_lock(&ucr->dev_mutex); |
729 | |
730 | /* Check pending MS card changes */ |
731 | err = rtsx_usb_read_register(ucr, CARD_INT_PEND, data: &val); |
732 | if (err) { |
733 | mutex_unlock(lock: &ucr->dev_mutex); |
734 | goto poll_again; |
735 | } |
736 | |
737 | /* Clear the pending */ |
738 | rtsx_usb_write_register(ucr, CARD_INT_PEND, |
739 | XD_INT | MS_INT | SD_INT, |
740 | XD_INT | MS_INT | SD_INT); |
741 | |
742 | mutex_unlock(lock: &ucr->dev_mutex); |
743 | |
744 | if (val & MS_INT) { |
745 | dev_dbg(ms_dev(host), "MS slot change detected\n" ); |
746 | memstick_detect_change(host: host->msh); |
747 | } |
748 | |
749 | poll_again: |
750 | pm_runtime_put_sync(dev: ms_dev(host)); |
751 | |
752 | if (!host->eject && host->power_mode == MEMSTICK_POWER_ON) |
753 | schedule_delayed_work(dwork: &host->poll_card, delay: 100); |
754 | } |
755 | |
756 | static int rtsx_usb_ms_drv_probe(struct platform_device *pdev) |
757 | { |
758 | struct memstick_host *msh; |
759 | struct rtsx_usb_ms *host; |
760 | struct rtsx_ucr *ucr; |
761 | int err; |
762 | |
763 | ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent)); |
764 | if (!ucr) |
765 | return -ENXIO; |
766 | |
767 | dev_dbg(&(pdev->dev), |
768 | "Realtek USB Memstick controller found\n" ); |
769 | |
770 | msh = memstick_alloc_host(extra: sizeof(*host), dev: &pdev->dev); |
771 | if (!msh) |
772 | return -ENOMEM; |
773 | |
774 | host = memstick_priv(host: msh); |
775 | host->ucr = ucr; |
776 | host->msh = msh; |
777 | host->pdev = pdev; |
778 | host->power_mode = MEMSTICK_POWER_OFF; |
779 | platform_set_drvdata(pdev, data: host); |
780 | |
781 | mutex_init(&host->host_mutex); |
782 | INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req); |
783 | |
784 | INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card); |
785 | |
786 | msh->request = rtsx_usb_ms_request; |
787 | msh->set_param = rtsx_usb_ms_set_param; |
788 | msh->caps = MEMSTICK_CAP_PAR4; |
789 | |
790 | pm_runtime_get_noresume(dev: ms_dev(host)); |
791 | pm_runtime_set_active(dev: ms_dev(host)); |
792 | pm_runtime_enable(dev: ms_dev(host)); |
793 | |
794 | err = memstick_add_host(host: msh); |
795 | if (err) |
796 | goto err_out; |
797 | |
798 | pm_runtime_put(dev: ms_dev(host)); |
799 | |
800 | return 0; |
801 | err_out: |
802 | pm_runtime_disable(dev: ms_dev(host)); |
803 | pm_runtime_put_noidle(dev: ms_dev(host)); |
804 | memstick_free_host(host: msh); |
805 | return err; |
806 | } |
807 | |
808 | static int rtsx_usb_ms_drv_remove(struct platform_device *pdev) |
809 | { |
810 | struct rtsx_usb_ms *host = platform_get_drvdata(pdev); |
811 | struct memstick_host *msh = host->msh; |
812 | int err; |
813 | |
814 | host->eject = true; |
815 | cancel_work_sync(work: &host->handle_req); |
816 | |
817 | mutex_lock(&host->host_mutex); |
818 | if (host->req) { |
819 | dev_dbg(ms_dev(host), |
820 | "%s: Controller removed during transfer\n" , |
821 | dev_name(&msh->dev)); |
822 | host->req->error = -ENOMEDIUM; |
823 | do { |
824 | err = memstick_next_req(host: msh, mrq: &host->req); |
825 | if (!err) |
826 | host->req->error = -ENOMEDIUM; |
827 | } while (!err); |
828 | } |
829 | mutex_unlock(lock: &host->host_mutex); |
830 | |
831 | /* Balance possible unbalanced usage count |
832 | * e.g. unconditional module removal |
833 | */ |
834 | if (pm_runtime_active(dev: ms_dev(host))) |
835 | pm_runtime_put(dev: ms_dev(host)); |
836 | |
837 | pm_runtime_disable(dev: ms_dev(host)); |
838 | memstick_remove_host(host: msh); |
839 | dev_dbg(ms_dev(host), |
840 | ": Realtek USB Memstick controller has been removed\n" ); |
841 | memstick_free_host(host: msh); |
842 | platform_set_drvdata(pdev, NULL); |
843 | |
844 | return 0; |
845 | } |
846 | |
847 | static struct platform_device_id rtsx_usb_ms_ids[] = { |
848 | { |
849 | .name = "rtsx_usb_ms" , |
850 | }, { |
851 | /* sentinel */ |
852 | } |
853 | }; |
854 | MODULE_DEVICE_TABLE(platform, rtsx_usb_ms_ids); |
855 | |
856 | static struct platform_driver rtsx_usb_ms_driver = { |
857 | .probe = rtsx_usb_ms_drv_probe, |
858 | .remove = rtsx_usb_ms_drv_remove, |
859 | .id_table = rtsx_usb_ms_ids, |
860 | .driver = { |
861 | .name = "rtsx_usb_ms" , |
862 | .pm = &rtsx_usb_ms_pm_ops, |
863 | }, |
864 | }; |
865 | module_platform_driver(rtsx_usb_ms_driver); |
866 | |
867 | MODULE_LICENSE("GPL v2" ); |
868 | MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>" ); |
869 | MODULE_DESCRIPTION("Realtek USB Memstick Card Host Driver" ); |
870 | |