From: Philip Homburg Date: Fri, 3 Mar 2006 15:37:51 +0000 (+0000) Subject: Support for DMA. X-Git-Tag: v3.1.2a~306 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/addsub.png?a=commitdiff_plain;h=816973d1fe1b2fde409e4381028dd68519dcaee9;p=minix.git Support for DMA. --- diff --git a/drivers/at_wini/at_wini.c b/drivers/at_wini/at_wini.c index 6c6871eff..f6c29f4d2 100644 --- a/drivers/at_wini/at_wini.c +++ b/drivers/at_wini/at_wini.c @@ -32,6 +32,9 @@ #define REG_CTL_BASE1 0x376 /* control base register of controller 1 */ #define PCI_CTL_OFF 2 /* Offset of control registers from BAR2 */ +#define PCI_DMA_2ND_OFF 8 /* Offset of DMA registers from BAR4 for + * secondary channel + */ #define REG_DATA 0 /* data register (offset from the base reg.) */ #define REG_PRECOMP 1 /* start of write precompensation */ @@ -42,6 +45,7 @@ #define REG_LDH 6 /* lba, drive and head */ #define LDH_DEFAULT 0xA0 /* ECC enable, 512 bytes per sector */ #define LDH_LBA 0x40 /* Use LBA addressing */ +#define LDH_DEV 0x10 /* Drive 1 iff set */ #define ldh_init(drive) (LDH_DEFAULT | ((drive) << 4)) /* Read only registers */ @@ -69,13 +73,17 @@ #define CMD_RECALIBRATE 0x10 /* recalibrate drive */ #define CMD_READ 0x20 /* read data */ #define CMD_READ_EXT 0x24 /* read data (LBA48 addressed) */ +#define CMD_READ_DMA_EXT 0x25 /* read data using DMA (w/ LBA48) */ #define CMD_WRITE 0x30 /* write data */ #define CMD_WRITE_EXT 0x34 /* write data (LBA48 addressed) */ +#define CMD_WRITE_DMA_EXT 0x35 /* write data using DMA (w/ LBA48) */ #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 CMD_READ_DMA 0xC8 /* read data using DMA */ +#define CMD_WRITE_DMA 0xCA /* write data using DMA */ #define ATA_IDENTIFY 0xEC /* identify drive */ /* #define REG_CTL 0x206 */ /* control register */ #define REG_CTL 0 /* control register */ @@ -88,9 +96,44 @@ /* Identify words */ #define ID_CAPABILITIES 0x31 /* Capabilities (49)*/ -#define ID_CAP_LBA 0x0200 +#define ID_CAP_LBA 0x0200 /* LBA supported */ +#define ID_CAP_DMA 0x0100 /* DMA supported */ +#define ID_FIELD_VALIDITY 0x35 /* Field Validity (53) */ +#define ID_FV_88 0x04 /* Word 88 is valid (UDMA) */ +#define ID_MULTIWORD_DMA 0x3f /* Multiword DMA (63) */ +#define ID_MWDMA_2_SEL 0x0400 /* Mode 2 is selected */ +#define ID_MWDMA_1_SEL 0x0200 /* Mode 1 is selected */ +#define ID_MWDMA_0_SEL 0x0100 /* Mode 0 is selected */ +#define ID_MWDMA_2_SUP 0x0004 /* Mode 2 is supported */ +#define ID_MWDMA_1_SUP 0x0002 /* Mode 1 is supported */ +#define ID_MWDMA_0_SUP 0x0001 /* Mode 0 is supported */ #define ID_CSS 0x53 /* Command Sets Supported (83) */ #define ID_CSS_LBA48 0x0400 +#define ID_ULTRA_DMA 0x58 /* Ultra DMA (88) */ +#define ID_UDMA_5_SEL 0x2000 /* Mode 5 is selected */ +#define ID_UDMA_4_SEL 0x1000 /* Mode 4 is selected */ +#define ID_UDMA_3_SEL 0x0800 /* Mode 3 is selected */ +#define ID_UDMA_2_SEL 0x0400 /* Mode 2 is selected */ +#define ID_UDMA_1_SEL 0x0200 /* Mode 1 is selected */ +#define ID_UDMA_0_SEL 0x0100 /* Mode 0 is selected */ +#define ID_UDMA_5_SUP 0x0020 /* Mode 5 is supported */ +#define ID_UDMA_4_SUP 0x0010 /* Mode 4 is supported */ +#define ID_UDMA_3_SUP 0x0008 /* Mode 3 is supported */ +#define ID_UDMA_2_SUP 0x0004 /* Mode 2 is supported */ +#define ID_UDMA_1_SUP 0x0002 /* Mode 1 is supported */ +#define ID_UDMA_0_SUP 0x0001 /* Mode 0 is supported */ + +/* DMA registers */ +#define DMA_COMMAND 0 /* Command register */ +#define DMA_CMD_WRITE 0x08 /* PCI bus master writes */ +#define DMA_CMD_START 0x01 /* Start Bus Master */ +#define DMA_STATUS 2 /* Status register */ +#define DMA_ST_D1_DMACAP 0x40 /* Drive 1 is DMA capable */ +#define DMA_ST_D0_DMACAP 0x20 /* Drive 0 is DMA capable */ +#define DMA_ST_INT 0x04 /* Interrupt */ +#define DMA_ST_ERROR 0x02 /* Error */ +#define DMA_ST_BM_ACTIVE 0x01 /* Bus Master IDE Active */ +#define DMA_PRDTP 4 /* PRD Table Pointer */ /* Check for the presence of LBA48 only on drives that are 'big'. */ #define LBA48_CHECK_SIZE 0x0f000000 @@ -224,11 +267,13 @@ PRIVATE struct wini { /* main drive struct, one entry per drive */ unsigned short w_status; /* device status register */ unsigned base_cmd; /* command base register */ unsigned base_ctl; /* control base register */ + unsigned base_dma; /* dma 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 */ + int dma; /* supports dma */ unsigned lcylinders; /* logical number of cylinders (BIOS) */ unsigned lheads; /* logical number of heads */ unsigned lsectors; /* logical number of sectors per track */ @@ -255,9 +300,37 @@ PRIVATE int w_drive; /* selected drive */ PRIVATE int w_controller; /* selected controller */ PRIVATE struct device *w_dv; /* device's base and size */ +/* XXX */ +#undef DMA_SECTORS +#undef DMA_BUF_SIZE + +#define DMA_SECTORS 64 +#define DMA_BUF_SIZE (DMA_SECTORS*SECTOR_SIZE) + +PRIVATE char dma_buf[DMA_BUF_SIZE]; +PRIVATE phys_bytes dma_buf_phys; + +#define N_PRDTE 1024 /* Should be enough for large requests */ +#if 0 +#undef N_PRDTE +#define N_PRDTE 4 +#endif + +PRIVATE struct prdte +{ + u32_t prdte_base; + u16_t prdte_count; + u8_t prdte_reserved; + u8_t prdte_flags; +} prdt[N_PRDTE]; +PRIVATE phys_bytes prdt_phys; + +#define PRDTE_FL_EOT 0x80 /* End of table */ + FORWARD _PROTOTYPE( void init_params, (void) ); -FORWARD _PROTOTYPE( void init_drive, (struct wini *, int, int, int, - int, int, int)); +FORWARD _PROTOTYPE( void init_drive, (struct wini *w, int base_cmd, + int base_ctl, int base_dma, int irq, int ack, int hook, + int drive) ); 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) ); @@ -269,6 +342,8 @@ FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, off_t position, iovec_t *iov, unsigned nr_req) ); FORWARD _PROTOTYPE( int com_out, (struct command *cmd) ); FORWARD _PROTOTYPE( int com_out_ext, (struct command *cmd) ); +FORWARD _PROTOTYPE( void setup_dma, (unsigned *sizep, int proc_nr, + iovec_t *iov, int do_write, int *do_copyoutp) ); FORWARD _PROTOTYPE( void w_need_reset, (void) ); FORWARD _PROTOTYPE( void ack_irqs, (unsigned int) ); FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) ); @@ -280,6 +355,7 @@ FORWARD _PROTOTYPE( int w_reset, (void) ); FORWARD _PROTOTYPE( void w_intr_wait, (void) ); FORWARD _PROTOTYPE( int at_intr_wait, (void) ); FORWARD _PROTOTYPE( int w_waitfor, (int mask, int value) ); +FORWARD _PROTOTYPE( int w_waitfor_dma, (int mask, int value) ); FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry) ); #if ENABLE_ATAPI FORWARD _PROTOTYPE( int atapi_sendpacket, (u8_t *packet, unsigned cnt) ); @@ -348,6 +424,19 @@ PRIVATE void init_params() env_parse("ata_instance", "d", 0, &w_instance, 0, 8); env_parse("atapi_debug", "d", 0, &atapi_debug, 0, 1); + s= sys_umap(SELF, D, (vir_bytes)dma_buf, sizeof(dma_buf), &dma_buf_phys); + if (s != 0) + panic("at_wini", "can't map dma buffer", s); + printf("init_params: got phys 0x%x for dma buffer at 0x%x\n", + dma_buf_phys, (vir_bytes)dma_buf); + + s= sys_umap(SELF, D, (vir_bytes)prdt, sizeof(prdt), &prdt_phys); + if (s != 0) + panic("at_wini", "can't map prd table", s); + printf("init_params: got phys 0x%x for prd table at 0x%x\n", + prdt_phys, (vir_bytes)prdt); + + if (w_instance == 0) { /* Get the number of drives from the BIOS data area */ if ((s=sys_vircopy(SELF, BIOS_SEG, NR_HD_DRIVES_ADDR, @@ -381,7 +470,7 @@ PRIVATE void init_params() init_drive(wn, drive < 2 ? REG_CMD_BASE0 : REG_CMD_BASE1, drive < 2 ? REG_CTL_BASE0 : REG_CTL_BASE1, - NO_IRQ, 0, 0, drive); + 0 /* no DMA */, NO_IRQ, 0, 0, drive); w_next_drive++; } } @@ -402,12 +491,14 @@ PRIVATE void init_params() /*===========================================================================* * init_drive * *===========================================================================*/ -PRIVATE void init_drive(struct wini *w, int base_cmd, int base_ctl, int irq, int ack, int hook, int drive) +PRIVATE void init_drive(struct wini *w, int base_cmd, int base_ctl, + int base_dma, int irq, int ack, int hook, int drive) { w->state = 0; w->w_status = 0; w->base_cmd = base_cmd; w->base_ctl = base_ctl; + w->base_dma = base_dma; w->irq = irq; w->irq_mask = 1 << irq; w->irq_need_ack = ack; @@ -415,6 +506,7 @@ PRIVATE void init_drive(struct wini *w, int base_cmd, int base_ctl, int irq, int w->ldhpref = ldh_init(drive); w->max_count = MAX_SECS << SECTOR_SHIFT; w->lba48 = 0; + w->dma = 0; } /*===========================================================================* @@ -422,13 +514,15 @@ PRIVATE void init_drive(struct wini *w, int base_cmd, int base_ctl, int irq, int *===========================================================================*/ PRIVATE void init_params_pci(int skip) { - int r, devind, drive; + int i, r, devind, drive; u16_t vid, did; + u32_t base_dma; + pci_init(); for(drive = w_next_drive; drive < MAX_DRIVES; drive++) wini[drive].state = IGNORING; - for(r = pci_first_dev(&devind, &vid, &did); - r != 0 && w_next_drive < MAX_DRIVES; r = pci_next_dev(&devind, &vid, &did)) { + for(r = pci_first_dev(&devind, &vid, &did); r != 0; + r = pci_next_dev(&devind, &vid, &did)) { int interface, irq, irq_hook; /* Base class must be 01h (mass storage), subclass must * be 01h (ATA). @@ -438,10 +532,6 @@ PRIVATE void init_params_pci(int skip) continue; } - if(w_pci_debug) - printf("init_params_pci: found device %04x/%04x at index %d\n", - vid, did, devind); - /* Found a controller. * Programming interface register tells us more. */ @@ -451,6 +541,15 @@ PRIVATE void init_params_pci(int skip) /* Any non-compat drives? */ if (interface & (ATA_IF_NOTCOMPAT1 | ATA_IF_NOTCOMPAT2)) { int s; + + if (w_next_drive >= MAX_DRIVES) + { + /* We can't accept more drives, but have to search for + * controllers operating in compatibility mode. + */ + continue; + } + irq_hook = irq; if (skip > 0) { if (w_pci_debug) printf("atapci skipping controller (remain %d)\n", skip); @@ -465,45 +564,66 @@ PRIVATE void init_params_pci(int skip) printf("atapci: couldn't enable IRQ line %d\n", irq); continue; } - } else { - /* If not.. this is not the ata-pci controller we're - * looking for. - */ - if (w_pci_debug) printf("atapci skipping compatability controller\n"); - continue; - } + } + + base_dma = pci_attr_r32(devind, PCI_BAR_5) & 0xffffffe0; /* Primary channel not in compatability mode? */ if (interface & ATA_IF_NOTCOMPAT1) { 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+PCI_CTL_OFF, - irq, 1, irq_hook, 0); + base_dma, irq, 1, irq_hook, 0); init_drive(&wini[w_next_drive+1], base_cmd, base_ctl+PCI_CTL_OFF, - irq, 1, irq_hook, 1); + base_dma, irq, 1, irq_hook, 1); if (w_pci_debug) 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); } + else + { + /* Update base_dma for compatibility device */ + for (i= 0; ilba48 = 1; } + + /* Check for DMA. Assume that only LBA capable devices can do + * DMA. + */ + w= id_word(ID_CAPABILITIES); + id_dma= !!(w & ID_CAP_DMA); + w= id_byte(ID_FIELD_VALIDITY)[0]; + ultra_dma= !!(w & ID_FV_88); + dma_base= wn->base_dma; + if (dma_base) + { + if (sys_inb(dma_base + DMA_STATUS, &dma_status) != OK) + { + panic(w_name(), + "unable to read DMA status register", + NO_NUM); + } + } + if (id_dma && dma_base) + { + w= id_word(ID_MULTIWORD_DMA); + if (w & (ID_MWDMA_2_SUP|ID_MWDMA_1_SUP|ID_MWDMA_0_SUP)) + { + printf( + "%s: multiword DMA modes supported:%s%s%s\n", + w_name(), + (w & ID_MWDMA_0_SUP) ? " 0" : "", + (w & ID_MWDMA_1_SUP) ? " 1" : "", + (w & ID_MWDMA_2_SUP) ? " 2" : ""); + } + if (w & (ID_MWDMA_0_SEL|ID_MWDMA_1_SEL|ID_MWDMA_2_SEL)) + { + printf( + "%s: multiword DMA mode selected:%s%s%s\n", + w_name(), + (w & ID_MWDMA_0_SEL) ? " 0" : "", + (w & ID_MWDMA_1_SEL) ? " 1" : "", + (w & ID_MWDMA_2_SEL) ? " 2" : ""); + } + if (ultra_dma) + { + w= id_word(ID_ULTRA_DMA); + if (w & (ID_UDMA_0_SUP|ID_UDMA_1_SUP| + ID_UDMA_2_SUP|ID_UDMA_3_SUP| + ID_UDMA_4_SUP|ID_UDMA_5_SUP)) + { + printf( + "%s: Ultra DMA modes supported:%s%s%s%s%s%s\n", + w_name(), + (w & ID_UDMA_0_SUP) ? " 0" : "", + (w & ID_UDMA_1_SUP) ? " 1" : "", + (w & ID_UDMA_2_SUP) ? " 2" : "", + (w & ID_UDMA_3_SUP) ? " 3" : "", + (w & ID_UDMA_4_SUP) ? " 4" : "", + (w & ID_UDMA_5_SUP) ? " 5" : ""); + } + if (w & (ID_UDMA_0_SEL|ID_UDMA_1_SEL| + ID_UDMA_2_SEL|ID_UDMA_3_SEL| + ID_UDMA_4_SEL|ID_UDMA_5_SEL)) + { + printf( + "%s: Ultra DMA mode selected:%s%s%s%s%s%s\n", + w_name(), + (w & ID_UDMA_0_SEL) ? " 0" : "", + (w & ID_UDMA_1_SEL) ? " 1" : "", + (w & ID_UDMA_2_SEL) ? " 2" : "", + (w & ID_UDMA_3_SEL) ? " 3" : "", + (w & ID_UDMA_4_SEL) ? " 4" : "", + (w & ID_UDMA_5_SEL) ? " 5" : ""); + } + } + wn->dma= 1; + } + else if (id_dma || dma_base) + { + printf("id_dma %d, dma_base 0x%x\n", id_dma, dma_base); + } + else + printf("no DMA support\n"); + +#if 0 + if (wn->dma && wn == &wini[0]) + { + printf("disabling DMA for drive 0\n"); + wn->dma= 0; + } +#endif } if (wn->lcylinders == 0) { @@ -852,7 +1062,8 @@ PRIVATE int w_specify() * do_transfer * *===========================================================================*/ PRIVATE int do_transfer(struct wini *wn, unsigned int precomp, - unsigned int count, unsigned int sector, unsigned int opcode) + unsigned int count, unsigned int sector, + unsigned int opcode, int do_dma) { struct command cmd; unsigned int sector_high; @@ -875,11 +1086,25 @@ PRIVATE int do_transfer(struct wini *wn, unsigned int precomp, cmd.precomp = precomp; cmd.count = count; - cmd.command = opcode == DEV_SCATTER ? CMD_WRITE : CMD_READ; + if (do_dma) + { + cmd.command = opcode == DEV_SCATTER ? CMD_WRITE_DMA : + CMD_READ_DMA; + } + else + cmd.command = opcode == DEV_SCATTER ? CMD_WRITE : CMD_READ; if (do_lba48) { - cmd.command = ((opcode == DEV_SCATTER) ? - CMD_WRITE_EXT : CMD_READ_EXT); + if (do_dma) + { + cmd.command = ((opcode == DEV_SCATTER) ? + CMD_WRITE_DMA_EXT : CMD_READ_DMA_EXT); + } + else + { + cmd.command = ((opcode == DEV_SCATTER) ? + CMD_WRITE_EXT : CMD_READ_EXT); + } cmd.count_prev= (count >> 8); cmd.sector = (sector >> 0) & 0xFF; cmd.cyl_lo = (sector >> 8) & 0xFF; @@ -921,10 +1146,11 @@ unsigned nr_req; /* length of request vector */ { struct wini *wn = w_wn; iovec_t *iop, *iov_end = iov + nr_req; - int r, s, errors; - unsigned long block, w_status; + int n, r, s, errors, do_dma, do_write, do_copyout; + unsigned long v, block, w_status; unsigned long dv_size = cv64ul(w_dv->dv_size); unsigned cylinder, head, sector, nbytes; + unsigned dma_buf_offset; #if ENABLE_ATAPI if (w_wn->state & ATAPI) { @@ -950,6 +1176,9 @@ unsigned nr_req; /* length of request vector */ if (position + nbytes > dv_size) nbytes = dv_size - position; block = div64u(add64ul(w_dv->dv_base, position), SECTOR_SIZE); + do_dma= wn->dma; + do_write= (opcode == DEV_SCATTER); + if (nbytes >= wn->max_count) { /* The drive can't do more then max_count at once. */ nbytes = wn->max_count; @@ -958,9 +1187,17 @@ unsigned nr_req; /* length of request vector */ /* First check to see if a reinitialization is needed. */ if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO); + if (do_dma) + { + setup_dma(&nbytes, proc_nr, iov, do_write, &do_copyout); +#if 0 + printf("nbytes = %d\n", nbytes); +#endif + } + /* Tell the controller to transfer nbytes bytes. */ r = do_transfer(wn, wn->precomp, (nbytes >> SECTOR_SHIFT), - block, opcode); + block, opcode, do_dma); if (opcode == DEV_SCATTER) { /* The specs call for a 400 ns wait after issuing the command. @@ -971,6 +1208,80 @@ unsigned nr_req; /* length of request vector */ panic(w_name(), "couldn't get status", NO_NUM); } + if (do_dma) + { + /* Wait for the interrupt, check DMA status and optionally + * copy out. + */ + + if ((r = at_intr_wait()) != OK) + { + /* Don't retry if sector marked bad or too many + * errors. + */ + if (r == ERR_BAD_SECTOR || ++errors == max_errors) { + w_command = CMD_IDLE; + return(EIO); + } + continue; + } + + /* Wait for DMA_ST_INT to get set */ + w_waitfor_dma(DMA_ST_INT, DMA_ST_INT); + + r= sys_inb(wn->base_dma + DMA_STATUS, &v); + if (r != 0) panic("at_wini", "w_transfer: sys_inb failed", r); + +#if 0 + printf("dma_status: 0x%x\n", v); +#endif + if (!(v & DMA_ST_INT)) + { + /* DMA did not complete successfully */ + if (v & DMA_ST_BM_ACTIVE) + panic(w_name(), "DMA did not complete", NO_NUM); + else if (v & DMA_ST_ERROR) + { + printf("at_wini: DMA error\n"); + r= EIO; + break; + } + else + panic(w_name(), "DMA buffer too small", NO_NUM); + } + else if (v & DMA_ST_BM_ACTIVE) + panic(w_name(), "DMA buffer too large", NO_NUM); + + dma_buf_offset= 0; + while (r == OK && nbytes > 0) + { + n= iov->iov_size; + if (n > nbytes) + n= nbytes; + + if (do_copyout) + { + s= sys_vircopy(SELF, D, + (vir_bytes)dma_buf+dma_buf_offset, + proc_nr, D, iov->iov_addr, n); + if (s != OK) + { + panic(w_name(), + "w_transfer: sys_vircopy failed", + s); + } + } + + /* Book the bytes successfully transferred. */ + nbytes -= n; + position += n; + iov->iov_addr += n; + if ((iov->iov_size -= n) == 0) + { iov++; nr_req--; } + dma_buf_offset += n; + } + } + while (r == OK && nbytes > 0) { /* For each sector, wait for an interrupt and fetch the data * (read), or supply data to the controller and wait for an @@ -1151,6 +1462,230 @@ struct command *cmd; /* Command block */ return(OK); } +/*===========================================================================* + * setup_dma * + *===========================================================================*/ +PRIVATE void setup_dma(sizep, proc_nr, iov, do_write, do_copyoutp) +unsigned *sizep; +int proc_nr; +iovec_t *iov; +int do_write; +int *do_copyoutp; +{ + phys_bytes phys, user_phys; + unsigned n, offset, size; + int i, j, r, bad; + unsigned long v; + struct wini *wn = w_wn; + + /* First try direct scatter/gather to the supplied buffers */ + size= *sizep; + i= 0; /* iov index */ + j= 0; /* prdt index */ + bad= 0; + offset= 0; /* Offset in current iov */ + +#if 0 + printf("setup_dma: proc_nr %d\n", proc_nr); +#endif + + while (size > 0) + { +#if 0 + printf( + "setup_dma: iov[%d]: addr 0x%x, size %d offset %d, size %d\n", + i, iov[i].iov_addr, iov[i].iov_size, offset, size); +#endif + + n= iov[i].iov_size-offset; + if (n > size) + n= size; + if (n == 0 || (n & 1)) + panic("at_wini", "bad size in iov", iov[i].iov_size); + r= sys_umap(proc_nr, D, iov[i].iov_addr+offset, n, &user_phys); + if (r != 0) + panic("at_wini", "can't map user buffer", r); + if (user_phys & 1) + { + /* Buffer is not aligned */ + printf("setup_dma: user buffer is not aligned\n"); + bad= 1; + break; + } + + /* vector is not allowed to cross a 64K boundary */ + if (user_phys/0x10000 != (user_phys+n-1)/0x10000) + n= ((user_phys/0x10000)+1)*0x10000 - user_phys; + + /* vector is not allowed to be bigger than 64K, but we get that + * for free. + */ + + if (j >= N_PRDTE) + { + /* Too many entries */ + bad= 1; + break; + } + + prdt[j].prdte_base= user_phys; + prdt[j].prdte_count= n; + prdt[j].prdte_reserved= 0; + prdt[j].prdte_flags= 0; + j++; + + offset += n; + if (offset >= iov[i].iov_size) + { + i++; + offset= 0; + } + + size -= n; + } + + if (!bad) + { + if (j <= 0 || j > N_PRDTE) + panic("at_wini", "bad prdt index", j); + prdt[j-1].prdte_flags |= PRDTE_FL_EOT; + +#if 0 + for (i= 0; i DMA_BUF_SIZE) + *sizep= size= DMA_BUF_SIZE; + + if (do_write) + { + /* Copy-in */ + for (offset= 0; offset < size; offset += n) + { + n= size-offset; + if (n > iov->iov_size) + n= iov->iov_size; + + r= sys_vircopy(proc_nr, D, iov->iov_addr, + SELF, D, (vir_bytes)dma_buf+offset, + n); + if (r != OK) + { + panic(w_name(), + "setup_dma: sys_vircopy failed", + r); + } + iov++; + } + } + + /* Fill-in the physical region descriptor table */ + phys= dma_buf_phys; + if (phys & 1) + { + /* Two byte alignment is required */ + panic("at_wini", "bad buffer alignment in setup_dma", + phys); + } + for (j= 0; jbase_dma + DMA_COMMAND, 0); + if (r != 0) panic("at_wini", "setup_dma: sys_outb failed", r); + + /* Verify that the bus master is not active */ + r= sys_inb(wn->base_dma + DMA_STATUS, &v); + if (r != 0) panic("at_wini", "setup_dma: sys_inb failed", r); + if (v & DMA_ST_BM_ACTIVE) + panic("at_wini", "Bus master IDE active", NO_NUM); + + if (prdt_phys & 3) + panic("at_wini", "prdt not aligned", prdt_phys); + r= sys_outl(wn->base_dma + DMA_PRDTP, prdt_phys); + if (r != 0) panic("at_wini", "setup_dma: sys_outl failed", r); + + /* Clear interrupt and error flags */ + r= sys_outb(wn->base_dma + DMA_STATUS, DMA_ST_INT | DMA_ST_ERROR); + if (r != 0) panic("at_wini", "setup_dma: sys_outb failed", r); + + /* Assume disk reads. Start DMA */ + v= DMA_CMD_START; + if (!do_write) + { + /* Disk reads generate PCI write cycles. */ + v |= DMA_CMD_WRITE; + } + r= sys_outb(wn->base_dma + DMA_COMMAND, v); + if (r != 0) panic("at_wini", "setup_dma: sys_outb failed", r); + +#if 0 + r= sys_inb(wn->base_dma + DMA_STATUS, &v); + if (r != 0) panic("at_wini", "setup_dma: sys_inb failed", r); + printf("dma status: 0x%x\n", v); +#endif +} + + /*===========================================================================* * w_need_reset * *===========================================================================*/ @@ -1373,6 +1908,35 @@ int value; /* required status */ return(0); } +/*===========================================================================* + * w_waitfor_dma * + *===========================================================================*/ +PRIVATE int w_waitfor_dma(mask, value) +int mask; /* status mask */ +int value; /* required status */ +{ +/* Wait until controller is in the required state. Return zero on timeout. + * An alarm that set a timeout flag is used. TIMEOUT is in micros, we need + * ticks. Disabling the alarm is not needed, because a static flag is used + * and a leftover timeout cannot do any harm. + */ + unsigned long w_status; + clock_t t0, t1; + int s; + + getuptime(&t0); + do { + if ((s=sys_inb(w_wn->base_dma + DMA_STATUS, &w_status)) != OK) + panic(w_name(),"Couldn't read register",s); + if ((w_status & mask) == value) { + return 1; + } + } while ((s=getuptime(&t1)) == OK && (t1-t0) < timeout_ticks ); + if (OK != s) printf("AT_WINI: warning, get_uptime failed: %d\n",s); + + return(0); +} + /*===========================================================================* * w_geometry * *===========================================================================*/