rev |
line source |
nuclear@78
|
1 #include <stdio.h>
|
nuclear@78
|
2 #include <stdlib.h>
|
nuclear@78
|
3 #include <string.h>
|
nuclear@83
|
4 #include <ctype.h>
|
nuclear@78
|
5 #include <inttypes.h>
|
nuclear@78
|
6 #include <assert.h>
|
nuclear@78
|
7 #include "ata.h"
|
nuclear@78
|
8 #include "intr.h"
|
nuclear@78
|
9 #include "asmops.h"
|
nuclear@87
|
10 #include "sched.h"
|
nuclear@86
|
11 #include "mutex.h"
|
nuclear@78
|
12
|
nuclear@78
|
13 /* registers */
|
nuclear@78
|
14 #define REG_DATA 0 /* R/W */
|
nuclear@78
|
15 #define REG_ERROR 1 /* R */
|
nuclear@78
|
16 #define REG_FEATURES 1 /* W */
|
nuclear@78
|
17 #define REG_COUNT 2 /* R/W */
|
nuclear@78
|
18 #define REG_LBA0 3 /* R/W */
|
nuclear@78
|
19 #define REG_LBA1 4 /* R/W */
|
nuclear@78
|
20 #define REG_LBA2 5 /* R/W */
|
nuclear@78
|
21 #define REG_DEVICE 6 /* R/W */
|
nuclear@78
|
22 #define REG_CMD 7 /* W */
|
nuclear@78
|
23 #define REG_STATUS 7 /* R */
|
nuclear@78
|
24
|
nuclear@78
|
25 #define REG_CTL 518
|
nuclear@78
|
26 #define REG_ALTSTAT 518
|
nuclear@78
|
27
|
nuclear@78
|
28 /* status bit fields */
|
nuclear@78
|
29 #define ST_ERR (1 << 0)
|
nuclear@78
|
30 #define ST_DRQ (1 << 3)
|
nuclear@78
|
31 #define ST_DRDY (1 << 6)
|
nuclear@78
|
32 #define ST_BSY (1 << 7)
|
nuclear@78
|
33
|
nuclear@78
|
34 /* device select bit in control register */
|
nuclear@78
|
35 #define DEV_SEL(x) (((x) & 1) << 4)
|
nuclear@86
|
36 #define DEV_LBA (1 << 6)
|
nuclear@78
|
37
|
nuclear@78
|
38 /* ATA commands */
|
nuclear@78
|
39 #define CMD_IDENTIFY 0xec
|
nuclear@86
|
40 #define CMD_READ 0x20
|
nuclear@86
|
41 #define CMD_READ48 0x24
|
nuclear@86
|
42 #define CMD_WRITE 0x30
|
nuclear@86
|
43 #define CMD_WRITE48 0x34
|
nuclear@78
|
44
|
nuclear@78
|
45
|
nuclear@78
|
46 struct device {
|
nuclear@78
|
47 int id; /* id of the device on its ATA interface (0 master, 1 slave) */
|
nuclear@78
|
48 int iface; /* ATA interface for this device (0 or 1) */
|
nuclear@78
|
49 int port_base; /* interface I/O port base */
|
nuclear@78
|
50
|
nuclear@78
|
51 uint32_t nsect_lba;
|
nuclear@78
|
52 uint64_t nsect_lba48;
|
nuclear@78
|
53 };
|
nuclear@78
|
54
|
nuclear@78
|
55
|
nuclear@89
|
56 static int readwrite_pio(int devno, uint64_t sect, void *buf, void (*rwdata)(struct device*, void*));
|
nuclear@78
|
57 static int identify(struct device *dev, int iface, int id);
|
nuclear@78
|
58 static void select_dev(struct device *dev);
|
nuclear@78
|
59 static int wait_busy(struct device *dev);
|
nuclear@78
|
60 static int wait_drq(struct device *dev);
|
nuclear@78
|
61 static void read_data(struct device *dev, void *buf);
|
nuclear@89
|
62 static void write_data(struct device *dev, void *buf);
|
nuclear@78
|
63 static inline uint8_t read_reg8(struct device *dev, int reg);
|
nuclear@78
|
64 static inline uint16_t read_reg16(struct device *dev, int reg);
|
nuclear@78
|
65 static inline void write_reg8(struct device *dev, int reg, uint8_t val);
|
nuclear@78
|
66 static inline void write_reg16(struct device *dev, int reg, uint16_t val);
|
nuclear@78
|
67 static void ata_intr(int inum);
|
nuclear@80
|
68 static void *atastr(void *res, void *src, int n);
|
nuclear@78
|
69 static char *size_str(uint64_t nsect, char *buf);
|
nuclear@87
|
70 static void print_error(int devid, int wr, uint32_t high, uint32_t low, unsigned char err);
|
nuclear@78
|
71
|
nuclear@78
|
72 /* last drive selected on each bus */
|
nuclear@78
|
73 static int drvsel[2] = {-1, -1};
|
nuclear@78
|
74
|
nuclear@78
|
75 /* 4 possible devices: 2 ATA interfaces with 2 devices each.
|
nuclear@78
|
76 * this will never change unless we start querying the PCI config space
|
nuclear@78
|
77 * for additional drives (in which case this whole init code must be
|
nuclear@78
|
78 * rewritten anyway), but I like it spelt out like this.
|
nuclear@78
|
79 */
|
nuclear@78
|
80 #define MAX_IFACES 2
|
nuclear@78
|
81 #define MAX_DEV (MAX_IFACES * 2)
|
nuclear@86
|
82 static struct device devices[MAX_DEV];
|
nuclear@88
|
83 static int ndev;
|
nuclear@86
|
84
|
nuclear@86
|
85 /* This serves as a sync point for I/O. While the mutex is held,
|
nuclear@86
|
86 * some process is doing I/O and all the others must wait.
|
nuclear@86
|
87 */
|
nuclear@86
|
88 static mutex_t pending;
|
nuclear@78
|
89
|
nuclear@78
|
90
|
nuclear@78
|
91 void init_ata(void)
|
nuclear@78
|
92 {
|
nuclear@78
|
93 int i;
|
nuclear@78
|
94
|
nuclear@78
|
95 interrupt(IRQ_TO_INTR(15), ata_intr);
|
nuclear@78
|
96
|
nuclear@88
|
97 ndev = 0;
|
nuclear@78
|
98 for(i=0; i<MAX_DEV; i++) {
|
nuclear@78
|
99 int iface = i / MAX_IFACES;
|
nuclear@78
|
100 int id = i % MAX_IFACES;
|
nuclear@78
|
101
|
nuclear@88
|
102 if(identify(devices + ndev, iface, id) == 0) {
|
nuclear@88
|
103 ndev++;
|
nuclear@78
|
104 }
|
nuclear@78
|
105 }
|
nuclear@88
|
106 }
|
nuclear@86
|
107
|
nuclear@88
|
108 int ata_num_devices(void)
|
nuclear@88
|
109 {
|
nuclear@88
|
110 return ndev;
|
nuclear@86
|
111 }
|
nuclear@86
|
112
|
nuclear@90
|
113 uint64_t ata_num_sectors(int devno)
|
nuclear@90
|
114 {
|
nuclear@90
|
115 struct device *dev = devices + devno;
|
nuclear@90
|
116
|
nuclear@90
|
117 if(dev->nsect_lba48) {
|
nuclear@90
|
118 return dev->nsect_lba48;
|
nuclear@90
|
119 }
|
nuclear@90
|
120 return dev->nsect_lba;
|
nuclear@90
|
121 }
|
nuclear@90
|
122
|
nuclear@86
|
123 int ata_read_pio(int devno, uint64_t sect, void *buf)
|
nuclear@86
|
124 {
|
nuclear@89
|
125 return readwrite_pio(devno, sect, buf, read_data);
|
nuclear@89
|
126 }
|
nuclear@89
|
127
|
nuclear@89
|
128 int ata_write_pio(int devno, uint64_t sect, void *buf)
|
nuclear@89
|
129 {
|
nuclear@89
|
130 return readwrite_pio(devno, sect, buf, write_data);
|
nuclear@89
|
131 }
|
nuclear@89
|
132
|
nuclear@89
|
133 static int readwrite_pio(int devno, uint64_t sect, void *buf, void (*rwdata)(struct device*, void*))
|
nuclear@89
|
134 {
|
nuclear@88
|
135 int use_irq, cmd, st, res = -1;
|
nuclear@87
|
136 uint32_t sect_low, sect_high;
|
nuclear@87
|
137 struct device *dev = devices + devno;
|
nuclear@86
|
138
|
nuclear@86
|
139 if(dev->id == -1) {
|
nuclear@86
|
140 return -1;
|
nuclear@86
|
141 }
|
nuclear@88
|
142 use_irq = get_current_proc() != 0;
|
nuclear@86
|
143
|
nuclear@86
|
144 if(use_irq) {
|
nuclear@86
|
145 /* wait for the interface to become available */
|
nuclear@86
|
146 mutex_lock(&pending);
|
nuclear@86
|
147 }
|
nuclear@86
|
148
|
nuclear@86
|
149 select_dev(dev);
|
nuclear@86
|
150
|
nuclear@86
|
151 /* LBA48 requires the high-order bits first */
|
nuclear@86
|
152 if(sect >= dev->nsect_lba) {
|
nuclear@87
|
153 sect_high = (uint32_t)(sect >> 24);
|
nuclear@86
|
154 sect_low = (uint32_t)sect & 0xffffff;
|
nuclear@86
|
155
|
nuclear@86
|
156 if(sect >= dev->nsect_lba48) {
|
nuclear@86
|
157 goto end;
|
nuclear@86
|
158 }
|
nuclear@86
|
159 cmd = CMD_READ48;
|
nuclear@86
|
160
|
nuclear@86
|
161 write_reg8(dev, REG_COUNT, 0);
|
nuclear@86
|
162 write_reg8(dev, REG_LBA0, sect_high & 0xff);
|
nuclear@86
|
163 write_reg8(dev, REG_LBA1, (sect_high >> 8) & 0xff);
|
nuclear@86
|
164 write_reg8(dev, REG_LBA2, (sect_high >> 16) & 0xff);
|
nuclear@86
|
165 } else {
|
nuclear@86
|
166 cmd = CMD_READ;
|
nuclear@87
|
167 sect_high = 0;
|
nuclear@87
|
168 sect_low = (uint32_t)sect & 0xfffffff;
|
nuclear@86
|
169 }
|
nuclear@86
|
170
|
nuclear@86
|
171 write_reg8(dev, REG_COUNT, 1);
|
nuclear@86
|
172 write_reg8(dev, REG_LBA0, sect_low & 0xff);
|
nuclear@86
|
173 write_reg8(dev, REG_LBA1, (sect_low >> 8) & 0xff);
|
nuclear@86
|
174 write_reg8(dev, REG_LBA2, (sect_low >> 16) & 0xff);
|
nuclear@87
|
175 write_reg8(dev, REG_DEVICE, ((sect_low >> 24) & 0xf) | DEV_LBA | DEV_SEL(dev->id));
|
nuclear@86
|
176 /* execute */
|
nuclear@86
|
177 write_reg8(dev, REG_CMD, cmd);
|
nuclear@86
|
178
|
nuclear@86
|
179 /* wait for the data to become available */
|
nuclear@86
|
180 do {
|
nuclear@86
|
181 if(use_irq) {
|
nuclear@86
|
182 /* also sleep on the mutex if we're called from userspace */
|
nuclear@86
|
183 wait(&pending);
|
nuclear@86
|
184 }
|
nuclear@87
|
185 } while(((st = read_reg8(dev, REG_ALTSTAT)) & (ST_DRQ | ST_ERR)) == 0);
|
nuclear@86
|
186
|
nuclear@86
|
187 if(st & ST_ERR) {
|
nuclear@87
|
188 //print_error(int devid, int wr, uint32_t high, uint32_t low, unsigned char err);
|
nuclear@87
|
189 unsigned char err;
|
nuclear@87
|
190
|
nuclear@87
|
191 err = read_reg8(dev, REG_ERROR);
|
nuclear@87
|
192 print_error(devno, 0, sect_high, sect_low, err);
|
nuclear@86
|
193 goto end;
|
nuclear@86
|
194 }
|
nuclear@86
|
195
|
nuclear@89
|
196 /* read/write the data and we're done */
|
nuclear@89
|
197 rwdata(dev, buf);
|
nuclear@86
|
198 res = 0;
|
nuclear@86
|
199 end:
|
nuclear@86
|
200 if(use_irq) {
|
nuclear@86
|
201 mutex_unlock(&pending);
|
nuclear@86
|
202 }
|
nuclear@86
|
203 return res;
|
nuclear@86
|
204 }
|
nuclear@86
|
205
|
nuclear@78
|
206 static int identify(struct device *dev, int iface, int id)
|
nuclear@78
|
207 {
|
nuclear@78
|
208 /* base address of the two ATA interfaces */
|
nuclear@78
|
209 static const int port_base[] = {0x1f0, 0x170};
|
nuclear@78
|
210 unsigned char st;
|
nuclear@78
|
211 uint16_t *info;
|
nuclear@78
|
212 char textbuf[42]; /* at most we need 40 chars for ident strings */
|
nuclear@78
|
213
|
nuclear@78
|
214 dev->id = id;
|
nuclear@78
|
215 dev->iface = iface;
|
nuclear@78
|
216 dev->port_base = port_base[iface];
|
nuclear@78
|
217
|
nuclear@78
|
218 /* a status of 0xff means there's no drive on the interface */
|
nuclear@78
|
219 if((st = read_reg8(dev, REG_ALTSTAT)) == 0xff) {
|
nuclear@78
|
220 return -1;
|
nuclear@78
|
221 }
|
nuclear@78
|
222
|
nuclear@78
|
223 select_dev(dev);
|
nuclear@78
|
224
|
nuclear@78
|
225 write_reg8(dev, REG_CMD, CMD_IDENTIFY);
|
nuclear@78
|
226
|
nuclear@78
|
227 if(!(st = read_reg8(dev, REG_ALTSTAT)) || (st & ST_ERR)) {
|
nuclear@78
|
228 /* does not exist */
|
nuclear@78
|
229 return -1;
|
nuclear@78
|
230 }
|
nuclear@78
|
231 if(wait_busy(dev) == -1) {
|
nuclear@78
|
232 /* got ST_ERR, not ATA */
|
nuclear@78
|
233 return -1;
|
nuclear@78
|
234 }
|
nuclear@78
|
235
|
nuclear@78
|
236 info = malloc(512);
|
nuclear@78
|
237 assert(info);
|
nuclear@78
|
238
|
nuclear@78
|
239 /* read the device information */
|
nuclear@78
|
240 read_data(dev, info);
|
nuclear@78
|
241
|
nuclear@78
|
242 /* print model and serial */
|
nuclear@83
|
243 printf("ata%d: %s", (dev->iface << 1) | dev->id, atastr(textbuf, info + 27, 40));
|
nuclear@83
|
244 printf(" [s/n: %s]", atastr(textbuf, info + 10, 20));
|
nuclear@78
|
245
|
nuclear@78
|
246 dev->nsect_lba = *(uint32_t*)(info + 60);
|
nuclear@78
|
247 dev->nsect_lba48 = *(uint64_t*)(info + 100) & 0xffffffffffffull;
|
nuclear@78
|
248
|
nuclear@78
|
249 if(!dev->nsect_lba) {
|
nuclear@78
|
250 printf(" drive does not support LBA, ignoring!\n");
|
nuclear@78
|
251 free(info);
|
nuclear@78
|
252 return -1;
|
nuclear@78
|
253 }
|
nuclear@78
|
254
|
nuclear@78
|
255 if(dev->nsect_lba48) {
|
nuclear@78
|
256 size_str(dev->nsect_lba48, textbuf);
|
nuclear@78
|
257 } else {
|
nuclear@78
|
258 size_str(dev->nsect_lba, textbuf);
|
nuclear@78
|
259 }
|
nuclear@83
|
260 printf(" size: %s\n", textbuf);
|
nuclear@78
|
261
|
nuclear@78
|
262 free(info);
|
nuclear@78
|
263 return 0;
|
nuclear@78
|
264 }
|
nuclear@78
|
265
|
nuclear@78
|
266 static void select_dev(struct device *dev)
|
nuclear@78
|
267 {
|
nuclear@78
|
268 /* if this is the currently selected device, thy work is done */
|
nuclear@78
|
269 if(drvsel[dev->iface] == dev->id)
|
nuclear@78
|
270 return;
|
nuclear@78
|
271
|
nuclear@78
|
272 /* wait for BSY and DRQ to clear */
|
nuclear@78
|
273 while(read_reg8(dev, REG_ALTSTAT) & (ST_BSY | ST_DRQ));
|
nuclear@78
|
274
|
nuclear@78
|
275 /* set the correct device bit to the device register */
|
nuclear@78
|
276 write_reg8(dev, REG_DEVICE, DEV_SEL(dev->id));
|
nuclear@86
|
277
|
nuclear@86
|
278 /* wait a bit to allow the device time to respond */
|
nuclear@86
|
279 iodelay(); iodelay(); iodelay(); iodelay();
|
nuclear@78
|
280 }
|
nuclear@78
|
281
|
nuclear@78
|
282 static int wait_busy(struct device *dev)
|
nuclear@78
|
283 {
|
nuclear@78
|
284 unsigned char st;
|
nuclear@78
|
285
|
nuclear@78
|
286 do {
|
nuclear@78
|
287 st = read_reg8(dev, REG_ALTSTAT);
|
nuclear@78
|
288 } while((st & ST_BSY) && !(st & ST_ERR));
|
nuclear@78
|
289
|
nuclear@78
|
290 return st & ST_ERR ? -1 : 0;
|
nuclear@78
|
291 }
|
nuclear@78
|
292
|
nuclear@78
|
293 static int wait_drq(struct device *dev)
|
nuclear@78
|
294 {
|
nuclear@78
|
295 unsigned char st;
|
nuclear@78
|
296
|
nuclear@78
|
297 do {
|
nuclear@78
|
298 st = read_reg8(dev, REG_ALTSTAT);
|
nuclear@78
|
299 } while(!(st & (ST_DRQ | ST_ERR)));
|
nuclear@78
|
300
|
nuclear@78
|
301 return st & ST_ERR ? -1 : 0;
|
nuclear@78
|
302 }
|
nuclear@78
|
303
|
nuclear@78
|
304 static void read_data(struct device *dev, void *buf)
|
nuclear@78
|
305 {
|
nuclear@78
|
306 int i;
|
nuclear@78
|
307 uint16_t *ptr = buf;
|
nuclear@78
|
308
|
nuclear@78
|
309 /* wait for the data request from the drive */
|
nuclear@78
|
310 wait_drq(dev);
|
nuclear@78
|
311
|
nuclear@78
|
312 /* ready to transfer */
|
nuclear@78
|
313 for(i=0; i<256; i++) {
|
nuclear@78
|
314 *ptr++ = read_reg16(dev, REG_DATA);
|
nuclear@78
|
315 }
|
nuclear@78
|
316 }
|
nuclear@78
|
317
|
nuclear@89
|
318 static void write_data(struct device *dev, void *buf)
|
nuclear@89
|
319 {
|
nuclear@89
|
320 int i;
|
nuclear@89
|
321 uint16_t *ptr = buf;
|
nuclear@89
|
322
|
nuclear@89
|
323 /* wait for the data request from the device */
|
nuclear@89
|
324 wait_drq(dev);
|
nuclear@89
|
325
|
nuclear@89
|
326 /* ready to transfer */
|
nuclear@89
|
327 for(i=0; i<256; i++) {
|
nuclear@89
|
328 write_reg16(dev, REG_DATA, *ptr++);
|
nuclear@89
|
329 }
|
nuclear@89
|
330 }
|
nuclear@89
|
331
|
nuclear@78
|
332 static inline uint8_t read_reg8(struct device *dev, int reg)
|
nuclear@78
|
333 {
|
nuclear@78
|
334 uint8_t val;
|
nuclear@78
|
335 inb(val, dev->port_base + reg);
|
nuclear@78
|
336 return val;
|
nuclear@78
|
337 }
|
nuclear@78
|
338
|
nuclear@78
|
339 static inline uint16_t read_reg16(struct device *dev, int reg)
|
nuclear@78
|
340 {
|
nuclear@78
|
341 uint16_t val;
|
nuclear@78
|
342 inw(val, dev->port_base + reg);
|
nuclear@78
|
343 return val;
|
nuclear@78
|
344 }
|
nuclear@78
|
345
|
nuclear@78
|
346 static inline void write_reg8(struct device *dev, int reg, uint8_t val)
|
nuclear@78
|
347 {
|
nuclear@78
|
348 outb(val, dev->port_base + reg);
|
nuclear@78
|
349 }
|
nuclear@78
|
350
|
nuclear@78
|
351 static inline void write_reg16(struct device *dev, int reg, uint16_t val)
|
nuclear@78
|
352 {
|
nuclear@78
|
353 outw(val, dev->port_base + reg);
|
nuclear@78
|
354 }
|
nuclear@78
|
355
|
nuclear@78
|
356 static void ata_intr(int inum)
|
nuclear@78
|
357 {
|
nuclear@78
|
358 printf("ATA interrupt\n");
|
nuclear@78
|
359 }
|
nuclear@78
|
360
|
nuclear@80
|
361 static void *atastr(void *res, void *src, int n)
|
nuclear@80
|
362 {
|
nuclear@80
|
363 int i;
|
nuclear@80
|
364 uint16_t *sptr = (uint16_t*)src;
|
nuclear@80
|
365 char *dptr = res;
|
nuclear@80
|
366
|
nuclear@80
|
367 for(i=0; i<n/2; i++) {
|
nuclear@80
|
368 *dptr++ = (*sptr & 0xff00) >> 8;
|
nuclear@80
|
369 *dptr++ = *sptr++ & 0xff;
|
nuclear@80
|
370 }
|
nuclear@83
|
371
|
nuclear@83
|
372 while(isspace(*--dptr));
|
nuclear@83
|
373 *++dptr = 0;
|
nuclear@80
|
374 return res;
|
nuclear@80
|
375 }
|
nuclear@80
|
376
|
nuclear@78
|
377 static char *size_str(uint64_t nsect, char *buf)
|
nuclear@78
|
378 {
|
nuclear@78
|
379 static const char *suffix[] = {"kb", "mb", "gb", "tb", "pb", 0};
|
nuclear@78
|
380 int i;
|
nuclear@82
|
381 unsigned int rem;
|
nuclear@78
|
382
|
nuclear@78
|
383 /* start with kilobytes */
|
nuclear@78
|
384 nsect /= 2;
|
nuclear@78
|
385
|
nuclear@78
|
386 for(i=0; nsect >= 1024 && suffix[i + 1]; i++) {
|
nuclear@82
|
387 rem = nsect % 1024;
|
nuclear@78
|
388 nsect /= 1024;
|
nuclear@78
|
389 }
|
nuclear@82
|
390 sprintf(buf, "%u.%u%s", (unsigned int)nsect, 100 * rem / 1024, suffix[i]);
|
nuclear@78
|
391 return buf;
|
nuclear@78
|
392 }
|
nuclear@78
|
393
|
nuclear@87
|
394 #define ERR_NM (1 << 1)
|
nuclear@87
|
395 #define ERR_ABRT (1 << 2)
|
nuclear@87
|
396 #define ERR_MCR (1 << 3)
|
nuclear@87
|
397 #define ERR_IDNF (1 << 4)
|
nuclear@87
|
398 #define ERR_MC (1 << 5)
|
nuclear@87
|
399 #define ERR_UNC (1 << 6)
|
nuclear@87
|
400
|
nuclear@87
|
401 static void print_error(int devid, int wr, uint32_t high, uint32_t low, unsigned char err)
|
nuclear@87
|
402 {
|
nuclear@87
|
403 printf("ata%d %s %serror ", devid, wr ? "write" : "read", err & ERR_UNC ? "uncorrectable " : "");
|
nuclear@87
|
404 printf("at sector %x%x: ", high, low);
|
nuclear@87
|
405
|
nuclear@87
|
406 if(err & ERR_ABRT)
|
nuclear@87
|
407 printf("abort ");
|
nuclear@87
|
408 if(err & ERR_IDNF)
|
nuclear@87
|
409 printf("invalid address ");
|
nuclear@87
|
410 if(err & ERR_NM)
|
nuclear@87
|
411 printf("no media ");
|
nuclear@87
|
412
|
nuclear@87
|
413 printf("(%x)\n", (unsigned int)err);
|
nuclear@87
|
414 }
|