]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * Copyright (C) 1996-1998 Linus Torvalds & authors (see below) |
3 | */ | |
4 | ||
5 | /* | |
6 | * Authors: | |
7 | * Jaromir Koutek <[email protected]>, | |
8 | * Jan Harkes <[email protected]>, | |
9 | * Mark Lord <[email protected]> | |
10 | * Some parts of code are from ali14xx.c and from rz1000.c. | |
1da177e4 LT |
11 | */ |
12 | ||
1da177e4 LT |
13 | #include <linux/types.h> |
14 | #include <linux/module.h> | |
15 | #include <linux/kernel.h> | |
1da177e4 | 16 | #include <linux/pci.h> |
1da177e4 LT |
17 | #include <linux/ide.h> |
18 | ||
19 | #include <asm/io.h> | |
20 | ||
ced3ec8a BZ |
21 | #define DRV_NAME "opti621" |
22 | ||
1da177e4 LT |
23 | #define READ_REG 0 /* index of Read cycle timing register */ |
24 | #define WRITE_REG 1 /* index of Write cycle timing register */ | |
25 | #define CNTRL_REG 3 /* index of Control register */ | |
26 | #define STRAP_REG 5 /* index of Strap register */ | |
27 | #define MISC_REG 6 /* index of Miscellaneous register */ | |
28 | ||
29 | static int reg_base; | |
30 | ||
e65dde71 BZ |
31 | static DEFINE_SPINLOCK(opti621_lock); |
32 | ||
1da177e4 LT |
33 | /* Write value to register reg, base of register |
34 | * is at reg_base (0x1f0 primary, 0x170 secondary, | |
35 | * if not changed by PCI configuration). | |
36 | * This is from setupvic.exe program. | |
37 | */ | |
0ecdca26 | 38 | static void write_reg(u8 value, int reg) |
1da177e4 | 39 | { |
0ecdca26 BZ |
40 | inw(reg_base + 1); |
41 | inw(reg_base + 1); | |
42 | outb(3, reg_base + 2); | |
43 | outb(value, reg_base + reg); | |
44 | outb(0x83, reg_base + 2); | |
1da177e4 LT |
45 | } |
46 | ||
1da177e4 LT |
47 | /* Read value from register reg, base of register |
48 | * is at reg_base (0x1f0 primary, 0x170 secondary, | |
49 | * if not changed by PCI configuration). | |
50 | * This is from setupvic.exe program. | |
51 | */ | |
0ecdca26 | 52 | static u8 read_reg(int reg) |
1da177e4 LT |
53 | { |
54 | u8 ret = 0; | |
55 | ||
0ecdca26 BZ |
56 | inw(reg_base + 1); |
57 | inw(reg_base + 1); | |
58 | outb(3, reg_base + 2); | |
59 | ret = inb(reg_base + reg); | |
60 | outb(0x83, reg_base + 2); | |
61 | ||
1da177e4 LT |
62 | return ret; |
63 | } | |
64 | ||
e085b3ca | 65 | static void opti621_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) |
1da177e4 | 66 | { |
7e59ea21 | 67 | ide_drive_t *pair = ide_get_pair_dev(drive); |
1da177e4 | 68 | unsigned long flags; |
e085b3ca BZ |
69 | unsigned long mode = drive->pio_mode, pair_mode; |
70 | const u8 pio = mode - XFER_PIO_0; | |
810253d4 BZ |
71 | u8 tim, misc, addr_pio = pio, clk; |
72 | ||
73 | /* DRDY is default 2 (by OPTi Databook) */ | |
80a65fc5 BZ |
74 | static const u8 addr_timings[2][5] = { |
75 | { 0x20, 0x10, 0x00, 0x00, 0x00 }, /* 33 MHz */ | |
76 | { 0x10, 0x10, 0x00, 0x00, 0x00 }, /* 25 MHz */ | |
810253d4 | 77 | }; |
80a65fc5 BZ |
78 | static const u8 data_rec_timings[2][5] = { |
79 | { 0x5b, 0x45, 0x32, 0x21, 0x20 }, /* 33 MHz */ | |
80 | { 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */ | |
810253d4 | 81 | }; |
1da177e4 | 82 | |
5bfb151f | 83 | ide_set_drivedata(drive, (void *)mode); |
6c987183 | 84 | |
7e59ea21 | 85 | if (pair) { |
5bfb151f JR |
86 | pair_mode = (unsigned long)ide_get_drivedata(pair); |
87 | if (pair_mode && pair_mode < mode) | |
88 | addr_pio = pair_mode - XFER_PIO_0; | |
6c987183 | 89 | } |
1da177e4 | 90 | |
21bd33a6 BZ |
91 | spin_lock_irqsave(&opti621_lock, flags); |
92 | ||
93 | reg_base = hwif->io_ports.data_addr; | |
94 | ||
95 | /* allow Register-B */ | |
96 | outb(0xc0, reg_base + CNTRL_REG); | |
97 | /* hmm, setupvic.exe does this ;-) */ | |
98 | outb(0xff, reg_base + 5); | |
99 | /* if reads 0xff, adapter not exist? */ | |
100 | (void)inb(reg_base + CNTRL_REG); | |
101 | /* if reads 0xc0, no interface exist? */ | |
102 | read_reg(CNTRL_REG); | |
103 | ||
104 | /* check CLK speed */ | |
105 | clk = read_reg(STRAP_REG) & 1; | |
106 | ||
107 | printk(KERN_INFO "%s: CLK = %d MHz\n", hwif->name, clk ? 25 : 33); | |
108 | ||
810253d4 BZ |
109 | tim = data_rec_timings[clk][pio]; |
110 | misc = addr_timings[clk][addr_pio]; | |
1da177e4 | 111 | |
6c987183 | 112 | /* select Index-0/1 for Register-A/B */ |
123995b9 | 113 | write_reg(drive->dn & 1, MISC_REG); |
0ecdca26 | 114 | /* set read cycle timings */ |
810253d4 | 115 | write_reg(tim, READ_REG); |
0ecdca26 | 116 | /* set write cycle timings */ |
810253d4 | 117 | write_reg(tim, WRITE_REG); |
1da177e4 | 118 | |
1da177e4 LT |
119 | /* use Register-A for drive 0 */ |
120 | /* use Register-B for drive 1 */ | |
0ecdca26 | 121 | write_reg(0x85, CNTRL_REG); |
1da177e4 LT |
122 | |
123 | /* set address setup, DRDY timings, */ | |
124 | /* and read prefetch for both drives */ | |
0ecdca26 | 125 | write_reg(misc, MISC_REG); |
1da177e4 | 126 | |
e65dde71 | 127 | spin_unlock_irqrestore(&opti621_lock, flags); |
1da177e4 LT |
128 | } |
129 | ||
ac95beed | 130 | static const struct ide_port_ops opti621_port_ops = { |
ac95beed BZ |
131 | .set_pio_mode = opti621_set_pio_mode, |
132 | }; | |
1da177e4 | 133 | |
80a65fc5 | 134 | static const struct ide_port_info opti621_chipset __devinitdata = { |
ced3ec8a | 135 | .name = DRV_NAME, |
80a65fc5 BZ |
136 | .enablebits = { {0x45, 0x80, 0x00}, {0x40, 0x08, 0x00} }, |
137 | .port_ops = &opti621_port_ops, | |
138 | .host_flags = IDE_HFLAG_NO_DMA, | |
139 | .pio_mask = ATA_PIO4, | |
1da177e4 LT |
140 | }; |
141 | ||
142 | static int __devinit opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id) | |
143 | { | |
6cdf6eb3 | 144 | return ide_pci_init_one(dev, &opti621_chipset, NULL); |
1da177e4 LT |
145 | } |
146 | ||
9cbcc5e3 BZ |
147 | static const struct pci_device_id opti621_pci_tbl[] = { |
148 | { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C621), 0 }, | |
80a65fc5 | 149 | { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C825), 0 }, |
1da177e4 LT |
150 | { 0, }, |
151 | }; | |
152 | MODULE_DEVICE_TABLE(pci, opti621_pci_tbl); | |
153 | ||
a9ab09e2 | 154 | static struct pci_driver opti621_pci_driver = { |
1da177e4 LT |
155 | .name = "Opti621_IDE", |
156 | .id_table = opti621_pci_tbl, | |
157 | .probe = opti621_init_one, | |
adc7f85a | 158 | .remove = ide_pci_remove, |
feb22b7f BZ |
159 | .suspend = ide_pci_suspend, |
160 | .resume = ide_pci_resume, | |
1da177e4 LT |
161 | }; |
162 | ||
82ab1eec | 163 | static int __init opti621_ide_init(void) |
1da177e4 | 164 | { |
a9ab09e2 | 165 | return ide_pci_register_driver(&opti621_pci_driver); |
1da177e4 LT |
166 | } |
167 | ||
adc7f85a BZ |
168 | static void __exit opti621_ide_exit(void) |
169 | { | |
a9ab09e2 | 170 | pci_unregister_driver(&opti621_pci_driver); |
adc7f85a BZ |
171 | } |
172 | ||
1da177e4 | 173 | module_init(opti621_ide_init); |
adc7f85a | 174 | module_exit(opti621_ide_exit); |
1da177e4 LT |
175 | |
176 | MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord"); | |
177 | MODULE_DESCRIPTION("PCI driver module for Opti621 IDE"); | |
178 | MODULE_LICENSE("GPL"); |