kern

annotate src/ata.c @ 80:4db99a52863e

fixed the "endianess" of the text messages in the ATA identify info block. this is the first time I've seen wrong byteorder in ascii text, the ATA committee should be commended.
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 Dec 2011 13:35:39 +0200
parents 251a65b62223
children 9c979413cfbf
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@78 255 sprintf(buf, "%u %s", (unsigned int)nsect, suffix[i]);
nuclear@78 256 return buf;
nuclear@78 257 }
nuclear@78 258