/* I/O Ports used by winchester disk controllers. */
/* Read and write registers */
-#define REG_BASE0 0x1F0 /* base register of controller 0 */
-#define REG_BASE1 0x170 /* base register of controller 1 */
+#define REG_CMD_BASE0 0x1F0 /* command base register of controller 0 */
+#define REG_CMD_BASE1 0x170 /* command base register of controller 1 */
+#define REG_CTL_BASE0 0x3F6 /* control base register of controller 0 */
+#define REG_CTL_BASE1 0x376 /* control base register of controller 1 */
+
#define REG_DATA 0 /* data register (offset from the base reg.) */
#define REG_PRECOMP 1 /* start of write precompensation */
#define REG_COUNT 2 /* sectors to transfer */
#define CMD_IDLE 0x00 /* for w_command: drive idle */
#define CMD_RECALIBRATE 0x10 /* recalibrate drive */
#define CMD_READ 0x20 /* read data */
+#define CMD_READ_EXT 0x24 /* read data (LBA48 addressed) */
#define CMD_WRITE 0x30 /* write data */
+#define CMD_WRITE_EXT 0x34 /* write data (LBA48 addressed) */
#define CMD_READVERIFY 0x40 /* read verify */
#define CMD_FORMAT 0x50 /* format track */
#define CMD_SEEK 0x70 /* seek cylinder */
#define CMD_DIAG 0x90 /* execute device diagnostics */
#define CMD_SPECIFY 0x91 /* specify parameters */
#define ATA_IDENTIFY 0xEC /* identify drive */
-#define REG_CTL 0x206 /* control register */
+/* #define REG_CTL 0x206 */ /* control register */
+#define REG_CTL 0 /* control register */
#define CTL_NORETRY 0x80 /* disable access retry */
#define CTL_NOECC 0x40 /* disable ecc retry */
#define CTL_EIGHTHEADS 0x08 /* more than eight heads */
/* Timeouts and max retries. */
int timeout_ticks = DEF_TIMEOUT_TICKS, max_errors = MAX_ERRORS;
int wakeup_ticks = WAKEUP;
-long w_standard_timeouts = 0, w_pci_debug = 0, w_instance = 0;
+long w_standard_timeouts = 0, w_pci_debug = 0, w_instance = 0, w_lba48 = 0;
int w_testing = 0, w_silent = 0;
PRIVATE struct wini { /* main drive struct, one entry per drive */
unsigned state; /* drive state: deaf, initialized, dead */
unsigned w_status; /* device status register */
- unsigned base; /* base register of the register file */
+ unsigned base_cmd; /* command base register */
+ unsigned base_ctl; /* control base register */
unsigned irq; /* interrupt request line */
unsigned irq_mask; /* 1 << irq */
unsigned irq_need_ack; /* irq needs to be acknowledged */
int irq_hook_id; /* id of irq hook at the kernel */
+ int lba48; /* supports lba48 */
unsigned lcylinders; /* logical number of cylinders (BIOS) */
unsigned lheads; /* logical number of heads */
unsigned lsectors; /* logical number of sectors per track */
PRIVATE struct device *w_dv; /* device's base and size */
FORWARD _PROTOTYPE( void init_params, (void) );
-FORWARD _PROTOTYPE( void init_drive, (struct wini *, int, int, int, int, int));
+FORWARD _PROTOTYPE( void init_drive, (struct wini *, int, int, int, int, int, int));
FORWARD _PROTOTYPE( void init_params_pci, (int) );
FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) );
FORWARD _PROTOTYPE( struct device *w_prepare, (int dev) );
env_parse("ata_std_timeout", "d", 0, &w_standard_timeouts, 0, 1);
env_parse("ata_pci_debug", "d", 0, &w_pci_debug, 0, 1);
env_parse("ata_instance", "d", 0, &w_instance, 0, 8);
+ env_parse("ata_lba48", "d", 0, &w_lba48, 0, 1);
if(w_instance == 0) {
/* Get the number of drives from the BIOS data area */
}
/* Fill in non-BIOS parameters. */
- init_drive(wn, drive < 2 ? REG_BASE0 : REG_BASE1, NO_IRQ, 0, 0, drive);
+ init_drive(wn,
+ drive < 2 ? REG_CMD_BASE0 : REG_CMD_BASE1,
+ drive < 2 ? REG_CTL_BASE0 : REG_CTL_BASE1,
+ NO_IRQ, 0, 0, drive);
w_next_drive++;
}
}
/*============================================================================*
* init_drive *
*============================================================================*/
-PRIVATE void init_drive(struct wini *w, int base, int irq, int ack, int hook, int drive)
+PRIVATE void init_drive(struct wini *w, int base_cmd, int base_ctl, int irq, int ack, int hook, int drive)
{
w->state = 0;
w->w_status = 0;
- w->base = base;
+ w->base_cmd = base_cmd;
+ w->base_ctl = base_ctl;
w->irq = irq;
w->irq_mask = 1 << irq;
w->irq_need_ack = ack;
w->irq_hook_id = hook;
w->ldhpref = ldh_init(drive);
w->max_count = MAX_SECS << SECTOR_SHIFT;
+ w->lba48 = 0;
}
/*============================================================================*
/* Primary channel not in compatability mode? */
if(interface & ATA_IF_NOTCOMPAT1) {
- u32_t base;
- base = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
- if(base != REG_BASE0 && base != REG_BASE1) {
- init_drive(&wini[w_next_drive], base, irq, 1, irq_hook, 0);
- init_drive(&wini[w_next_drive+1], base, irq, 1, irq_hook, 1);
- if(w_pci_debug)
- printf("atapci %d: 0x%x irq %d\n", devind, base, irq);
- } else printf("atapci: ignored drives on primary channel, base %x\n", base);
+ u32_t base_cmd, base_ctl;
+ base_cmd = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
+ base_ctl = pci_attr_r32(devind, PCI_BAR_2) & 0xffffffe0;
+ if(base_cmd != REG_CMD_BASE0 && base_cmd != REG_CMD_BASE1) {
+ init_drive(&wini[w_next_drive],
+ base_cmd, base_ctl, irq, 1, irq_hook, 0);
+ init_drive(&wini[w_next_drive+1],
+ base_cmd, base_ctl, irq, 1, irq_hook, 1);
+ if(w_pci_debug || 1)
+ printf("atapci %d: 0x%x 0x%x irq %d\n", devind, base_cmd, base_ctl, irq);
+ } else printf("atapci: ignored drives on primary channel, base %x\n", base_cmd);
}
/* Secondary channel not in compatability mode? */
if(interface & ATA_IF_NOTCOMPAT2) {
- u32_t base;
- base = pci_attr_r32(devind, PCI_BAR_3) & 0xffffffe0;
- if(base != REG_BASE0 && base != REG_BASE1) {
- init_drive(&wini[w_next_drive+2], base, irq, 1, irq_hook, 2);
- init_drive(&wini[w_next_drive+3], base, irq, 1, irq_hook, 3);
- if(w_pci_debug)
- printf("atapci %d: 0x%x irq %d\n", devind, base, irq);
- } else printf("atapci: ignored drives on secondary channel, base %x\n", base);
+ u32_t base_cmd, base_ctl;
+ base_cmd = pci_attr_r32(devind, PCI_BAR_3) & 0xffffffe0;
+ base_ctl = pci_attr_r32(devind, PCI_BAR_4) & 0xffffffe0;
+ if(base_cmd != REG_CMD_BASE0 && base_cmd != REG_CMD_BASE1) {
+ init_drive(&wini[w_next_drive+2],
+ base_cmd, base_ctl, irq, 1, irq_hook, 2);
+ init_drive(&wini[w_next_drive+3],
+ base_cmd, base_ctl, irq, 1, irq_hook, 3);
+ if(w_pci_debug || 1)
+ printf("atapci %d: 0x%x 0x%x irq %d\n", devind, base_cmd, base_ctl, irq);
+ } else printf("atapci: ignored drives on secondary channel, base %x\n", base_cmd);
}
w_next_drive += 4;
}
wn->state |= SMART;
/* Device information. */
- if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK)
+ if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
/* Why are the strings byte swapped??? */
*/
wn->ldhpref |= LDH_LBA;
size = id_longword(60);
+
+ if(w_lba48 && ((id_word(83)) & (1L << 10))) {
+ /* Drive is LBA48 capable (and LBA48 is turned on). */
+ if(id_word(102) || id_word(103)) {
+ /* If no. of sectors doesn't fit in 32 bits,
+ * trunacte to this. So it's LBA32 for now.
+ * This can still address devices up to 2TB
+ * though.
+ */
+ size = ULONG_MAX;
+ } else {
+ /* Actual number of sectors fits in 32 bits. */
+ size = id_longword(100);
+ }
+
+ wn->lba48 = 1;
+ }
}
if (wn->lcylinders == 0) {
wn->state |= ATAPI;
/* Device information. */
- if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, 512)) != OK)
+ if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, 512)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
/* Why are the strings byte swapped??? */
return(OK);
}
+/*===========================================================================*
+ * do_transfer *
+ *===========================================================================*/
+PRIVATE int do_transfer(struct wini *wn, unsigned int precomp, unsigned int count,
+ unsigned int sector, unsigned int opcode)
+{
+ struct command cmd;
+ unsigned secspcyl = wn->pheads * wn->psectors;
+
+ cmd.precomp = precomp;
+ cmd.count = count;
+ cmd.command = opcode == DEV_SCATTER ? CMD_WRITE : CMD_READ;
+ /*
+ if (w_lba48 && wn->lba48) {
+ } else */
+ if (wn->ldhpref & LDH_LBA) {
+ cmd.sector = (sector >> 0) & 0xFF;
+ cmd.cyl_lo = (sector >> 8) & 0xFF;
+ cmd.cyl_hi = (sector >> 16) & 0xFF;
+ cmd.ldh = wn->ldhpref | ((sector >> 24) & 0xF);
+ } else {
+ int cylinder, head, sec;
+ cylinder = sector / secspcyl;
+ head = (sector % secspcyl) / wn->psectors;
+ sec = sector % wn->psectors;
+ cmd.sector = sec + 1;
+ cmd.cyl_lo = cylinder & BYTE;
+ cmd.cyl_hi = (cylinder >> 8) & BYTE;
+ cmd.ldh = wn->ldhpref | head;
+ }
+
+ return com_out(&cmd);
+}
+
/*===========================================================================*
* w_transfer *
*===========================================================================*/
int r, s, errors;
unsigned long block;
unsigned long dv_size = cv64ul(w_dv->dv_size);
- struct command cmd;
unsigned cylinder, head, sector, nbytes;
- unsigned secspcyl = wn->pheads * wn->psectors;
#if ENABLE_ATAPI
if (w_wn->state & ATAPI) {
if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO);
/* Tell the controller to transfer nbytes bytes. */
+#if 1
+ r = do_transfer(wn, wn->precomp, ((nbytes >> SECTOR_SHIFT) & BYTE),
+ block, opcode);
+#else
cmd.precomp = wn->precomp;
cmd.count = (nbytes >> SECTOR_SHIFT) & BYTE;
if (wn->ldhpref & LDH_LBA) {
cmd.command = opcode == DEV_SCATTER ? CMD_WRITE : CMD_READ;
r = com_out(&cmd);
+#endif
while (r == OK && nbytes > 0) {
/* For each sector, wait for an interrupt and fetch the data
if ((r = at_intr_wait()) != OK) {
/* An error, send data to the bit bucket. */
if (w_wn->w_status & STATUS_DRQ) {
- if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK)
+ if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
}
break;
/* Copy bytes to or from the device's buffer. */
if (opcode == DEV_GATHER) {
- if ((s=sys_insw(wn->base + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK)
+ if ((s=sys_insw(wn->base_cmd + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
} else {
- if ((s=sys_outsw(wn->base + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK)
+ if ((s=sys_outsw(wn->base_cmd + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
/* Data sent, wait for an interrupt. */
/* Output the command block to the winchester controller and return status */
struct wini *wn = w_wn;
- unsigned base = wn->base;
+ unsigned base_cmd = wn->base_cmd;
+ unsigned base_ctl = wn->base_ctl;
pvb_pair_t outbyte[7]; /* vector for sys_voutb() */
int s; /* status for sys_(v)outb() */
}
/* Select drive. */
- if ((s=sys_outb(base + REG_LDH, cmd->ldh)) != OK)
+ if ((s=sys_outb(base_cmd + REG_LDH, cmd->ldh)) != OK)
panic(w_name(),"Couldn't write register to select drive",s);
if (!w_waitfor(STATUS_BSY, 0)) {
wn->w_status = STATUS_ADMBSY;
w_command = cmd->command;
- pv_set(outbyte[0], base + REG_CTL, wn->pheads >= 8 ? CTL_EIGHTHEADS : 0);
- pv_set(outbyte[1], base + REG_PRECOMP, cmd->precomp);
- pv_set(outbyte[2], base + REG_COUNT, cmd->count);
- pv_set(outbyte[3], base + REG_SECTOR, cmd->sector);
- pv_set(outbyte[4], base + REG_CYL_LO, cmd->cyl_lo);
- pv_set(outbyte[5], base + REG_CYL_HI, cmd->cyl_hi);
- pv_set(outbyte[6], base + REG_COMMAND, cmd->command);
+ pv_set(outbyte[0], base_ctl + REG_CTL, wn->pheads >= 8 ? CTL_EIGHTHEADS : 0);
+ pv_set(outbyte[1], base_cmd + REG_PRECOMP, cmd->precomp);
+ pv_set(outbyte[2], base_cmd + REG_COUNT, cmd->count);
+ pv_set(outbyte[3], base_cmd + REG_SECTOR, cmd->sector);
+ pv_set(outbyte[4], base_cmd + REG_CYL_LO, cmd->cyl_lo);
+ pv_set(outbyte[5], base_cmd + REG_CYL_HI, cmd->cyl_hi);
+ pv_set(outbyte[6], base_cmd + REG_COMMAND, cmd->command);
if ((s=sys_voutb(outbyte,7)) != OK)
panic(w_name(),"Couldn't write registers with sys_voutb()",s);
return(OK);
int dr = 0;
for (wn = wini; wn < &wini[MAX_DRIVES]; wn++, dr++) {
- if (wn->base == w_wn->base) {
+ if (wn->base_cmd == w_wn->base_cmd) {
wn->state |= DEAF;
wn->state &= ~INITIALIZED;
}
tickdelay(RECOVERY_TICKS);
/* Strobe reset bit */
- if ((s=sys_outb(wn->base + REG_CTL, CTL_RESET)) != OK)
+ if ((s=sys_outb(wn->base_ctl + REG_CTL, CTL_RESET)) != OK)
panic(w_name(),"Couldn't strobe reset bit",s);
tickdelay(DELAY_TICKS);
- if ((s=sys_outb(wn->base + REG_CTL, 0)) != OK)
+ if ((s=sys_outb(wn->base_ctl + REG_CTL, 0)) != OK)
panic(w_name(),"Couldn't strobe reset bit",s);
tickdelay(DELAY_TICKS);
/* The error register should be checked now, but some drives mess it up. */
for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) {
- if (wn->base == w_wn->base) {
+ if (wn->base_cmd == w_wn->base_cmd) {
wn->state &= ~DEAF;
if (w_wn->irq_need_ack) {
/* Make sure irq is actually enabled.. */
if (m.m_type == SYN_ALARM) { /* but check for timeout */
w_timeout(); /* a.o. set w_status */
} else if (m.m_type == HARD_INT) {
- sys_inb(w_wn->base + REG_STATUS, &w_wn->w_status);
+ sys_inb(w_wn->base_cmd + REG_STATUS, &w_wn->w_status);
ack_irqs(m.NOTIFY_ARG);
} else {
printf("AT_WINI got unexpected message %d from %d\n",
if ((w_wn->w_status & (STATUS_BSY | STATUS_WF | STATUS_ERR)) == 0) {
r = OK;
} else {
- if ((s=sys_inb(w_wn->base + REG_ERROR, &inbval)) != OK)
+ if ((s=sys_inb(w_wn->base_cmd + REG_ERROR, &inbval)) != OK)
panic(w_name(),"Couldn't read register",s);
if ((w_wn->w_status & STATUS_ERR) && (inbval & ERROR_BB)) {
r = ERR_BAD_SECTOR; /* sector marked bad, retries won't help */
int s;
getuptime(&t0);
do {
- if ((s=sys_inb(w_wn->base + REG_STATUS, &w_wn->w_status)) != OK)
+ if ((s=sys_inb(w_wn->base_cmd + REG_STATUS, &w_wn->w_status)) != OK)
panic(w_name(),"Couldn't read register",s);
if ((w_wn->w_status & mask) == value) {
return 1;
chunk = before;
if (chunk > count) chunk = count;
if (chunk > DMA_BUF_SIZE) chunk = DMA_BUF_SIZE;
- if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, chunk)) != OK)
+ if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, chunk)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
before -= chunk;
count -= chunk;
chunk = nbytes;
if (chunk > count) chunk = count;
if (chunk > iov->iov_size) chunk = iov->iov_size;
- if ((s=sys_insw(wn->base + REG_DATA, proc_nr, (void *) iov->iov_addr, chunk)) != OK)
+ if ((s=sys_insw(wn->base_cmd + REG_DATA, proc_nr, (void *) iov->iov_addr, chunk)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
position += chunk;
nbytes -= chunk;
while (count > 0) { /* Excess data. */
chunk = count;
if (chunk > DMA_BUF_SIZE) chunk = DMA_BUF_SIZE;
- if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, chunk)) != OK)
+ if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, chunk)) != OK)
panic(w_name(),"Call to sys_insw() failed", s);
count -= chunk;
}
if(wn->state & IGNORING) return ERR;
/* Select Master/Slave drive */
- if ((s=sys_outb(wn->base + REG_DRIVE, wn->ldhpref)) != OK)
+ if ((s=sys_outb(wn->base_cmd + REG_DRIVE, wn->ldhpref)) != OK)
panic(w_name(),"Couldn't select master/ slave drive",s);
if (!w_waitfor(STATUS_BSY | STATUS_DRQ, 0)) {
#endif
w_command = ATAPI_PACKETCMD;
- pv_set(outbyte[0], wn->base + REG_FEAT, 0);
- pv_set(outbyte[1], wn->base + REG_IRR, 0);
- pv_set(outbyte[2], wn->base + REG_SAMTAG, 0);
- pv_set(outbyte[3], wn->base + REG_CNT_LO, (cnt >> 0) & 0xFF);
- pv_set(outbyte[4], wn->base + REG_CNT_HI, (cnt >> 8) & 0xFF);
- pv_set(outbyte[5], wn->base + REG_COMMAND, w_command);
+ pv_set(outbyte[0], wn->base_cmd + REG_FEAT, 0);
+ pv_set(outbyte[1], wn->base_cmd + REG_IRR, 0);
+ pv_set(outbyte[2], wn->base_cmd + REG_SAMTAG, 0);
+ pv_set(outbyte[3], wn->base_cmd + REG_CNT_LO, (cnt >> 0) & 0xFF);
+ pv_set(outbyte[4], wn->base_cmd + REG_CNT_HI, (cnt >> 8) & 0xFF);
+ pv_set(outbyte[5], wn->base_cmd + REG_COMMAND, w_command);
if ((s=sys_voutb(outbyte,6)) != OK)
panic(w_name(),"Couldn't write registers with sys_voutb()",s);
wn->w_status |= STATUS_ADMBSY; /* Command not at all done yet. */
/* Send the command packet to the device. */
- if ((s=sys_outsw(wn->base + REG_DATA, SELF, packet, 12)) != OK)
+ if ((s=sys_outsw(wn->base_cmd + REG_DATA, SELF, packet, 12)) != OK)
panic(w_name(),"sys_outsw() failed", s);
return(OK);
}
for (drive = 0; drive < MAX_DRIVES && irqs; drive++) {
if(!(wini[drive].state & IGNORING) && wini[drive].irq_need_ack &&
(wini[drive].irq_mask & irqs)) {
- if(sys_inb((wini[drive].base + REG_STATUS), &wini[drive].w_status) != OK)
+ if(sys_inb((wini[drive].base_cmd + REG_STATUS), &wini[drive].w_status) != OK)
printf("couldn't ack irq on drive %d\n", drive);
if (sys_irqenable(&wini[drive].irq_hook_id) != OK)
printf("couldn't re-enable drive %d\n", drive);
w_intr_wait();
/* Request series of device I/O. */
- inbyte[0].port = wn->base + REG_ERROR;
- inbyte[1].port = wn->base + REG_CNT_LO;
- inbyte[2].port = wn->base + REG_CNT_HI;
- inbyte[3].port = wn->base + REG_IRR;
+ inbyte[0].port = wn->base_cmd + REG_ERROR;
+ inbyte[1].port = wn->base_cmd + REG_CNT_LO;
+ inbyte[2].port = wn->base_cmd + REG_CNT_HI;
+ inbyte[3].port = wn->base_cmd + REG_IRR;
if ((s=sys_vinb(inbyte, 4)) != OK)
panic(w_name(),"ATAPI failed sys_vinb()", s);
e = inbyte[0].value;