kern

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