diff options
Diffstat (limited to 'drivers/ide/ali14xx.c')
-rw-r--r-- | drivers/ide/ali14xx.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/drivers/ide/ali14xx.c b/drivers/ide/ali14xx.c new file mode 100644 index 000000000000..90da1f953ed0 --- /dev/null +++ b/drivers/ide/ali14xx.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1996 Linus Torvalds & author (see below) | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | * ALI M14xx chipset EIDE controller | ||
7 | * | ||
8 | * Works for ALI M1439/1443/1445/1487/1489 chipsets. | ||
9 | * | ||
10 | * Adapted from code developed by derekn@vw.ece.cmu.edu. -ml | ||
11 | * Derek's notes follow: | ||
12 | * | ||
13 | * I think the code should be pretty understandable, | ||
14 | * but I'll be happy to (try to) answer questions. | ||
15 | * | ||
16 | * The critical part is in the setupDrive function. The initRegisters | ||
17 | * function doesn't seem to be necessary, but the DOS driver does it, so | ||
18 | * I threw it in. | ||
19 | * | ||
20 | * I've only tested this on my system, which only has one disk. I posted | ||
21 | * it to comp.sys.linux.hardware, so maybe some other people will try it | ||
22 | * out. | ||
23 | * | ||
24 | * Derek Noonburg (derekn@ece.cmu.edu) | ||
25 | * 95-sep-26 | ||
26 | * | ||
27 | * Update 96-jul-13: | ||
28 | * | ||
29 | * I've since upgraded to two disks and a CD-ROM, with no trouble, and | ||
30 | * I've also heard from several others who have used it successfully. | ||
31 | * This driver appears to work with both the 1443/1445 and the 1487/1489 | ||
32 | * chipsets. I've added support for PIO mode 4 for the 1487. This | ||
33 | * seems to work just fine on the 1443 also, although I'm not sure it's | ||
34 | * advertised as supporting mode 4. (I've been running a WDC AC21200 in | ||
35 | * mode 4 for a while now with no trouble.) -Derek | ||
36 | */ | ||
37 | |||
38 | #include <linux/module.h> | ||
39 | #include <linux/types.h> | ||
40 | #include <linux/kernel.h> | ||
41 | #include <linux/delay.h> | ||
42 | #include <linux/timer.h> | ||
43 | #include <linux/mm.h> | ||
44 | #include <linux/ioport.h> | ||
45 | #include <linux/blkdev.h> | ||
46 | #include <linux/ide.h> | ||
47 | #include <linux/init.h> | ||
48 | |||
49 | #include <asm/io.h> | ||
50 | |||
51 | #define DRV_NAME "ali14xx" | ||
52 | |||
53 | /* port addresses for auto-detection */ | ||
54 | #define ALI_NUM_PORTS 4 | ||
55 | static const int ports[ALI_NUM_PORTS] __initdata = | ||
56 | { 0x074, 0x0f4, 0x034, 0x0e4 }; | ||
57 | |||
58 | /* register initialization data */ | ||
59 | typedef struct { u8 reg, data; } RegInitializer; | ||
60 | |||
61 | static const RegInitializer initData[] __initdata = { | ||
62 | {0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00}, | ||
63 | {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f}, | ||
64 | {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00}, | ||
65 | {0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00}, | ||
66 | {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00}, | ||
67 | {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff}, | ||
68 | {0x35, 0x03}, {0x00, 0x00} | ||
69 | }; | ||
70 | |||
71 | /* timing parameter registers for each drive */ | ||
72 | static struct { u8 reg1, reg2, reg3, reg4; } regTab[4] = { | ||
73 | {0x03, 0x26, 0x04, 0x27}, /* drive 0 */ | ||
74 | {0x05, 0x28, 0x06, 0x29}, /* drive 1 */ | ||
75 | {0x2b, 0x30, 0x2c, 0x31}, /* drive 2 */ | ||
76 | {0x2d, 0x32, 0x2e, 0x33}, /* drive 3 */ | ||
77 | }; | ||
78 | |||
79 | static int basePort; /* base port address */ | ||
80 | static int regPort; /* port for register number */ | ||
81 | static int dataPort; /* port for register data */ | ||
82 | static u8 regOn; /* output to base port to access registers */ | ||
83 | static u8 regOff; /* output to base port to close registers */ | ||
84 | |||
85 | /*------------------------------------------------------------------------*/ | ||
86 | |||
87 | /* | ||
88 | * Read a controller register. | ||
89 | */ | ||
90 | static inline u8 inReg(u8 reg) | ||
91 | { | ||
92 | outb_p(reg, regPort); | ||
93 | return inb(dataPort); | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Write a controller register. | ||
98 | */ | ||
99 | static void outReg(u8 data, u8 reg) | ||
100 | { | ||
101 | outb_p(reg, regPort); | ||
102 | outb_p(data, dataPort); | ||
103 | } | ||
104 | |||
105 | static DEFINE_SPINLOCK(ali14xx_lock); | ||
106 | |||
107 | /* | ||
108 | * Set PIO mode for the specified drive. | ||
109 | * This function computes timing parameters | ||
110 | * and sets controller registers accordingly. | ||
111 | */ | ||
112 | static void ali14xx_set_pio_mode(ide_drive_t *drive, const u8 pio) | ||
113 | { | ||
114 | int driveNum; | ||
115 | int time1, time2; | ||
116 | u8 param1, param2, param3, param4; | ||
117 | unsigned long flags; | ||
118 | int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50; | ||
119 | struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); | ||
120 | |||
121 | /* calculate timing, according to PIO mode */ | ||
122 | time1 = ide_pio_cycle_time(drive, pio); | ||
123 | time2 = t->active; | ||
124 | param3 = param1 = (time2 * bus_speed + 999) / 1000; | ||
125 | param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1; | ||
126 | if (pio < 3) { | ||
127 | param3 += 8; | ||
128 | param4 += 8; | ||
129 | } | ||
130 | printk(KERN_DEBUG "%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n", | ||
131 | drive->name, pio, time1, time2, param1, param2, param3, param4); | ||
132 | |||
133 | /* stuff timing parameters into controller registers */ | ||
134 | driveNum = (drive->hwif->index << 1) + (drive->dn & 1); | ||
135 | spin_lock_irqsave(&ali14xx_lock, flags); | ||
136 | outb_p(regOn, basePort); | ||
137 | outReg(param1, regTab[driveNum].reg1); | ||
138 | outReg(param2, regTab[driveNum].reg2); | ||
139 | outReg(param3, regTab[driveNum].reg3); | ||
140 | outReg(param4, regTab[driveNum].reg4); | ||
141 | outb_p(regOff, basePort); | ||
142 | spin_unlock_irqrestore(&ali14xx_lock, flags); | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Auto-detect the IDE controller port. | ||
147 | */ | ||
148 | static int __init findPort(void) | ||
149 | { | ||
150 | int i; | ||
151 | u8 t; | ||
152 | unsigned long flags; | ||
153 | |||
154 | local_irq_save(flags); | ||
155 | for (i = 0; i < ALI_NUM_PORTS; ++i) { | ||
156 | basePort = ports[i]; | ||
157 | regOff = inb(basePort); | ||
158 | for (regOn = 0x30; regOn <= 0x33; ++regOn) { | ||
159 | outb_p(regOn, basePort); | ||
160 | if (inb(basePort) == regOn) { | ||
161 | regPort = basePort + 4; | ||
162 | dataPort = basePort + 8; | ||
163 | t = inReg(0) & 0xf0; | ||
164 | outb_p(regOff, basePort); | ||
165 | local_irq_restore(flags); | ||
166 | if (t != 0x50) | ||
167 | return 0; | ||
168 | return 1; /* success */ | ||
169 | } | ||
170 | } | ||
171 | outb_p(regOff, basePort); | ||
172 | } | ||
173 | local_irq_restore(flags); | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Initialize controller registers with default values. | ||
179 | */ | ||
180 | static int __init initRegisters(void) | ||
181 | { | ||
182 | const RegInitializer *p; | ||
183 | u8 t; | ||
184 | unsigned long flags; | ||
185 | |||
186 | local_irq_save(flags); | ||
187 | outb_p(regOn, basePort); | ||
188 | for (p = initData; p->reg != 0; ++p) | ||
189 | outReg(p->data, p->reg); | ||
190 | outb_p(0x01, regPort); | ||
191 | t = inb(regPort) & 0x01; | ||
192 | outb_p(regOff, basePort); | ||
193 | local_irq_restore(flags); | ||
194 | return t; | ||
195 | } | ||
196 | |||
197 | static const struct ide_port_ops ali14xx_port_ops = { | ||
198 | .set_pio_mode = ali14xx_set_pio_mode, | ||
199 | }; | ||
200 | |||
201 | static const struct ide_port_info ali14xx_port_info = { | ||
202 | .name = DRV_NAME, | ||
203 | .chipset = ide_ali14xx, | ||
204 | .port_ops = &ali14xx_port_ops, | ||
205 | .host_flags = IDE_HFLAG_NO_DMA, | ||
206 | .pio_mask = ATA_PIO4, | ||
207 | }; | ||
208 | |||
209 | static int __init ali14xx_probe(void) | ||
210 | { | ||
211 | printk(KERN_DEBUG "ali14xx: base=0x%03x, regOn=0x%02x.\n", | ||
212 | basePort, regOn); | ||
213 | |||
214 | /* initialize controller registers */ | ||
215 | if (!initRegisters()) { | ||
216 | printk(KERN_ERR "ali14xx: Chip initialization failed.\n"); | ||
217 | return 1; | ||
218 | } | ||
219 | |||
220 | return ide_legacy_device_add(&ali14xx_port_info, 0); | ||
221 | } | ||
222 | |||
223 | static int probe_ali14xx; | ||
224 | |||
225 | module_param_named(probe, probe_ali14xx, bool, 0); | ||
226 | MODULE_PARM_DESC(probe, "probe for ALI M14xx chipsets"); | ||
227 | |||
228 | static int __init ali14xx_init(void) | ||
229 | { | ||
230 | if (probe_ali14xx == 0) | ||
231 | goto out; | ||
232 | |||
233 | /* auto-detect IDE controller port */ | ||
234 | if (findPort()) { | ||
235 | if (ali14xx_probe()) | ||
236 | return -ENODEV; | ||
237 | return 0; | ||
238 | } | ||
239 | printk(KERN_ERR "ali14xx: not found.\n"); | ||
240 | out: | ||
241 | return -ENODEV; | ||
242 | } | ||
243 | |||
244 | module_init(ali14xx_init); | ||
245 | |||
246 | MODULE_AUTHOR("see local file"); | ||
247 | MODULE_DESCRIPTION("support of ALI 14XX IDE chipsets"); | ||
248 | MODULE_LICENSE("GPL"); | ||