Skip to content

Instantly share code, notes, and snippets.

@pdumais
Created September 28, 2015 00:32
Show Gist options
  • Select an option

  • Save pdumais/3b860d4174dd130b125f to your computer and use it in GitHub Desktop.

Select an option

Save pdumais/3b860d4174dd130b125f to your computer and use it in GitHub Desktop.
ata driver
void atahandler(unsigned short base, unsigned char channel)
{
unsigned char val;
INPORTB(val,base+7);
if (val&1)
{
INPORTB(val,base+1);
pf("ERROR! %x\r\n",val);
}
else if (pendingRequest[channel].pending)
{
pendingRequest[channel].pending = 0;
callback(pendingRequest[channel].dev,pendingRequest[channel].block,pendingRequest[channel].count);
}
INPORTB(val,busMasterRegister+2+(channel*8));
OUTPORTB(4,busMasterRegister+2+(channel*8));
}
void atahandler1()
{
atahandler(CH1BASE,0);
}
void atahandler2()
{
atahandler(CH2BASE,1);
}
void ata_select_device(unsigned short dev, unsigned char slave)
{
unsigned char val;
unsigned int reg = CH1BASE - (dev<<7);
if ((channelSlaveSelection&(1<<dev)) == ((slave&1)<<dev)) return;
//OUTPORTB(0xA0 | (slave<<4),dev+ATA_REG_HDDEVSEL); // send "Select" command
OUTPORTB(0xE0 | (slave<<4),reg+ATA_REG_HDDEVSEL); // send "Select" command
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
channelSlaveSelection &= ~(1<<dev);
channelSlaveSelection |= ((slave&1)<<dev);
}
void ata_init_dev(unsigned short dev, unsigned char slave)
{
unsigned int i;
unsigned char val;
unsigned int val2;
unsigned int signature;
unsigned int reg = CH1BASE - (dev<<7);
pf("ATA DEVICE %x,%x: ",reg,slave);
ata_select_device(dev,slave);
OUTPORTB(ATA_CMD_IDENTIFY,reg+ATA_REG_COMMAND); // identify
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
INPORTB(val,reg+0x206);
while (1)
{
INPORTB(val,reg+ATA_REG_STATUS);
if (val == 0)
{
pf("none\r\n");
return;
}
else if (val&ATA_SR_ERR)
{
// Identify command does not work for ATAPI devices. Need to send IDENTIFY_PACKET.
// but we will just ignore it here, we dont care about cdroms yet.
pf("ATAPI\r\n");
return;
}
else if (!(val&ATA_SR_BSY)&&(val&ATA_SR_DRQ))
{
break;
}
}
for (i=0;i<128;i++)
{
INPORTL(val2,reg+ATA_REG_DATA);
((unsigned int*)tmpBuffer)[i] = val2;
}
val2 = *((unsigned int *)(tmpBuffer+164));
if (val2 & (1<<26))
{
val2 = *((unsigned int *)(tmpBuffer+200));
pf("supported 48bit LBA device of size %x\r\n",val2*512);
OUTPORTB(3,reg+ATA_REG_FEATURES); // DMA
}
else
{
pf("unsupported drive\r\n");
}
}
void init_ata(atairqcallback irqcallback)
{
int dev;
callback = irqcallback;
dev = pci_getDevice(0x8086,0x7010);
pci_enableBusMastering(dev);
busMasterRegister = pci_getBar(dev,4) & ~1;
OUTPORTL(PRDT1,busMasterRegister+0x04); // set PRDT1
OUTPORTL(PRDT2,busMasterRegister+0x0C); // set PRDT2
pf("IDE bus mastering Device: %x, bar4=%x\r\n", dev,busMasterRegister);
ata_init_dev(0,0);
ata_init_dev(0,1);
ata_init_dev(1,0);
ata_init_dev(1,1);
// Warning: we hardcode irq 14 and 15 here, but we should read it from PCI device
registerIRQ(&atahandler1,14);
registerIRQ(&atahandler2,15);
}
void convertDevId(unsigned int dev, unsigned int *device, unsigned char *slave)
{
switch (dev)
{
case 0:
*device = 0;
*slave = 0;
break;
case 1:
*device = 0;
*slave = 1;
break;
case 2:
*device = 1;
*slave = 0;
break;
case 3:
*device = 1;
*slave = 1;
break;
}
}
int ata_read(unsigned int dev, unsigned long sector, char* buffer, unsigned long count)
{
unsigned int device;
unsigned char slave,val;
unsigned short reg,bmr;
struct PRD *prdt = (struct PRD*)PRDT1;
convertDevId(dev,&device,&slave);
ata_select_device(device,slave);
pendingRequest[device].pending = 1;
pendingRequest[device].block = sector;
pendingRequest[device].count = count;
pendingRequest[device].dev = dev;
if (device==0)
{
bmr = busMasterRegister;
reg = CH1BASE;
}
else
{
bmr = busMasterRegister+8;
reg = CH2BASE;
}
// setup PRD
prdt[device].addr = buffer;
prdt[device].size = count*512; // sector size = 512
prdt[device].reserved = 0x8000;
// Stop DMA
OUTPORTB(0b00001000,bmr);
// write sector count and LBA48 address
OUTPORTB(((count>>8)&0xFF),reg+2);
OUTPORTB(((sector>>24)&0xFF),reg+3);
OUTPORTB(((sector>>32)&0xFF),reg+4);
OUTPORTB(((sector>>40)&0xFF),reg+5);
OUTPORTB((count&0xFF),reg+2);
OUTPORTB((sector&0xFF),reg+3);
OUTPORTB(((sector>>8)&0xFF),reg+4);
OUTPORTB(((sector>>16)&0xFF),reg+5);
OUTPORTB(0x25,reg+7);
// Start DMA (read)
OUTPORTB(0b00001001,bmr);
}
int ata_write(unsigned int dev, unsigned long sector, char* buffer, unsigned long count)
{
unsigned int device;
unsigned char slave,val;
unsigned short reg,bmr;
struct PRD *prdt = (struct PRD*)PRDT1;
convertDevId(dev,&device,&slave);
ata_select_device(device,slave);
pendingRequest[device].pending = 1;
pendingRequest[device].block = sector;
pendingRequest[device].count = count;
pendingRequest[device].dev = dev;
if (device==0)
{
bmr = busMasterRegister;
reg = CH1BASE;
}
else
{
bmr = busMasterRegister+8;
reg = CH2BASE;
}
// setup PRD
prdt[device].addr = buffer;
prdt[device].size = count*512; // sector size = 512
prdt[device].reserved = 0x8000;
// Stop DMA
OUTPORTB(0b00000000,bmr);
// write sector count and LBA48 address
OUTPORTB(((count>>8)&0xFF),reg+2);
OUTPORTB(((sector>>24)&0xFF),reg+3);
OUTPORTB(((sector>>32)&0xFF),reg+4);
OUTPORTB(((sector>>40)&0xFF),reg+5);
OUTPORTB((count&0xFF),reg+2);
OUTPORTB((sector&0xFF),reg+3);
OUTPORTB(((sector>>8)&0xFF),reg+4);
OUTPORTB(((sector>>16)&0xFF),reg+5);
OUTPORTB(0x35,reg+7);
// Start DMA (read)
OUTPORTB(0b00000001,bmr);
//TODO: need to do a cache flush after writing
}
unsigned char ata_isBusy(dev)
{
unsigned int device;
unsigned char slave;
convertDevId(dev,&device,&slave);
return pendingRequest[device].pending;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment