diff options
author | Hannes Reinecke <hare@suse.de> | 2014-11-24 09:37:25 -0500 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2014-11-24 10:13:05 -0500 |
commit | 3a7e7be2a9a2105742c3c88d466ea2158a03a837 (patch) | |
tree | d7a33f176c139a6378a296d25e79841963592363 /drivers/scsi/am53c974.c | |
parent | 3170866f8865809290f4b99e61a096ba39a01472 (diff) |
am53c974: add new driver
This patch adds a new implementation for the Tekram DC-390T /
AMD AM53c974 SCSI controller, based on the generic
esp_scsi infrastructure.
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'drivers/scsi/am53c974.c')
-rw-r--r-- | drivers/scsi/am53c974.c | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/drivers/scsi/am53c974.c b/drivers/scsi/am53c974.c new file mode 100644 index 000000000000..a16c9cf0f0a2 --- /dev/null +++ b/drivers/scsi/am53c974.c | |||
@@ -0,0 +1,537 @@ | |||
1 | /* | ||
2 | * AMD am53c974 driver. | ||
3 | * Copyright (c) 2014 Hannes Reinecke, SUSE Linux GmbH | ||
4 | */ | ||
5 | |||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/pci.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | |||
13 | #include <scsi/scsi_host.h> | ||
14 | |||
15 | #include "esp_scsi.h" | ||
16 | |||
17 | #define DRV_MODULE_NAME "am53c974" | ||
18 | #define DRV_MODULE_VERSION "1.00" | ||
19 | |||
20 | static bool am53c974_debug; | ||
21 | |||
22 | #define esp_dma_log(f, a...) \ | ||
23 | do { \ | ||
24 | if (am53c974_debug) \ | ||
25 | shost_printk(KERN_DEBUG, esp->host, f, ##a); \ | ||
26 | } while (0) | ||
27 | |||
28 | #define ESP_DMA_CMD 0x10 | ||
29 | #define ESP_DMA_STC 0x11 | ||
30 | #define ESP_DMA_SPA 0x12 | ||
31 | #define ESP_DMA_WBC 0x13 | ||
32 | #define ESP_DMA_WAC 0x14 | ||
33 | #define ESP_DMA_STATUS 0x15 | ||
34 | #define ESP_DMA_SMDLA 0x16 | ||
35 | #define ESP_DMA_WMAC 0x17 | ||
36 | |||
37 | #define ESP_DMA_CMD_IDLE 0x00 | ||
38 | #define ESP_DMA_CMD_BLAST 0x01 | ||
39 | #define ESP_DMA_CMD_ABORT 0x02 | ||
40 | #define ESP_DMA_CMD_START 0x03 | ||
41 | #define ESP_DMA_CMD_MASK 0x03 | ||
42 | #define ESP_DMA_CMD_DIAG 0x04 | ||
43 | #define ESP_DMA_CMD_MDL 0x10 | ||
44 | #define ESP_DMA_CMD_INTE_P 0x20 | ||
45 | #define ESP_DMA_CMD_INTE_D 0x40 | ||
46 | #define ESP_DMA_CMD_DIR 0x80 | ||
47 | |||
48 | #define ESP_DMA_STAT_PWDN 0x01 | ||
49 | #define ESP_DMA_STAT_ERROR 0x02 | ||
50 | #define ESP_DMA_STAT_ABORT 0x04 | ||
51 | #define ESP_DMA_STAT_DONE 0x08 | ||
52 | #define ESP_DMA_STAT_SCSIINT 0x10 | ||
53 | #define ESP_DMA_STAT_BCMPLT 0x20 | ||
54 | |||
55 | /* EEPROM is accessed with 16-bit values */ | ||
56 | #define DC390_EEPROM_READ 0x80 | ||
57 | #define DC390_EEPROM_LEN 0x40 | ||
58 | |||
59 | /* | ||
60 | * DC390 EEPROM | ||
61 | * | ||
62 | * 8 * 4 bytes of per-device options | ||
63 | * followed by HBA specific options | ||
64 | */ | ||
65 | |||
66 | /* Per-device options */ | ||
67 | #define DC390_EE_MODE1 0x00 | ||
68 | #define DC390_EE_SPEED 0x01 | ||
69 | |||
70 | /* HBA-specific options */ | ||
71 | #define DC390_EE_ADAPT_SCSI_ID 0x40 | ||
72 | #define DC390_EE_MODE2 0x41 | ||
73 | #define DC390_EE_DELAY 0x42 | ||
74 | #define DC390_EE_TAG_CMD_NUM 0x43 | ||
75 | |||
76 | #define DC390_EE_MODE1_PARITY_CHK 0x01 | ||
77 | #define DC390_EE_MODE1_SYNC_NEGO 0x02 | ||
78 | #define DC390_EE_MODE1_EN_DISC 0x04 | ||
79 | #define DC390_EE_MODE1_SEND_START 0x08 | ||
80 | #define DC390_EE_MODE1_TCQ 0x10 | ||
81 | |||
82 | #define DC390_EE_MODE2_MORE_2DRV 0x01 | ||
83 | #define DC390_EE_MODE2_GREATER_1G 0x02 | ||
84 | #define DC390_EE_MODE2_RST_SCSI_BUS 0x04 | ||
85 | #define DC390_EE_MODE2_ACTIVE_NEGATION 0x08 | ||
86 | #define DC390_EE_MODE2_NO_SEEK 0x10 | ||
87 | #define DC390_EE_MODE2_LUN_CHECK 0x20 | ||
88 | |||
89 | struct pci_esp_priv { | ||
90 | struct esp *esp; | ||
91 | u8 dma_status; | ||
92 | }; | ||
93 | |||
94 | static void pci_esp_dma_drain(struct esp *esp); | ||
95 | |||
96 | static inline struct pci_esp_priv *pci_esp_get_priv(struct esp *esp) | ||
97 | { | ||
98 | struct pci_dev *pdev = esp->dev; | ||
99 | |||
100 | return pci_get_drvdata(pdev); | ||
101 | } | ||
102 | |||
103 | static void pci_esp_write8(struct esp *esp, u8 val, unsigned long reg) | ||
104 | { | ||
105 | iowrite8(val, esp->regs + (reg * 4UL)); | ||
106 | } | ||
107 | |||
108 | static u8 pci_esp_read8(struct esp *esp, unsigned long reg) | ||
109 | { | ||
110 | return ioread8(esp->regs + (reg * 4UL)); | ||
111 | } | ||
112 | |||
113 | static void pci_esp_write32(struct esp *esp, u32 val, unsigned long reg) | ||
114 | { | ||
115 | return iowrite32(val, esp->regs + (reg * 4UL)); | ||
116 | } | ||
117 | |||
118 | static dma_addr_t pci_esp_map_single(struct esp *esp, void *buf, | ||
119 | size_t sz, int dir) | ||
120 | { | ||
121 | return pci_map_single(esp->dev, buf, sz, dir); | ||
122 | } | ||
123 | |||
124 | static int pci_esp_map_sg(struct esp *esp, struct scatterlist *sg, | ||
125 | int num_sg, int dir) | ||
126 | { | ||
127 | return pci_map_sg(esp->dev, sg, num_sg, dir); | ||
128 | } | ||
129 | |||
130 | static void pci_esp_unmap_single(struct esp *esp, dma_addr_t addr, | ||
131 | size_t sz, int dir) | ||
132 | { | ||
133 | pci_unmap_single(esp->dev, addr, sz, dir); | ||
134 | } | ||
135 | |||
136 | static void pci_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, | ||
137 | int num_sg, int dir) | ||
138 | { | ||
139 | pci_unmap_sg(esp->dev, sg, num_sg, dir); | ||
140 | } | ||
141 | |||
142 | static int pci_esp_irq_pending(struct esp *esp) | ||
143 | { | ||
144 | struct pci_esp_priv *pep = pci_esp_get_priv(esp); | ||
145 | |||
146 | pep->dma_status = pci_esp_read8(esp, ESP_DMA_STATUS); | ||
147 | esp_dma_log("dma intr dreg[%02x]\n", pep->dma_status); | ||
148 | |||
149 | if (pep->dma_status & (ESP_DMA_STAT_ERROR | | ||
150 | ESP_DMA_STAT_ABORT | | ||
151 | ESP_DMA_STAT_DONE | | ||
152 | ESP_DMA_STAT_SCSIINT)) | ||
153 | return 1; | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static void pci_esp_reset_dma(struct esp *esp) | ||
159 | { | ||
160 | /* Nothing to do ? */ | ||
161 | } | ||
162 | |||
163 | static void pci_esp_dma_drain(struct esp *esp) | ||
164 | { | ||
165 | u8 resid; | ||
166 | int lim = 1000; | ||
167 | |||
168 | |||
169 | if ((esp->sreg & ESP_STAT_PMASK) == ESP_DOP || | ||
170 | (esp->sreg & ESP_STAT_PMASK) == ESP_DIP) | ||
171 | /* Data-In or Data-Out, nothing to be done */ | ||
172 | return; | ||
173 | |||
174 | while (--lim > 0) { | ||
175 | resid = pci_esp_read8(esp, ESP_FFLAGS) & ESP_FF_FBYTES; | ||
176 | if (resid <= 1) | ||
177 | break; | ||
178 | cpu_relax(); | ||
179 | } | ||
180 | if (resid > 1) { | ||
181 | /* FIFO not cleared */ | ||
182 | shost_printk(KERN_INFO, esp->host, | ||
183 | "FIFO not cleared, %d bytes left\n", | ||
184 | resid); | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * When there is a residual BCMPLT will never be set | ||
189 | * (obviously). But we still have to issue the BLAST | ||
190 | * command, otherwise the data will not being transferred. | ||
191 | * But we'll never know when the BLAST operation is | ||
192 | * finished. So check for some time and give up eventually. | ||
193 | */ | ||
194 | lim = 1000; | ||
195 | pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_BLAST, ESP_DMA_CMD); | ||
196 | while (pci_esp_read8(esp, ESP_DMA_STATUS) & ESP_DMA_STAT_BCMPLT) { | ||
197 | if (--lim == 0) | ||
198 | break; | ||
199 | cpu_relax(); | ||
200 | } | ||
201 | pci_esp_write8(esp, ESP_DMA_CMD_DIR | ESP_DMA_CMD_IDLE, ESP_DMA_CMD); | ||
202 | esp_dma_log("DMA blast done (%d tries, %d bytes left)\n", lim, resid); | ||
203 | } | ||
204 | |||
205 | static void pci_esp_dma_invalidate(struct esp *esp) | ||
206 | { | ||
207 | struct pci_esp_priv *pep = pci_esp_get_priv(esp); | ||
208 | |||
209 | esp_dma_log("invalidate DMA\n"); | ||
210 | |||
211 | pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD); | ||
212 | pep->dma_status = 0; | ||
213 | } | ||
214 | |||
215 | static int pci_esp_dma_error(struct esp *esp) | ||
216 | { | ||
217 | struct pci_esp_priv *pep = pci_esp_get_priv(esp); | ||
218 | |||
219 | if (pep->dma_status & ESP_DMA_STAT_ERROR) { | ||
220 | u8 dma_cmd = pci_esp_read8(esp, ESP_DMA_CMD); | ||
221 | |||
222 | if ((dma_cmd & ESP_DMA_CMD_MASK) == ESP_DMA_CMD_START) | ||
223 | pci_esp_write8(esp, ESP_DMA_CMD_ABORT, ESP_DMA_CMD); | ||
224 | |||
225 | return 1; | ||
226 | } | ||
227 | if (pep->dma_status & ESP_DMA_STAT_ABORT) { | ||
228 | pci_esp_write8(esp, ESP_DMA_CMD_IDLE, ESP_DMA_CMD); | ||
229 | pep->dma_status = pci_esp_read8(esp, ESP_DMA_CMD); | ||
230 | return 1; | ||
231 | } | ||
232 | return 0; | ||
233 | } | ||
234 | |||
235 | static void pci_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, | ||
236 | u32 dma_count, int write, u8 cmd) | ||
237 | { | ||
238 | struct pci_esp_priv *pep = pci_esp_get_priv(esp); | ||
239 | u32 val = 0; | ||
240 | |||
241 | BUG_ON(!(cmd & ESP_CMD_DMA)); | ||
242 | |||
243 | pep->dma_status = 0; | ||
244 | |||
245 | /* Set DMA engine to IDLE */ | ||
246 | if (write) | ||
247 | /* DMA write direction logic is inverted */ | ||
248 | val |= ESP_DMA_CMD_DIR; | ||
249 | pci_esp_write8(esp, ESP_DMA_CMD_IDLE | val, ESP_DMA_CMD); | ||
250 | |||
251 | pci_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); | ||
252 | pci_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); | ||
253 | |||
254 | pci_esp_write32(esp, esp_count, ESP_DMA_STC); | ||
255 | pci_esp_write32(esp, addr, ESP_DMA_SPA); | ||
256 | |||
257 | esp_dma_log("start dma addr[%x] count[%d:%d]\n", | ||
258 | addr, esp_count, dma_count); | ||
259 | |||
260 | scsi_esp_cmd(esp, cmd); | ||
261 | /* Send DMA Start command */ | ||
262 | pci_esp_write8(esp, ESP_DMA_CMD_START | val, ESP_DMA_CMD); | ||
263 | } | ||
264 | |||
265 | static const struct esp_driver_ops pci_esp_ops = { | ||
266 | .esp_write8 = pci_esp_write8, | ||
267 | .esp_read8 = pci_esp_read8, | ||
268 | .map_single = pci_esp_map_single, | ||
269 | .map_sg = pci_esp_map_sg, | ||
270 | .unmap_single = pci_esp_unmap_single, | ||
271 | .unmap_sg = pci_esp_unmap_sg, | ||
272 | .irq_pending = pci_esp_irq_pending, | ||
273 | .reset_dma = pci_esp_reset_dma, | ||
274 | .dma_drain = pci_esp_dma_drain, | ||
275 | .dma_invalidate = pci_esp_dma_invalidate, | ||
276 | .send_dma_cmd = pci_esp_send_dma_cmd, | ||
277 | .dma_error = pci_esp_dma_error, | ||
278 | }; | ||
279 | |||
280 | /* | ||
281 | * Read DC-390 eeprom | ||
282 | */ | ||
283 | static void dc390_eeprom_prepare_read(struct pci_dev *pdev, u8 cmd) | ||
284 | { | ||
285 | u8 carry_flag = 1, j = 0x80, bval; | ||
286 | int i; | ||
287 | |||
288 | for (i = 0; i < 9; i++) { | ||
289 | if (carry_flag) { | ||
290 | pci_write_config_byte(pdev, 0x80, 0x40); | ||
291 | bval = 0xc0; | ||
292 | } else | ||
293 | bval = 0x80; | ||
294 | |||
295 | udelay(160); | ||
296 | pci_write_config_byte(pdev, 0x80, bval); | ||
297 | udelay(160); | ||
298 | pci_write_config_byte(pdev, 0x80, 0); | ||
299 | udelay(160); | ||
300 | |||
301 | carry_flag = (cmd & j) ? 1 : 0; | ||
302 | j >>= 1; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | static u16 dc390_eeprom_get_data(struct pci_dev *pdev) | ||
307 | { | ||
308 | int i; | ||
309 | u16 wval = 0; | ||
310 | u8 bval; | ||
311 | |||
312 | for (i = 0; i < 16; i++) { | ||
313 | wval <<= 1; | ||
314 | |||
315 | pci_write_config_byte(pdev, 0x80, 0x80); | ||
316 | udelay(160); | ||
317 | pci_write_config_byte(pdev, 0x80, 0x40); | ||
318 | udelay(160); | ||
319 | pci_read_config_byte(pdev, 0x00, &bval); | ||
320 | |||
321 | if (bval == 0x22) | ||
322 | wval |= 1; | ||
323 | } | ||
324 | |||
325 | return wval; | ||
326 | } | ||
327 | |||
328 | static void dc390_read_eeprom(struct pci_dev *pdev, u16 *ptr) | ||
329 | { | ||
330 | u8 cmd = DC390_EEPROM_READ, i; | ||
331 | |||
332 | for (i = 0; i < DC390_EEPROM_LEN; i++) { | ||
333 | pci_write_config_byte(pdev, 0xc0, 0); | ||
334 | udelay(160); | ||
335 | |||
336 | dc390_eeprom_prepare_read(pdev, cmd++); | ||
337 | *ptr++ = dc390_eeprom_get_data(pdev); | ||
338 | |||
339 | pci_write_config_byte(pdev, 0x80, 0); | ||
340 | pci_write_config_byte(pdev, 0x80, 0); | ||
341 | udelay(160); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | static void dc390_check_eeprom(struct esp *esp) | ||
346 | { | ||
347 | u8 EEbuf[128]; | ||
348 | u16 *ptr = (u16 *)EEbuf, wval = 0; | ||
349 | int i; | ||
350 | |||
351 | dc390_read_eeprom((struct pci_dev *)esp->dev, ptr); | ||
352 | |||
353 | for (i = 0; i < DC390_EEPROM_LEN; i++, ptr++) | ||
354 | wval += *ptr; | ||
355 | |||
356 | /* no Tekram EEprom found */ | ||
357 | if (wval != 0x1234) { | ||
358 | struct pci_dev *pdev = esp->dev; | ||
359 | dev_printk(KERN_INFO, &pdev->dev, | ||
360 | "No valid Tekram EEprom found\n"); | ||
361 | return; | ||
362 | } | ||
363 | esp->scsi_id = EEbuf[DC390_EE_ADAPT_SCSI_ID]; | ||
364 | esp->num_tags = 2 << EEbuf[DC390_EE_TAG_CMD_NUM]; | ||
365 | } | ||
366 | |||
367 | static int pci_esp_probe_one(struct pci_dev *pdev, | ||
368 | const struct pci_device_id *id) | ||
369 | { | ||
370 | struct scsi_host_template *hostt = &scsi_esp_template; | ||
371 | int err = -ENODEV; | ||
372 | struct Scsi_Host *shost; | ||
373 | struct esp *esp; | ||
374 | struct pci_esp_priv *pep; | ||
375 | |||
376 | if (pci_enable_device(pdev)) { | ||
377 | dev_printk(KERN_INFO, &pdev->dev, "cannot enable device\n"); | ||
378 | return -ENODEV; | ||
379 | } | ||
380 | |||
381 | if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { | ||
382 | dev_printk(KERN_INFO, &pdev->dev, | ||
383 | "failed to set 32bit DMA mask\n"); | ||
384 | goto fail_disable_device; | ||
385 | } | ||
386 | |||
387 | shost = scsi_host_alloc(hostt, sizeof(struct esp)); | ||
388 | if (!shost) { | ||
389 | dev_printk(KERN_INFO, &pdev->dev, | ||
390 | "failed to allocate scsi host\n"); | ||
391 | err = -ENOMEM; | ||
392 | goto fail_disable_device; | ||
393 | } | ||
394 | |||
395 | pep = kzalloc(sizeof(struct pci_esp_priv), GFP_KERNEL); | ||
396 | if (!pep) { | ||
397 | dev_printk(KERN_INFO, &pdev->dev, | ||
398 | "failed to allocate esp_priv\n"); | ||
399 | err = -ENOMEM; | ||
400 | goto fail_host_alloc; | ||
401 | } | ||
402 | |||
403 | esp = shost_priv(shost); | ||
404 | esp->host = shost; | ||
405 | esp->dev = pdev; | ||
406 | esp->ops = &pci_esp_ops; | ||
407 | /* | ||
408 | * The am53c974 HBA has a design flaw of generating | ||
409 | * spurious DMA completion interrupts when using | ||
410 | * DMA for command submission. | ||
411 | */ | ||
412 | esp->flags |= ESP_FLAG_USE_FIFO; | ||
413 | pep->esp = esp; | ||
414 | |||
415 | if (pci_request_regions(pdev, DRV_MODULE_NAME)) { | ||
416 | dev_printk(KERN_ERR, &pdev->dev, | ||
417 | "pci memory selection failed\n"); | ||
418 | goto fail_priv_alloc; | ||
419 | } | ||
420 | |||
421 | esp->regs = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); | ||
422 | if (!esp->regs) { | ||
423 | dev_printk(KERN_ERR, &pdev->dev, "pci I/O map failed\n"); | ||
424 | err = -EINVAL; | ||
425 | goto fail_release_regions; | ||
426 | } | ||
427 | esp->dma_regs = esp->regs; | ||
428 | |||
429 | pci_set_master(pdev); | ||
430 | |||
431 | esp->command_block = pci_alloc_consistent(pdev, 16, | ||
432 | &esp->command_block_dma); | ||
433 | if (!esp->command_block) { | ||
434 | dev_printk(KERN_ERR, &pdev->dev, | ||
435 | "failed to allocate command block\n"); | ||
436 | err = -ENOMEM; | ||
437 | goto fail_unmap_regs; | ||
438 | } | ||
439 | |||
440 | err = request_irq(pdev->irq, scsi_esp_intr, IRQF_SHARED, | ||
441 | DRV_MODULE_NAME, esp); | ||
442 | if (err < 0) { | ||
443 | dev_printk(KERN_ERR, &pdev->dev, "failed to register IRQ\n"); | ||
444 | goto fail_unmap_command_block; | ||
445 | } | ||
446 | |||
447 | esp->scsi_id = 7; | ||
448 | dc390_check_eeprom(esp); | ||
449 | |||
450 | shost->this_id = esp->scsi_id; | ||
451 | shost->max_id = 8; | ||
452 | shost->irq = pdev->irq; | ||
453 | shost->io_port = pci_resource_start(pdev, 0); | ||
454 | shost->n_io_port = pci_resource_len(pdev, 0); | ||
455 | shost->unique_id = shost->io_port; | ||
456 | esp->scsi_id_mask = (1 << esp->scsi_id); | ||
457 | /* Assume 40MHz clock */ | ||
458 | esp->cfreq = 40000000; | ||
459 | |||
460 | pci_set_drvdata(pdev, pep); | ||
461 | |||
462 | err = scsi_esp_register(esp, &pdev->dev); | ||
463 | if (err) | ||
464 | goto fail_free_irq; | ||
465 | |||
466 | return 0; | ||
467 | |||
468 | fail_free_irq: | ||
469 | free_irq(pdev->irq, esp); | ||
470 | fail_unmap_command_block: | ||
471 | pci_free_consistent(pdev, 16, esp->command_block, | ||
472 | esp->command_block_dma); | ||
473 | fail_unmap_regs: | ||
474 | pci_iounmap(pdev, esp->regs); | ||
475 | fail_release_regions: | ||
476 | pci_release_regions(pdev); | ||
477 | fail_priv_alloc: | ||
478 | kfree(pep); | ||
479 | fail_host_alloc: | ||
480 | scsi_host_put(shost); | ||
481 | fail_disable_device: | ||
482 | pci_disable_device(pdev); | ||
483 | |||
484 | return err; | ||
485 | } | ||
486 | |||
487 | static void pci_esp_remove_one(struct pci_dev *pdev) | ||
488 | { | ||
489 | struct pci_esp_priv *pep = pci_get_drvdata(pdev); | ||
490 | struct esp *esp = pep->esp; | ||
491 | |||
492 | scsi_esp_unregister(esp); | ||
493 | free_irq(pdev->irq, esp); | ||
494 | pci_free_consistent(pdev, 16, esp->command_block, | ||
495 | esp->command_block_dma); | ||
496 | pci_iounmap(pdev, esp->regs); | ||
497 | pci_release_regions(pdev); | ||
498 | pci_disable_device(pdev); | ||
499 | kfree(pep); | ||
500 | |||
501 | scsi_host_put(esp->host); | ||
502 | } | ||
503 | |||
504 | static struct pci_device_id am53c974_pci_tbl[] = { | ||
505 | { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, | ||
506 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, | ||
507 | { } | ||
508 | }; | ||
509 | MODULE_DEVICE_TABLE(pci, am53c974_pci_tbl); | ||
510 | |||
511 | static struct pci_driver am53c974_driver = { | ||
512 | .name = DRV_MODULE_NAME, | ||
513 | .id_table = am53c974_pci_tbl, | ||
514 | .probe = pci_esp_probe_one, | ||
515 | .remove = pci_esp_remove_one, | ||
516 | }; | ||
517 | |||
518 | static int __init am53c974_module_init(void) | ||
519 | { | ||
520 | return pci_register_driver(&am53c974_driver); | ||
521 | } | ||
522 | |||
523 | static void __exit am53c974_module_exit(void) | ||
524 | { | ||
525 | pci_unregister_driver(&am53c974_driver); | ||
526 | } | ||
527 | |||
528 | MODULE_DESCRIPTION("AM53C974 SCSI driver"); | ||
529 | MODULE_AUTHOR("Hannes Reinecke <hare@suse.de>"); | ||
530 | MODULE_LICENSE("GPL"); | ||
531 | MODULE_VERSION(DRV_MODULE_VERSION); | ||
532 | |||
533 | module_param(am53c974_debug, bool, 0644); | ||
534 | MODULE_PARM_DESC(am53c974_debug, "Enable debugging"); | ||
535 | |||
536 | module_init(am53c974_module_init); | ||
537 | module_exit(am53c974_module_exit); | ||