]>
Commit | Line | Data |
---|---|---|
60fdd931 AD |
1 | /* |
2 | * jmb38x_ms.c - JMicron jmb38x MemoryStick card reader | |
3 | * | |
4 | * Copyright (C) 2008 Alex Dubov <oakad@yahoo.com> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | */ | |
11 | ||
12 | #include <linux/spinlock.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/pci.h> | |
d3597ea2 | 15 | #include <linux/dma-mapping.h> |
60fdd931 AD |
16 | #include <linux/delay.h> |
17 | #include <linux/highmem.h> | |
18 | #include <linux/memstick.h> | |
5a0e3ad6 | 19 | #include <linux/slab.h> |
c47e7893 | 20 | #include <linux/module.h> |
60fdd931 AD |
21 | |
22 | #define DRIVER_NAME "jmb38x_ms" | |
23 | ||
90ab5ee9 | 24 | static bool no_dma; |
60fdd931 AD |
25 | module_param(no_dma, bool, 0644); |
26 | ||
27 | enum { | |
28 | DMA_ADDRESS = 0x00, | |
29 | BLOCK = 0x04, | |
30 | DMA_CONTROL = 0x08, | |
31 | TPC_P0 = 0x0c, | |
32 | TPC_P1 = 0x10, | |
33 | TPC = 0x14, | |
34 | HOST_CONTROL = 0x18, | |
35 | DATA = 0x1c, | |
36 | STATUS = 0x20, | |
37 | INT_STATUS = 0x24, | |
38 | INT_STATUS_ENABLE = 0x28, | |
39 | INT_SIGNAL_ENABLE = 0x2c, | |
40 | TIMER = 0x30, | |
41 | TIMER_CONTROL = 0x34, | |
42 | PAD_OUTPUT_ENABLE = 0x38, | |
43 | PAD_PU_PD = 0x3c, | |
44 | CLOCK_DELAY = 0x40, | |
45 | ADMA_ADDRESS = 0x44, | |
46 | CLOCK_CONTROL = 0x48, | |
47 | LED_CONTROL = 0x4c, | |
48 | VERSION = 0x50 | |
49 | }; | |
50 | ||
51 | struct jmb38x_ms_host { | |
52 | struct jmb38x_ms *chip; | |
53 | void __iomem *addr; | |
54 | spinlock_t lock; | |
f1d82698 | 55 | struct tasklet_struct notify; |
60fdd931 | 56 | int id; |
b98cb4b7 | 57 | char host_id[32]; |
60fdd931 AD |
58 | int irq; |
59 | unsigned int block_pos; | |
60 | unsigned long timeout_jiffies; | |
61 | struct timer_list timer; | |
62 | struct memstick_request *req; | |
60fdd931 AD |
63 | unsigned char cmd_flags; |
64 | unsigned char io_pos; | |
23c5947a | 65 | unsigned char ifmode; |
60fdd931 AD |
66 | unsigned int io_word[2]; |
67 | }; | |
68 | ||
69 | struct jmb38x_ms { | |
70 | struct pci_dev *pdev; | |
71 | int host_cnt; | |
72 | struct memstick_host *hosts[]; | |
73 | }; | |
74 | ||
75 | #define BLOCK_COUNT_MASK 0xffff0000 | |
76 | #define BLOCK_SIZE_MASK 0x00000fff | |
77 | ||
78 | #define DMA_CONTROL_ENABLE 0x00000001 | |
79 | ||
80 | #define TPC_DATA_SEL 0x00008000 | |
81 | #define TPC_DIR 0x00004000 | |
82 | #define TPC_WAIT_INT 0x00002000 | |
83 | #define TPC_GET_INT 0x00000800 | |
84 | #define TPC_CODE_SZ_MASK 0x00000700 | |
85 | #define TPC_DATA_SZ_MASK 0x00000007 | |
86 | ||
8e82f8c3 AD |
87 | #define HOST_CONTROL_TDELAY_EN 0x00040000 |
88 | #define HOST_CONTROL_HW_OC_P 0x00010000 | |
60fdd931 AD |
89 | #define HOST_CONTROL_RESET_REQ 0x00008000 |
90 | #define HOST_CONTROL_REI 0x00004000 | |
91 | #define HOST_CONTROL_LED 0x00000400 | |
92 | #define HOST_CONTROL_FAST_CLK 0x00000200 | |
93 | #define HOST_CONTROL_RESET 0x00000100 | |
94 | #define HOST_CONTROL_POWER_EN 0x00000080 | |
95 | #define HOST_CONTROL_CLOCK_EN 0x00000040 | |
8e82f8c3 | 96 | #define HOST_CONTROL_REO 0x00000008 |
60fdd931 AD |
97 | #define HOST_CONTROL_IF_SHIFT 4 |
98 | ||
99 | #define HOST_CONTROL_IF_SERIAL 0x0 | |
100 | #define HOST_CONTROL_IF_PAR4 0x1 | |
101 | #define HOST_CONTROL_IF_PAR8 0x3 | |
102 | ||
ead70773 AD |
103 | #define STATUS_BUSY 0x00080000 |
104 | #define STATUS_MS_DAT7 0x00040000 | |
105 | #define STATUS_MS_DAT6 0x00020000 | |
106 | #define STATUS_MS_DAT5 0x00010000 | |
107 | #define STATUS_MS_DAT4 0x00008000 | |
108 | #define STATUS_MS_DAT3 0x00004000 | |
109 | #define STATUS_MS_DAT2 0x00002000 | |
110 | #define STATUS_MS_DAT1 0x00001000 | |
111 | #define STATUS_MS_DAT0 0x00000800 | |
60fdd931 AD |
112 | #define STATUS_HAS_MEDIA 0x00000400 |
113 | #define STATUS_FIFO_EMPTY 0x00000200 | |
114 | #define STATUS_FIFO_FULL 0x00000100 | |
ead70773 AD |
115 | #define STATUS_MS_CED 0x00000080 |
116 | #define STATUS_MS_ERR 0x00000040 | |
117 | #define STATUS_MS_BRQ 0x00000020 | |
118 | #define STATUS_MS_CNK 0x00000001 | |
60fdd931 AD |
119 | |
120 | #define INT_STATUS_TPC_ERR 0x00080000 | |
121 | #define INT_STATUS_CRC_ERR 0x00040000 | |
122 | #define INT_STATUS_TIMER_TO 0x00020000 | |
123 | #define INT_STATUS_HSK_TO 0x00010000 | |
124 | #define INT_STATUS_ANY_ERR 0x00008000 | |
125 | #define INT_STATUS_FIFO_WRDY 0x00000080 | |
126 | #define INT_STATUS_FIFO_RRDY 0x00000040 | |
127 | #define INT_STATUS_MEDIA_OUT 0x00000010 | |
128 | #define INT_STATUS_MEDIA_IN 0x00000008 | |
129 | #define INT_STATUS_DMA_BOUNDARY 0x00000004 | |
130 | #define INT_STATUS_EOTRAN 0x00000002 | |
131 | #define INT_STATUS_EOTPC 0x00000001 | |
132 | ||
133 | #define INT_STATUS_ALL 0x000f801f | |
134 | ||
135 | #define PAD_OUTPUT_ENABLE_MS 0x0F3F | |
136 | ||
137 | #define PAD_PU_PD_OFF 0x7FFF0000 | |
138 | #define PAD_PU_PD_ON_MS_SOCK0 0x5f8f0000 | |
139 | #define PAD_PU_PD_ON_MS_SOCK1 0x0f0f0000 | |
140 | ||
23c5947a | 141 | #define CLOCK_CONTROL_BY_MMIO 0x00000008 |
cf821e8f | 142 | #define CLOCK_CONTROL_40MHZ 0x00000001 |
23c5947a TI |
143 | #define CLOCK_CONTROL_50MHZ 0x00000002 |
144 | #define CLOCK_CONTROL_60MHZ 0x00000010 | |
145 | #define CLOCK_CONTROL_62_5MHZ 0x00000004 | |
cf821e8f AD |
146 | #define CLOCK_CONTROL_OFF 0x00000000 |
147 | ||
8e82f8c3 | 148 | #define PCI_CTL_CLOCK_DLY_ADDR 0x000000b0 |
8e82f8c3 | 149 | |
60fdd931 AD |
150 | enum { |
151 | CMD_READY = 0x01, | |
152 | FIFO_READY = 0x02, | |
153 | REG_DATA = 0x04, | |
ead70773 | 154 | DMA_DATA = 0x08 |
60fdd931 AD |
155 | }; |
156 | ||
157 | static unsigned int jmb38x_ms_read_data(struct jmb38x_ms_host *host, | |
158 | unsigned char *buf, unsigned int length) | |
159 | { | |
160 | unsigned int off = 0; | |
161 | ||
162 | while (host->io_pos && length) { | |
163 | buf[off++] = host->io_word[0] & 0xff; | |
164 | host->io_word[0] >>= 8; | |
165 | length--; | |
166 | host->io_pos--; | |
167 | } | |
168 | ||
169 | if (!length) | |
170 | return off; | |
171 | ||
172 | while (!(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) { | |
173 | if (length < 4) | |
174 | break; | |
175 | *(unsigned int *)(buf + off) = __raw_readl(host->addr + DATA); | |
176 | length -= 4; | |
177 | off += 4; | |
178 | } | |
179 | ||
180 | if (length | |
181 | && !(STATUS_FIFO_EMPTY & readl(host->addr + STATUS))) { | |
182 | host->io_word[0] = readl(host->addr + DATA); | |
183 | for (host->io_pos = 4; host->io_pos; --host->io_pos) { | |
184 | buf[off++] = host->io_word[0] & 0xff; | |
185 | host->io_word[0] >>= 8; | |
186 | length--; | |
187 | if (!length) | |
188 | break; | |
189 | } | |
190 | } | |
191 | ||
192 | return off; | |
193 | } | |
194 | ||
195 | static unsigned int jmb38x_ms_read_reg_data(struct jmb38x_ms_host *host, | |
196 | unsigned char *buf, | |
197 | unsigned int length) | |
198 | { | |
199 | unsigned int off = 0; | |
200 | ||
201 | while (host->io_pos > 4 && length) { | |
202 | buf[off++] = host->io_word[0] & 0xff; | |
203 | host->io_word[0] >>= 8; | |
204 | length--; | |
205 | host->io_pos--; | |
206 | } | |
207 | ||
208 | if (!length) | |
209 | return off; | |
210 | ||
211 | while (host->io_pos && length) { | |
212 | buf[off++] = host->io_word[1] & 0xff; | |
213 | host->io_word[1] >>= 8; | |
214 | length--; | |
215 | host->io_pos--; | |
216 | } | |
217 | ||
218 | return off; | |
219 | } | |
220 | ||
221 | static unsigned int jmb38x_ms_write_data(struct jmb38x_ms_host *host, | |
222 | unsigned char *buf, | |
223 | unsigned int length) | |
224 | { | |
225 | unsigned int off = 0; | |
226 | ||
227 | if (host->io_pos) { | |
228 | while (host->io_pos < 4 && length) { | |
229 | host->io_word[0] |= buf[off++] << (host->io_pos * 8); | |
230 | host->io_pos++; | |
231 | length--; | |
232 | } | |
233 | } | |
234 | ||
235 | if (host->io_pos == 4 | |
236 | && !(STATUS_FIFO_FULL & readl(host->addr + STATUS))) { | |
237 | writel(host->io_word[0], host->addr + DATA); | |
238 | host->io_pos = 0; | |
239 | host->io_word[0] = 0; | |
240 | } else if (host->io_pos) { | |
241 | return off; | |
242 | } | |
243 | ||
244 | if (!length) | |
245 | return off; | |
246 | ||
247 | while (!(STATUS_FIFO_FULL & readl(host->addr + STATUS))) { | |
248 | if (length < 4) | |
249 | break; | |
250 | ||
251 | __raw_writel(*(unsigned int *)(buf + off), | |
252 | host->addr + DATA); | |
253 | length -= 4; | |
254 | off += 4; | |
255 | } | |
256 | ||
257 | switch (length) { | |
258 | case 3: | |
259 | host->io_word[0] |= buf[off + 2] << 16; | |
260 | host->io_pos++; | |
261 | case 2: | |
262 | host->io_word[0] |= buf[off + 1] << 8; | |
263 | host->io_pos++; | |
264 | case 1: | |
265 | host->io_word[0] |= buf[off]; | |
266 | host->io_pos++; | |
267 | } | |
268 | ||
269 | off += host->io_pos; | |
270 | ||
271 | return off; | |
272 | } | |
273 | ||
274 | static unsigned int jmb38x_ms_write_reg_data(struct jmb38x_ms_host *host, | |
275 | unsigned char *buf, | |
276 | unsigned int length) | |
277 | { | |
278 | unsigned int off = 0; | |
279 | ||
280 | while (host->io_pos < 4 && length) { | |
281 | host->io_word[0] &= ~(0xff << (host->io_pos * 8)); | |
282 | host->io_word[0] |= buf[off++] << (host->io_pos * 8); | |
283 | host->io_pos++; | |
284 | length--; | |
285 | } | |
286 | ||
287 | if (!length) | |
288 | return off; | |
289 | ||
290 | while (host->io_pos < 8 && length) { | |
291 | host->io_word[1] &= ~(0xff << (host->io_pos * 8)); | |
292 | host->io_word[1] |= buf[off++] << (host->io_pos * 8); | |
293 | host->io_pos++; | |
294 | length--; | |
295 | } | |
296 | ||
297 | return off; | |
298 | } | |
299 | ||
300 | static int jmb38x_ms_transfer_data(struct jmb38x_ms_host *host) | |
301 | { | |
302 | unsigned int length; | |
303 | unsigned int off; | |
8195096b | 304 | unsigned int t_size, p_cnt; |
60fdd931 AD |
305 | unsigned char *buf; |
306 | struct page *pg; | |
307 | unsigned long flags = 0; | |
308 | ||
309 | if (host->req->long_data) { | |
310 | length = host->req->sg.length - host->block_pos; | |
311 | off = host->req->sg.offset + host->block_pos; | |
312 | } else { | |
313 | length = host->req->data_len - host->block_pos; | |
314 | off = 0; | |
315 | } | |
316 | ||
317 | while (length) { | |
8195096b AM |
318 | unsigned int uninitialized_var(p_off); |
319 | ||
60fdd931 AD |
320 | if (host->req->long_data) { |
321 | pg = nth_page(sg_page(&host->req->sg), | |
322 | off >> PAGE_SHIFT); | |
323 | p_off = offset_in_page(off); | |
324 | p_cnt = PAGE_SIZE - p_off; | |
325 | p_cnt = min(p_cnt, length); | |
326 | ||
327 | local_irq_save(flags); | |
328 | buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + p_off; | |
329 | } else { | |
330 | buf = host->req->data + host->block_pos; | |
331 | p_cnt = host->req->data_len - host->block_pos; | |
332 | } | |
333 | ||
334 | if (host->req->data_dir == WRITE) | |
335 | t_size = !(host->cmd_flags & REG_DATA) | |
336 | ? jmb38x_ms_write_data(host, buf, p_cnt) | |
337 | : jmb38x_ms_write_reg_data(host, buf, p_cnt); | |
338 | else | |
339 | t_size = !(host->cmd_flags & REG_DATA) | |
340 | ? jmb38x_ms_read_data(host, buf, p_cnt) | |
341 | : jmb38x_ms_read_reg_data(host, buf, p_cnt); | |
342 | ||
343 | if (host->req->long_data) { | |
344 | kunmap_atomic(buf - p_off, KM_BIO_SRC_IRQ); | |
345 | local_irq_restore(flags); | |
346 | } | |
347 | ||
348 | if (!t_size) | |
349 | break; | |
350 | host->block_pos += t_size; | |
351 | length -= t_size; | |
352 | off += t_size; | |
353 | } | |
354 | ||
355 | if (!length && host->req->data_dir == WRITE) { | |
356 | if (host->cmd_flags & REG_DATA) { | |
357 | writel(host->io_word[0], host->addr + TPC_P0); | |
358 | writel(host->io_word[1], host->addr + TPC_P1); | |
359 | } else if (host->io_pos) { | |
360 | writel(host->io_word[0], host->addr + DATA); | |
361 | } | |
362 | } | |
363 | ||
364 | return length; | |
365 | } | |
366 | ||
367 | static int jmb38x_ms_issue_cmd(struct memstick_host *msh) | |
368 | { | |
369 | struct jmb38x_ms_host *host = memstick_priv(msh); | |
370 | unsigned char *data; | |
371 | unsigned int data_len, cmd, t_val; | |
372 | ||
373 | if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) { | |
c4c66cf1 | 374 | dev_dbg(&msh->dev, "no media status\n"); |
60fdd931 AD |
375 | host->req->error = -ETIME; |
376 | return host->req->error; | |
377 | } | |
378 | ||
8e82f8c3 | 379 | dev_dbg(&msh->dev, "control %08x\n", readl(host->addr + HOST_CONTROL)); |
c4c66cf1 GKH |
380 | dev_dbg(&msh->dev, "status %08x\n", readl(host->addr + INT_STATUS)); |
381 | dev_dbg(&msh->dev, "hstatus %08x\n", readl(host->addr + STATUS)); | |
60fdd931 AD |
382 | |
383 | host->cmd_flags = 0; | |
384 | host->block_pos = 0; | |
385 | host->io_pos = 0; | |
386 | host->io_word[0] = 0; | |
387 | host->io_word[1] = 0; | |
388 | ||
389 | cmd = host->req->tpc << 16; | |
390 | cmd |= TPC_DATA_SEL; | |
391 | ||
392 | if (host->req->data_dir == READ) | |
393 | cmd |= TPC_DIR; | |
23c5947a TI |
394 | |
395 | if (host->req->need_card_int) { | |
396 | if (host->ifmode == MEMSTICK_SERIAL) | |
397 | cmd |= TPC_GET_INT; | |
398 | else | |
399 | cmd |= TPC_WAIT_INT; | |
400 | } | |
60fdd931 AD |
401 | |
402 | data = host->req->data; | |
403 | ||
ead70773 AD |
404 | if (!no_dma) |
405 | host->cmd_flags |= DMA_DATA; | |
60fdd931 AD |
406 | |
407 | if (host->req->long_data) { | |
408 | data_len = host->req->sg.length; | |
409 | } else { | |
410 | data_len = host->req->data_len; | |
ead70773 | 411 | host->cmd_flags &= ~DMA_DATA; |
60fdd931 AD |
412 | } |
413 | ||
414 | if (data_len <= 8) { | |
415 | cmd &= ~(TPC_DATA_SEL | 0xf); | |
416 | host->cmd_flags |= REG_DATA; | |
417 | cmd |= data_len & 0xf; | |
ead70773 | 418 | host->cmd_flags &= ~DMA_DATA; |
60fdd931 AD |
419 | } |
420 | ||
ead70773 | 421 | if (host->cmd_flags & DMA_DATA) { |
60fdd931 AD |
422 | if (1 != pci_map_sg(host->chip->pdev, &host->req->sg, 1, |
423 | host->req->data_dir == READ | |
424 | ? PCI_DMA_FROMDEVICE | |
425 | : PCI_DMA_TODEVICE)) { | |
426 | host->req->error = -ENOMEM; | |
427 | return host->req->error; | |
428 | } | |
429 | data_len = sg_dma_len(&host->req->sg); | |
430 | writel(sg_dma_address(&host->req->sg), | |
431 | host->addr + DMA_ADDRESS); | |
432 | writel(((1 << 16) & BLOCK_COUNT_MASK) | |
433 | | (data_len & BLOCK_SIZE_MASK), | |
434 | host->addr + BLOCK); | |
435 | writel(DMA_CONTROL_ENABLE, host->addr + DMA_CONTROL); | |
436 | } else if (!(host->cmd_flags & REG_DATA)) { | |
437 | writel(((1 << 16) & BLOCK_COUNT_MASK) | |
438 | | (data_len & BLOCK_SIZE_MASK), | |
439 | host->addr + BLOCK); | |
440 | t_val = readl(host->addr + INT_STATUS_ENABLE); | |
441 | t_val |= host->req->data_dir == READ | |
442 | ? INT_STATUS_FIFO_RRDY | |
443 | : INT_STATUS_FIFO_WRDY; | |
444 | ||
445 | writel(t_val, host->addr + INT_STATUS_ENABLE); | |
446 | writel(t_val, host->addr + INT_SIGNAL_ENABLE); | |
447 | } else { | |
448 | cmd &= ~(TPC_DATA_SEL | 0xf); | |
449 | host->cmd_flags |= REG_DATA; | |
450 | cmd |= data_len & 0xf; | |
451 | ||
452 | if (host->req->data_dir == WRITE) { | |
453 | jmb38x_ms_transfer_data(host); | |
454 | writel(host->io_word[0], host->addr + TPC_P0); | |
455 | writel(host->io_word[1], host->addr + TPC_P1); | |
456 | } | |
457 | } | |
458 | ||
459 | mod_timer(&host->timer, jiffies + host->timeout_jiffies); | |
460 | writel(HOST_CONTROL_LED | readl(host->addr + HOST_CONTROL), | |
461 | host->addr + HOST_CONTROL); | |
462 | host->req->error = 0; | |
463 | ||
464 | writel(cmd, host->addr + TPC); | |
c4c66cf1 | 465 | dev_dbg(&msh->dev, "executing TPC %08x, len %x\n", cmd, data_len); |
60fdd931 AD |
466 | |
467 | return 0; | |
468 | } | |
469 | ||
470 | static void jmb38x_ms_complete_cmd(struct memstick_host *msh, int last) | |
471 | { | |
472 | struct jmb38x_ms_host *host = memstick_priv(msh); | |
473 | unsigned int t_val = 0; | |
474 | int rc; | |
475 | ||
476 | del_timer(&host->timer); | |
477 | ||
c4c66cf1 | 478 | dev_dbg(&msh->dev, "c control %08x\n", |
60fdd931 | 479 | readl(host->addr + HOST_CONTROL)); |
c4c66cf1 | 480 | dev_dbg(&msh->dev, "c status %08x\n", |
60fdd931 | 481 | readl(host->addr + INT_STATUS)); |
c4c66cf1 | 482 | dev_dbg(&msh->dev, "c hstatus %08x\n", readl(host->addr + STATUS)); |
60fdd931 | 483 | |
ead70773 AD |
484 | host->req->int_reg = readl(host->addr + STATUS) & 0xff; |
485 | ||
486 | writel(0, host->addr + BLOCK); | |
487 | writel(0, host->addr + DMA_CONTROL); | |
60fdd931 | 488 | |
ead70773 | 489 | if (host->cmd_flags & DMA_DATA) { |
60fdd931 AD |
490 | pci_unmap_sg(host->chip->pdev, &host->req->sg, 1, |
491 | host->req->data_dir == READ | |
492 | ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); | |
493 | } else { | |
494 | t_val = readl(host->addr + INT_STATUS_ENABLE); | |
495 | if (host->req->data_dir == READ) | |
496 | t_val &= ~INT_STATUS_FIFO_RRDY; | |
497 | else | |
498 | t_val &= ~INT_STATUS_FIFO_WRDY; | |
499 | ||
500 | writel(t_val, host->addr + INT_STATUS_ENABLE); | |
501 | writel(t_val, host->addr + INT_SIGNAL_ENABLE); | |
502 | } | |
503 | ||
504 | writel((~HOST_CONTROL_LED) & readl(host->addr + HOST_CONTROL), | |
505 | host->addr + HOST_CONTROL); | |
506 | ||
507 | if (!last) { | |
508 | do { | |
509 | rc = memstick_next_req(msh, &host->req); | |
510 | } while (!rc && jmb38x_ms_issue_cmd(msh)); | |
511 | } else { | |
512 | do { | |
513 | rc = memstick_next_req(msh, &host->req); | |
514 | if (!rc) | |
515 | host->req->error = -ETIME; | |
516 | } while (!rc); | |
517 | } | |
518 | } | |
519 | ||
520 | static irqreturn_t jmb38x_ms_isr(int irq, void *dev_id) | |
521 | { | |
522 | struct memstick_host *msh = dev_id; | |
523 | struct jmb38x_ms_host *host = memstick_priv(msh); | |
524 | unsigned int irq_status; | |
525 | ||
526 | spin_lock(&host->lock); | |
527 | irq_status = readl(host->addr + INT_STATUS); | |
528 | dev_dbg(&host->chip->pdev->dev, "irq_status = %08x\n", irq_status); | |
529 | if (irq_status == 0 || irq_status == (~0)) { | |
530 | spin_unlock(&host->lock); | |
531 | return IRQ_NONE; | |
532 | } | |
533 | ||
534 | if (host->req) { | |
535 | if (irq_status & INT_STATUS_ANY_ERR) { | |
536 | if (irq_status & INT_STATUS_CRC_ERR) | |
537 | host->req->error = -EILSEQ; | |
23c5947a TI |
538 | else if (irq_status & INT_STATUS_TPC_ERR) { |
539 | dev_dbg(&host->chip->pdev->dev, "TPC_ERR\n"); | |
540 | jmb38x_ms_complete_cmd(msh, 0); | |
541 | } else | |
60fdd931 AD |
542 | host->req->error = -ETIME; |
543 | } else { | |
ead70773 | 544 | if (host->cmd_flags & DMA_DATA) { |
60fdd931 AD |
545 | if (irq_status & INT_STATUS_EOTRAN) |
546 | host->cmd_flags |= FIFO_READY; | |
547 | } else { | |
548 | if (irq_status & (INT_STATUS_FIFO_RRDY | |
549 | | INT_STATUS_FIFO_WRDY)) | |
550 | jmb38x_ms_transfer_data(host); | |
551 | ||
552 | if (irq_status & INT_STATUS_EOTRAN) { | |
553 | jmb38x_ms_transfer_data(host); | |
554 | host->cmd_flags |= FIFO_READY; | |
555 | } | |
556 | } | |
557 | ||
558 | if (irq_status & INT_STATUS_EOTPC) { | |
559 | host->cmd_flags |= CMD_READY; | |
560 | if (host->cmd_flags & REG_DATA) { | |
561 | if (host->req->data_dir == READ) { | |
562 | host->io_word[0] | |
563 | = readl(host->addr | |
564 | + TPC_P0); | |
565 | host->io_word[1] | |
566 | = readl(host->addr | |
567 | + TPC_P1); | |
568 | host->io_pos = 8; | |
569 | ||
570 | jmb38x_ms_transfer_data(host); | |
571 | } | |
572 | host->cmd_flags |= FIFO_READY; | |
573 | } | |
574 | } | |
575 | } | |
576 | } | |
577 | ||
578 | if (irq_status & (INT_STATUS_MEDIA_IN | INT_STATUS_MEDIA_OUT)) { | |
579 | dev_dbg(&host->chip->pdev->dev, "media changed\n"); | |
580 | memstick_detect_change(msh); | |
581 | } | |
582 | ||
583 | writel(irq_status, host->addr + INT_STATUS); | |
584 | ||
585 | if (host->req | |
586 | && (((host->cmd_flags & CMD_READY) | |
587 | && (host->cmd_flags & FIFO_READY)) | |
588 | || host->req->error)) | |
589 | jmb38x_ms_complete_cmd(msh, 0); | |
590 | ||
591 | spin_unlock(&host->lock); | |
592 | return IRQ_HANDLED; | |
593 | } | |
594 | ||
595 | static void jmb38x_ms_abort(unsigned long data) | |
596 | { | |
597 | struct memstick_host *msh = (struct memstick_host *)data; | |
598 | struct jmb38x_ms_host *host = memstick_priv(msh); | |
599 | unsigned long flags; | |
600 | ||
601 | dev_dbg(&host->chip->pdev->dev, "abort\n"); | |
602 | spin_lock_irqsave(&host->lock, flags); | |
603 | if (host->req) { | |
604 | host->req->error = -ETIME; | |
605 | jmb38x_ms_complete_cmd(msh, 0); | |
606 | } | |
607 | spin_unlock_irqrestore(&host->lock, flags); | |
608 | } | |
609 | ||
f1d82698 | 610 | static void jmb38x_ms_req_tasklet(unsigned long data) |
60fdd931 | 611 | { |
f1d82698 | 612 | struct memstick_host *msh = (struct memstick_host *)data; |
60fdd931 AD |
613 | struct jmb38x_ms_host *host = memstick_priv(msh); |
614 | unsigned long flags; | |
615 | int rc; | |
616 | ||
617 | spin_lock_irqsave(&host->lock, flags); | |
f1d82698 AD |
618 | if (!host->req) { |
619 | do { | |
620 | rc = memstick_next_req(msh, &host->req); | |
621 | dev_dbg(&host->chip->pdev->dev, "tasklet req %d\n", rc); | |
622 | } while (!rc && jmb38x_ms_issue_cmd(msh)); | |
60fdd931 | 623 | } |
60fdd931 AD |
624 | spin_unlock_irqrestore(&host->lock, flags); |
625 | } | |
626 | ||
f1d82698 AD |
627 | static void jmb38x_ms_dummy_submit(struct memstick_host *msh) |
628 | { | |
629 | return; | |
630 | } | |
631 | ||
632 | static void jmb38x_ms_submit_req(struct memstick_host *msh) | |
633 | { | |
634 | struct jmb38x_ms_host *host = memstick_priv(msh); | |
635 | ||
636 | tasklet_schedule(&host->notify); | |
637 | } | |
638 | ||
b7789998 | 639 | static int jmb38x_ms_reset(struct jmb38x_ms_host *host) |
60fdd931 | 640 | { |
b7789998 | 641 | int cnt; |
60fdd931 | 642 | |
b7789998 AD |
643 | writel(HOST_CONTROL_RESET_REQ | HOST_CONTROL_CLOCK_EN |
644 | | readl(host->addr + HOST_CONTROL), | |
645 | host->addr + HOST_CONTROL); | |
646 | mmiowb(); | |
647 | ||
648 | for (cnt = 0; cnt < 20; ++cnt) { | |
649 | if (!(HOST_CONTROL_RESET_REQ | |
650 | & readl(host->addr + HOST_CONTROL))) | |
651 | goto reset_next; | |
60fdd931 | 652 | |
cf821e8f | 653 | ndelay(20); |
60fdd931 | 654 | } |
b7789998 | 655 | dev_dbg(&host->chip->pdev->dev, "reset_req timeout\n"); |
60fdd931 | 656 | |
b7789998 AD |
657 | reset_next: |
658 | writel(HOST_CONTROL_RESET | HOST_CONTROL_CLOCK_EN | |
659 | | readl(host->addr + HOST_CONTROL), | |
660 | host->addr + HOST_CONTROL); | |
661 | mmiowb(); | |
662 | ||
663 | for (cnt = 0; cnt < 20; ++cnt) { | |
664 | if (!(HOST_CONTROL_RESET | |
665 | & readl(host->addr + HOST_CONTROL))) | |
666 | goto reset_ok; | |
667 | ||
668 | ndelay(20); | |
669 | } | |
670 | dev_dbg(&host->chip->pdev->dev, "reset timeout\n"); | |
671 | return -EIO; | |
672 | ||
673 | reset_ok: | |
cf821e8f | 674 | mmiowb(); |
60fdd931 | 675 | writel(INT_STATUS_ALL, host->addr + INT_SIGNAL_ENABLE); |
cf821e8f | 676 | writel(INT_STATUS_ALL, host->addr + INT_STATUS_ENABLE); |
b7789998 | 677 | return 0; |
60fdd931 AD |
678 | } |
679 | ||
b7789998 AD |
680 | static int jmb38x_ms_set_param(struct memstick_host *msh, |
681 | enum memstick_param param, | |
682 | int value) | |
60fdd931 AD |
683 | { |
684 | struct jmb38x_ms_host *host = memstick_priv(msh); | |
cf821e8f | 685 | unsigned int host_ctl = readl(host->addr + HOST_CONTROL); |
23c5947a | 686 | unsigned int clock_ctl = CLOCK_CONTROL_BY_MMIO, clock_delay = 0; |
b7789998 | 687 | int rc = 0; |
60fdd931 AD |
688 | |
689 | switch (param) { | |
690 | case MEMSTICK_POWER: | |
691 | if (value == MEMSTICK_POWER_ON) { | |
b7789998 AD |
692 | rc = jmb38x_ms_reset(host); |
693 | if (rc) | |
694 | return rc; | |
695 | ||
696 | host_ctl = 7; | |
697 | host_ctl |= HOST_CONTROL_POWER_EN | |
23c5947a | 698 | | HOST_CONTROL_CLOCK_EN; |
b7789998 | 699 | writel(host_ctl, host->addr + HOST_CONTROL); |
60fdd931 AD |
700 | |
701 | writel(host->id ? PAD_PU_PD_ON_MS_SOCK1 | |
cf821e8f | 702 | : PAD_PU_PD_ON_MS_SOCK0, |
60fdd931 AD |
703 | host->addr + PAD_PU_PD); |
704 | ||
705 | writel(PAD_OUTPUT_ENABLE_MS, | |
706 | host->addr + PAD_OUTPUT_ENABLE); | |
707 | ||
b7789998 | 708 | msleep(10); |
60fdd931 AD |
709 | dev_dbg(&host->chip->pdev->dev, "power on\n"); |
710 | } else if (value == MEMSTICK_POWER_OFF) { | |
cf821e8f AD |
711 | host_ctl &= ~(HOST_CONTROL_POWER_EN |
712 | | HOST_CONTROL_CLOCK_EN); | |
713 | writel(host_ctl, host->addr + HOST_CONTROL); | |
60fdd931 AD |
714 | writel(0, host->addr + PAD_OUTPUT_ENABLE); |
715 | writel(PAD_PU_PD_OFF, host->addr + PAD_PU_PD); | |
716 | dev_dbg(&host->chip->pdev->dev, "power off\n"); | |
b7789998 AD |
717 | } else |
718 | return -EINVAL; | |
60fdd931 AD |
719 | break; |
720 | case MEMSTICK_INTERFACE: | |
23c5947a TI |
721 | dev_dbg(&host->chip->pdev->dev, |
722 | "Set Host Interface Mode to %d\n", value); | |
723 | host_ctl &= ~(HOST_CONTROL_FAST_CLK | HOST_CONTROL_REI | | |
724 | HOST_CONTROL_REO); | |
725 | host_ctl |= HOST_CONTROL_TDELAY_EN | HOST_CONTROL_HW_OC_P; | |
60fdd931 | 726 | host_ctl &= ~(3 << HOST_CONTROL_IF_SHIFT); |
60fdd931 AD |
727 | |
728 | if (value == MEMSTICK_SERIAL) { | |
60fdd931 AD |
729 | host_ctl |= HOST_CONTROL_IF_SERIAL |
730 | << HOST_CONTROL_IF_SHIFT; | |
731 | host_ctl |= HOST_CONTROL_REI; | |
23c5947a TI |
732 | clock_ctl |= CLOCK_CONTROL_40MHZ; |
733 | clock_delay = 0; | |
60fdd931 | 734 | } else if (value == MEMSTICK_PAR4) { |
23c5947a | 735 | host_ctl |= HOST_CONTROL_FAST_CLK; |
60fdd931 AD |
736 | host_ctl |= HOST_CONTROL_IF_PAR4 |
737 | << HOST_CONTROL_IF_SHIFT; | |
23c5947a TI |
738 | host_ctl |= HOST_CONTROL_REO; |
739 | clock_ctl |= CLOCK_CONTROL_40MHZ; | |
740 | clock_delay = 4; | |
60fdd931 AD |
741 | } else if (value == MEMSTICK_PAR8) { |
742 | host_ctl |= HOST_CONTROL_FAST_CLK; | |
743 | host_ctl |= HOST_CONTROL_IF_PAR8 | |
744 | << HOST_CONTROL_IF_SHIFT; | |
23c5947a TI |
745 | clock_ctl |= CLOCK_CONTROL_50MHZ; |
746 | clock_delay = 0; | |
b7789998 AD |
747 | } else |
748 | return -EINVAL; | |
8e82f8c3 | 749 | |
60fdd931 | 750 | writel(host_ctl, host->addr + HOST_CONTROL); |
23c5947a | 751 | writel(CLOCK_CONTROL_OFF, host->addr + CLOCK_CONTROL); |
cf821e8f | 752 | writel(clock_ctl, host->addr + CLOCK_CONTROL); |
23c5947a TI |
753 | pci_write_config_byte(host->chip->pdev, |
754 | PCI_CTL_CLOCK_DLY_ADDR + 1, | |
755 | clock_delay); | |
756 | host->ifmode = value; | |
60fdd931 AD |
757 | break; |
758 | }; | |
b7789998 | 759 | return 0; |
60fdd931 AD |
760 | } |
761 | ||
68860b96 TI |
762 | #define PCI_PMOS0_CONTROL 0xae |
763 | #define PMOS0_ENABLE 0x01 | |
764 | #define PMOS0_OVERCURRENT_LEVEL_2_4V 0x06 | |
765 | #define PMOS0_EN_OVERCURRENT_DEBOUNCE 0x40 | |
766 | #define PMOS0_SW_LED_POLARITY_ENABLE 0x80 | |
767 | #define PMOS0_ACTIVE_BITS (PMOS0_ENABLE | PMOS0_EN_OVERCURRENT_DEBOUNCE | \ | |
768 | PMOS0_OVERCURRENT_LEVEL_2_4V) | |
8930c8aa TI |
769 | #define PCI_PMOS1_CONTROL 0xbd |
770 | #define PMOS1_ACTIVE_BITS 0x4a | |
68860b96 TI |
771 | #define PCI_CLOCK_CTL 0xb9 |
772 | ||
773 | static int jmb38x_ms_pmos(struct pci_dev *pdev, int flag) | |
774 | { | |
775 | unsigned char val; | |
776 | ||
777 | pci_read_config_byte(pdev, PCI_PMOS0_CONTROL, &val); | |
778 | if (flag) | |
779 | val |= PMOS0_ACTIVE_BITS; | |
780 | else | |
781 | val &= ~PMOS0_ACTIVE_BITS; | |
782 | pci_write_config_byte(pdev, PCI_PMOS0_CONTROL, val); | |
783 | dev_dbg(&pdev->dev, "JMB38x: set PMOS0 val 0x%x\n", val); | |
784 | ||
8930c8aa TI |
785 | if (pci_resource_flags(pdev, 1)) { |
786 | pci_read_config_byte(pdev, PCI_PMOS1_CONTROL, &val); | |
787 | if (flag) | |
788 | val |= PMOS1_ACTIVE_BITS; | |
789 | else | |
790 | val &= ~PMOS1_ACTIVE_BITS; | |
791 | pci_write_config_byte(pdev, PCI_PMOS1_CONTROL, val); | |
792 | dev_dbg(&pdev->dev, "JMB38x: set PMOS1 val 0x%x\n", val); | |
793 | } | |
794 | ||
68860b96 TI |
795 | pci_read_config_byte(pdev, PCI_CLOCK_CTL, &val); |
796 | pci_write_config_byte(pdev, PCI_CLOCK_CTL, val & ~0x0f); | |
797 | pci_write_config_byte(pdev, PCI_CLOCK_CTL, val | 0x01); | |
798 | dev_dbg(&pdev->dev, "Clock Control by PCI config is disabled!\n"); | |
799 | ||
800 | return 0; | |
801 | } | |
802 | ||
60fdd931 AD |
803 | #ifdef CONFIG_PM |
804 | ||
805 | static int jmb38x_ms_suspend(struct pci_dev *dev, pm_message_t state) | |
806 | { | |
807 | struct jmb38x_ms *jm = pci_get_drvdata(dev); | |
808 | int cnt; | |
809 | ||
810 | for (cnt = 0; cnt < jm->host_cnt; ++cnt) { | |
811 | if (!jm->hosts[cnt]) | |
812 | break; | |
813 | memstick_suspend_host(jm->hosts[cnt]); | |
814 | } | |
815 | ||
816 | pci_save_state(dev); | |
817 | pci_enable_wake(dev, pci_choose_state(dev, state), 0); | |
818 | pci_disable_device(dev); | |
819 | pci_set_power_state(dev, pci_choose_state(dev, state)); | |
820 | return 0; | |
821 | } | |
822 | ||
823 | static int jmb38x_ms_resume(struct pci_dev *dev) | |
824 | { | |
825 | struct jmb38x_ms *jm = pci_get_drvdata(dev); | |
826 | int rc; | |
827 | ||
828 | pci_set_power_state(dev, PCI_D0); | |
829 | pci_restore_state(dev); | |
830 | rc = pci_enable_device(dev); | |
831 | if (rc) | |
832 | return rc; | |
833 | pci_set_master(dev); | |
834 | ||
68860b96 | 835 | jmb38x_ms_pmos(dev, 1); |
60fdd931 AD |
836 | |
837 | for (rc = 0; rc < jm->host_cnt; ++rc) { | |
838 | if (!jm->hosts[rc]) | |
839 | break; | |
840 | memstick_resume_host(jm->hosts[rc]); | |
841 | memstick_detect_change(jm->hosts[rc]); | |
842 | } | |
843 | ||
844 | return 0; | |
845 | } | |
846 | ||
847 | #else | |
848 | ||
849 | #define jmb38x_ms_suspend NULL | |
850 | #define jmb38x_ms_resume NULL | |
851 | ||
852 | #endif /* CONFIG_PM */ | |
853 | ||
854 | static int jmb38x_ms_count_slots(struct pci_dev *pdev) | |
855 | { | |
856 | int cnt, rc = 0; | |
857 | ||
858 | for (cnt = 0; cnt < PCI_ROM_RESOURCE; ++cnt) { | |
859 | if (!(IORESOURCE_MEM & pci_resource_flags(pdev, cnt))) | |
860 | break; | |
861 | ||
862 | if (256 != pci_resource_len(pdev, cnt)) | |
863 | break; | |
864 | ||
865 | ++rc; | |
866 | } | |
867 | return rc; | |
868 | } | |
869 | ||
870 | static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt) | |
871 | { | |
872 | struct memstick_host *msh; | |
873 | struct jmb38x_ms_host *host; | |
874 | ||
875 | msh = memstick_alloc_host(sizeof(struct jmb38x_ms_host), | |
876 | &jm->pdev->dev); | |
877 | if (!msh) | |
878 | return NULL; | |
879 | ||
880 | host = memstick_priv(msh); | |
881 | host->chip = jm; | |
882 | host->addr = ioremap(pci_resource_start(jm->pdev, cnt), | |
883 | pci_resource_len(jm->pdev, cnt)); | |
884 | if (!host->addr) | |
885 | goto err_out_free; | |
886 | ||
887 | spin_lock_init(&host->lock); | |
888 | host->id = cnt; | |
b98cb4b7 | 889 | snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d", |
60fdd931 AD |
890 | host->id); |
891 | host->irq = jm->pdev->irq; | |
ead70773 | 892 | host->timeout_jiffies = msecs_to_jiffies(1000); |
f1d82698 AD |
893 | |
894 | tasklet_init(&host->notify, jmb38x_ms_req_tasklet, (unsigned long)msh); | |
895 | msh->request = jmb38x_ms_submit_req; | |
60fdd931 | 896 | msh->set_param = jmb38x_ms_set_param; |
ead70773 | 897 | |
60fdd931 AD |
898 | msh->caps = MEMSTICK_CAP_PAR4 | MEMSTICK_CAP_PAR8; |
899 | ||
900 | setup_timer(&host->timer, jmb38x_ms_abort, (unsigned long)msh); | |
901 | ||
902 | if (!request_irq(host->irq, jmb38x_ms_isr, IRQF_SHARED, host->host_id, | |
903 | msh)) | |
904 | return msh; | |
905 | ||
906 | iounmap(host->addr); | |
907 | err_out_free: | |
908 | kfree(msh); | |
909 | return NULL; | |
910 | } | |
911 | ||
912 | static void jmb38x_ms_free_host(struct memstick_host *msh) | |
913 | { | |
914 | struct jmb38x_ms_host *host = memstick_priv(msh); | |
915 | ||
916 | free_irq(host->irq, msh); | |
917 | iounmap(host->addr); | |
918 | memstick_free_host(msh); | |
919 | } | |
920 | ||
921 | static int jmb38x_ms_probe(struct pci_dev *pdev, | |
922 | const struct pci_device_id *dev_id) | |
923 | { | |
924 | struct jmb38x_ms *jm; | |
925 | int pci_dev_busy = 0; | |
926 | int rc, cnt; | |
927 | ||
284901a9 | 928 | rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); |
60fdd931 AD |
929 | if (rc) |
930 | return rc; | |
931 | ||
932 | rc = pci_enable_device(pdev); | |
933 | if (rc) | |
934 | return rc; | |
935 | ||
936 | pci_set_master(pdev); | |
937 | ||
938 | rc = pci_request_regions(pdev, DRIVER_NAME); | |
939 | if (rc) { | |
940 | pci_dev_busy = 1; | |
941 | goto err_out; | |
942 | } | |
943 | ||
68860b96 | 944 | jmb38x_ms_pmos(pdev, 1); |
60fdd931 AD |
945 | |
946 | cnt = jmb38x_ms_count_slots(pdev); | |
947 | if (!cnt) { | |
948 | rc = -ENODEV; | |
949 | pci_dev_busy = 1; | |
950 | goto err_out; | |
951 | } | |
952 | ||
953 | jm = kzalloc(sizeof(struct jmb38x_ms) | |
954 | + cnt * sizeof(struct memstick_host *), GFP_KERNEL); | |
955 | if (!jm) { | |
956 | rc = -ENOMEM; | |
957 | goto err_out_int; | |
958 | } | |
959 | ||
960 | jm->pdev = pdev; | |
961 | jm->host_cnt = cnt; | |
962 | pci_set_drvdata(pdev, jm); | |
963 | ||
964 | for (cnt = 0; cnt < jm->host_cnt; ++cnt) { | |
965 | jm->hosts[cnt] = jmb38x_ms_alloc_host(jm, cnt); | |
966 | if (!jm->hosts[cnt]) | |
967 | break; | |
968 | ||
969 | rc = memstick_add_host(jm->hosts[cnt]); | |
970 | ||
971 | if (rc) { | |
972 | jmb38x_ms_free_host(jm->hosts[cnt]); | |
973 | jm->hosts[cnt] = NULL; | |
974 | break; | |
975 | } | |
976 | } | |
977 | ||
978 | if (cnt) | |
979 | return 0; | |
980 | ||
981 | rc = -ENODEV; | |
982 | ||
983 | pci_set_drvdata(pdev, NULL); | |
984 | kfree(jm); | |
985 | err_out_int: | |
986 | pci_release_regions(pdev); | |
987 | err_out: | |
988 | if (!pci_dev_busy) | |
989 | pci_disable_device(pdev); | |
990 | return rc; | |
991 | } | |
992 | ||
993 | static void jmb38x_ms_remove(struct pci_dev *dev) | |
994 | { | |
995 | struct jmb38x_ms *jm = pci_get_drvdata(dev); | |
996 | struct jmb38x_ms_host *host; | |
997 | int cnt; | |
998 | unsigned long flags; | |
999 | ||
1000 | for (cnt = 0; cnt < jm->host_cnt; ++cnt) { | |
1001 | if (!jm->hosts[cnt]) | |
1002 | break; | |
1003 | ||
1004 | host = memstick_priv(jm->hosts[cnt]); | |
1005 | ||
f1d82698 AD |
1006 | jm->hosts[cnt]->request = jmb38x_ms_dummy_submit; |
1007 | tasklet_kill(&host->notify); | |
60fdd931 AD |
1008 | writel(0, host->addr + INT_SIGNAL_ENABLE); |
1009 | writel(0, host->addr + INT_STATUS_ENABLE); | |
1010 | mmiowb(); | |
1011 | dev_dbg(&jm->pdev->dev, "interrupts off\n"); | |
1012 | spin_lock_irqsave(&host->lock, flags); | |
1013 | if (host->req) { | |
1014 | host->req->error = -ETIME; | |
1015 | jmb38x_ms_complete_cmd(jm->hosts[cnt], 1); | |
1016 | } | |
1017 | spin_unlock_irqrestore(&host->lock, flags); | |
1018 | ||
1019 | memstick_remove_host(jm->hosts[cnt]); | |
1020 | dev_dbg(&jm->pdev->dev, "host removed\n"); | |
1021 | ||
1022 | jmb38x_ms_free_host(jm->hosts[cnt]); | |
1023 | } | |
1024 | ||
68860b96 TI |
1025 | jmb38x_ms_pmos(dev, 0); |
1026 | ||
60fdd931 AD |
1027 | pci_set_drvdata(dev, NULL); |
1028 | pci_release_regions(dev); | |
1029 | pci_disable_device(dev); | |
1030 | kfree(jm); | |
1031 | } | |
1032 | ||
1033 | static struct pci_device_id jmb38x_ms_id_tbl [] = { | |
8930c8aa TI |
1034 | { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB38X_MS) }, |
1035 | { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB385_MS) }, | |
1036 | { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB390_MS) }, | |
60fdd931 AD |
1037 | { } |
1038 | }; | |
1039 | ||
1040 | static struct pci_driver jmb38x_ms_driver = { | |
1041 | .name = DRIVER_NAME, | |
1042 | .id_table = jmb38x_ms_id_tbl, | |
1043 | .probe = jmb38x_ms_probe, | |
1044 | .remove = jmb38x_ms_remove, | |
1045 | .suspend = jmb38x_ms_suspend, | |
1046 | .resume = jmb38x_ms_resume | |
1047 | }; | |
1048 | ||
1049 | static int __init jmb38x_ms_init(void) | |
1050 | { | |
1051 | return pci_register_driver(&jmb38x_ms_driver); | |
1052 | } | |
1053 | ||
1054 | static void __exit jmb38x_ms_exit(void) | |
1055 | { | |
1056 | pci_unregister_driver(&jmb38x_ms_driver); | |
1057 | } | |
1058 | ||
1059 | MODULE_AUTHOR("Alex Dubov"); | |
1060 | MODULE_DESCRIPTION("JMicron jmb38x MemoryStick driver"); | |
1061 | MODULE_LICENSE("GPL"); | |
1062 | MODULE_DEVICE_TABLE(pci, jmb38x_ms_id_tbl); | |
1063 | ||
1064 | module_init(jmb38x_ms_init); | |
1065 | module_exit(jmb38x_ms_exit); |