kern

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