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