Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
99451dce RT |
2 | /* Realtek USB Memstick Card Interface driver |
3 | * | |
4 | * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. | |
5 | * | |
99451dce RT |
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> | |
e455b69d | 17 | #include <linux/rtsx_usb.h> |
99451dce RT |
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; | |
6827ca57 | 32 | struct delayed_work poll_card; |
99451dce RT |
33 | |
34 | u8 ssc_depth; | |
35 | unsigned int clock; | |
36 | int power_mode; | |
37 | unsigned char ifmode; | |
38 | bool eject; | |
6827ca57 | 39 | bool system_suspending; |
99451dce RT |
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, i, 0, 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, i, 0, 0); | |
76 | ||
77 | /* CARD_PULL_CTLx */ | |
78 | for (i = 0xFD60; i <= 0xFD65; i++) | |
79 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, i, 0, 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, 0, 0); | |
83 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_SELECT, 0, 0); | |
84 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_CLK_EN, 0, 0); | |
85 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, CARD_PWR_CTL, 0, 0); | |
86 | ||
87 | rtsx_usb_send_cmd(ucr, MODE_CR, 100); | |
88 | rtsx_usb_get_rsp(ucr, 21, 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, 0xFF, 0x55); | |
117 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); | |
118 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); | |
119 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); | |
120 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); | |
121 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5); | |
122 | ||
123 | return rtsx_usb_send_cmd(ucr, MODE_C, 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, 0xFF, 0x65); | |
131 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); | |
132 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); | |
133 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); | |
134 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x56); | |
135 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59); | |
136 | ||
137 | return rtsx_usb_send_cmd(ucr, MODE_C, 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, 0xFF, 0x55); | |
145 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); | |
146 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); | |
147 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); | |
148 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); | |
149 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0xA5); | |
150 | ||
151 | return rtsx_usb_send_cmd(ucr, MODE_C, 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, 0xFF, 0x65); | |
159 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL2, 0xFF, 0x55); | |
160 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL3, 0xFF, 0x95); | |
161 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL4, 0xFF, 0x55); | |
162 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL5, 0xFF, 0x55); | |
163 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PULL_CTL6, 0xFF, 0x59); | |
164 | ||
165 | return rtsx_usb_send_cmd(ucr, MODE_C, 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, 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, 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(800, 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, 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, 0); | |
218 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0); | |
219 | ||
220 | err = rtsx_usb_send_cmd(ucr, MODE_C, 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, 0xFF, tpc); | |
266 | if (card->id.type == MEMSTICK_TYPE_PRO) { | |
267 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_H, | |
268 | 0xFF, (u8)(sec_cnt >> 8)); | |
269 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_SECTOR_CNT_L, | |
270 | 0xFF, (u8)sec_cnt); | |
271 | } | |
272 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); | |
273 | ||
274 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC3, | |
275 | 0xFF, (u8)(length >> 24)); | |
276 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC2, | |
277 | 0xFF, (u8)(length >> 16)); | |
278 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC1, | |
279 | 0xFF, (u8)(length >> 8)); | |
280 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_TC0, 0xFF, | |
281 | (u8)length); | |
282 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MC_DMA_CTL, | |
283 | 0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512); | |
284 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, | |
285 | 0x01, RING_BUFFER); | |
286 | ||
287 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, | |
288 | 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 | STAGE_MS_STATUS, 100); | |
293 | if (err) | |
294 | return err; | |
295 | ||
296 | err = rtsx_usb_transfer_data(ucr, pipe, sg, length, | |
297 | 1, NULL, 10000); | |
298 | if (err) | |
299 | goto err_out; | |
300 | ||
301 | err = rtsx_usb_get_rsp(ucr, 3, 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, 0xFF, data[i]); | |
329 | ||
330 | if (cnt % 2) | |
331 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, | |
332 | PPBUF_BASE2 + i, 0xFF, 0xFF); | |
333 | ||
334 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); | |
335 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); | |
336 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); | |
337 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, | |
338 | 0x01, PINGPONG_BUFFER); | |
339 | ||
340 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, | |
341 | 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, 0, 0); | |
345 | ||
346 | err = rtsx_usb_send_cmd(ucr, MODE_CR, 100); | |
347 | if (err) | |
348 | return err; | |
349 | ||
350 | err = rtsx_usb_get_rsp(ucr, 2, 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, &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, 0xFF, tpc); | |
395 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); | |
396 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); | |
397 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_DATA_SOURCE, | |
398 | 0x01, PINGPONG_BUFFER); | |
399 | ||
400 | rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, MS_TRANSFER, | |
401 | 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, 0, 0); | |
406 | if (cnt % 2) | |
407 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0); | |
408 | else | |
409 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, | |
410 | PPBUF_BASE2 + cnt - 1, 0, 0); | |
411 | ||
412 | rtsx_usb_add_cmd(ucr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); | |
413 | ||
414 | err = rtsx_usb_send_cmd(ucr, MODE_CR, 100); | |
415 | if (err) | |
416 | return err; | |
417 | ||
418 | err = rtsx_usb_get_rsp(ucr, cnt + 2, 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, &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, req->data_dir, | |
471 | req->tpc, cfg, &(req->sg)); | |
472 | } else { | |
473 | if (req->data_dir == READ) | |
474 | err = ms_read_bytes(host, req->tpc, cfg, | |
475 | req->data_len, req->data, &int_reg); | |
476 | else | |
477 | err = ms_write_bytes(host, req->tpc, cfg, | |
478 | req->data_len, req->data, &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, MS_TPC_GET_INT, | |
486 | NO_WAIT_INT, 1, &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) { | |
9158cb29 | 515 | pm_runtime_get_sync(ms_dev(host)); |
99451dce RT |
516 | do { |
517 | rc = memstick_next_req(msh, &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(&ucr->dev_mutex); | |
531 | ||
532 | dev_dbg(ms_dev(host), "req result %d\n", | |
533 | host->req->error); | |
534 | } | |
535 | } while (!rc); | |
6827ca57 | 536 | pm_runtime_put_sync(ms_dev(host)); |
99451dce RT |
537 | } |
538 | ||
539 | } | |
540 | ||
541 | static void rtsx_usb_ms_request(struct memstick_host *msh) | |
542 | { | |
543 | struct rtsx_usb_ms *host = memstick_priv(msh); | |
544 | ||
545 | dev_dbg(ms_dev(host), "--> %s\n", __func__); | |
546 | ||
547 | if (!host->eject) | |
548 | schedule_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(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 | ||
9158cb29 | 563 | pm_runtime_get_sync(ms_dev(host)); |
99451dce RT |
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) { | |
6827ca57 | 576 | pm_runtime_get_noresume(ms_dev(host)); |
99451dce | 577 | err = ms_power_on(host); |
6827ca57 KHF |
578 | if (err) |
579 | pm_runtime_put_noidle(ms_dev(host)); | |
99451dce RT |
580 | } else if (value == MEMSTICK_POWER_OFF) { |
581 | err = ms_power_off(host); | |
6827ca57 | 582 | if (!err) |
99451dce | 583 | pm_runtime_put_noidle(ms_dev(host)); |
99451dce RT |
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, 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, 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, clock, | |
613 | ssc_depth, false, true, 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(&ucr->dev_mutex); | |
6827ca57 | 629 | pm_runtime_put_sync(ms_dev(host)); |
99451dce RT |
630 | |
631 | /* power-on delay */ | |
6827ca57 | 632 | if (param == MEMSTICK_POWER && value == MEMSTICK_POWER_ON) { |
99451dce RT |
633 | usleep_range(10000, 12000); |
634 | ||
6827ca57 KHF |
635 | if (!host->eject) |
636 | schedule_delayed_work(&host->poll_card, 100); | |
637 | } | |
638 | ||
99451dce RT |
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 | ||
6827ca57 KHF |
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 | */ | |
99451dce | 663 | |
6827ca57 | 664 | host->system_suspending = true; |
99451dce | 665 | memstick_suspend_host(msh); |
6827ca57 | 666 | |
99451dce RT |
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 | ||
99451dce | 675 | memstick_resume_host(msh); |
6827ca57 KHF |
676 | host->system_suspending = false; |
677 | ||
99451dce RT |
678 | return 0; |
679 | } | |
680 | #endif /* CONFIG_PM_SLEEP */ | |
681 | ||
6827ca57 KHF |
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->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) | |
99451dce | 717 | { |
6827ca57 KHF |
718 | struct rtsx_usb_ms *host = container_of(work, struct rtsx_usb_ms, |
719 | poll_card.work); | |
99451dce | 720 | struct rtsx_ucr *ucr = host->ucr; |
99451dce | 721 | int err; |
6827ca57 | 722 | u8 val; |
99451dce | 723 | |
6827ca57 KHF |
724 | if (host->eject || host->power_mode != MEMSTICK_POWER_ON) |
725 | return; | |
99451dce | 726 | |
6827ca57 KHF |
727 | pm_runtime_get_sync(ms_dev(host)); |
728 | mutex_lock(&ucr->dev_mutex); | |
99451dce | 729 | |
6827ca57 KHF |
730 | /* Check pending MS card changes */ |
731 | err = rtsx_usb_read_register(ucr, CARD_INT_PEND, &val); | |
732 | if (err) { | |
99451dce | 733 | mutex_unlock(&ucr->dev_mutex); |
6827ca57 KHF |
734 | goto poll_again; |
735 | } | |
99451dce | 736 | |
6827ca57 KHF |
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); | |
99451dce | 741 | |
6827ca57 | 742 | mutex_unlock(&ucr->dev_mutex); |
99451dce | 743 | |
6827ca57 KHF |
744 | if (val & MS_INT) { |
745 | dev_dbg(ms_dev(host), "MS slot change detected\n"); | |
746 | memstick_detect_change(host->msh); | |
99451dce RT |
747 | } |
748 | ||
6827ca57 KHF |
749 | poll_again: |
750 | pm_runtime_put_sync(ms_dev(host)); | |
751 | ||
752 | if (!host->eject && host->power_mode == MEMSTICK_POWER_ON) | |
753 | schedule_delayed_work(&host->poll_card, 100); | |
99451dce RT |
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(sizeof(*host), &pdev->dev); | |
771 | if (!msh) | |
772 | return -ENOMEM; | |
773 | ||
774 | host = memstick_priv(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, host); | |
780 | ||
781 | mutex_init(&host->host_mutex); | |
782 | INIT_WORK(&host->handle_req, rtsx_usb_ms_handle_req); | |
783 | ||
6827ca57 | 784 | INIT_DELAYED_WORK(&host->poll_card, rtsx_usb_ms_poll_card); |
99451dce RT |
785 | |
786 | msh->request = rtsx_usb_ms_request; | |
787 | msh->set_param = rtsx_usb_ms_set_param; | |
788 | msh->caps = MEMSTICK_CAP_PAR4; | |
789 | ||
6827ca57 KHF |
790 | pm_runtime_get_noresume(ms_dev(host)); |
791 | pm_runtime_set_active(ms_dev(host)); | |
792 | pm_runtime_enable(ms_dev(host)); | |
793 | ||
99451dce RT |
794 | err = memstick_add_host(msh); |
795 | if (err) | |
796 | goto err_out; | |
797 | ||
6827ca57 KHF |
798 | pm_runtime_put(ms_dev(host)); |
799 | ||
99451dce RT |
800 | return 0; |
801 | err_out: | |
802 | memstick_free_host(msh); | |
01a7e8e0 | 803 | pm_runtime_disable(ms_dev(host)); |
6827ca57 | 804 | pm_runtime_put_noidle(ms_dev(host)); |
99451dce RT |
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); | |
6827ca57 | 811 | struct memstick_host *msh = host->msh; |
99451dce RT |
812 | int err; |
813 | ||
99451dce RT |
814 | host->eject = true; |
815 | cancel_work_sync(&host->handle_req); | |
816 | ||
817 | mutex_lock(&host->host_mutex); | |
818 | if (host->req) { | |
ba9d5f83 | 819 | dev_dbg(ms_dev(host), |
99451dce RT |
820 | "%s: Controller removed during transfer\n", |
821 | dev_name(&msh->dev)); | |
822 | host->req->error = -ENOMEDIUM; | |
823 | do { | |
824 | err = memstick_next_req(msh, &host->req); | |
825 | if (!err) | |
826 | host->req->error = -ENOMEDIUM; | |
827 | } while (!err); | |
828 | } | |
829 | mutex_unlock(&host->host_mutex); | |
830 | ||
99451dce RT |
831 | memstick_remove_host(msh); |
832 | memstick_free_host(msh); | |
833 | ||
834 | /* Balance possible unbalanced usage count | |
835 | * e.g. unconditional module removal | |
836 | */ | |
837 | if (pm_runtime_active(ms_dev(host))) | |
838 | pm_runtime_put(ms_dev(host)); | |
839 | ||
ba9d5f83 | 840 | pm_runtime_disable(ms_dev(host)); |
99451dce RT |
841 | platform_set_drvdata(pdev, NULL); |
842 | ||
ba9d5f83 | 843 | dev_dbg(ms_dev(host), |
99451dce RT |
844 | ": Realtek USB Memstick controller has been removed\n"); |
845 | ||
846 | return 0; | |
847 | } | |
848 | ||
99451dce RT |
849 | static struct platform_device_id rtsx_usb_ms_ids[] = { |
850 | { | |
851 | .name = "rtsx_usb_ms", | |
852 | }, { | |
853 | /* sentinel */ | |
854 | } | |
855 | }; | |
856 | MODULE_DEVICE_TABLE(platform, rtsx_usb_ms_ids); | |
857 | ||
858 | static struct platform_driver rtsx_usb_ms_driver = { | |
859 | .probe = rtsx_usb_ms_drv_probe, | |
860 | .remove = rtsx_usb_ms_drv_remove, | |
861 | .id_table = rtsx_usb_ms_ids, | |
862 | .driver = { | |
99451dce RT |
863 | .name = "rtsx_usb_ms", |
864 | .pm = &rtsx_usb_ms_pm_ops, | |
865 | }, | |
866 | }; | |
867 | module_platform_driver(rtsx_usb_ms_driver); | |
868 | ||
869 | MODULE_LICENSE("GPL v2"); | |
870 | MODULE_AUTHOR("Roger Tseng <rogerable@realtek.com>"); | |
871 | MODULE_DESCRIPTION("Realtek USB Memstick Card Host Driver"); |