diff options
Diffstat (limited to 'drivers/ide/pci/tc86c001.c')
-rw-r--r-- | drivers/ide/pci/tc86c001.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c new file mode 100644 index 000000000000..2ad72bbda342 --- /dev/null +++ b/drivers/ide/pci/tc86c001.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * drivers/ide/pci/tc86c001.c Version 1.00 Dec 12, 2006 | ||
3 | * | ||
4 | * Copyright (C) 2002 Toshiba Corporation | ||
5 | * Copyright (C) 2005-2006 MontaVista Software, Inc. <source@mvista.com> | ||
6 | * | ||
7 | * This file is licensed under the terms of the GNU General Public | ||
8 | * License version 2. This program is licensed "as is" without any | ||
9 | * warranty of any kind, whether express or implied. | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/ide.h> | ||
15 | |||
16 | static inline u8 tc86c001_ratemask(ide_drive_t *drive) | ||
17 | { | ||
18 | return eighty_ninty_three(drive) ? 2 : 1; | ||
19 | } | ||
20 | |||
21 | static int tc86c001_tune_chipset(ide_drive_t *drive, u8 speed) | ||
22 | { | ||
23 | ide_hwif_t *hwif = HWIF(drive); | ||
24 | unsigned long scr_port = hwif->config_data + (drive->dn ? 0x02 : 0x00); | ||
25 | u16 mode, scr = hwif->INW(scr_port); | ||
26 | |||
27 | speed = ide_rate_filter(tc86c001_ratemask(drive), speed); | ||
28 | |||
29 | switch (speed) { | ||
30 | case XFER_UDMA_4: mode = 0x00c0; break; | ||
31 | case XFER_UDMA_3: mode = 0x00b0; break; | ||
32 | case XFER_UDMA_2: mode = 0x00a0; break; | ||
33 | case XFER_UDMA_1: mode = 0x0090; break; | ||
34 | case XFER_UDMA_0: mode = 0x0080; break; | ||
35 | case XFER_MW_DMA_2: mode = 0x0070; break; | ||
36 | case XFER_MW_DMA_1: mode = 0x0060; break; | ||
37 | case XFER_MW_DMA_0: mode = 0x0050; break; | ||
38 | case XFER_PIO_4: mode = 0x0400; break; | ||
39 | case XFER_PIO_3: mode = 0x0300; break; | ||
40 | case XFER_PIO_2: mode = 0x0200; break; | ||
41 | case XFER_PIO_1: mode = 0x0100; break; | ||
42 | case XFER_PIO_0: | ||
43 | default: mode = 0x0000; break; | ||
44 | } | ||
45 | |||
46 | scr &= (speed < XFER_MW_DMA_0) ? 0xf8ff : 0xff0f; | ||
47 | scr |= mode; | ||
48 | hwif->OUTW(scr, scr_port); | ||
49 | |||
50 | return ide_config_drive_speed(drive, speed); | ||
51 | } | ||
52 | |||
53 | static void tc86c001_tune_drive(ide_drive_t *drive, u8 pio) | ||
54 | { | ||
55 | pio = ide_get_best_pio_mode(drive, pio, 4, NULL); | ||
56 | (void) tc86c001_tune_chipset(drive, XFER_PIO_0 + pio); | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * HACKITY HACK | ||
61 | * | ||
62 | * This is a workaround for the limitation 5 of the TC86C001 IDE controller: | ||
63 | * if a DMA transfer terminates prematurely, the controller leaves the device's | ||
64 | * interrupt request (INTRQ) pending and does not generate a PCI interrupt (or | ||
65 | * set the interrupt bit in the DMA status register), thus no PCI interrupt | ||
66 | * will occur until a DMA transfer has been successfully completed. | ||
67 | * | ||
68 | * We work around this by initiating dummy, zero-length DMA transfer on | ||
69 | * a DMA timeout expiration. I found no better way to do this with the current | ||
70 | * IDE core than to temporarily replace a higher level driver's timer expiry | ||
71 | * handler with our own backing up to that handler in case our recovery fails. | ||
72 | */ | ||
73 | static int tc86c001_timer_expiry(ide_drive_t *drive) | ||
74 | { | ||
75 | ide_hwif_t *hwif = HWIF(drive); | ||
76 | ide_expiry_t *expiry = ide_get_hwifdata(hwif); | ||
77 | ide_hwgroup_t *hwgroup = HWGROUP(drive); | ||
78 | u8 dma_stat = hwif->INB(hwif->dma_status); | ||
79 | |||
80 | /* Restore a higher level driver's expiry handler first. */ | ||
81 | hwgroup->expiry = expiry; | ||
82 | |||
83 | if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */ | ||
84 | unsigned long sc_base = hwif->config_data; | ||
85 | unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); | ||
86 | u8 dma_cmd = hwif->INB(hwif->dma_command); | ||
87 | |||
88 | printk(KERN_WARNING "%s: DMA interrupt possibly stuck, " | ||
89 | "attempting recovery...\n", drive->name); | ||
90 | |||
91 | /* Stop DMA */ | ||
92 | hwif->OUTB(dma_cmd & ~0x01, hwif->dma_command); | ||
93 | |||
94 | /* Setup the dummy DMA transfer */ | ||
95 | hwif->OUTW(0, sc_base + 0x0a); /* Sector Count */ | ||
96 | hwif->OUTW(0, twcr_port); /* Transfer Word Count 1 or 2 */ | ||
97 | |||
98 | /* Start the dummy DMA transfer */ | ||
99 | hwif->OUTB(0x00, hwif->dma_command); /* clear R_OR_WCTR for write */ | ||
100 | hwif->OUTB(0x01, hwif->dma_command); /* set START_STOPBM */ | ||
101 | |||
102 | /* | ||
103 | * If an interrupt was pending, it should come thru shortly. | ||
104 | * If not, a higher level driver's expiry handler should | ||
105 | * eventually cause some kind of recovery from the DMA stall. | ||
106 | */ | ||
107 | return WAIT_MIN_SLEEP; | ||
108 | } | ||
109 | |||
110 | /* Chain to the restored expiry handler if DMA wasn't active. */ | ||
111 | if (likely(expiry != NULL)) | ||
112 | return expiry(drive); | ||
113 | |||
114 | /* If there was no handler, "emulate" that for ide_timer_expiry()... */ | ||
115 | return -1; | ||
116 | } | ||
117 | |||
118 | static void tc86c001_dma_start(ide_drive_t *drive) | ||
119 | { | ||
120 | ide_hwif_t *hwif = HWIF(drive); | ||
121 | ide_hwgroup_t *hwgroup = HWGROUP(drive); | ||
122 | unsigned long sc_base = hwif->config_data; | ||
123 | unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); | ||
124 | unsigned long nsectors = hwgroup->rq->nr_sectors; | ||
125 | |||
126 | /* | ||
127 | * We have to manually load the sector count and size into | ||
128 | * the appropriate system control registers for DMA to work | ||
129 | * with LBA48 and ATAPI devices... | ||
130 | */ | ||
131 | hwif->OUTW(nsectors, sc_base + 0x0a); /* Sector Count */ | ||
132 | hwif->OUTW(SECTOR_SIZE / 2, twcr_port); /* Transfer Word Count 1/2 */ | ||
133 | |||
134 | /* Install our timeout expiry hook, saving the current handler... */ | ||
135 | ide_set_hwifdata(hwif, hwgroup->expiry); | ||
136 | hwgroup->expiry = &tc86c001_timer_expiry; | ||
137 | |||
138 | ide_dma_start(drive); | ||
139 | } | ||
140 | |||
141 | static int tc86c001_busproc(ide_drive_t *drive, int state) | ||
142 | { | ||
143 | ide_hwif_t *hwif = HWIF(drive); | ||
144 | unsigned long sc_base = hwif->config_data; | ||
145 | u16 scr1; | ||
146 | |||
147 | /* System Control 1 Register bit 11 (ATA Hard Reset) read */ | ||
148 | scr1 = hwif->INW(sc_base + 0x00); | ||
149 | |||
150 | switch (state) { | ||
151 | case BUSSTATE_ON: | ||
152 | if (!(scr1 & 0x0800)) | ||
153 | return 0; | ||
154 | scr1 &= ~0x0800; | ||
155 | |||
156 | hwif->drives[0].failures = hwif->drives[1].failures = 0; | ||
157 | break; | ||
158 | case BUSSTATE_OFF: | ||
159 | if (scr1 & 0x0800) | ||
160 | return 0; | ||
161 | scr1 |= 0x0800; | ||
162 | |||
163 | hwif->drives[0].failures = hwif->drives[0].max_failures + 1; | ||
164 | hwif->drives[1].failures = hwif->drives[1].max_failures + 1; | ||
165 | break; | ||
166 | default: | ||
167 | return -EINVAL; | ||
168 | } | ||
169 | |||
170 | /* System Control 1 Register bit 11 (ATA Hard Reset) write */ | ||
171 | hwif->OUTW(scr1, sc_base + 0x00); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | static int config_chipset_for_dma(ide_drive_t *drive) | ||
176 | { | ||
177 | u8 speed = ide_dma_speed(drive, tc86c001_ratemask(drive)); | ||
178 | |||
179 | if (!speed) | ||
180 | return 0; | ||
181 | |||
182 | (void) tc86c001_tune_chipset(drive, speed); | ||
183 | return ide_dma_enable(drive); | ||
184 | } | ||
185 | |||
186 | static int tc86c001_config_drive_xfer_rate(ide_drive_t *drive) | ||
187 | { | ||
188 | ide_hwif_t *hwif = HWIF(drive); | ||
189 | struct hd_driveid *id = drive->id; | ||
190 | |||
191 | if ((id->capability & 1) && drive->autodma) { | ||
192 | |||
193 | if (ide_use_dma(drive) && config_chipset_for_dma(drive)) | ||
194 | return hwif->ide_dma_on(drive); | ||
195 | |||
196 | goto fast_ata_pio; | ||
197 | |||
198 | } else if ((id->capability & 8) || (id->field_valid & 2)) { | ||
199 | fast_ata_pio: | ||
200 | tc86c001_tune_drive(drive, 255); | ||
201 | return hwif->ide_dma_off_quietly(drive); | ||
202 | } | ||
203 | /* IORDY not supported */ | ||
204 | return 0; | ||
205 | } | ||
206 | |||
207 | static void __devinit init_hwif_tc86c001(ide_hwif_t *hwif) | ||
208 | { | ||
209 | unsigned long sc_base = pci_resource_start(hwif->pci_dev, 5); | ||
210 | u16 scr1 = hwif->INW(sc_base + 0x00);; | ||
211 | |||
212 | /* System Control 1 Register bit 15 (Soft Reset) set */ | ||
213 | hwif->OUTW(scr1 | 0x8000, sc_base + 0x00); | ||
214 | |||
215 | /* System Control 1 Register bit 14 (FIFO Reset) set */ | ||
216 | hwif->OUTW(scr1 | 0x4000, sc_base + 0x00); | ||
217 | |||
218 | /* System Control 1 Register: reset clear */ | ||
219 | hwif->OUTW(scr1 & ~0xc000, sc_base + 0x00); | ||
220 | |||
221 | /* Store the system control register base for convenience... */ | ||
222 | hwif->config_data = sc_base; | ||
223 | |||
224 | hwif->tuneproc = &tc86c001_tune_drive; | ||
225 | hwif->speedproc = &tc86c001_tune_chipset; | ||
226 | hwif->busproc = &tc86c001_busproc; | ||
227 | |||
228 | hwif->drives[0].autotune = hwif->drives[1].autotune = 1; | ||
229 | |||
230 | if (!hwif->dma_base) | ||
231 | return; | ||
232 | |||
233 | /* | ||
234 | * Sector Count Control Register bits 0 and 1 set: | ||
235 | * software sets Sector Count Register for master and slave device | ||
236 | */ | ||
237 | hwif->OUTW(0x0003, sc_base + 0x0c); | ||
238 | |||
239 | /* Sector Count Register limit */ | ||
240 | hwif->rqsize = 0xffff; | ||
241 | |||
242 | hwif->atapi_dma = 1; | ||
243 | hwif->ultra_mask = 0x1f; | ||
244 | hwif->mwdma_mask = 0x07; | ||
245 | |||
246 | hwif->ide_dma_check = &tc86c001_config_drive_xfer_rate; | ||
247 | hwif->dma_start = &tc86c001_dma_start; | ||
248 | |||
249 | if (!hwif->udma_four) { | ||
250 | /* | ||
251 | * System Control 1 Register bit 13 (PDIAGN): | ||
252 | * 0=80-pin cable, 1=40-pin cable | ||
253 | */ | ||
254 | scr1 = hwif->INW(sc_base + 0x00); | ||
255 | hwif->udma_four = (scr1 & 0x2000) ? 0 : 1; | ||
256 | } | ||
257 | |||
258 | if (!noautodma) | ||
259 | hwif->autodma = 1; | ||
260 | hwif->drives[0].autodma = hwif->drives[1].autodma = hwif->autodma; | ||
261 | } | ||
262 | |||
263 | static unsigned int __devinit init_chipset_tc86c001(struct pci_dev *dev, | ||
264 | const char *name) | ||
265 | { | ||
266 | int err = pci_request_region(dev, 5, name); | ||
267 | |||
268 | if (err) | ||
269 | printk(KERN_ERR "%s: system control regs already in use", name); | ||
270 | return err; | ||
271 | } | ||
272 | |||
273 | static ide_pci_device_t tc86c001_chipset __devinitdata = { | ||
274 | .name = "TC86C001", | ||
275 | .init_chipset = init_chipset_tc86c001, | ||
276 | .init_hwif = init_hwif_tc86c001, | ||
277 | .channels = 1, | ||
278 | .autodma = AUTODMA, | ||
279 | .bootable = OFF_BOARD | ||
280 | }; | ||
281 | |||
282 | static int __devinit tc86c001_init_one(struct pci_dev *dev, | ||
283 | const struct pci_device_id *id) | ||
284 | { | ||
285 | return ide_setup_pci_device(dev, &tc86c001_chipset); | ||
286 | } | ||
287 | |||
288 | static struct pci_device_id tc86c001_pci_tbl[] = { | ||
289 | { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE, | ||
290 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | ||
291 | { 0, } | ||
292 | }; | ||
293 | MODULE_DEVICE_TABLE(pci, tc86c001_pci_tbl); | ||
294 | |||
295 | static struct pci_driver driver = { | ||
296 | .name = "TC86C001", | ||
297 | .id_table = tc86c001_pci_tbl, | ||
298 | .probe = tc86c001_init_one | ||
299 | }; | ||
300 | |||
301 | static int __init tc86c001_ide_init(void) | ||
302 | { | ||
303 | return ide_pci_register_driver(&driver); | ||
304 | } | ||
305 | module_init(tc86c001_ide_init); | ||
306 | |||
307 | MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); | ||
308 | MODULE_DESCRIPTION("PCI driver module for TC86C001 IDE"); | ||
309 | MODULE_LICENSE("GPL"); | ||