1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* Realtek PCI-Express Memstick Card Interface driver |
3 | * |
4 | * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. |
5 | * |
6 | * Author: |
7 | * Wei WANG <wei_wang@realsil.com.cn> |
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/memstick.h> |
15 | #include <linux/rtsx_pci.h> |
16 | #include <asm/unaligned.h> |
17 | |
18 | struct realtek_pci_ms { |
19 | struct platform_device *pdev; |
20 | struct rtsx_pcr *pcr; |
21 | struct memstick_host *msh; |
22 | struct memstick_request *req; |
23 | |
24 | struct mutex host_mutex; |
25 | struct work_struct handle_req; |
26 | |
27 | u8 ssc_depth; |
28 | unsigned int clock; |
29 | unsigned char ifmode; |
30 | bool eject; |
31 | }; |
32 | |
33 | static inline struct device *ms_dev(struct realtek_pci_ms *host) |
34 | { |
35 | return &(host->pdev->dev); |
36 | } |
37 | |
38 | static inline void ms_clear_error(struct realtek_pci_ms *host) |
39 | { |
40 | rtsx_pci_write_register(pcr: host->pcr, CARD_STOP, |
41 | MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR); |
42 | } |
43 | |
44 | #ifdef DEBUG |
45 | |
46 | static void ms_print_debug_regs(struct realtek_pci_ms *host) |
47 | { |
48 | struct rtsx_pcr *pcr = host->pcr; |
49 | u16 i; |
50 | u8 *ptr; |
51 | |
52 | /* Print MS host internal registers */ |
53 | rtsx_pci_init_cmd(pcr); |
54 | for (i = 0xFD40; i <= 0xFD44; i++) |
55 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg_addr: i, mask: 0, data: 0); |
56 | for (i = 0xFD52; i <= 0xFD69; i++) |
57 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, reg_addr: i, mask: 0, data: 0); |
58 | rtsx_pci_send_cmd(pcr, timeout: 100); |
59 | |
60 | ptr = rtsx_pci_get_cmd_data(pcr); |
61 | for (i = 0xFD40; i <= 0xFD44; i++) |
62 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , i, *(ptr++)); |
63 | for (i = 0xFD52; i <= 0xFD69; i++) |
64 | dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n" , i, *(ptr++)); |
65 | } |
66 | |
67 | #else |
68 | |
69 | #define ms_print_debug_regs(host) |
70 | |
71 | #endif |
72 | |
73 | static int ms_power_on(struct realtek_pci_ms *host) |
74 | { |
75 | struct rtsx_pcr *pcr = host->pcr; |
76 | int err; |
77 | |
78 | rtsx_pci_init_cmd(pcr); |
79 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, mask: 0x07, MS_MOD_SEL); |
80 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, |
81 | CARD_SHARE_MASK, CARD_SHARE_48_MS); |
82 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, |
83 | MS_CLK_EN, MS_CLK_EN); |
84 | err = rtsx_pci_send_cmd(pcr, timeout: 100); |
85 | if (err < 0) |
86 | return err; |
87 | |
88 | err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD); |
89 | if (err < 0) |
90 | return err; |
91 | |
92 | err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD); |
93 | if (err < 0) |
94 | return err; |
95 | |
96 | /* Wait ms power stable */ |
97 | msleep(msecs: 150); |
98 | |
99 | err = rtsx_pci_write_register(pcr, CARD_OE, |
100 | MS_OUTPUT_EN, MS_OUTPUT_EN); |
101 | if (err < 0) |
102 | return err; |
103 | |
104 | return 0; |
105 | } |
106 | |
107 | static int ms_power_off(struct realtek_pci_ms *host) |
108 | { |
109 | struct rtsx_pcr *pcr = host->pcr; |
110 | int err; |
111 | |
112 | rtsx_pci_init_cmd(pcr); |
113 | |
114 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, data: 0); |
115 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, data: 0); |
116 | |
117 | err = rtsx_pci_send_cmd(pcr, timeout: 100); |
118 | if (err < 0) |
119 | return err; |
120 | |
121 | err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD); |
122 | if (err < 0) |
123 | return err; |
124 | |
125 | return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD); |
126 | } |
127 | |
128 | static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir, |
129 | u8 tpc, u8 cfg, struct scatterlist *sg) |
130 | { |
131 | struct rtsx_pcr *pcr = host->pcr; |
132 | int err; |
133 | unsigned int length = sg->length; |
134 | u16 sec_cnt = (u16)(length / 512); |
135 | u8 val, trans_mode, dma_dir; |
136 | struct memstick_dev *card = host->msh->card; |
137 | bool pro_card = card->id.type == MEMSTICK_TYPE_PRO; |
138 | |
139 | dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n" , |
140 | __func__, tpc, (data_dir == READ) ? "READ" : "WRITE" , |
141 | length); |
142 | |
143 | if (data_dir == READ) { |
144 | dma_dir = DMA_DIR_FROM_CARD; |
145 | trans_mode = pro_card ? MS_TM_AUTO_READ : MS_TM_NORMAL_READ; |
146 | } else { |
147 | dma_dir = DMA_DIR_TO_CARD; |
148 | trans_mode = pro_card ? MS_TM_AUTO_WRITE : MS_TM_NORMAL_WRITE; |
149 | } |
150 | |
151 | rtsx_pci_init_cmd(pcr); |
152 | |
153 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, mask: 0xFF, data: tpc); |
154 | if (pro_card) { |
155 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H, |
156 | mask: 0xFF, data: (u8)(sec_cnt >> 8)); |
157 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L, |
158 | mask: 0xFF, data: (u8)sec_cnt); |
159 | } |
160 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, mask: 0xFF, data: cfg); |
161 | |
162 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, |
163 | DMA_DONE_INT, DMA_DONE_INT); |
164 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, mask: 0xFF, data: (u8)(length >> 24)); |
165 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, mask: 0xFF, data: (u8)(length >> 16)); |
166 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, mask: 0xFF, data: (u8)(length >> 8)); |
167 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, mask: 0xFF, data: (u8)length); |
168 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, |
169 | mask: 0x03 | DMA_PACK_SIZE_MASK, data: dma_dir | DMA_EN | DMA_512); |
170 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, |
171 | mask: 0x01, RING_BUFFER); |
172 | |
173 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, |
174 | mask: 0xFF, MS_TRANSFER_START | trans_mode); |
175 | rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, |
176 | MS_TRANSFER_END, MS_TRANSFER_END); |
177 | |
178 | rtsx_pci_send_cmd_no_wait(pcr); |
179 | |
180 | err = rtsx_pci_transfer_data(pcr, sglist: sg, num_sg: 1, read: data_dir == READ, timeout: 10000); |
181 | if (err < 0) { |
182 | ms_clear_error(host); |
183 | return err; |
184 | } |
185 | |
186 | rtsx_pci_read_register(pcr, MS_TRANS_CFG, data: &val); |
187 | if (pro_card) { |
188 | if (val & (MS_INT_CMDNK | MS_INT_ERR | |
189 | MS_CRC16_ERR | MS_RDY_TIMEOUT)) |
190 | return -EIO; |
191 | } else { |
192 | if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) |
193 | return -EIO; |
194 | } |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | static int ms_write_bytes(struct realtek_pci_ms *host, u8 tpc, |
200 | u8 cfg, u8 cnt, u8 *data, u8 *int_reg) |
201 | { |
202 | struct rtsx_pcr *pcr = host->pcr; |
203 | int err, i; |
204 | |
205 | dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n" , __func__, tpc); |
206 | |
207 | if (!data) |
208 | return -EINVAL; |
209 | |
210 | rtsx_pci_init_cmd(pcr); |
211 | |
212 | for (i = 0; i < cnt; i++) |
213 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, |
214 | PPBUF_BASE2 + i, mask: 0xFF, data: data[i]); |
215 | if (cnt % 2) |
216 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, |
217 | PPBUF_BASE2 + i, mask: 0xFF, data: 0xFF); |
218 | |
219 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, mask: 0xFF, data: tpc); |
220 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, mask: 0xFF, data: cnt); |
221 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, mask: 0xFF, data: cfg); |
222 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, |
223 | mask: 0x01, PINGPONG_BUFFER); |
224 | |
225 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, |
226 | mask: 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES); |
227 | rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, |
228 | MS_TRANSFER_END, MS_TRANSFER_END); |
229 | if (int_reg) |
230 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, mask: 0, data: 0); |
231 | |
232 | err = rtsx_pci_send_cmd(pcr, timeout: 5000); |
233 | if (err < 0) { |
234 | u8 val; |
235 | |
236 | rtsx_pci_read_register(pcr, MS_TRANS_CFG, data: &val); |
237 | dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n" , val); |
238 | |
239 | if (int_reg) |
240 | *int_reg = val & 0x0F; |
241 | |
242 | ms_print_debug_regs(host); |
243 | |
244 | ms_clear_error(host); |
245 | |
246 | if (!(tpc & 0x08)) { |
247 | if (val & MS_CRC16_ERR) |
248 | return -EIO; |
249 | } else { |
250 | if (!(val & 0x80)) { |
251 | if (val & (MS_INT_ERR | MS_INT_CMDNK)) |
252 | return -EIO; |
253 | } |
254 | } |
255 | |
256 | return -ETIMEDOUT; |
257 | } |
258 | |
259 | if (int_reg) { |
260 | u8 *ptr = rtsx_pci_get_cmd_data(pcr) + 1; |
261 | *int_reg = *ptr & 0x0F; |
262 | } |
263 | |
264 | return 0; |
265 | } |
266 | |
267 | static int ms_read_bytes(struct realtek_pci_ms *host, u8 tpc, |
268 | u8 cfg, u8 cnt, u8 *data, u8 *int_reg) |
269 | { |
270 | struct rtsx_pcr *pcr = host->pcr; |
271 | int err, i; |
272 | u8 *ptr; |
273 | |
274 | dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n" , __func__, tpc); |
275 | |
276 | if (!data) |
277 | return -EINVAL; |
278 | |
279 | rtsx_pci_init_cmd(pcr); |
280 | |
281 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, mask: 0xFF, data: tpc); |
282 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, mask: 0xFF, data: cnt); |
283 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, mask: 0xFF, data: cfg); |
284 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, |
285 | mask: 0x01, PINGPONG_BUFFER); |
286 | |
287 | rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, |
288 | mask: 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES); |
289 | rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, |
290 | MS_TRANSFER_END, MS_TRANSFER_END); |
291 | for (i = 0; i < cnt - 1; i++) |
292 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + i, mask: 0, data: 0); |
293 | if (cnt % 2) |
294 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + cnt, mask: 0, data: 0); |
295 | else |
296 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, |
297 | PPBUF_BASE2 + cnt - 1, mask: 0, data: 0); |
298 | if (int_reg) |
299 | rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, mask: 0, data: 0); |
300 | |
301 | err = rtsx_pci_send_cmd(pcr, timeout: 5000); |
302 | if (err < 0) { |
303 | u8 val; |
304 | |
305 | rtsx_pci_read_register(pcr, MS_TRANS_CFG, data: &val); |
306 | dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n" , val); |
307 | |
308 | if (int_reg) |
309 | *int_reg = val & 0x0F; |
310 | |
311 | ms_print_debug_regs(host); |
312 | |
313 | ms_clear_error(host); |
314 | |
315 | if (!(tpc & 0x08)) { |
316 | if (val & MS_CRC16_ERR) |
317 | return -EIO; |
318 | } else { |
319 | if (!(val & 0x80)) { |
320 | if (val & (MS_INT_ERR | MS_INT_CMDNK)) |
321 | return -EIO; |
322 | } |
323 | } |
324 | |
325 | return -ETIMEDOUT; |
326 | } |
327 | |
328 | ptr = rtsx_pci_get_cmd_data(pcr) + 1; |
329 | for (i = 0; i < cnt; i++) |
330 | data[i] = *ptr++; |
331 | |
332 | if (int_reg) |
333 | *int_reg = *ptr & 0x0F; |
334 | |
335 | return 0; |
336 | } |
337 | |
338 | static int rtsx_pci_ms_issue_cmd(struct realtek_pci_ms *host) |
339 | { |
340 | struct memstick_request *req = host->req; |
341 | int err = 0; |
342 | u8 cfg = 0, int_reg; |
343 | |
344 | dev_dbg(ms_dev(host), "%s\n" , __func__); |
345 | |
346 | if (req->need_card_int) { |
347 | if (host->ifmode != MEMSTICK_SERIAL) |
348 | cfg = WAIT_INT; |
349 | } |
350 | |
351 | if (req->long_data) { |
352 | err = ms_transfer_data(host, data_dir: req->data_dir, |
353 | tpc: req->tpc, cfg, sg: &(req->sg)); |
354 | } else { |
355 | if (req->data_dir == READ) { |
356 | err = ms_read_bytes(host, tpc: req->tpc, cfg, |
357 | cnt: req->data_len, data: req->data, int_reg: &int_reg); |
358 | } else { |
359 | err = ms_write_bytes(host, tpc: req->tpc, cfg, |
360 | cnt: req->data_len, data: req->data, int_reg: &int_reg); |
361 | } |
362 | } |
363 | if (err < 0) |
364 | return err; |
365 | |
366 | if (req->need_card_int && (host->ifmode == MEMSTICK_SERIAL)) { |
367 | err = ms_read_bytes(host, tpc: MS_TPC_GET_INT, |
368 | NO_WAIT_INT, cnt: 1, data: &int_reg, NULL); |
369 | if (err < 0) |
370 | return err; |
371 | } |
372 | |
373 | if (req->need_card_int) { |
374 | dev_dbg(ms_dev(host), "int_reg: 0x%02x\n" , int_reg); |
375 | |
376 | if (int_reg & MS_INT_CMDNK) |
377 | req->int_reg |= MEMSTICK_INT_CMDNAK; |
378 | if (int_reg & MS_INT_BREQ) |
379 | req->int_reg |= MEMSTICK_INT_BREQ; |
380 | if (int_reg & MS_INT_ERR) |
381 | req->int_reg |= MEMSTICK_INT_ERR; |
382 | if (int_reg & MS_INT_CED) |
383 | req->int_reg |= MEMSTICK_INT_CED; |
384 | } |
385 | |
386 | return 0; |
387 | } |
388 | |
389 | static void rtsx_pci_ms_handle_req(struct work_struct *work) |
390 | { |
391 | struct realtek_pci_ms *host = container_of(work, |
392 | struct realtek_pci_ms, handle_req); |
393 | struct rtsx_pcr *pcr = host->pcr; |
394 | struct memstick_host *msh = host->msh; |
395 | int rc; |
396 | |
397 | mutex_lock(&pcr->pcr_mutex); |
398 | |
399 | rtsx_pci_start_run(pcr); |
400 | |
401 | rtsx_pci_switch_clock(pcr: host->pcr, card_clock: host->clock, ssc_depth: host->ssc_depth, |
402 | initial_mode: false, double_clk: true, vpclk: false); |
403 | rtsx_pci_write_register(pcr, CARD_SELECT, mask: 0x07, MS_MOD_SEL); |
404 | rtsx_pci_write_register(pcr, CARD_SHARE_MODE, |
405 | CARD_SHARE_MASK, CARD_SHARE_48_MS); |
406 | |
407 | if (!host->req) { |
408 | do { |
409 | rc = memstick_next_req(host: msh, mrq: &host->req); |
410 | dev_dbg(ms_dev(host), "next req %d\n" , rc); |
411 | |
412 | if (!rc) |
413 | host->req->error = rtsx_pci_ms_issue_cmd(host); |
414 | } while (!rc); |
415 | } |
416 | |
417 | mutex_unlock(lock: &pcr->pcr_mutex); |
418 | } |
419 | |
420 | static void rtsx_pci_ms_request(struct memstick_host *msh) |
421 | { |
422 | struct realtek_pci_ms *host = memstick_priv(host: msh); |
423 | |
424 | dev_dbg(ms_dev(host), "--> %s\n" , __func__); |
425 | |
426 | if (rtsx_pci_card_exclusive_check(pcr: host->pcr, RTSX_MS_CARD)) |
427 | return; |
428 | |
429 | schedule_work(work: &host->handle_req); |
430 | } |
431 | |
432 | static int rtsx_pci_ms_set_param(struct memstick_host *msh, |
433 | enum memstick_param param, int value) |
434 | { |
435 | struct realtek_pci_ms *host = memstick_priv(host: msh); |
436 | struct rtsx_pcr *pcr = host->pcr; |
437 | unsigned int clock = 0; |
438 | u8 ssc_depth = 0; |
439 | int err; |
440 | |
441 | dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n" , |
442 | __func__, param, value); |
443 | |
444 | err = rtsx_pci_card_exclusive_check(pcr: host->pcr, RTSX_MS_CARD); |
445 | if (err) |
446 | return err; |
447 | |
448 | switch (param) { |
449 | case MEMSTICK_POWER: |
450 | if (value == MEMSTICK_POWER_ON) |
451 | err = ms_power_on(host); |
452 | else if (value == MEMSTICK_POWER_OFF) |
453 | err = ms_power_off(host); |
454 | else |
455 | return -EINVAL; |
456 | break; |
457 | |
458 | case MEMSTICK_INTERFACE: |
459 | if (value == MEMSTICK_SERIAL) { |
460 | clock = 19000000; |
461 | ssc_depth = RTSX_SSC_DEPTH_500K; |
462 | |
463 | err = rtsx_pci_write_register(pcr, MS_CFG, mask: 0x58, |
464 | MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT); |
465 | if (err < 0) |
466 | return err; |
467 | } else if (value == MEMSTICK_PAR4) { |
468 | clock = 39000000; |
469 | ssc_depth = RTSX_SSC_DEPTH_1M; |
470 | |
471 | err = rtsx_pci_write_register(pcr, MS_CFG, |
472 | mask: 0x58, MS_BUS_WIDTH_4 | PUSH_TIME_ODD); |
473 | if (err < 0) |
474 | return err; |
475 | } else { |
476 | return -EINVAL; |
477 | } |
478 | |
479 | err = rtsx_pci_switch_clock(pcr, card_clock: clock, |
480 | ssc_depth, initial_mode: false, double_clk: true, vpclk: false); |
481 | if (err < 0) |
482 | return err; |
483 | |
484 | host->ssc_depth = ssc_depth; |
485 | host->clock = clock; |
486 | host->ifmode = value; |
487 | break; |
488 | } |
489 | |
490 | return 0; |
491 | } |
492 | |
493 | #ifdef CONFIG_PM |
494 | |
495 | static int rtsx_pci_ms_suspend(struct platform_device *pdev, pm_message_t state) |
496 | { |
497 | struct realtek_pci_ms *host = platform_get_drvdata(pdev); |
498 | struct memstick_host *msh = host->msh; |
499 | |
500 | dev_dbg(ms_dev(host), "--> %s\n" , __func__); |
501 | |
502 | memstick_suspend_host(host: msh); |
503 | return 0; |
504 | } |
505 | |
506 | static int rtsx_pci_ms_resume(struct platform_device *pdev) |
507 | { |
508 | struct realtek_pci_ms *host = platform_get_drvdata(pdev); |
509 | struct memstick_host *msh = host->msh; |
510 | |
511 | dev_dbg(ms_dev(host), "--> %s\n" , __func__); |
512 | |
513 | memstick_resume_host(host: msh); |
514 | return 0; |
515 | } |
516 | |
517 | #else /* CONFIG_PM */ |
518 | |
519 | #define rtsx_pci_ms_suspend NULL |
520 | #define rtsx_pci_ms_resume NULL |
521 | |
522 | #endif /* CONFIG_PM */ |
523 | |
524 | static void rtsx_pci_ms_card_event(struct platform_device *pdev) |
525 | { |
526 | struct realtek_pci_ms *host = platform_get_drvdata(pdev); |
527 | |
528 | memstick_detect_change(host: host->msh); |
529 | } |
530 | |
531 | static int rtsx_pci_ms_drv_probe(struct platform_device *pdev) |
532 | { |
533 | struct memstick_host *msh; |
534 | struct realtek_pci_ms *host; |
535 | struct rtsx_pcr *pcr; |
536 | struct pcr_handle *handle = pdev->dev.platform_data; |
537 | int rc; |
538 | |
539 | if (!handle) |
540 | return -ENXIO; |
541 | |
542 | pcr = handle->pcr; |
543 | if (!pcr) |
544 | return -ENXIO; |
545 | |
546 | dev_dbg(&(pdev->dev), |
547 | ": Realtek PCI-E Memstick controller found\n" ); |
548 | |
549 | msh = memstick_alloc_host(extra: sizeof(*host), dev: &pdev->dev); |
550 | if (!msh) |
551 | return -ENOMEM; |
552 | |
553 | host = memstick_priv(host: msh); |
554 | host->pcr = pcr; |
555 | host->msh = msh; |
556 | host->pdev = pdev; |
557 | platform_set_drvdata(pdev, data: host); |
558 | pcr->slots[RTSX_MS_CARD].p_dev = pdev; |
559 | pcr->slots[RTSX_MS_CARD].card_event = rtsx_pci_ms_card_event; |
560 | |
561 | mutex_init(&host->host_mutex); |
562 | |
563 | INIT_WORK(&host->handle_req, rtsx_pci_ms_handle_req); |
564 | msh->request = rtsx_pci_ms_request; |
565 | msh->set_param = rtsx_pci_ms_set_param; |
566 | msh->caps = MEMSTICK_CAP_PAR4; |
567 | |
568 | rc = memstick_add_host(host: msh); |
569 | if (rc) { |
570 | memstick_free_host(host: msh); |
571 | return rc; |
572 | } |
573 | |
574 | return 0; |
575 | } |
576 | |
577 | static int rtsx_pci_ms_drv_remove(struct platform_device *pdev) |
578 | { |
579 | struct realtek_pci_ms *host = platform_get_drvdata(pdev); |
580 | struct rtsx_pcr *pcr; |
581 | struct memstick_host *msh; |
582 | int rc; |
583 | |
584 | if (!host) |
585 | return 0; |
586 | |
587 | pcr = host->pcr; |
588 | pcr->slots[RTSX_MS_CARD].p_dev = NULL; |
589 | pcr->slots[RTSX_MS_CARD].card_event = NULL; |
590 | msh = host->msh; |
591 | host->eject = true; |
592 | cancel_work_sync(work: &host->handle_req); |
593 | |
594 | mutex_lock(&host->host_mutex); |
595 | if (host->req) { |
596 | dev_dbg(&(pdev->dev), |
597 | "%s: Controller removed during transfer\n" , |
598 | dev_name(&msh->dev)); |
599 | |
600 | rtsx_pci_complete_unfinished_transfer(pcr); |
601 | |
602 | host->req->error = -ENOMEDIUM; |
603 | do { |
604 | rc = memstick_next_req(host: msh, mrq: &host->req); |
605 | if (!rc) |
606 | host->req->error = -ENOMEDIUM; |
607 | } while (!rc); |
608 | } |
609 | mutex_unlock(lock: &host->host_mutex); |
610 | |
611 | memstick_remove_host(host: msh); |
612 | memstick_free_host(host: msh); |
613 | |
614 | dev_dbg(&(pdev->dev), |
615 | ": Realtek PCI-E Memstick controller has been removed\n" ); |
616 | |
617 | return 0; |
618 | } |
619 | |
620 | static struct platform_device_id rtsx_pci_ms_ids[] = { |
621 | { |
622 | .name = DRV_NAME_RTSX_PCI_MS, |
623 | }, { |
624 | /* sentinel */ |
625 | } |
626 | }; |
627 | MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids); |
628 | |
629 | static struct platform_driver rtsx_pci_ms_driver = { |
630 | .probe = rtsx_pci_ms_drv_probe, |
631 | .remove = rtsx_pci_ms_drv_remove, |
632 | .id_table = rtsx_pci_ms_ids, |
633 | .suspend = rtsx_pci_ms_suspend, |
634 | .resume = rtsx_pci_ms_resume, |
635 | .driver = { |
636 | .name = DRV_NAME_RTSX_PCI_MS, |
637 | }, |
638 | }; |
639 | module_platform_driver(rtsx_pci_ms_driver); |
640 | |
641 | MODULE_LICENSE("GPL" ); |
642 | MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>" ); |
643 | MODULE_DESCRIPTION("Realtek PCI-E Memstick Card Host Driver" ); |
644 | |