kern
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/ata.c Tue Dec 06 12:56:46 2011 +0200 1.3 @@ -0,0 +1,248 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <inttypes.h> 1.8 +#include <assert.h> 1.9 +#include "ata.h" 1.10 +#include "intr.h" 1.11 +#include "asmops.h" 1.12 + 1.13 +/* registers */ 1.14 +#define REG_DATA 0 /* R/W */ 1.15 +#define REG_ERROR 1 /* R */ 1.16 +#define REG_FEATURES 1 /* W */ 1.17 +#define REG_COUNT 2 /* R/W */ 1.18 +#define REG_LBA0 3 /* R/W */ 1.19 +#define REG_LBA1 4 /* R/W */ 1.20 +#define REG_LBA2 5 /* R/W */ 1.21 +#define REG_DEVICE 6 /* R/W */ 1.22 +#define REG_CMD 7 /* W */ 1.23 +#define REG_STATUS 7 /* R */ 1.24 + 1.25 +#define REG_CTL 518 1.26 +#define REG_ALTSTAT 518 1.27 + 1.28 +/* status bit fields */ 1.29 +#define ST_ERR (1 << 0) 1.30 +#define ST_DRQ (1 << 3) 1.31 +#define ST_DRDY (1 << 6) 1.32 +#define ST_BSY (1 << 7) 1.33 + 1.34 +/* device select bit in control register */ 1.35 +#define DEV_SEL(x) (((x) & 1) << 4) 1.36 + 1.37 +/* ATA commands */ 1.38 +#define CMD_IDENTIFY 0xec 1.39 + 1.40 + 1.41 +struct device { 1.42 + int id; /* id of the device on its ATA interface (0 master, 1 slave) */ 1.43 + int iface; /* ATA interface for this device (0 or 1) */ 1.44 + int port_base; /* interface I/O port base */ 1.45 + 1.46 + uint32_t nsect_lba; 1.47 + uint64_t nsect_lba48; 1.48 +}; 1.49 + 1.50 + 1.51 +static int identify(struct device *dev, int iface, int id); 1.52 +static void select_dev(struct device *dev); 1.53 +static int wait_busy(struct device *dev); 1.54 +static int wait_drq(struct device *dev); 1.55 +static void read_data(struct device *dev, void *buf); 1.56 +static inline uint8_t read_reg8(struct device *dev, int reg); 1.57 +static inline uint16_t read_reg16(struct device *dev, int reg); 1.58 +static inline void write_reg8(struct device *dev, int reg, uint8_t val); 1.59 +static inline void write_reg16(struct device *dev, int reg, uint16_t val); 1.60 +static void ata_intr(int inum); 1.61 +static char *size_str(uint64_t nsect, char *buf); 1.62 + 1.63 +/* last drive selected on each bus */ 1.64 +static int drvsel[2] = {-1, -1}; 1.65 + 1.66 +/* 4 possible devices: 2 ATA interfaces with 2 devices each. 1.67 + * this will never change unless we start querying the PCI config space 1.68 + * for additional drives (in which case this whole init code must be 1.69 + * rewritten anyway), but I like it spelt out like this. 1.70 + */ 1.71 +#define MAX_IFACES 2 1.72 +#define MAX_DEV (MAX_IFACES * 2) 1.73 +static struct device dev[MAX_DEV]; 1.74 + 1.75 + 1.76 +void init_ata(void) 1.77 +{ 1.78 + int i; 1.79 + 1.80 + interrupt(IRQ_TO_INTR(15), ata_intr); 1.81 + 1.82 + for(i=0; i<MAX_DEV; i++) { 1.83 + int iface = i / MAX_IFACES; 1.84 + int id = i % MAX_IFACES; 1.85 + 1.86 + if(identify(dev + i, iface, id) == -1) { 1.87 + dev[i].id = -1; 1.88 + } 1.89 + } 1.90 +} 1.91 + 1.92 +static int identify(struct device *dev, int iface, int id) 1.93 +{ 1.94 + /* base address of the two ATA interfaces */ 1.95 + static const int port_base[] = {0x1f0, 0x170}; 1.96 + unsigned char st; 1.97 + uint16_t *info; 1.98 + char textbuf[42]; /* at most we need 40 chars for ident strings */ 1.99 + 1.100 + dev->id = id; 1.101 + dev->iface = iface; 1.102 + dev->port_base = port_base[iface]; 1.103 + 1.104 + /* a status of 0xff means there's no drive on the interface */ 1.105 + if((st = read_reg8(dev, REG_ALTSTAT)) == 0xff) { 1.106 + return -1; 1.107 + } 1.108 + 1.109 + select_dev(dev); 1.110 + /* wait a bit to allow the device time to respond */ 1.111 + iodelay(); iodelay(); iodelay(); iodelay(); 1.112 + 1.113 + write_reg8(dev, REG_CMD, CMD_IDENTIFY); 1.114 + 1.115 + if(!(st = read_reg8(dev, REG_ALTSTAT)) || (st & ST_ERR)) { 1.116 + /* does not exist */ 1.117 + return -1; 1.118 + } 1.119 + if(wait_busy(dev) == -1) { 1.120 + /* got ST_ERR, not ATA */ 1.121 + return -1; 1.122 + } 1.123 + 1.124 + info = malloc(512); 1.125 + assert(info); 1.126 + 1.127 + /* read the device information */ 1.128 + read_data(dev, info); 1.129 + 1.130 + /* print model and serial */ 1.131 + memcpy(textbuf, info + 27, 40); 1.132 + textbuf[40] = 0; 1.133 + printf("- found ata drive (%d,%d): %s\n", dev->iface, dev->id, textbuf); 1.134 + 1.135 + memcpy(textbuf, info + 10, 20); 1.136 + textbuf[20] = 0; 1.137 + printf(" s/n: %s\n", textbuf); 1.138 + 1.139 + dev->nsect_lba = *(uint32_t*)(info + 60); 1.140 + dev->nsect_lba48 = *(uint64_t*)(info + 100) & 0xffffffffffffull; 1.141 + 1.142 + if(!dev->nsect_lba) { 1.143 + printf(" drive does not support LBA, ignoring!\n"); 1.144 + free(info); 1.145 + return -1; 1.146 + } 1.147 + 1.148 + if(dev->nsect_lba48) { 1.149 + size_str(dev->nsect_lba48, textbuf); 1.150 + } else { 1.151 + size_str(dev->nsect_lba, textbuf); 1.152 + } 1.153 + printf(" size: %s\n", textbuf); 1.154 + 1.155 + free(info); 1.156 + return 0; 1.157 +} 1.158 + 1.159 +static void select_dev(struct device *dev) 1.160 +{ 1.161 + /* if this is the currently selected device, thy work is done */ 1.162 + if(drvsel[dev->iface] == dev->id) 1.163 + return; 1.164 + 1.165 + /* wait for BSY and DRQ to clear */ 1.166 + while(read_reg8(dev, REG_ALTSTAT) & (ST_BSY | ST_DRQ)); 1.167 + 1.168 + /* set the correct device bit to the device register */ 1.169 + write_reg8(dev, REG_DEVICE, DEV_SEL(dev->id)); 1.170 +} 1.171 + 1.172 +static int wait_busy(struct device *dev) 1.173 +{ 1.174 + unsigned char st; 1.175 + 1.176 + do { 1.177 + st = read_reg8(dev, REG_ALTSTAT); 1.178 + } while((st & ST_BSY) && !(st & ST_ERR)); 1.179 + 1.180 + return st & ST_ERR ? -1 : 0; 1.181 +} 1.182 + 1.183 +static int wait_drq(struct device *dev) 1.184 +{ 1.185 + unsigned char st; 1.186 + 1.187 + do { 1.188 + st = read_reg8(dev, REG_ALTSTAT); 1.189 + } while(!(st & (ST_DRQ | ST_ERR))); 1.190 + 1.191 + return st & ST_ERR ? -1 : 0; 1.192 +} 1.193 + 1.194 +static void read_data(struct device *dev, void *buf) 1.195 +{ 1.196 + int i; 1.197 + uint16_t *ptr = buf; 1.198 + 1.199 + /* wait for the data request from the drive */ 1.200 + wait_drq(dev); 1.201 + 1.202 + /* ready to transfer */ 1.203 + for(i=0; i<256; i++) { 1.204 + *ptr++ = read_reg16(dev, REG_DATA); 1.205 + } 1.206 +} 1.207 + 1.208 +static inline uint8_t read_reg8(struct device *dev, int reg) 1.209 +{ 1.210 + uint8_t val; 1.211 + inb(val, dev->port_base + reg); 1.212 + return val; 1.213 +} 1.214 + 1.215 +static inline uint16_t read_reg16(struct device *dev, int reg) 1.216 +{ 1.217 + uint16_t val; 1.218 + inw(val, dev->port_base + reg); 1.219 + return val; 1.220 +} 1.221 + 1.222 +static inline void write_reg8(struct device *dev, int reg, uint8_t val) 1.223 +{ 1.224 + outb(val, dev->port_base + reg); 1.225 +} 1.226 + 1.227 +static inline void write_reg16(struct device *dev, int reg, uint16_t val) 1.228 +{ 1.229 + outw(val, dev->port_base + reg); 1.230 +} 1.231 + 1.232 +static void ata_intr(int inum) 1.233 +{ 1.234 + printf("ATA interrupt\n"); 1.235 +} 1.236 + 1.237 +static char *size_str(uint64_t nsect, char *buf) 1.238 +{ 1.239 + static const char *suffix[] = {"kb", "mb", "gb", "tb", "pb", 0}; 1.240 + int i; 1.241 + 1.242 + /* start with kilobytes */ 1.243 + nsect /= 2; 1.244 + 1.245 + for(i=0; nsect >= 1024 && suffix[i + 1]; i++) { 1.246 + nsect /= 1024; 1.247 + } 1.248 + sprintf(buf, "%u %s", (unsigned int)nsect, suffix[i]); 1.249 + return buf; 1.250 +} 1.251 +