/**************************************************************************************
$Id: ide.c,v 1.9 2002/04/29 23:34:53 reddawg Exp $


**************************************************************************************/

#include <ubixos/video.h>
#include <ubixos/io.h>
#include <ubixos/8259.h>
#include <ubixos/gdt.h>
#include <ubixos/idt.h>
#include <ubixos/8259.h>
#include <ubixos/delay.h>
#include <drivers/ide.h>

ide_dev_t ide_dev[2];

static __u32 interrupt_occured=0;

void initIde(void) {
  unsigned char tmp;
  kprint("Initializing IDE....\n");
  ide_dev[0].sel=ide_dev[2].sel=0xA0;
  ide_dev[1].sel=ide_dev[3].sel=0xB0;
  ide_dev[0].ioadr=ide_dev[1].ioadr=0x1F0;
  ide_dev[2].ioadr=ide_dev[3].ioadr=0x170;
  ide_dev[0].irq=ide_dev[1].irq=0x4000;
  ide_dev[2].irq=ide_dev[3].irq=0x8000;
  set_vector(irq14, 14+0x20, (D_INT + D_PRESENT + D_DPL3));
  set_vector(irq15, 15, (D_INT + D_PRESENT + D_DPL3));
  enable_irq(14);
  enable_irq(15);
  //outportb(0x1F2,0x55);
  //tmp = inportb(0x1F2);
  //kprint("[");
  //printlong(tmp);
  //kprint("]\n");
  ideProbe();
  }

void irq14() {
  interrupt_occured|=ide_dev[0].irq;
  outportb(0xA0,0x20);
  outportb(0x20,0x20);
  }

void irq15() {
  interrupt_occured|=ide_dev[2].irq;
  outportb(0xA0,0x20);
  outportb(0x20,0x20);
  }

void ideProbe(void) {
  __u8 temp1,temp2,whichdrive;
  __u16 ioadr,temp;
 for (whichdrive=0;whichdrive<4;whichdrive+=2) {
  ioadr=ide_dev[whichdrive].ioadr;
  outportb(ioadr+ATA_REG_CNT,0x55);
  outportb(ioadr+ATA_REG_SECT,0xAA);
  temp1=inportb(ioadr+ATA_REG_CNT);
  temp2=inportb(ioadr+ATA_REG_SECT);
  if (temp1!=0x55 || temp2!=0xAA) {
NO_DRIVES:
    ide_dev[whichdrive+1].ioadr=ide_dev[whichdrive].ioadr=0;
    continue;
    }
  outportb(ioadr+ATA_REG_SLCT,0xE);
  delay(1);
  outportb(ioadr+ATA_REG_SLCT,0x8);
  delay(1);
  for (temp=200;temp;temp--) {
    if ((inportb(ioadr+ATA_REG_STAT)&0x80)==0) { break; }
    delay(1);
    }
  if (!temp) {
   kprintf("ide_probe: no master on I/F 0x%03X\r\n",ioadr);
   goto NO_DRIVES;
  }
  kprintf("hd%1u (0x%03X, master): ",whichdrive,ioadr);
    ide_probe_dev(&ide_dev[whichdrive]);
  if(!ide_select(&ide_dev[whichdrive+1]))
  {
   ide_select(&ide_dev[whichdrive]);
   ide_dev[whichdrive+1].ioadr=0;
   kprintf("ide_probe: no slave on I/F 0x%03X\r\n",ioadr);
   continue;
         }
  kprintf("hd%1u (0x%03X, slave): ",whichdrive+1,ioadr);
  ide_probe_dev(&ide_dev[whichdrive+1]);
 }
}

void ide_probe_dev(ide_dev_t * dev)
{
 __u8 temp1,temp2;
 __u16 ioadr,temp;
 ioadr=dev->ioadr;
 temp1=inportb(ioadr+ATA_REG_CNT);
 temp2=inportb(ioadr+ATA_REG_SECT);
 if(temp1!=0x01 || temp2!=0x01)
 {
  kprintf("no drive here\r\n");
NO_DRIVE:
  dev->ioadr=0;
  dev->sel=0;
  return;
 }
 temp1=inportb(ioadr+ATA_REG_LOCYL);
 temp2=inportb(ioadr+ATA_REG_HICYL);
 temp=inportb(ioadr+ATA_REG_STAT);
 clear_irq_occured();
 if(temp1==0x14 && temp2==0xEB)
 {
  kprintf("ATAPI CD-ROM, ");
  dev->flags|=ATA_FLG_ATAPI;
  temp1=ATA_CMD_PID;
  outportb(ioadr+ATA_REG_CMD,temp1);
  temp=WAIT_PID;
  dev->bytes_per_block=2048;
 } else if(!temp1 && !temp2 && temp)
 {
  kprintf("ATA HDD, ");
  temp1=ATA_CMD_ID;
  outportb(ioadr+ATA_REG_CMD,temp1);
  temp=WAIT_ID;
  dev->bytes_per_block=512;
 } else {
  kprintf("unknown drive type\r\n");
  goto NO_DRIVE;
 }
 delay(4);
             if(!ide_await_interrupt(0xC000,temp))
 {
  kprintf("cannot identify device\r\n");
  goto NO_DRIVE;
 }
 inportb(ioadr+ATA_REG_STAT);
 ide_insw(ioadr+ATA_REG_DATA,(__u16 *)&dev->hwif,sizeof(ide_hwif_t)>>1);
 temp2=1;
 if(temp1==ATA_CMD_PID)
 {
  if((dev->hwif.Model[0]=='N' && dev->hwif.Model[1]=='E') ||
     (dev->hwif.Model[0]=='F' && dev->hwif.Model[1]=='X') ||
     (dev->hwif.Model[0]=='P' && dev->hwif.Model[1]=='i')) temp2=0;
 }
 for(temp=0;temp<40;temp+=2)
 {
  kprintf("%c",dev->hwif.Model[temp^temp2]);
  kprintf("%c",dev->hwif.Model[temp^temp2^1]);
 }
 kprintf("\r\n");
 kprintf("         C/H/S=%u:%u:%u, ",dev->hwif.PhysCyls,
  dev->hwif.PhysHeads,dev->hwif.PhysSects);
 dev->sects=dev->hwif.PhysSects;
 dev->heads=dev->hwif.PhysHeads;
 dev->cyls=dev->hwif.PhysCyls;
 if(dev->hwif.Capability & 2)
 {
  kprintf("LBA, ");
  dev->flags|=ATA_FLG_LBA;
 }
 if(dev->hwif.Capability & 1)
 {
  kprintf("DMA, ");
  dev->flags|=ATA_FLG_DMA;
 }
 if((dev->hwif.MultSectValid & 1) && dev->hwif.MultSect)
 {
  temp=dev->hwif.MaxMult;
          kprintf("MaxMult=%u, ",temp);
 } else temp=1;
 dev->mult_count=temp;
 if((dev->flags & ATA_FLG_LBA) && !ide_check_lba_geometry(dev))
 {
  kprintf("BADLBA, ");
 }
 kprintf("%uK cache\r\n",dev->hwif.BufSize>>1);
 kprintf("         BufType=%u ECCBytes=%u DwordIO=0x%X\r\n",
  dev->hwif.BufType,dev->hwif.ECCBytes,dev->hwif.DwordIO);
}

int ide_select(ide_dev_t * dev)
{
 __u16 temp;
 temp=inportb(dev->ioadr+ATA_REG_DRVHD);
 if(((temp^dev->sel)&0x10)==0) return 1;
 outportb(dev->ioadr+ATA_REG_DRVHD,dev->sel);
 delay(1);
 for(temp=WAIT_READY;temp;temp--)
 {
  if((inportb(dev->ioadr+ATA_REG_STAT)&0x80)==0) break;
  delay(1);
 }
 return temp!=0 ? 1 : 0;
}

int ide_await_interrupt(__u32 mask,__u32 timeout)
{
 __u32 intr;
 for(;timeout;timeout--)
 {
  intr=interrupt_occured;
  if(intr & mask) break;
  delay(1);
 }
 if(!timeout) return 0;
 interrupt_occured&=~mask;
 return intr & mask;
}

void ide_insw(__u16 Adr,__u16 * Data,__u16 Count)
{
 for(;Count;Count--) *Data++=inportw(Adr);
}

int ide_check_lba_geometry(ide_dev_t * dev)
{
 __u32 lba_sects,chs_sects,_10_percent;
 lba_sects=dev->hwif.LBASects;
 chs_sects=dev->cyls*dev->heads*dev->sects;
 _10_percent=chs_sects/10;
 if((lba_sects-chs_sects)<_10_percent) return 1;
 lba_sects=(lba_sects<<16)|(lba_sects>>16);
 if((lba_sects-chs_sects)<_10_percent)
 {
  dev->hwif.LBASects=lba_sects;
  dev->lba_blocks=lba_sects;
  dev->capabilities|=ATA_CAP_LBA;
  return 1;
 }
 dev->lba_blocks=0;
 dev->capabilities&=~ATA_CAP_LBA;
}

void set_irq_occured(__u32 m)
{
 interrupt_occured=m;
}

void clear_irq_occured(void)
{
 set_irq_occured(0);
}