kern

annotate src/ata.c @ 82:8b92b0c1c220

added decimal digits to the disk size output
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 06 Dec 2011 13:45:32 +0200
parents 9c979413cfbf
children 4ef83db5f4cd
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@82 248 unsigned int rem;
nuclear@78 249
nuclear@78 250 /* start with kilobytes */
nuclear@78 251 nsect /= 2;
nuclear@78 252
nuclear@78 253 for(i=0; nsect >= 1024 && suffix[i + 1]; i++) {
nuclear@82 254 rem = nsect % 1024;
nuclear@78 255 nsect /= 1024;
nuclear@78 256 }
nuclear@82 257 sprintf(buf, "%u.%u%s", (unsigned int)nsect, 100 * rem / 1024, suffix[i]);
nuclear@78 258 return buf;
nuclear@78 259 }
nuclear@78 260