| git.druid.rocks | index | druid520 | kaboom | src/ | drivers/ | ata.nsc |
src/drivers/ata.nsc
/*
* ata/ide pio driver, primary bus, master drive, lba28. polling only
* (no dma, no irq-driven i/o) -- matches tape-kernel's own approach,
* which this is a clean-room rewrite of, not a port.
*
* a 512-byte sector is 64 8-byte words. reads/writes go through the
* same packed-word technique as vga: *p is always a full 8-byte
* load/store, no byte/halfword deref exists, so four consecutive
* 16-bit port reads get packed into one u64 before a single *ptr
* store (and symmetrically unpacked on write), rather than one store
* per port word.
*/
void outb(u16 port, u8 val);
u8 inb(u16 port);
void outw(u16 port, u16 val);
u16 inw(u16 port);
/* status register bits (port 0x1f7 on read) */
u8
ata_status(void)
{
return inb((u16)0x1f7);
}
i32
ata_wait_not_busy(void)
{
i32 tries;
tries = 100000;
while(tries > 0)
{
if((ata_status() & (u8)0x80) == (u8)0)
{
return 1;
}
tries = tries - 1;
}
return 0; /* timed out -- no drive, or it's wedged */
}
i32
ata_wait_drq(void)
{
i32 tries;
u8 st;
tries = 100000;
while(tries > 0)
{
st = ata_status();
if((st & (u8)0x01) != (u8)0)
{
return 0; /* err bit set */
}
if((st & (u8)0x08) != (u8)0)
{
return 1; /* drq set, data ready */
}
tries = tries - 1;
}
return 0;
}
void
ata_select_lba(u32 lba)
{
u8 head;
head = (u8)0xe0 | (u8)((lba >> (u32)24) & (u32)0x0f);
outb((u16)0x1f6, head);
outb((u16)0x1f2, (u8)1); /* sector count = 1 */
outb((u16)0x1f3, (u8)(lba & (u32)0xff));
outb((u16)0x1f4, (u8)((lba >> (u32)8) & (u32)0xff));
outb((u16)0x1f5, (u8)((lba >> (u32)16) & (u32)0xff));
}
/* reads one 512-byte sector at lba into buf (must have room for 512
* bytes). returns 1 on success, 0 on timeout/error. */
global i32
ata_read_sector(u32 lba, ptr buf)
{
ptr dst;
u64 w0;
u64 w1;
u64 w2;
u64 w3;
u64 packed;
i32 i;
if(ata_wait_not_busy() == 0)
{
return 0;
}
ata_select_lba(lba);
outb((u16)0x1f7, (u8)0x20); /* read sectors */
if(ata_wait_not_busy() == 0)
{
return 0;
}
if(ata_wait_drq() == 0)
{
return 0;
}
dst = buf;
i = 0;
while(i < 64)
{
w0 = (u64)inw((u16)0x1f0);
w1 = (u64)inw((u16)0x1f0);
w2 = (u64)inw((u16)0x1f0);
w3 = (u64)inw((u16)0x1f0);
packed = w0 | (w1 << (u64)16) | (w2 << (u64)32) | (w3 << (u64)48);
*dst = (i64)packed;
dst = dst + 8;
i = i + 1;
}
return 1;
}
/* writes one 512-byte sector at lba from buf. returns 1 on success, 0
* on timeout/error. */
global i32
ata_write_sector(u32 lba, ptr buf)
{
ptr src;
u64 packed;
u16 w0;
u16 w1;
u16 w2;
u16 w3;
i32 i;
if(ata_wait_not_busy() == 0)
{
return 0;
}
ata_select_lba(lba);
outb((u16)0x1f7, (u8)0x30); /* write sectors */
if(ata_wait_not_busy() == 0)
{
return 0;
}
if(ata_wait_drq() == 0)
{
return 0;
}
src = buf;
i = 0;
while(i < 64)
{
packed = (u64)*src;
w0 = (u16)(packed & (u64)0xffff);
w1 = (u16)((packed >> (u64)16) & (u64)0xffff);
w2 = (u16)((packed >> (u64)32) & (u64)0xffff);
w3 = (u16)((packed >> (u64)48) & (u64)0xffff);
outw((u16)0x1f0, w0);
outw((u16)0x1f0, w1);
outw((u16)0x1f0, w2);
outw((u16)0x1f0, w3);
src = src + 8;
i = i + 1;
}
outb((u16)0x1f7, (u8)0xe7); /* cache flush */
if(ata_wait_not_busy() == 0)
{
return 0;
}
return 1;
}