kern

view src/ata.c @ 83:4ef83db5f4cd

foo
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 Dec 2011 15:53:57 +0200
parents 8b92b0c1c220
children 379332fc1667
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <inttypes.h>
6 #include <assert.h>
7 #include "ata.h"
8 #include "intr.h"
9 #include "asmops.h"
11 /* registers */
12 #define REG_DATA 0 /* R/W */
13 #define REG_ERROR 1 /* R */
14 #define REG_FEATURES 1 /* W */
15 #define REG_COUNT 2 /* R/W */
16 #define REG_LBA0 3 /* R/W */
17 #define REG_LBA1 4 /* R/W */
18 #define REG_LBA2 5 /* R/W */
19 #define REG_DEVICE 6 /* R/W */
20 #define REG_CMD 7 /* W */
21 #define REG_STATUS 7 /* R */
23 #define REG_CTL 518
24 #define REG_ALTSTAT 518
26 /* status bit fields */
27 #define ST_ERR (1 << 0)
28 #define ST_DRQ (1 << 3)
29 #define ST_DRDY (1 << 6)
30 #define ST_BSY (1 << 7)
32 /* device select bit in control register */
33 #define DEV_SEL(x) (((x) & 1) << 4)
35 /* ATA commands */
36 #define CMD_IDENTIFY 0xec
39 struct device {
40 int id; /* id of the device on its ATA interface (0 master, 1 slave) */
41 int iface; /* ATA interface for this device (0 or 1) */
42 int port_base; /* interface I/O port base */
44 uint32_t nsect_lba;
45 uint64_t nsect_lba48;
46 };
49 static int identify(struct device *dev, int iface, int id);
50 static void select_dev(struct device *dev);
51 static int wait_busy(struct device *dev);
52 static int wait_drq(struct device *dev);
53 static void read_data(struct device *dev, void *buf);
54 static inline uint8_t read_reg8(struct device *dev, int reg);
55 static inline uint16_t read_reg16(struct device *dev, int reg);
56 static inline void write_reg8(struct device *dev, int reg, uint8_t val);
57 static inline void write_reg16(struct device *dev, int reg, uint16_t val);
58 static void ata_intr(int inum);
59 static void *atastr(void *res, void *src, int n);
60 static char *size_str(uint64_t nsect, char *buf);
62 /* last drive selected on each bus */
63 static int drvsel[2] = {-1, -1};
65 /* 4 possible devices: 2 ATA interfaces with 2 devices each.
66 * this will never change unless we start querying the PCI config space
67 * for additional drives (in which case this whole init code must be
68 * rewritten anyway), but I like it spelt out like this.
69 */
70 #define MAX_IFACES 2
71 #define MAX_DEV (MAX_IFACES * 2)
72 static struct device dev[MAX_DEV];
75 void init_ata(void)
76 {
77 int i;
79 interrupt(IRQ_TO_INTR(15), ata_intr);
81 for(i=0; i<MAX_DEV; i++) {
82 int iface = i / MAX_IFACES;
83 int id = i % MAX_IFACES;
85 if(identify(dev + i, iface, id) == -1) {
86 dev[i].id = -1;
87 }
88 }
89 }
91 static int identify(struct device *dev, int iface, int id)
92 {
93 /* base address of the two ATA interfaces */
94 static const int port_base[] = {0x1f0, 0x170};
95 unsigned char st;
96 uint16_t *info;
97 char textbuf[42]; /* at most we need 40 chars for ident strings */
99 dev->id = id;
100 dev->iface = iface;
101 dev->port_base = port_base[iface];
103 /* a status of 0xff means there's no drive on the interface */
104 if((st = read_reg8(dev, REG_ALTSTAT)) == 0xff) {
105 return -1;
106 }
108 select_dev(dev);
109 /* wait a bit to allow the device time to respond */
110 iodelay(); iodelay(); iodelay(); iodelay();
112 write_reg8(dev, REG_CMD, CMD_IDENTIFY);
114 if(!(st = read_reg8(dev, REG_ALTSTAT)) || (st & ST_ERR)) {
115 /* does not exist */
116 return -1;
117 }
118 if(wait_busy(dev) == -1) {
119 /* got ST_ERR, not ATA */
120 return -1;
121 }
123 info = malloc(512);
124 assert(info);
126 /* read the device information */
127 read_data(dev, info);
129 /* print model and serial */
130 printf("ata%d: %s", (dev->iface << 1) | dev->id, atastr(textbuf, info + 27, 40));
131 printf(" [s/n: %s]", atastr(textbuf, info + 10, 20));
133 dev->nsect_lba = *(uint32_t*)(info + 60);
134 dev->nsect_lba48 = *(uint64_t*)(info + 100) & 0xffffffffffffull;
136 if(!dev->nsect_lba) {
137 printf(" drive does not support LBA, ignoring!\n");
138 free(info);
139 return -1;
140 }
142 if(dev->nsect_lba48) {
143 size_str(dev->nsect_lba48, textbuf);
144 } else {
145 size_str(dev->nsect_lba, textbuf);
146 }
147 printf(" size: %s\n", textbuf);
149 free(info);
150 return 0;
151 }
153 static void select_dev(struct device *dev)
154 {
155 /* if this is the currently selected device, thy work is done */
156 if(drvsel[dev->iface] == dev->id)
157 return;
159 /* wait for BSY and DRQ to clear */
160 while(read_reg8(dev, REG_ALTSTAT) & (ST_BSY | ST_DRQ));
162 /* set the correct device bit to the device register */
163 write_reg8(dev, REG_DEVICE, DEV_SEL(dev->id));
164 }
166 static int wait_busy(struct device *dev)
167 {
168 unsigned char st;
170 do {
171 st = read_reg8(dev, REG_ALTSTAT);
172 } while((st & ST_BSY) && !(st & ST_ERR));
174 return st & ST_ERR ? -1 : 0;
175 }
177 static int wait_drq(struct device *dev)
178 {
179 unsigned char st;
181 do {
182 st = read_reg8(dev, REG_ALTSTAT);
183 } while(!(st & (ST_DRQ | ST_ERR)));
185 return st & ST_ERR ? -1 : 0;
186 }
188 static void read_data(struct device *dev, void *buf)
189 {
190 int i;
191 uint16_t *ptr = buf;
193 /* wait for the data request from the drive */
194 wait_drq(dev);
196 /* ready to transfer */
197 for(i=0; i<256; i++) {
198 *ptr++ = read_reg16(dev, REG_DATA);
199 }
200 }
202 static inline uint8_t read_reg8(struct device *dev, int reg)
203 {
204 uint8_t val;
205 inb(val, dev->port_base + reg);
206 return val;
207 }
209 static inline uint16_t read_reg16(struct device *dev, int reg)
210 {
211 uint16_t val;
212 inw(val, dev->port_base + reg);
213 return val;
214 }
216 static inline void write_reg8(struct device *dev, int reg, uint8_t val)
217 {
218 outb(val, dev->port_base + reg);
219 }
221 static inline void write_reg16(struct device *dev, int reg, uint16_t val)
222 {
223 outw(val, dev->port_base + reg);
224 }
226 static void ata_intr(int inum)
227 {
228 printf("ATA interrupt\n");
229 }
231 static void *atastr(void *res, void *src, int n)
232 {
233 int i;
234 uint16_t *sptr = (uint16_t*)src;
235 char *dptr = res;
237 for(i=0; i<n/2; i++) {
238 *dptr++ = (*sptr & 0xff00) >> 8;
239 *dptr++ = *sptr++ & 0xff;
240 }
242 while(isspace(*--dptr));
243 *++dptr = 0;
244 return res;
245 }
247 static char *size_str(uint64_t nsect, char *buf)
248 {
249 static const char *suffix[] = {"kb", "mb", "gb", "tb", "pb", 0};
250 int i;
251 unsigned int rem;
253 /* start with kilobytes */
254 nsect /= 2;
256 for(i=0; nsect >= 1024 && suffix[i + 1]; i++) {
257 rem = nsect % 1024;
258 nsect /= 1024;
259 }
260 sprintf(buf, "%u.%u%s", (unsigned int)nsect, 100 * rem / 1024, suffix[i]);
261 return buf;
262 }