nuclear@78: #include nuclear@78: #include nuclear@78: #include nuclear@78: #include nuclear@78: #include nuclear@78: #include "ata.h" nuclear@78: #include "intr.h" nuclear@78: #include "asmops.h" nuclear@78: nuclear@78: /* registers */ nuclear@78: #define REG_DATA 0 /* R/W */ nuclear@78: #define REG_ERROR 1 /* R */ nuclear@78: #define REG_FEATURES 1 /* W */ nuclear@78: #define REG_COUNT 2 /* R/W */ nuclear@78: #define REG_LBA0 3 /* R/W */ nuclear@78: #define REG_LBA1 4 /* R/W */ nuclear@78: #define REG_LBA2 5 /* R/W */ nuclear@78: #define REG_DEVICE 6 /* R/W */ nuclear@78: #define REG_CMD 7 /* W */ nuclear@78: #define REG_STATUS 7 /* R */ nuclear@78: nuclear@78: #define REG_CTL 518 nuclear@78: #define REG_ALTSTAT 518 nuclear@78: nuclear@78: /* status bit fields */ nuclear@78: #define ST_ERR (1 << 0) nuclear@78: #define ST_DRQ (1 << 3) nuclear@78: #define ST_DRDY (1 << 6) nuclear@78: #define ST_BSY (1 << 7) nuclear@78: nuclear@78: /* device select bit in control register */ nuclear@78: #define DEV_SEL(x) (((x) & 1) << 4) nuclear@78: nuclear@78: /* ATA commands */ nuclear@78: #define CMD_IDENTIFY 0xec nuclear@78: nuclear@78: nuclear@78: struct device { nuclear@78: int id; /* id of the device on its ATA interface (0 master, 1 slave) */ nuclear@78: int iface; /* ATA interface for this device (0 or 1) */ nuclear@78: int port_base; /* interface I/O port base */ nuclear@78: nuclear@78: uint32_t nsect_lba; nuclear@78: uint64_t nsect_lba48; nuclear@78: }; nuclear@78: nuclear@78: nuclear@78: static int identify(struct device *dev, int iface, int id); nuclear@78: static void select_dev(struct device *dev); nuclear@78: static int wait_busy(struct device *dev); nuclear@78: static int wait_drq(struct device *dev); nuclear@78: static void read_data(struct device *dev, void *buf); nuclear@78: static inline uint8_t read_reg8(struct device *dev, int reg); nuclear@78: static inline uint16_t read_reg16(struct device *dev, int reg); nuclear@78: static inline void write_reg8(struct device *dev, int reg, uint8_t val); nuclear@78: static inline void write_reg16(struct device *dev, int reg, uint16_t val); nuclear@78: static void ata_intr(int inum); nuclear@80: static void *atastr(void *res, void *src, int n); nuclear@78: static char *size_str(uint64_t nsect, char *buf); nuclear@78: nuclear@78: /* last drive selected on each bus */ nuclear@78: static int drvsel[2] = {-1, -1}; nuclear@78: nuclear@78: /* 4 possible devices: 2 ATA interfaces with 2 devices each. nuclear@78: * this will never change unless we start querying the PCI config space nuclear@78: * for additional drives (in which case this whole init code must be nuclear@78: * rewritten anyway), but I like it spelt out like this. nuclear@78: */ nuclear@78: #define MAX_IFACES 2 nuclear@78: #define MAX_DEV (MAX_IFACES * 2) nuclear@78: static struct device dev[MAX_DEV]; nuclear@78: nuclear@78: nuclear@78: void init_ata(void) nuclear@78: { nuclear@78: int i; nuclear@78: nuclear@78: interrupt(IRQ_TO_INTR(15), ata_intr); nuclear@78: nuclear@78: for(i=0; iid = id; nuclear@78: dev->iface = iface; nuclear@78: dev->port_base = port_base[iface]; nuclear@78: nuclear@78: /* a status of 0xff means there's no drive on the interface */ nuclear@78: if((st = read_reg8(dev, REG_ALTSTAT)) == 0xff) { nuclear@78: return -1; nuclear@78: } nuclear@78: nuclear@78: select_dev(dev); nuclear@78: /* wait a bit to allow the device time to respond */ nuclear@78: iodelay(); iodelay(); iodelay(); iodelay(); nuclear@78: nuclear@78: write_reg8(dev, REG_CMD, CMD_IDENTIFY); nuclear@78: nuclear@78: if(!(st = read_reg8(dev, REG_ALTSTAT)) || (st & ST_ERR)) { nuclear@78: /* does not exist */ nuclear@78: return -1; nuclear@78: } nuclear@78: if(wait_busy(dev) == -1) { nuclear@78: /* got ST_ERR, not ATA */ nuclear@78: return -1; nuclear@78: } nuclear@78: nuclear@78: info = malloc(512); nuclear@78: assert(info); nuclear@78: nuclear@78: /* read the device information */ nuclear@78: read_data(dev, info); nuclear@78: nuclear@78: /* print model and serial */ nuclear@80: printf("- found ata drive (%d,%d): %s\n", dev->iface, dev->id, atastr(textbuf, info + 27, 40)); nuclear@80: printf(" s/n: %s\n", atastr(textbuf, info + 10, 20)); nuclear@78: nuclear@78: dev->nsect_lba = *(uint32_t*)(info + 60); nuclear@78: dev->nsect_lba48 = *(uint64_t*)(info + 100) & 0xffffffffffffull; nuclear@78: nuclear@78: if(!dev->nsect_lba) { nuclear@78: printf(" drive does not support LBA, ignoring!\n"); nuclear@78: free(info); nuclear@78: return -1; nuclear@78: } nuclear@78: nuclear@78: if(dev->nsect_lba48) { nuclear@78: size_str(dev->nsect_lba48, textbuf); nuclear@78: } else { nuclear@78: size_str(dev->nsect_lba, textbuf); nuclear@78: } nuclear@78: printf(" size: %s\n", textbuf); nuclear@78: nuclear@78: free(info); nuclear@78: return 0; nuclear@78: } nuclear@78: nuclear@78: static void select_dev(struct device *dev) nuclear@78: { nuclear@78: /* if this is the currently selected device, thy work is done */ nuclear@78: if(drvsel[dev->iface] == dev->id) nuclear@78: return; nuclear@78: nuclear@78: /* wait for BSY and DRQ to clear */ nuclear@78: while(read_reg8(dev, REG_ALTSTAT) & (ST_BSY | ST_DRQ)); nuclear@78: nuclear@78: /* set the correct device bit to the device register */ nuclear@78: write_reg8(dev, REG_DEVICE, DEV_SEL(dev->id)); nuclear@78: } nuclear@78: nuclear@78: static int wait_busy(struct device *dev) nuclear@78: { nuclear@78: unsigned char st; nuclear@78: nuclear@78: do { nuclear@78: st = read_reg8(dev, REG_ALTSTAT); nuclear@78: } while((st & ST_BSY) && !(st & ST_ERR)); nuclear@78: nuclear@78: return st & ST_ERR ? -1 : 0; nuclear@78: } nuclear@78: nuclear@78: static int wait_drq(struct device *dev) nuclear@78: { nuclear@78: unsigned char st; nuclear@78: nuclear@78: do { nuclear@78: st = read_reg8(dev, REG_ALTSTAT); nuclear@78: } while(!(st & (ST_DRQ | ST_ERR))); nuclear@78: nuclear@78: return st & ST_ERR ? -1 : 0; nuclear@78: } nuclear@78: nuclear@78: static void read_data(struct device *dev, void *buf) nuclear@78: { nuclear@78: int i; nuclear@78: uint16_t *ptr = buf; nuclear@78: nuclear@78: /* wait for the data request from the drive */ nuclear@78: wait_drq(dev); nuclear@78: nuclear@78: /* ready to transfer */ nuclear@78: for(i=0; i<256; i++) { nuclear@78: *ptr++ = read_reg16(dev, REG_DATA); nuclear@78: } nuclear@78: } nuclear@78: nuclear@78: static inline uint8_t read_reg8(struct device *dev, int reg) nuclear@78: { nuclear@78: uint8_t val; nuclear@78: inb(val, dev->port_base + reg); nuclear@78: return val; nuclear@78: } nuclear@78: nuclear@78: static inline uint16_t read_reg16(struct device *dev, int reg) nuclear@78: { nuclear@78: uint16_t val; nuclear@78: inw(val, dev->port_base + reg); nuclear@78: return val; nuclear@78: } nuclear@78: nuclear@78: static inline void write_reg8(struct device *dev, int reg, uint8_t val) nuclear@78: { nuclear@78: outb(val, dev->port_base + reg); nuclear@78: } nuclear@78: nuclear@78: static inline void write_reg16(struct device *dev, int reg, uint16_t val) nuclear@78: { nuclear@78: outw(val, dev->port_base + reg); nuclear@78: } nuclear@78: nuclear@78: static void ata_intr(int inum) nuclear@78: { nuclear@78: printf("ATA interrupt\n"); nuclear@78: } nuclear@78: nuclear@80: static void *atastr(void *res, void *src, int n) nuclear@80: { nuclear@80: int i; nuclear@80: uint16_t *sptr = (uint16_t*)src; nuclear@80: char *dptr = res; nuclear@80: nuclear@80: for(i=0; i> 8; nuclear@80: *dptr++ = *sptr++ & 0xff; nuclear@80: } nuclear@80: *dptr = 0; nuclear@80: return res; nuclear@80: } nuclear@80: nuclear@78: static char *size_str(uint64_t nsect, char *buf) nuclear@78: { nuclear@78: static const char *suffix[] = {"kb", "mb", "gb", "tb", "pb", 0}; nuclear@78: int i; nuclear@78: nuclear@78: /* start with kilobytes */ nuclear@78: nsect /= 2; nuclear@78: nuclear@78: for(i=0; nsect >= 1024 && suffix[i + 1]; i++) { nuclear@78: nsect /= 1024; nuclear@78: } nuclear@78: sprintf(buf, "%u %s", (unsigned int)nsect, suffix[i]); nuclear@78: return buf; nuclear@78: } nuclear@78: