Blame view
drivers/ide/ns87415.c
8.98 KB
09c434b8a
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
1da177e4c
|
2 |
/* |
1da177e4c
|
3 4 5 6 7 8 9 |
* Copyright (C) 1997-1998 Mark Lord <mlord@pobox.com> * Copyright (C) 1998 Eddie C. Dost <ecd@skynet.be> * Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org> * Copyright (C) 2004 Grant Grundler <grundler at parisc-linux.org> * * Inspired by an earlier effort from David S. Miller <davem@redhat.com> */ |
1da177e4c
|
10 11 12 |
#include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> |
1da177e4c
|
13 |
#include <linux/interrupt.h> |
1da177e4c
|
14 15 16 17 18 19 |
#include <linux/pci.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <asm/io.h> |
ced3ec8aa
|
20 |
#define DRV_NAME "ns87415" |
1da177e4c
|
21 22 23 24 25 26 27 28 |
#ifdef CONFIG_SUPERIO /* SUPERIO 87560 is a PoS chip that NatSem denies exists. * Unfortunately, it's built-in on all Astro-based PA-RISC workstations * which use the integrated NS87514 cell for CD-ROM support. * i.e we have to support for CD-ROM installs. * See drivers/parisc/superio.c for more gory details. */ #include <asm/superio.h> |
1da177e4c
|
29 30 31 32 33 34 35 36 |
#define SUPERIO_IDE_MAX_RETRIES 25 /* Because of a defect in Super I/O, all reads of the PCI DMA status * registers, IDE status register and the IDE select register need to be * retried */ static u8 superio_ide_inb (unsigned long port) { |
761052e67
|
37 38 39 40 |
u8 tmp; int retries = SUPERIO_IDE_MAX_RETRIES; /* printk(" [ reading port 0x%x with retry ] ", port); */ |
1da177e4c
|
41 |
|
761052e67
|
42 43 44 45 46 47 48 |
do { tmp = inb(port); if (tmp == 0) udelay(50); } while (tmp == 0 && retries-- > 0); return tmp; |
1da177e4c
|
49 |
} |
b73c7ee25
|
50 51 52 53 |
static u8 superio_read_status(ide_hwif_t *hwif) { return superio_ide_inb(hwif->io_ports.status_addr); } |
592b53152
|
54 |
static u8 superio_dma_sff_read_status(ide_hwif_t *hwif) |
b2f951aab
|
55 |
{ |
cab7f8eda
|
56 |
return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); |
b2f951aab
|
57 |
} |
3153c26b5
|
58 59 |
static void superio_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid) |
ea23b8ba0
|
60 61 |
{ struct ide_io_ports *io_ports = &drive->hwif->io_ports; |
ea23b8ba0
|
62 |
|
60f85019c
|
63 |
if (valid & IDE_VALID_ERROR) |
676251193
|
64 |
tf->error = inb(io_ports->feature_addr); |
60f85019c
|
65 |
if (valid & IDE_VALID_NSECT) |
ea23b8ba0
|
66 |
tf->nsect = inb(io_ports->nsect_addr); |
60f85019c
|
67 |
if (valid & IDE_VALID_LBAL) |
ea23b8ba0
|
68 |
tf->lbal = inb(io_ports->lbal_addr); |
60f85019c
|
69 |
if (valid & IDE_VALID_LBAM) |
ea23b8ba0
|
70 |
tf->lbam = inb(io_ports->lbam_addr); |
60f85019c
|
71 |
if (valid & IDE_VALID_LBAH) |
ea23b8ba0
|
72 |
tf->lbah = inb(io_ports->lbah_addr); |
60f85019c
|
73 |
if (valid & IDE_VALID_DEVICE) |
ea23b8ba0
|
74 |
tf->device = superio_ide_inb(io_ports->device_addr); |
ea23b8ba0
|
75 |
} |
abb596b25
|
76 |
static void ns87415_dev_select(ide_drive_t *drive); |
374e042c3
|
77 78 79 80 |
static const struct ide_tp_ops superio_tp_ops = { .exec_command = ide_exec_command, .read_status = superio_read_status, .read_altstatus = ide_read_altstatus, |
ecf3a31d2
|
81 |
.write_devctl = ide_write_devctl, |
374e042c3
|
82 |
|
abb596b25
|
83 |
.dev_select = ns87415_dev_select, |
374e042c3
|
84 85 86 87 88 89 |
.tf_load = ide_tf_load, .tf_read = superio_tf_read, .input_data = ide_input_data, .output_data = ide_output_data, }; |
fe31edc8a
|
90 |
static void superio_init_iops(struct hwif_s *hwif) |
1da177e4c
|
91 |
{ |
36501650e
|
92 |
struct pci_dev *pdev = to_pci_dev(hwif->dev); |
761052e67
|
93 |
u32 dma_stat; |
36501650e
|
94 |
u8 port = hwif->channel, tmp; |
1da177e4c
|
95 |
|
761052e67
|
96 |
dma_stat = (pci_resource_start(pdev, 4) & ~3) + (!port ? 2 : 0xa); |
1da177e4c
|
97 98 |
/* Clear error/interrupt, enable dma */ |
761052e67
|
99 100 |
tmp = superio_ide_inb(dma_stat); outb(tmp | 0x66, dma_stat); |
1da177e4c
|
101 |
} |
592b53152
|
102 103 |
#else #define superio_dma_sff_read_status ide_dma_sff_read_status |
1da177e4c
|
104 105 106 107 108 |
#endif static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 }; /* |
97100fc81
|
109 |
* This routine either enables/disables (according to IDE_DFLAG_PRESENT) |
898ec223f
|
110 |
* the IRQ associated with the port, |
1da177e4c
|
111 112 113 114 |
* and selects either PIO or DMA handshaking for the next I/O operation. */ static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma) { |
898ec223f
|
115 |
ide_hwif_t *hwif = drive->hwif; |
36501650e
|
116 |
struct pci_dev *dev = to_pci_dev(hwif->dev); |
1da177e4c
|
117 |
unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data; |
1da177e4c
|
118 119 120 121 122 123 124 |
unsigned long flags; local_irq_save(flags); new = *old; /* Adjust IRQ enable bit */ bit = 1 << (8 + hwif->channel); |
97100fc81
|
125 126 127 128 129 |
if (drive->dev_flags & IDE_DFLAG_PRESENT) new &= ~bit; else new |= bit; |
1da177e4c
|
130 131 |
/* Select PIO or DMA, DMA may only be selected for one drive/channel. */ |
123995b97
|
132 133 |
bit = 1 << (20 + (drive->dn & 1) + (hwif->channel << 1)); other = 1 << (20 + (1 - (drive->dn & 1)) + (hwif->channel << 1)); |
1da177e4c
|
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
new = use_dma ? ((new & ~other) | bit) : (new & ~bit); if (new != *old) { unsigned char stat; /* * Don't change DMA engine settings while Write Buffers * are busy. */ (void) pci_read_config_byte(dev, 0x43, &stat); while (stat & 0x03) { udelay(1); (void) pci_read_config_byte(dev, 0x43, &stat); } *old = new; (void) pci_write_config_dword(dev, 0x40, new); /* * And let things settle... */ udelay(10); } local_irq_restore(flags); } |
abb596b25
|
160 |
static void ns87415_dev_select(ide_drive_t *drive) |
1da177e4c
|
161 |
{ |
97100fc81
|
162 163 |
ns87415_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); |
abb596b25
|
164 165 |
outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); |
1da177e4c
|
166 |
} |
a6d67ffa7
|
167 168 169 170 171 |
static void ns87415_dma_start(ide_drive_t *drive) { ns87415_prepare_drive(drive, 1); ide_dma_start(drive); } |
5e37bdc08
|
172 |
static int ns87415_dma_end(ide_drive_t *drive) |
1da177e4c
|
173 |
{ |
898ec223f
|
174 |
ide_hwif_t *hwif = drive->hwif; |
1da177e4c
|
175 |
u8 dma_stat = 0, dma_cmd = 0; |
592b53152
|
176 |
dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); |
cab7f8eda
|
177 178 |
/* get DMA command mode */ dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); |
1da177e4c
|
179 |
/* stop DMA */ |
cab7f8eda
|
180 |
outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); |
1da177e4c
|
181 |
/* from ERRATA: clear the INTR & ERROR bits */ |
cab7f8eda
|
182 183 |
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD); |
1da177e4c
|
184 |
|
1da177e4c
|
185 |
ns87415_prepare_drive(drive, 0); |
a6d67ffa7
|
186 187 188 |
/* verify good DMA status */ return (dma_stat & 7) != 4; |
1da177e4c
|
189 |
} |
fe31edc8a
|
190 |
static void init_hwif_ns87415 (ide_hwif_t *hwif) |
1da177e4c
|
191 |
{ |
36501650e
|
192 |
struct pci_dev *dev = to_pci_dev(hwif->dev); |
1da177e4c
|
193 194 195 196 197 198 |
unsigned int ctrl, using_inta; u8 progif; #ifdef __sparc_v9__ int timeout; u8 stat; #endif |
1da177e4c
|
199 200 201 202 203 |
/* * We cannot probe for IRQ: both ports share common IRQ on INTA. * Also, leave IRQ masked during drive probing, to prevent infinite * interrupts from a potentially floating INTA.. * |
abb596b25
|
204 |
* IRQs get unmasked in dev_select() when drive is first used. |
1da177e4c
|
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
*/ (void) pci_read_config_dword(dev, 0x40, &ctrl); (void) pci_read_config_byte(dev, 0x09, &progif); /* is irq in "native" mode? */ using_inta = progif & (1 << (hwif->channel << 1)); if (!using_inta) using_inta = ctrl & (1 << (4 + hwif->channel)); if (hwif->mate) { hwif->select_data = hwif->mate->select_data; } else { hwif->select_data = (unsigned long) &ns87415_control[ns87415_count++]; ctrl |= (1 << 8) | (1 << 9); /* mask both IRQs */ if (using_inta) ctrl &= ~(1 << 6); /* unmask INTA */ *((unsigned int *)hwif->select_data) = ctrl; (void) pci_write_config_dword(dev, 0x40, ctrl); /* * Set prefetch size to 512 bytes for both ports, * but don't turn on/off prefetching here. */ pci_write_config_byte(dev, 0x55, 0xee); #ifdef __sparc_v9__ /* |
9d501529b
|
231 |
* XXX: Reset the device, if we don't it will not respond to |
fdd88f0af
|
232 |
* dev_select() properly during first ide_probe_port(). |
1da177e4c
|
233 234 |
*/ timeout = 10000; |
4c3032d8a
|
235 |
outb(12, hwif->io_ports.ctl_addr); |
1da177e4c
|
236 |
udelay(10); |
4c3032d8a
|
237 |
outb(8, hwif->io_ports.ctl_addr); |
1da177e4c
|
238 239 |
do { udelay(50); |
374e042c3
|
240 |
stat = hwif->tp_ops->read_status(hwif); |
3a7d24841
|
241 242 243 |
if (stat == 0xff) break; } while ((stat & ATA_BUSY) && --timeout); |
1da177e4c
|
244 245 246 247 |
#endif } if (!using_inta) |
973d9e743
|
248 |
hwif->irq = pci_get_legacy_ide_irq(dev, hwif->channel); |
1da177e4c
|
249 250 251 |
if (!hwif->dma_base) return; |
cab7f8eda
|
252 |
outb(0x60, hwif->dma_base + ATA_DMA_STATUS); |
1da177e4c
|
253 |
} |
abb596b25
|
254 255 256 257 258 259 260 261 262 263 264 265 |
static const struct ide_tp_ops ns87415_tp_ops = { .exec_command = ide_exec_command, .read_status = ide_read_status, .read_altstatus = ide_read_altstatus, .write_devctl = ide_write_devctl, .dev_select = ns87415_dev_select, .tf_load = ide_tf_load, .tf_read = ide_tf_read, .input_data = ide_input_data, .output_data = ide_output_data, |
ac95beedf
|
266 |
}; |
f37afdaca
|
267 268 |
static const struct ide_dma_ops ns87415_dma_ops = { .dma_host_set = ide_dma_host_set, |
a6d67ffa7
|
269 270 |
.dma_setup = ide_dma_setup, .dma_start = ns87415_dma_start, |
5e37bdc08
|
271 |
.dma_end = ns87415_dma_end, |
f37afdaca
|
272 273 |
.dma_test_irq = ide_dma_test_irq, .dma_lost_irq = ide_dma_lost_irq, |
22117d6ea
|
274 |
.dma_timer_expiry = ide_dma_sff_timer_expiry, |
592b53152
|
275 |
.dma_sff_read_status = superio_dma_sff_read_status, |
5e37bdc08
|
276 |
}; |
fe31edc8a
|
277 |
static const struct ide_port_info ns87415_chipset = { |
ced3ec8aa
|
278 |
.name = DRV_NAME, |
1da177e4c
|
279 |
.init_hwif = init_hwif_ns87415, |
abb596b25
|
280 |
.tp_ops = &ns87415_tp_ops, |
5e37bdc08
|
281 |
.dma_ops = &ns87415_dma_ops, |
33c1002ed
|
282 |
.host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | |
5e71d9c5a
|
283 |
IDE_HFLAG_NO_ATAPI_DMA, |
1da177e4c
|
284 |
}; |
fe31edc8a
|
285 |
static int ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id) |
1da177e4c
|
286 |
{ |
374e042c3
|
287 288 289 290 291 292 293 294 295 |
struct ide_port_info d = ns87415_chipset; #ifdef CONFIG_SUPERIO if (PCI_SLOT(dev->devfn) == 0xE) { /* Built-in - assume it's under superio. */ d.init_iops = superio_init_iops; d.tp_ops = &superio_tp_ops; } #endif |
6cdf6eb35
|
296 |
return ide_pci_init_one(dev, &d, NULL); |
1da177e4c
|
297 |
} |
9cbcc5e3c
|
298 299 |
static const struct pci_device_id ns87415_pci_tbl[] = { { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87415), 0 }, |
1da177e4c
|
300 301 302 |
{ 0, }, }; MODULE_DEVICE_TABLE(pci, ns87415_pci_tbl); |
a9ab09e26
|
303 |
static struct pci_driver ns87415_pci_driver = { |
1da177e4c
|
304 305 306 |
.name = "NS87415_IDE", .id_table = ns87415_pci_tbl, .probe = ns87415_init_one, |
aa6e518d7
|
307 |
.remove = ide_pci_remove, |
feb22b7f8
|
308 309 |
.suspend = ide_pci_suspend, .resume = ide_pci_resume, |
1da177e4c
|
310 |
}; |
82ab1eece
|
311 |
static int __init ns87415_ide_init(void) |
1da177e4c
|
312 |
{ |
a9ab09e26
|
313 |
return ide_pci_register_driver(&ns87415_pci_driver); |
1da177e4c
|
314 |
} |
aa6e518d7
|
315 316 |
static void __exit ns87415_ide_exit(void) { |
a9ab09e26
|
317 |
pci_unregister_driver(&ns87415_pci_driver); |
aa6e518d7
|
318 |
} |
1da177e4c
|
319 |
module_init(ns87415_ide_init); |
aa6e518d7
|
320 |
module_exit(ns87415_ide_exit); |
1da177e4c
|
321 322 323 324 |
MODULE_AUTHOR("Mark Lord, Eddie Dost, Andre Hedrick"); MODULE_DESCRIPTION("PCI driver module for NS87415 IDE"); MODULE_LICENSE("GPL"); |