diff -urN linux-2.6.20-rc2-mm1/drivers/ide/ide.c linux.piodma/drivers/ide/ide.c --- linux-2.6.20-rc2-mm1/drivers/ide/ide.c 2007-01-18 13:02:51.000000000 +0000 +++ linux.piodma/drivers/ide/ide.c 2007-01-19 01:49:19.000000000 +0000 @@ -187,6 +187,17 @@ EXPORT_SYMBOL(noautodma); +#ifdef CONFIG_IDE_PIODMA +#ifdef CONFIG_IDE_PIODMA_FORCE +int piodma_init = 2; +#else +int piodma_init = 0; +#endif +#endif + +EXPORT_SYMBOL(piodma_init); + + /* * This is declared extern in ide.h, for access by other IDE modules: */ @@ -665,6 +676,9 @@ unregister_blkdev(hwif->major, hwif->name); spin_lock_irq(&ide_lock); +#ifdef CONFIG_IDE_PIODMA + if(hwif->piodmach >= 0) ide_piodma_free(hwif); +#endif if (hwif->dma_base) { (void) ide_release_dma(hwif); @@ -798,6 +812,10 @@ memcpy(&hwif->hw, hw, sizeof(*hw)); memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports)); hwif->irq = hw->irq; +#ifdef CONFIG_IDE_PIODMA + hwif->io_phys = hw->io_phys; + hwif->piodmach = -1; +#endif hwif->noprobe = 0; hwif->chipset = hw->chipset; hwif->gendev.parent = hw->dev; @@ -809,7 +827,6 @@ if (hwifp) *hwifp = hwif; - return (initializing || hwif->present) ? index : -1; } @@ -1640,7 +1657,7 @@ */ static const char *ide_words[] = { "noprobe", "serialize", "autotune", "noautotune", - "reset", "dma", "ata66", "minus8", "minus9", + "reset", "dma", "ata66", "piodma", "minus9", "minus10", "four", "qd65xx", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", NULL }; hw = s[3] - '0'; @@ -1715,8 +1732,14 @@ #endif /* CONFIG_BLK_DEV_4DRIVES */ case -10: /* minus10 */ case -9: /* minus9 */ - case -8: /* minus8 */ goto bad_option; + case -8: /* "piodma" */ +#ifdef CONFIG_IDE_PIODMA + piodma_init = 2; + goto done; +#else + goto bad_option; +#endif case -7: /* ata66 */ #ifdef CONFIG_BLK_DEV_IDEPCI hwif->udma_four = 1; diff -urN linux-2.6.20-rc2-mm1/drivers/ide/ide-disk.c linux.piodma/drivers/ide/ide-disk.c --- linux-2.6.20-rc2-mm1/drivers/ide/ide-disk.c 2006-11-29 21:57:37.000000000 +0000 +++ linux.piodma/drivers/ide/ide-disk.c 2007-01-18 02:37:01.000000000 +0000 @@ -281,6 +281,12 @@ command = lba48 ? WIN_READ_EXT : WIN_READ; } +#ifdef CONFIG_IDE_PIODMA + if(hwif->piodma && !drive->mult_count) { + dma_map_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_FROM_DEVICE); + ide_execute_command(drive, command, &task_in_intr_piodma, WAIT_CMD, NULL); + } else +#endif ide_execute_command(drive, command, &task_in_intr, WAIT_CMD, NULL); return ide_started; } else { @@ -295,6 +301,12 @@ /* FIXME: ->OUTBSYNC ? */ hwif->OUTB(command, IDE_COMMAND_REG); +#ifdef CONFIG_IDE_PIODMA + if(hwif->piodma > 1 && !drive->mult_count) { + dma_map_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_TO_DEVICE); + return pre_task_out_intr_piodma(drive, rq); + } else +#endif return pre_task_out_intr(drive, rq); } } diff -urN linux-2.6.20-rc2-mm1/drivers/ide/ide-probe.c linux.piodma/drivers/ide/ide-probe.c --- linux-2.6.20-rc2-mm1/drivers/ide/ide-probe.c 2007-01-18 13:02:51.000000000 +0000 +++ linux.piodma/drivers/ide/ide-probe.c 2007-01-19 01:49:27.000000000 +0000 @@ -887,6 +887,7 @@ return -1; } + if (hwif->present) { u16 unit = 0; int ret; @@ -1384,6 +1385,28 @@ done: init_gendisk(hwif); +#ifdef CONFIG_IDE_PIODMA + printk(KERN_INFO "%s: PIODMA support: ",hwif->name); + if (piodma_init) { + if(hwif->io_phys == 0) { + printk("not supported\n"); + return -ENOTSUPP; + } + hwif->piodma = piodma_init; + ide_piodma_allocate(hwif); + if(hwif->piodmach < 0) { + printk("busy?\n"); + return -EBUSY; + } + if(hwif->piodma == 1) + printk("using DMA for PIO reads\n"); + else + printk("using DMA for PIO reads and writes\n"); + } else { + printk("disabled\n"); + } +#endif + hwif->present = 1; /* success */ return 1; diff -urN linux-2.6.20-rc2-mm1/drivers/ide/ide-proc.c linux.piodma/drivers/ide/ide-proc.c --- linux-2.6.20-rc2-mm1/drivers/ide/ide-proc.c 2006-11-29 21:57:37.000000000 +0000 +++ linux.piodma/drivers/ide/ide-proc.c 2007-01-18 08:42:35.000000000 +0000 @@ -38,6 +38,10 @@ #include +#ifdef CONFIG_IDE_PIODMA +#include +#endif + static int proc_ide_read_imodel (char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -476,7 +480,98 @@ } } +#ifdef CONFIG_IDE_PIODMA +extern unsigned ide_piodma_burst_size; + +static int proc_ide_write_piodma_burst_size + (struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + char buf[4]; + unsigned val; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (count > 3) + count = 3; + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + buf[count] = '\0'; + val = simple_strtoul(buf, (char **)NULL, 10); + if((val < 1) || (val > 3)) return -EINVAL; + printk(KERN_DEBUG "Setting PIODMA burst size %d\n", val); + switch(val) { + case 1: ide_piodma_burst_size = DCMD_BURST8; break; + case 2: ide_piodma_burst_size = DCMD_BURST16; break; + case 3: ide_piodma_burst_size = DCMD_BURST32; break; + } + return count; +} +static int proc_ide_write_piodma + (struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + char buf[4]; + unsigned val; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (count > 3) + count = 3; + if (copy_from_user(buf, buffer, count)) + return -EFAULT; + buf[count] = '\0'; + val = simple_strtoul(buf, (char **)NULL, 10); + if((val > 2) || (val < 0)) return -EINVAL; + if((val == 1) || (val == 2)) { + if(!hwif->piodma) { + if(hwif->io_phys == 0) return -ENOTSUPP; + ide_piodma_allocate(hwif); + if(hwif->piodmach < 0) return -EBUSY; + } + printk(KERN_DEBUG "Setting PIODMA mode %d\n", val); + hwif->piodma = val; + } else { + printk(KERN_DEBUG "Turning PIODMA off\n"); + hwif->piodma = 0; + } + return count; +} + +static int proc_ide_read_piodma + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + + len = sprintf(page, "%d\n", hwif->piodma); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} +static int proc_ide_read_piodma_burst_size + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len; + int bs; + switch(ide_piodma_burst_size) { + case DCMD_BURST32: bs = 3; break; + case DCMD_BURST16: bs = 2; break; + case DCMD_BURST8: bs = 1; break; + default: bs = -1; + } + + len = sprintf(page, "%d\n", bs); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} +#endif + static ide_proc_entry_t hwif_entries[] = { +#ifdef CONFIG_IDE_PIODMA + { "piodma", S_IFREG|S_IRUGO|S_IWUSR, + proc_ide_read_piodma, + proc_ide_write_piodma }, + { "piodma_burst_size", S_IFREG|S_IRUGO|S_IWUSR, + proc_ide_read_piodma_burst_size, + proc_ide_write_piodma_burst_size }, +#endif { "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL }, { "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL }, { "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL }, diff -urN linux-2.6.20-rc2-mm1/drivers/ide/ide-taskfile.c linux.piodma/drivers/ide/ide-taskfile.c --- linux-2.6.20-rc2-mm1/drivers/ide/ide-taskfile.c 2006-11-29 21:57:37.000000000 +0000 +++ linux.piodma/drivers/ide/ide-taskfile.c 2007-01-18 17:02:22.000000000 +0000 @@ -51,6 +51,11 @@ #include #include +#ifdef CONFIG_IDE_PIODMA +#include +#include +#endif + static void ata_bswap_data (void *buffer, int wcount) { u16 *p = buffer; @@ -90,6 +95,11 @@ args.tfRegister[IDE_COMMAND_OFFSET] = WIN_PIDENTIFY; args.command_type = IDE_DRIVE_TASK_IN; args.data_phase = TASKFILE_IN; +#ifdef CONFIG_IDE_PIODMA + if (HWIF(drive)->piodma && !drive->mult_count) + args.handler = &task_in_intr_piodma; + else +#endif args.handler = &task_in_intr; return ide_raw_taskfile(drive, &args, buf); } @@ -382,6 +392,214 @@ ide_end_request(drive, 1, rq->hard_nr_sectors); } +#ifdef CONFIG_IDE_PIODMA +unsigned ide_piodma_burst_size = DCMD_BURST16; +EXPORT_SYMBOL(ide_piodma_burst_size); + +static void +ide_piodma_intr(int channel, void *data) +{ + unsigned long flags; + unsigned dmach; + unsigned dcsr; + ide_hwif_t *hwif; + ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)data; +// printk("ide_piodma_intr: recieved interrupt\n"); + + spin_lock_irqsave(&ide_lock, flags); + hwif = hwgroup->hwif; + dmach = hwif->piodmach; + if(!hwgroup->busy || hwif->nleft + || hwgroup->handler != task_in_intr_piodma) + printk("ide_piodma_intr: not expecting interrupt\n"); + dcsr = DCSR(dmach); + /* disable dma, clear interrupts */ + DCSR(dmach) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + if(!(dcsr & DCSR_ENDINTR)) { + hwif->piodma = 0; + spin_unlock_irqrestore(&ide_lock, flags); + printk(KERN_ERR "ide_piodma_intr: unexpected DCSR value 0x%x, disabling piodma\n", dcsr); + return; + } + spin_unlock_irqrestore(&ide_lock, flags); + ide_intr(hwif->irq, data); +} + +void ide_piodma_allocate(ide_hwif_t *hwif) +{ + hwif->piodmach = pxa_request_dma("ide piodma", DMA_PRIO_LOW, ide_piodma_intr, hwif->hwgroup); + if(hwif->piodmach < 0) return; +} + +void ide_piodma_free(ide_hwif_t *hwif) +{ + hwif->piodma = 0; + if(!hwif->io_phys || hwif->piodmach < 0) return; + pxa_free_dma(hwif->piodmach); + hwif->piodmach = -1; +} + +static int +ide_piodma_setup(ide_hwif_t *hwif) +{ + unsigned dmach = hwif->piodmach; + struct scatterlist *sg = hwif->sg_table; + unsigned int nleft = hwif->nleft; + + if(DCSR(dmach) & DCSR_RUN) { + printk("ide_piodma_setup: error: dma is not complete yet! DCSR=0x%x, DTADR=0x%x, DCMD=0x%x\n", + DCSR(dmach), DTADR(dmach), DCMD(dmach)); + DCSR(dmach) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + hwif->piodma = 0; + return -1; + } + + DCSR(dmach) = DCSR_NODESC; + DSADR(dmach) = hwif->io_phys; + DTADR(dmach) = sg_dma_address(&sg[hwif->cursg]) + hwif->cursg_ofs*SECTOR_SIZE; + if(nleft == 1) DCMD(dmach) = DCMD_ENDIRQEN | DCMD_INCTRGADDR | ide_piodma_burst_size | SECTOR_SIZE; + else DCMD(dmach) = DCMD_INCTRGADDR | ide_piodma_burst_size | SECTOR_SIZE; + + hwif->nleft = nleft-1; /* we don't support multisector transfers... */ + hwif->cursg_ofs++; + if ((hwif->cursg_ofs * SECTOR_SIZE) == sg[hwif->cursg].length) { + hwif->cursg++; + hwif->cursg_ofs = 0; + } + + DCSR(dmach) |= DCSR_RUN; + return 0; +} + +static int +ide_piodma_setup_out(ide_hwif_t *hwif) +{ + unsigned dmach = hwif->piodmach; + struct scatterlist *sg = hwif->sg_table; + unsigned int nleft = hwif->nleft; + + if(DCSR(dmach) & DCSR_RUN) { + printk("ide_piodma_setup_out: error: dma is not complete yet! DCSR=0x%x, DTADR=0x%x, DCMD=0x%x\n", + DCSR(dmach), DTADR(dmach), DCMD(dmach)); + DCSR(dmach) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + hwif->piodma = 0; + return -1; + } + + DCSR(dmach) = DCSR_NODESC; + DTADR(dmach) = hwif->io_phys; + DSADR(dmach) = sg_dma_address(&sg[hwif->cursg]) + hwif->cursg_ofs*SECTOR_SIZE; + DCMD(dmach) = DCMD_INCSRCADDR | ide_piodma_burst_size | SECTOR_SIZE; + + hwif->nleft = nleft-1; /* we don't support multisector transfers... */ + hwif->cursg_ofs++; + if ((hwif->cursg_ofs * SECTOR_SIZE) == sg[hwif->cursg].length) { + hwif->cursg++; + hwif->cursg_ofs = 0; + } + + DCSR(dmach) |= DCSR_RUN; + return 0; +} + +/* + * Handler for command with PIO DMA (Read/Read Burst). + */ +ide_startstop_t task_in_intr_piodma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = HWGROUP(drive)->rq; + u8 stat = hwif->INB(IDE_STATUS_REG); + + /* new way for dealing with premature shared PCI interrupts */ + if(hwif->nleft) { + if(!OK_STAT(stat, DATA_READY, BAD_R_STAT)) { + if (stat & (ERR_STAT | DRQ_STAT)) { + dma_unmap_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_FROM_DEVICE); + return task_error(drive, rq, __FUNCTION__, stat); + } + /* No data yet, so wait for another IRQ. */ + ide_set_handler(drive, &task_in_intr_piodma, WAIT_WORSTCASE, NULL); + return ide_started; + } + + ide_set_handler(drive, &task_in_intr_piodma, WAIT_WORSTCASE, NULL); + if(ide_piodma_setup(hwif)) { + dma_unmap_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_FROM_DEVICE); + return task_error(drive, rq, __FUNCTION__, stat); + } + return ide_started; + } else { + if(DCSR(hwif->piodmach) & DCSR_RUN) { + printk("task_in_intr_piodma: dma is still running\n"); + ide_set_handler(drive, &task_in_intr_piodma, WAIT_WORSTCASE, NULL); + return ide_started; + } + } + + dma_unmap_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_FROM_DEVICE); + + /* If it was the last datablock check status and finish transfer. */ + stat = wait_drive_not_busy(drive); + if (!OK_STAT(stat, 0, BAD_R_STAT)) + return task_error(drive, rq, __FUNCTION__, stat); + task_end_request(drive, rq, stat); + return ide_stopped; +} +EXPORT_SYMBOL(task_in_intr_piodma); + +static ide_startstop_t task_out_intr_piodma (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = HWGROUP(drive)->rq; + u8 stat = hwif->INB(IDE_STATUS_REG); + + if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) goto error; + + /* Deal with unexpected ATA data phase. */ + if (((stat & DRQ_STAT) == 0) ^ !hwif->nleft) goto error; + + + if (!hwif->nleft) { + dma_unmap_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_TO_DEVICE); + task_end_request(drive, rq, stat); + return ide_stopped; + } + + /* Still data left to transfer. */ + ide_piodma_setup_out(hwif); + ide_set_handler(drive, &task_out_intr_piodma, WAIT_WORSTCASE, NULL); + + return ide_started; + +error: + dma_unmap_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_TO_DEVICE); + return task_error(drive, rq, __FUNCTION__, stat); +} + +ide_startstop_t pre_task_out_intr_piodma (ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = drive->hwif; + ide_startstop_t startstop; + + if (ide_wait_stat(&startstop, drive, DATA_READY, + drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %sWRITE%s\n", + drive->name, + drive->hwif->data_phase ? "MULT" : "", + drive->addressing ? "_EXT" : ""); + dma_unmap_sg(hwif->gendev.parent, hwif->sg_table, hwif->sg_nents, DMA_TO_DEVICE); + return startstop; + } + + ide_set_handler(drive, &task_out_intr_piodma, WAIT_WORSTCASE, NULL); + ide_piodma_setup_out(drive->hwif); + + return ide_started; +} +EXPORT_SYMBOL(pre_task_out_intr_piodma); +#endif + /* * Handler for command with PIO data-in phase (Read/Read Multiple). */ @@ -602,7 +820,17 @@ } /* fall through */ case TASKFILE_OUT: +#ifdef CONFIG_IDE_PIODMA + if ((HWIF(drive)->piodma > 1) && !drive->mult_count) + args.prehandler = &pre_task_out_intr_piodma; + else +#endif args.prehandler = &pre_task_out_intr; +#ifdef CONFIG_IDE_PIODMA + if ((HWIF(drive)->piodma > 1) && !drive->mult_count) + args.handler = &task_out_intr_piodma; + else +#endif args.handler = &task_out_intr; err = ide_diag_taskfile(drive, &args, taskout, outbuf); break; @@ -617,6 +845,11 @@ } /* fall through */ case TASKFILE_IN: +#ifdef CONFIG_IDE_PIODMA + if (HWIF(drive)->piodma && !drive->mult_count) + args.handler = &task_in_intr_piodma; + else +#endif args.handler = &task_in_intr; err = ide_diag_taskfile(drive, &args, taskin, inbuf); break; diff -urN linux-2.6.20-rc2-mm1/drivers/ide/Kconfig linux.piodma/drivers/ide/Kconfig --- linux-2.6.20-rc2-mm1/drivers/ide/Kconfig 2007-01-18 13:10:36.000000000 +0000 +++ linux.piodma/drivers/ide/Kconfig 2007-01-18 03:08:39.000000000 +0000 @@ -160,6 +160,30 @@ If in doubt, say N. +config IDE_PIODMA + bool "PIODMA support" + depends on PXA27x + help + This enables PIO tranfers to be performed using DMA bursts. + Enabling this option reduces CPU overhead and improves + transfer speeds. + + By default PIODMA is disabled. To enable use idex=piodma + on the kernel command-line or echo 1 or 2 (2 for reads and writes) + to /proc/ide/idex/piodma, where x is the ide interface number. + Also by default burst size is 8 bytes, this can be increased to 16 + or 32 bytes. + + If in doubt, say N. + +config IDE_PIODMA_FORCE + bool "Use PIODMA by default" + depends on IDE_PIODMA + help + By default PIODMA is disabled. + + If in doubt, say N. + config BLK_DEV_IDECS tristate "PCMCIA IDE support" depends on PCMCIA diff -urN linux-2.6.20-rc2-mm1/drivers/ide/legacy/ide-cs.c linux.piodma/drivers/ide/legacy/ide-cs.c --- linux-2.6.20-rc2-mm1/drivers/ide/legacy/ide-cs.c 2007-01-18 13:02:51.000000000 +0000 +++ linux.piodma/drivers/ide/legacy/ide-cs.c 2007-01-19 00:43:33.000000000 +0000 @@ -54,6 +54,10 @@ #include #include +#ifdef CONFIG_IDE_PIODMA +#include <../drivers/pcmcia/soc_common.h> +#endif + /*====================================================================*/ /* Module parameters */ @@ -73,6 +77,7 @@ #define DEBUG(n, args...) #endif +#define IO_SPEED 0 // 0 = use default /*====================================================================*/ static const char ide_major[] = { @@ -85,11 +90,17 @@ int ndev; dev_node_t node; int hd; +#ifdef CONFIG_IDE_PIODMA + dma_addr_t io_phys; +#endif } ide_info_t; static void ide_release(struct pcmcia_device *); +#ifdef CONFIG_IDE_PIODMA +static int ide_config(struct pcmcia_device *, long io_virt_to_phys); +#else static int ide_config(struct pcmcia_device *); - +#endif static void ide_detach(struct pcmcia_device *p_dev); @@ -107,6 +118,11 @@ { ide_info_t *info; +#ifdef CONFIG_IDE_PIODMA + #define to_soc_pcmcia_socket(x) container_of(x, struct soc_pcmcia_socket, socket) + struct soc_pcmcia_socket *sock; +#endif + DEBUG(0, "ide_attach()\n"); /* Create new ide device */ @@ -125,7 +141,12 @@ link->conf.Attributes = CONF_ENABLE_IRQ; link->conf.IntType = INT_MEMORY_AND_IO; +#ifdef CONFIG_IDE_PIODMA + sock = to_soc_pcmcia_socket(link->socket); + return ide_config(link, sock->res_io.start-(long)sock->virt_io); +#else return ide_config(link); +#endif } /* ide_attach */ /*====================================================================== @@ -146,7 +167,18 @@ kfree(link->priv); } /* ide_detach */ -static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, struct pcmcia_device *handle) +static void idecs_mmio_fixup(ide_hwif_t *hwif) +{ + default_hwif_mmiops(hwif); + hwif->mmio = 2; + + ide_undecoded_slave(hwif); +} +static int idecs_register(unsigned long io, unsigned long ctl, unsigned long irq, +#ifdef CONFIG_IDE_PIODMA + unsigned long io_phys, +#endif + struct pcmcia_device *handle, int is_mmio) { hw_regs_t hw; memset(&hw, 0, sizeof(hw)); @@ -154,7 +186,21 @@ hw.irq = irq; hw.chipset = ide_pci; hw.dev = &handle->dev; - return ide_register_hw_with_fixup(&hw, NULL, ide_undecoded_slave); +#ifdef CONFIG_IDE_PIODMA + hw.io_phys = io_phys; +#endif + if(is_mmio) + return ide_register_hw_with_fixup(&hw, NULL, idecs_mmio_fixup); + else + return ide_register_hw_with_fixup(&hw, NULL, ide_undecoded_slave); +} + +void outb_io(unsigned char value, unsigned long port) { + outb(value, port); +} + +void outb_mem(unsigned char value, unsigned long port) { + writeb(value, (void __iomem *) port); } /*====================================================================== @@ -168,7 +214,11 @@ #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) +#ifdef CONFIG_IDE_PIODMA +static int ide_config(struct pcmcia_device *link, long io_virt_to_phys) +#else static int ide_config(struct pcmcia_device *link) +#endif { ide_info_t *info = link->priv; tuple_t tuple; @@ -180,10 +230,13 @@ } *stk = NULL; cistpl_cftable_entry_t *cfg; int i, pass, last_ret = 0, last_fn = 0, hd, is_kme = 0; - unsigned long io_base, ctl_base; + unsigned long io_base, ctl_base, is_mmio, try_slave; + void (*my_outb)(unsigned char, unsigned long); DEBUG(0, "ide_config(0x%p)\n", link); + printk(KERN_INFO "ide-cs: Initialising CFA device\n"); + stk = kzalloc(sizeof(*stk), GFP_KERNEL); if (!stk) goto err_mem; cfg = &stk->parse.cftable_entry; @@ -200,7 +253,7 @@ /* Not sure if this is right... look up the current Vcc */ CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link, &stk->conf)); - pass = io_base = ctl_base = 0; + pass = io_base = ctl_base = is_mmio = try_slave = 0; tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; tuple.Attributes = 0; CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); @@ -248,11 +301,45 @@ goto next_entry; io_base = link->io.BasePort1; ctl_base = link->io.BasePort1 + 0x0e; + + if (io->win[0].len >= 0x20) + try_slave = 1; + } else goto next_entry; /* If we've got this far, we're done */ break; } + + if ((cfg->mem.nwin > 0) || (stk->dflt.mem.nwin > 0)) { + win_req_t req; + memreq_t map; + cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &stk->dflt.mem; + + if (mem->win[0].len < 16) + goto next_entry; + + req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM; + req.Attributes |= WIN_ENABLE; + req.Base = mem->win[0].host_addr; + req.Size = 0; + + req.AccessSpeed = IO_SPEED; + if (pcmcia_request_window(&link, &req, &link->win) != 0) + goto next_entry; + map.Page = 0; map.CardOffset = mem->win[0].card_addr; + if (pcmcia_map_mem_page(link->win, &map) != 0) + goto next_entry; + + io_base = (unsigned long) ioremap(req.Base, req.Size); + ctl_base = io_base + 0x0e; + is_mmio = 1; + + if (mem->win[0].len >= 0x20) + try_slave = 1; + break; + } + next_entry: if (cfg->flags & CISTPL_CFTABLE_DEFAULT) memcpy(&stk->dflt, cfg, sizeof(stk->dflt)); @@ -267,22 +354,40 @@ CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); + + if(is_mmio) + my_outb = outb_mem; + else + my_outb = outb_io; /* disable drive interrupts during IDE probe */ - outb(0x02, ctl_base); + my_outb(0x02, ctl_base); /* special setup for KXLC005 card */ if (is_kme) - outb(0x81, ctl_base+1); + my_outb(0x81, ctl_base+1); /* retry registration in case device is still spinning up */ for (hd = -1, i = 0; i < 10; i++) { - hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link); + hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, +#ifdef CONFIG_IDE_PIODMA + io_base+io_virt_to_phys, link, is_mmio); +#else + link, is_mmio); +#endif if (hd >= 0) break; - if (link->io.NumPorts1 == 0x20) { - outb(0x02, ctl_base + 0x10); + + if (try_slave) { + my_outb(0x02, ctl_base + 0x10); + hd = idecs_register(io_base + 0x10, ctl_base + 0x10, - link->irq.AssignedIRQ, link); +#ifdef CONFIG_IDE_PIODMA + link->irq.AssignedIRQ, io_base+io_virt_to_phys, + link, is_mmio); +#else + link->irq.AssignedIRQ, link, is_mmio); +#endif + if (hd >= 0) { io_base += 0x10; ctl_base += 0x10; @@ -307,6 +412,9 @@ link->dev_node = &info->node; printk(KERN_INFO "ide-cs: %s: Vpp = %d.%d\n", info->node.dev_name, link->conf.Vpp / 10, link->conf.Vpp % 10); + if (is_mmio) + printk(KERN_INFO "ide-cs: %s: MMIO mode enabled\n", + info->node.dev_name); kfree(stk); return 0; --- linux-2.6.20-rc2-mm1/include/linux/ide.h 2007-01-18 13:04:01.000000000 +0000 +++ linux.piodma/include/linux/ide.h 2007-01-19 00:39:07.000000000 +0000 @@ -215,6 +215,9 @@ ide_ack_intr_t *ack_intr; /* acknowledge interrupt */ hwif_chipset_t chipset; struct device *dev; +#ifdef CONFIG_IDE_PIODMA + dma_addr_t io_phys; +#endif } hw_regs_t; /* @@ -804,6 +807,13 @@ void *hwif_data; /* extra hwif data */ unsigned dma; +#ifdef CONFIG_IDE_PIODMA + unsigned piodma; + int piodmach; + dma_addr_t io_phys; +#endif + void (*led_act)(void *data, int rw); + } ____cacheline_internodealigned_in_smp ide_hwif_t; /* @@ -1009,6 +1019,9 @@ extern ide_hwif_t ide_hwifs[]; /* master data repository */ #endif extern int noautodma; +#ifdef CONFIG_IDE_PIODMA +extern int piodma_init; +#endif extern int ide_end_request (ide_drive_t *drive, int uptodate, int nrsecs); int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq, @@ -1158,7 +1171,10 @@ extern ide_startstop_t task_no_data_intr(ide_drive_t *); extern ide_startstop_t task_in_intr(ide_drive_t *); extern ide_startstop_t pre_task_out_intr(ide_drive_t *, struct request *); - +#ifdef CONFIG_IDE_PIODMA +extern ide_startstop_t task_in_intr_piodma(ide_drive_t *); +extern ide_startstop_t pre_task_out_intr_piodma(ide_drive_t *, struct request *); +#endif extern int ide_raw_taskfile(ide_drive_t *, ide_task_t *, u8 *); int ide_taskfile_ioctl(ide_drive_t *, unsigned int, unsigned long); @@ -1380,4 +1396,9 @@ return dev ? pcibus_to_node(dev->bus) : -1; } +#ifdef CONFIG_IDE_PIODMA +extern void ide_piodma_free(ide_hwif_t *hwif); +extern void ide_piodma_allocate(ide_hwif_t *hwif); +#endif + #endif /* _IDE_H */