kern

annotate src/ata.c @ 90:7ff2b4971216

started work on the filesystem
author John Tsiombikas <nuclear@member.fsf.org>
date Fri, 09 Dec 2011 13:44:15 +0200
parents 2f555c81ae67
children
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 }