kern

view src/ata.c @ 78:251a65b62223

of course I forgot to actually add the new files... every fucking time...
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 Dec 2011 12:56:46 +0200
parents
children 4db99a52863e
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <assert.h>
6 #include "ata.h"
7 #include "intr.h"
8 #include "asmops.h"
10 /* registers */
11 #define REG_DATA 0 /* R/W */
12 #define REG_ERROR 1 /* R */
13 #define REG_FEATURES 1 /* W */
14 #define REG_COUNT 2 /* R/W */
15 #define REG_LBA0 3 /* R/W */
16 #define REG_LBA1 4 /* R/W */
17 #define REG_LBA2 5 /* R/W */
18 #define REG_DEVICE 6 /* R/W */
19 #define REG_CMD 7 /* W */
20 #define REG_STATUS 7 /* R */
22 #define REG_CTL 518
23 #define REG_ALTSTAT 518
25 /* status bit fields */
26 #define ST_ERR (1 << 0)
27 #define ST_DRQ (1 << 3)
28 #define ST_DRDY (1 << 6)
29 #define ST_BSY (1 << 7)
31 /* device select bit in control register */
32 #define DEV_SEL(x) (((x) & 1) << 4)
34 /* ATA commands */
35 #define CMD_IDENTIFY 0xec
38 struct device {
39 int id; /* id of the device on its ATA interface (0 master, 1 slave) */
40 int iface; /* ATA interface for this device (0 or 1) */
41 int port_base; /* interface I/O port base */
43 uint32_t nsect_lba;
44 uint64_t nsect_lba48;
45 };
48 static int identify(struct device *dev, int iface, int id);
49 static void select_dev(struct device *dev);
50 static int wait_busy(struct device *dev);
51 static int wait_drq(struct device *dev);
52 static void read_data(struct device *dev, void *buf);
53 static inline uint8_t read_reg8(struct device *dev, int reg);
54 static inline uint16_t read_reg16(struct device *dev, int reg);
55 static inline void write_reg8(struct device *dev, int reg, uint8_t val);
56 static inline void write_reg16(struct device *dev, int reg, uint16_t val);
57 static void ata_intr(int inum);
58 static char *size_str(uint64_t nsect, char *buf);
60 /* last drive selected on each bus */
61 static int drvsel[2] = {-1, -1};
63 /* 4 possible devices: 2 ATA interfaces with 2 devices each.
64 * this will never change unless we start querying the PCI config space
65 * for additional drives (in which case this whole init code must be
66 * rewritten anyway), but I like it spelt out like this.
67 */
68 #define MAX_IFACES 2
69 #define MAX_DEV (MAX_IFACES * 2)
70 static struct device dev[MAX_DEV];
73 void init_ata(void)
74 {
75 int i;
77 interrupt(IRQ_TO_INTR(15), ata_intr);
79 for(i=0; i<MAX_DEV; i++) {
80 int iface = i / MAX_IFACES;
81 int id = i % MAX_IFACES;
83 if(identify(dev + i, iface, id) == -1) {
84 dev[i].id = -1;
85 }
86 }
87 }
89 static int identify(struct device *dev, int iface, int id)
90 {
91 /* base address of the two ATA interfaces */
92 static const int port_base[] = {0x1f0, 0x170};
93 unsigned char st;
94 uint16_t *info;
95 char textbuf[42]; /* at most we need 40 chars for ident strings */
97 dev->id = id;
98 dev->iface = iface;
99 dev->port_base = port_base[iface];
101 /* a status of 0xff means there's no drive on the interface */
102 if((st = read_reg8(dev, REG_ALTSTAT)) == 0xff) {
103 return -1;
104 }
106 select_dev(dev);
107 /* wait a bit to allow the device time to respond */
108 iodelay(); iodelay(); iodelay(); iodelay();
110 write_reg8(dev, REG_CMD, CMD_IDENTIFY);
112 if(!(st = read_reg8(dev, REG_ALTSTAT)) || (st & ST_ERR)) {
113 /* does not exist */
114 return -1;
115 }
116 if(wait_busy(dev) == -1) {
117 /* got ST_ERR, not ATA */
118 return -1;
119 }
121 info = malloc(512);
122 assert(info);
124 /* read the device information */
125 read_data(dev, info);
127 /* print model and serial */
128 memcpy(textbuf, info + 27, 40);
129 textbuf[40] = 0;
130 printf("- found ata drive (%d,%d): %s\n", dev->iface, dev->id, textbuf);
132 memcpy(textbuf, info + 10, 20);
133 textbuf[20] = 0;
134 printf(" s/n: %s\n", textbuf);
136 dev->nsect_lba = *(uint32_t*)(info + 60);
137 dev->nsect_lba48 = *(uint64_t*)(info + 100) & 0xffffffffffffull;
139 if(!dev->nsect_lba) {
140 printf(" drive does not support LBA, ignoring!\n");
141 free(info);
142 return -1;
143 }
145 if(dev->nsect_lba48) {
146 size_str(dev->nsect_lba48, textbuf);
147 } else {
148 size_str(dev->nsect_lba, textbuf);
149 }
150 printf(" size: %s\n", textbuf);
152 free(info);
153 return 0;
154 }
156 static void select_dev(struct device *dev)
157 {
158 /* if this is the currently selected device, thy work is done */
159 if(drvsel[dev->iface] == dev->id)
160 return;
162 /* wait for BSY and DRQ to clear */
163 while(read_reg8(dev, REG_ALTSTAT) & (ST_BSY | ST_DRQ));
165 /* set the correct device bit to the device register */
166 write_reg8(dev, REG_DEVICE, DEV_SEL(dev->id));
167 }
169 static int wait_busy(struct device *dev)
170 {
171 unsigned char st;
173 do {
174 st = read_reg8(dev, REG_ALTSTAT);
175 } while((st & ST_BSY) && !(st & ST_ERR));
177 return st & ST_ERR ? -1 : 0;
178 }
180 static int wait_drq(struct device *dev)
181 {
182 unsigned char st;
184 do {
185 st = read_reg8(dev, REG_ALTSTAT);
186 } while(!(st & (ST_DRQ | ST_ERR)));
188 return st & ST_ERR ? -1 : 0;
189 }
191 static void read_data(struct device *dev, void *buf)
192 {
193 int i;
194 uint16_t *ptr = buf;
196 /* wait for the data request from the drive */
197 wait_drq(dev);
199 /* ready to transfer */
200 for(i=0; i<256; i++) {
201 *ptr++ = read_reg16(dev, REG_DATA);
202 }
203 }
205 static inline uint8_t read_reg8(struct device *dev, int reg)
206 {
207 uint8_t val;
208 inb(val, dev->port_base + reg);
209 return val;
210 }
212 static inline uint16_t read_reg16(struct device *dev, int reg)
213 {
214 uint16_t val;
215 inw(val, dev->port_base + reg);
216 return val;
217 }
219 static inline void write_reg8(struct device *dev, int reg, uint8_t val)
220 {
221 outb(val, dev->port_base + reg);
222 }
224 static inline void write_reg16(struct device *dev, int reg, uint16_t val)
225 {
226 outw(val, dev->port_base + reg);
227 }
229 static void ata_intr(int inum)
230 {
231 printf("ATA interrupt\n");
232 }
234 static char *size_str(uint64_t nsect, char *buf)
235 {
236 static const char *suffix[] = {"kb", "mb", "gb", "tb", "pb", 0};
237 int i;
239 /* start with kilobytes */
240 nsect /= 2;
242 for(i=0; nsect >= 1024 && suffix[i + 1]; i++) {
243 nsect /= 1024;
244 }
245 sprintf(buf, "%u %s", (unsigned int)nsect, suffix[i]);
246 return buf;
247 }