10 #define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0)
11 #define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1)
12 #define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2)
13 #define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3)
14 #define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4)
15 #define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5)
16 #define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6)
17 #define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7)
18 #define reg_command(CHANNEL) reg_status (CHANNEL)
19 #define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206)
20 #define reg_alt_status(CHANNEL) reg_ctl (CHANNEL)
22 /* Alternate Status Register bits. */
32 /* Control Register bits. */
36 /* Device Register bits. */
37 #define DEV_MBS 0xa0 /* Must be set. */
38 #define DEV_LBA 0x40 /* Linear based addressing. */
39 #define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */
42 #define CMD_IDENTIFY 0xec /* IDENTIFY DEVICE. */
43 #define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */
44 #define CMD_READ_SECTOR_NORETRY 0x21 /* READ SECTOR without retries. */
45 #define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */
46 #define CMD_WRITE_SECTOR_NORETRY 0x31 /* WRITE SECTOR without retries. */
51 struct channel *channel;
55 disk_sector_no capacity;
65 bool expecting_interrupt;
66 struct semaphore completion_wait;
71 #define CHANNEL_CNT (sizeof channels / sizeof *channels)
72 static struct channel channels[2];
75 wait_until_idle (const struct disk *d)
79 for(i = 0; i < 1000; i++)
81 if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0)
86 printk ("%s: idle timeout\n", d->name);
89 /* Wait up to 30 seconds for disk D to clear BSY. */
91 wait_while_busy (const struct disk *d)
93 struct channel *c = d->channel;
96 for (i = 0; i < 3000; i++)
99 printk ("%s: busy, waiting...", d->name);
100 if (!(inb (reg_alt_status (c)) & STA_BSY))
114 select_device (const struct disk *d)
116 struct channel *c = d->channel;
117 uint8_t dev = DEV_MBS;
120 outb (reg_device (c), dev);
121 inb (reg_alt_status (c));
126 select_device_wait (const struct disk *d)
134 reset_channel (struct channel *c)
139 /* The ATA reset sequence depends on which devices are present,
140 so we start by detecting device presence. */
141 for (device = 0; device < 2; device++)
143 struct disk *d = &c->dev[device];
147 outb (reg_nsect (c), 0x55);
148 outb (reg_lbal (c), 0xaa);
150 outb (reg_nsect (c), 0xaa);
151 outb (reg_lbal (c), 0x55);
153 outb (reg_nsect (c), 0x55);
154 outb (reg_lbal (c), 0xaa);
156 present[device] = (inb (reg_nsect (c)) == 0x55
157 && inb (reg_lbal (c)) == 0xaa);
160 /* Issue soft reset sequence, which selects device 0 as a side effect.
161 Also enable interrupts. */
162 outb (reg_ctl (c), 0);
164 outb (reg_ctl (c), CTL_SRST);
166 outb (reg_ctl (c), 0);
170 /* Wait for device 0 to clear BSY. */
173 select_device (&c->dev[0]);
174 wait_while_busy (&c->dev[0]);
177 /* Wait for device 1 to clear BSY. */
182 select_device (&c->dev[1]);
183 for (i = 0; i < 3000; i++)
185 if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1)
189 wait_while_busy (&c->dev[1]);
194 check_device_type (struct disk *d)
196 struct channel *c = d->channel;
197 uint8_t error, lbam, lbah;
201 error = inb (reg_error (c));
202 lbam = inb (reg_lbam (c));
203 lbah = inb (reg_lbah (c));
205 if (error != 1 && (error != 0x81 || d->device == 1))
208 return error != 0x81;
212 d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3);
218 execute_command (struct disk *d, uint8_t command)
220 struct channel *c = d->channel;
222 /* Interrupts must be enabled or our semaphore will never be
223 up'd by the completion handler. */
224 ASSERT (intr_get_level () == IF_ON);
226 /* Atomically note that we expect an interrupt and send the
227 command to the device. */
229 c->expecting_interrupt = true;
230 outb (reg_command (c), command);
233 /* Wait for the command to complete. */
234 sema_down (&c->completion_wait);
238 input_sector (struct channel *c, void *sector)
242 ASSERT (sector != NULL);
244 status = inb (reg_status (c));
245 if (status & STA_DRQ)
247 /* Command was successful. Read data into SECTOR. */
248 insw (reg_data (c), sector, DISK_SECTOR_SIZE / 2);
253 /* Command failed. */
259 output_sector (struct channel *c, const void *sector)
263 ASSERT (sector != NULL);
265 status = inb (reg_status (c));
266 if (status & STA_DRQ)
268 /* Command was successful. Write data into SECTOR. */
269 outsw (reg_data (c), sector, DISK_SECTOR_SIZE / 2);
274 /* Command failed. */
280 printk_ata_string (char *string, size_t word_cnt)
285 for (last = word_cnt * 2 - 1; last >= 0; last--)
286 if (string[last ^ 1] != ' ')
289 for (i = 0; i <= last; i++)
290 printk ("%c", string[i ^ 1]);
294 identify_ata_device (struct disk *d)
296 uint16_t id[DISK_SECTOR_SIZE / 2];
300 select_device_wait (d);
301 execute_command (d, CMD_IDENTIFY);
304 if (!input_sector (d->channel, id))
310 /* Calculate capacity. */
311 d->capacity = id[60] | ((uint32_t) id[61] << 16);
313 /* Print identification message. */
314 printk ("%s: detected %'"PRDSNu" sector (", d->name, d->capacity);
315 if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024 * 1024)
316 printk ("%"PRDSNu" GB",
317 d->capacity / (1024 / DISK_SECTOR_SIZE * 1024 * 1024));
318 else if (d->capacity > 1024 / DISK_SECTOR_SIZE * 1024)
319 printk ("%"PRDSNu" MB", d->capacity / (1024 / DISK_SECTOR_SIZE * 1024));
320 else if (d->capacity > 1024 / DISK_SECTOR_SIZE)
321 printk ("%"PRDSNu" kB", d->capacity / (1024 / DISK_SECTOR_SIZE));
323 printk ("%"PRDSNu" byte", d->capacity * DISK_SECTOR_SIZE);
324 printk (") disk \"");
325 printk_ata_string ((char *) &id[27], 20);
327 printk_ata_string ((char *) &id[10], 10);
332 interrupt_handler (struct intr_frame *f)
336 for (c = channels; c < channels + CHANNEL_CNT; c++)
337 if (f->vec_no == c->irq)
339 if (c->expecting_interrupt)
341 /* Acknowledge interrupt. */
342 inb (reg_status (c));
344 /* Wake up waiter. */
345 sema_up (&c->completion_wait);
348 printk ("%s: unexpected interrupt\n", c->name);
360 for (channel = 0; channel < CHANNEL_CNT; channel++)
362 struct channel *c = &channels[channel];
365 /* Initialize channel. */
366 snprintf (c->name, sizeof c->name, "ide%d", channel);
380 lock_init (&c->lock, c->name);
381 c->expecting_interrupt = false;
382 sema_init (&c->completion_wait, 0, c->name);
384 /* Initialize devices. */
385 for (device = 0; device < 2; device++)
387 struct disk *d = &c->dev[device];
388 snprintf (d->name, sizeof d->name, "%s:%d", c->name, device);
396 /* Register interrupt handler. */
397 intr_register (c->irq, 0, IF_OFF, interrupt_handler, c->name);
399 /* Reset hardware. */
402 /* Distinguish ATA hard disks from other devices. */
403 if (check_device_type (&c->dev[0]))
404 check_device_type (&c->dev[1]);
406 /* Read hard disk identity information. */
407 for (device = 0; device < 2; device++)
408 if (c->dev[device].is_ata)
409 identify_ata_device (&c->dev[device]);
418 ASSERT (idx >= 0 && idx < 4);
419 d = &channels[idx / 2].dev[idx % 2];
420 return d->is_ata ? d : NULL;
424 disk_size (struct disk *d)
432 select_sector (struct disk *d, disk_sector_no sec_no)
434 struct channel *c = d->channel;
436 ASSERT (sec_no < d->capacity);
437 ASSERT (sec_no < (1UL << 28));
439 select_device_wait (d);
440 outb (reg_nsect (c), 1);
441 outb (reg_lbal (c), sec_no);
442 outb (reg_lbam (c), sec_no >> 8);
443 outb (reg_lbah (c), (sec_no >> 16));
444 outb (reg_device (c),
445 DEV_MBS | DEV_LBA | (d->device == 1 ? DEV_DEV : 0) | (sec_no >> 24));
449 disk_read (struct disk *d, disk_sector_no sec_no, void *buffer)
452 ASSERT (buffer != NULL);
454 lock_acquire (&d->channel->lock);
455 select_sector (d, sec_no);
456 execute_command (d, CMD_READ_SECTOR_RETRY);
458 if (!input_sector (d->channel, buffer))
459 panic ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
463 disk_write (struct disk *d, disk_sector_no sec_no, const void *buffer)
466 ASSERT (buffer != NULL);
468 lock_acquire (&d->channel->lock);
469 select_sector (d, sec_no);
470 execute_command (d, CMD_WRITE_SECTOR_RETRY);
472 if (!output_sector (d->channel, buffer))
473 panic ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
474 lock_release (&d->channel->lock);