/* * $Id: s3c-ide.c,v 1.9 2008/06/11 01:00:33 scsuh Exp $ * * linux/drivers/ide/arm/s3c-ide.c * Copyright(C) Samsung Electronics 2006 * Seung-Chull, Suh * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../ide-timing.h" #include #include #include #include #include #include #include "s3c-ide.h" #define DRV_NAME "s3c-ide" #define DRV_VERSION "0.7" #define DRV_AUTHOR "Seung-Chull, Suh " #undef DBG_ATA #ifdef DBG_ATA #define DbgAta(x...) printk(x) #else #define DbgAta(x...) do {} while(0); #endif static s3c_ide_hwif_t s3c_ide_hwif; static void __iomem *ata_base; /*========================================================================= * ata controller register functions *========================================================================= */ static inline int ata_status_check (ide_drive_t * drive, u8 startend) { u8 stat; uint i; for (i=0; i<10000000; i++) { stat = HWIF(drive)->INB(IDE_STATUS_REG); if ((stat == 0x58) && (startend == 0)) // for starting return 0; else if ((stat == 0x50) && (startend == 1))// for ending return 0; } printk("got error in ata_status_check : %08x\n", stat); return -1; } static inline int bus_fifo_status_check (BUS_STATE status) { u32 temp; uint i; for (i=0; i<10000000; i++) { temp = readl(ata_base + S3C_BUS_FIFO_STATUS); /* wait for IDLE */ if ((temp == 0) && (status == IDLE)) { return 0; } /* wait for PAUSEW */ else if (((temp >> 16) == 0x5) && (status == PAUSEW)) { return 0; } /* wait for PAUSER2 and read/write pointer 0 */ else if ((temp == 0x60000) && (status == PAUSER2)) { return 0; } } return temp; } /* * return 0: OK * return -1: got error */ static inline int wait_for_host_ready (void) { u32 temp; uint i; for (i=0; i<10000000; i++) { temp = readl(ata_base + S3C_ATA_FIFO_STATUS); if ((temp >> 28) == 0) { return 0; } DbgAta("S3C_ATA_FIFO_STATUS: %08x\n", temp); } printk("got error in host ready: %08x\n", temp); return -1; } #ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA static u8 read_dev_reg (uint reg) { u8 temp; wait_for_host_ready(); temp = readb(ata_base + reg); wait_for_host_ready(); temp = readb(ata_base + S3C_ATA_PIO_RDATA); return temp; } #define STATUS_DEVICE_BUSY 0x80 static int wait_for_dev_ready (void) { u8 temp; uint i; for (i=0; i<1000000; i++) { temp = read_dev_reg((uint)S3C_ATA_PIO_CSD); // printf("temp2: %08x\n", temp); if ((temp & STATUS_DEVICE_BUSY) == 0) { DbgAta("temp2: %08x\n", temp); DbgAta("wait_for_dev_ready: %d\n", i); return 0; } } printk("wait_for_dev_ready: error\n"); return -1; } #endif #if defined(CONFIG_BLK_DEV_IDE_S3C_UDMA) || defined(CONFIG_BLK_DEV_IDE_S3C_PDMA) static void set_config_mode (ATA_MODE mode, int rw) { u32 reg = readl(ata_base + S3C_ATA_CFG) & ~(0x39c); /* set mode */ reg |= mode << 2; /* DMA write mode */ if (mode && rw) reg |= 0x10; /* set ATA DMA auto mode (enable multi block transfer) */ if (mode == UDMA) reg |= 0x380; writel(reg, ata_base + S3C_ATA_CFG); DbgAta("2:S3C_ATA_CFG = 0x%08x\n", readl(ata_base + S3C_ATA_CFG)); } #endif #if defined CONFIG_ARCH_S3C2443 || defined (CONFIG_CPU_S3C2450) static void init_buf_ctrl (void) { #if 0 /* for EVT0 board */ u32 reg = readl(S3C_GPBCON); reg &= 0xffffc0fc; reg |= (1<<12 | 1<<10 | 1<<8 | 1<<0); writel(reg, S3C_GPBCON); printk("GPBCON = 0x%08x\n", readl(S3C_GPBCON)); #else /* for EVT1 board */ u32 reg = readl(S3C2410_GPADAT); reg &= ~(1<<5); writel(reg, S3C2410_GPADAT); #endif } static void change_board_buf_mode (ATA_MODE mode, uint rw) { u32 reg = readl(S3C2410_GPBDAT) & 0xffffff8e; /* 0,4,5,6 */ if (mode == UDMA) { /* GPB0->high,GPB4->high => UDMA mode */ reg |= (1<<4)|(1); if (rw) reg |= (1<<5); } else { /* GPB0->high,GPB4->low => PIO mode */ reg |= (1<<6)|1; } writel(reg, S3C2410_GPBDAT); } #endif /* CONFIG_ARCH_S3C2443 */ static void set_ata_enable (uint on) { u32 temp; temp = readl(ata_base + S3C_ATA_CTRL); if (on) writel(temp | 0x1, ata_base + S3C_ATA_CTRL); else writel(temp & 0xfffffffe, ata_base + S3C_ATA_CTRL); } static void set_endian_mode (uint little) { u32 reg = readl(ata_base + S3C_ATA_CFG); /* set Little endian */ if (little) reg &= (~0x40); else reg |= 0x40; writel(reg, ata_base + S3C_ATA_CFG); } static inline void set_trans_command (ATA_TRANSFER_CMD cmd) { wait_for_host_ready(); writel(cmd, ata_base + S3C_ATA_CMD); } /* * this filter only allows PIO and UDMA. * swdma and mwdma is not supported now. by scsuh */ static u8 s3c_ide_ratefilter (ide_drive_t *drive, u8 speed) { if (drive->media != ide_disk) return min(speed, (u8)XFER_PIO_4); switch(speed) { case XFER_UDMA_6: case XFER_UDMA_5: speed = XFER_UDMA_4;// S3C6400 & S3C6410 can support upto UDMA4. break; case XFER_UDMA_4: case XFER_UDMA_3: case XFER_UDMA_2: case XFER_UDMA_1: case XFER_UDMA_0: break; default: speed = min(speed, (u8)XFER_PIO_4); break; } return speed; } static int s3c_ide_tune_chipset (ide_drive_t * drive, u8 xferspeed) { ide_hwif_t *hwif = drive->hwif; s3c_ide_hwif_t *s3c_hwif = (s3c_ide_hwif_t *) hwif->hwif_data; u8 speed = s3c_ide_ratefilter(drive, xferspeed); if (XFER_PIO_0 <= speed && speed <= XFER_PIO_4) { ulong ata_cfg = readl(ata_base + S3C_ATA_CFG); switch (speed) { case XFER_PIO_0: case XFER_PIO_1: case XFER_PIO_2: ata_cfg &= (~0x2); //IORDY disable break; case XFER_PIO_3: case XFER_PIO_4: ata_cfg |= 0x2; //IORDY enable break; } writel(ata_cfg, ata_base + S3C_ATA_CFG); writel(s3c_hwif->piotime[speed-XFER_PIO_0], ata_base + S3C_ATA_PIO_TIME); } else { writel(s3c_hwif->piotime[0], ata_base + S3C_ATA_PIO_TIME); writel(s3c_hwif->udmatime[speed-XFER_UDMA_0], ata_base + S3C_ATA_UDMA_TIME); } return ide_config_drive_speed(drive, speed); } void read_status (uint index) { u32 reg; reg = readl(ata_base + S3C_ATA_IRQ); // printk("%d: S3C_ATA_IRQ: %08x\n", index, reg); writel(reg, ata_base + S3C_ATA_IRQ); } int s3c_ide_ack_intr (ide_hwif_t *hwif) { #ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA s3c_ide_hwif_t *s3c_hwif = (s3c_ide_hwif_t *) hwif->hwif_data; #endif u32 reg; reg = readl(ata_base + S3C_ATA_IRQ); DbgAta("S3C_ATA_IRQ: %08x\n", reg); #ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA s3c_hwif->irq_sta = reg; #endif return 1; } static void s3c_ide_tune_drive (ide_drive_t * drive, u8 pio) { pio = ide_get_best_pio_mode(drive, 255, pio, NULL); (void) s3c_ide_tune_chipset(drive, (XFER_PIO_0 + pio)); } /* * XXX: It is board-specific to support UDMA in smdk24xx. * not supported: SMDK241X, SMDK2443 * by scsuh */ #ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA static void s3c_ide_dma_host_on (ide_drive_t * drive) { DbgAta("##### %s\n", __FUNCTION__); } static int s3c_ide_dma_on (ide_drive_t * drive) { DbgAta("##### %s\n", __FUNCTION__); drive->using_dma = 1; s3c_ide_dma_host_on(drive); return 0; } static int s3c_ide_dma_check (ide_drive_t * drive) { u8 speed; DbgAta("##### %s\n", __FUNCTION__); /* Is the drive in our DMA black list? */ if (s3c_ide_hwif.black_list) { drive->using_dma = 0; /* Borrowed the warning message from ide-dma.c */ printk(KERN_WARNING "%s: Disabling DMA for %s (blacklisted)\n", drive->name, drive->id->model); } else drive->using_dma = 1; speed = ide_find_best_mode(drive, 0x47); DbgAta("speed: %08x\n", speed); if (drive->autodma && (speed & XFER_MODE) == XFER_UDMA) { s3c_ide_tune_chipset(drive, speed); return HWIF(drive)->ide_dma_on(drive); } HWIF(drive)->dma_off_quietly(drive); return 0; } #if 0 static ide_startstop_t s3c_ide_dma_intr (ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); s3c_ide_hwif_t *s3c_hwif = (s3c_ide_hwif_t *) hwif->hwif_data; ulong reg = s3c_hwif->irq_sta; ide_hwgroup_t *hwgroup = hwif->hwgroup; DbgAta("reg = %08lx\n", reg); DbgAta("S3C_ATA_XFR_CNT: %08x\n", readl(S3C_ATA_XFR_CNT)); DbgAta("index:%d\n", s3c_hwif->index); if (reg & 0x10) { DbgAta("write: %08lx %08lx\n", s3c_hwif->table[s3c_hwif->index].addr, s3c_hwif->table[s3c_hwif->index].len); writel(s3c_hwif->table[s3c_hwif->index].len-0x20, S3C_ATA_SBUF_SIZE); writel(s3c_hwif->table[s3c_hwif->index].addr, S3C_ATA_SBUF_START); } else if (reg & 0x08) { DbgAta("read: %08lx %08lx\n", s3c_hwif->table[s3c_hwif->index].addr, s3c_hwif->table[s3c_hwif->index].len); writel(s3c_hwif->table[s3c_hwif->index].len-0x20, S3C_ATA_TBUF_SIZE); writel(s3c_hwif->table[s3c_hwif->index].addr, S3C_ATA_TBUF_START); } s3c_hwif->index++; set_trans_command(ATA_CMD_CONTINUE); hwgroup->handler = s3c_ide_dma_intr; return 1; } #endif static int s3c_ide_build_sglist (ide_drive_t * drive, struct request *rq) { ide_hwif_t *hwif = drive->hwif; s3c_ide_hwif_t *s3c_hwif = (s3c_ide_hwif_t *) hwif->hwif_data; struct scatterlist *sg = hwif->sg_table; DbgAta("##### %s\n", __FUNCTION__); ide_map_sg(drive, rq); if (rq_data_dir(rq) == READ) hwif->sg_dma_direction = DMA_FROM_DEVICE; else hwif->sg_dma_direction = DMA_TO_DEVICE; return dma_map_sg(&s3c_hwif->dev->dev, sg, hwif->sg_nents, hwif->sg_dma_direction); } static int s3c_ide_build_dmatable (ide_drive_t * drive) { int i, size, count = 0; ide_hwif_t *hwif = HWIF(drive); struct request *rq = HWGROUP(drive)->rq; s3c_ide_hwif_t *s3c_hwif = (s3c_ide_hwif_t *) hwif->hwif_data; struct scatterlist *sg; u32 addr_reg, size_reg; DbgAta("##### %s\n", __FUNCTION__); if (rq_data_dir(rq) == WRITE) { addr_reg = S3C_ATA_SBUF_START; size_reg = S3C_ATA_SBUF_SIZE; } else { addr_reg = S3C_ATA_TBUF_START; size_reg = S3C_ATA_TBUF_SIZE; } /* Save for interrupt context */ s3c_hwif->drive = drive; /* Build sglist */ hwif->sg_nents = size = s3c_ide_build_sglist(drive, rq); DbgAta("##### hwif->sg_nents %d\n", hwif->sg_nents); if (size > 1) { s3c_hwif->pseudo_dma = 1; DbgAta("sg_nents is more than 1: %d\n", hwif->sg_nents); } if (!size) return 0; /* fill the descriptors */ sg = hwif->sg_table; for (i=0, sg = hwif->sg_table; itable[i].addr = sg_dma_address(sg); s3c_hwif->table[i].len = sg_dma_len(sg); count += s3c_hwif->table[i].len; DbgAta("data: %08lx %08lx\n", s3c_hwif->table[i].addr, s3c_hwif->table[i].len); } s3c_hwif->table[i].addr = 0; s3c_hwif->table[i].len = 0; s3c_hwif->queue_size = i; DbgAta("total size: %08x, %d\n", count, s3c_hwif->queue_size); DbgAta("rw: %08lx %08lx\n", s3c_hwif->table[0].addr, s3c_hwif->table[0].len); writel(s3c_hwif->table[0].len-0x1, ata_base + size_reg); writel(s3c_hwif->table[0].addr, ata_base + addr_reg); s3c_hwif->index = 1; writel(count, ata_base + S3C_ATA_XFR_NUM); return 1; } static int s3c_ide_dma_setup (ide_drive_t * drive) { struct request *rq = HWGROUP(drive)->rq; if (!s3c_ide_build_dmatable(drive)) { ide_map_sg(drive, rq); DbgAta("return 1\n"); return 1; } drive->waiting_for_dma = 1; return 0; } static void s3c_ide_dma_exec_cmd (ide_drive_t * drive, u8 command) { wait_for_host_ready(); /* issue cmd to drive */ ide_execute_command(drive, command, &ide_dma_intr, (WAIT_CMD), NULL); } static void s3c_ide_dma_start (ide_drive_t * drive) { struct request *rq = HWGROUP(drive)->rq; uint rw = (rq_data_dir(rq) == WRITE); #if defined CONFIG_ARCH_S3C2443 || defined (CONFIG_CPU_S3C2450) /* apply at specific set using buffer control */ change_board_buf_mode(UDMA, rw); #endif ata_status_check(drive, 0); writel(0x00000003, ata_base + S3C_ATA_IRQ_MSK); wait_for_host_ready(); /* must be added to wait for Device ready. by scsuh */ wait_for_dev_ready(); bus_fifo_status_check(IDLE); set_config_mode(UDMA, rw); set_trans_command(ATA_CMD_START); return; } static int s3c_ide_dma_end (ide_drive_t * drive) { u32 stat = 0; ide_hwif_t *hwif = HWIF(drive); s3c_ide_hwif_t *s3c_hwif = (s3c_ide_hwif_t *) hwif->hwif_data; DbgAta("##### %s\n", __FUNCTION__); DbgAta("left: %08x, %08x\n", readl(ata_base + S3C_ATA_XFR_CNT), readl(ata_base + S3C_ATA_XFR_NUM)); #if defined CONFIG_ARCH_S3C2443 || defined (CONFIG_CPU_S3C2450) /* apply at specific set using buffer control */ change_board_buf_mode(PIO_CPU, 0); #endif wait_for_host_ready(); stat = readl(ata_base + S3C_BUS_FIFO_STATUS); if ( (stat >> 16) == 0x5 ) { /* in case of PAUSEW. */ writel(ATA_CMD_CONTINUE, ata_base + S3C_ATA_CMD); } bus_fifo_status_check(IDLE); writel(0x00000003, ata_base + S3C_ATA_IRQ_MSK); set_config_mode(PIO_CPU, 0); ata_status_check(drive, 1); drive->waiting_for_dma = 0; if (hwif->sg_nents) { dma_unmap_sg(&s3c_hwif->dev->dev, hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); hwif->sg_nents = 0; } return 0; } static int s3c_ide_dma_timeout (ide_drive_t * drive) { printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); if (HWIF(drive)->ide_dma_test_irq(drive)) return 0; return HWIF(drive)->ide_dma_end(drive); } /* XXX: we must implement correct function. by scsuh. */ static int s3c_ide_dma_test_irq (ide_drive_t * drive) { return 1; } static void s3c_ide_dma_host_off (ide_drive_t * drive) { u32 reg; DbgAta("##### %s\n", __FUNCTION__); #if defined CONFIG_ARCH_S3C2443 || defined (CONFIG_CPU_S3C2450) /* apply at specific set using buffer control */ change_board_buf_mode(PIO_CPU, 0); #endif set_config_mode(PIO_CPU, 0); mdelay(10); /* disable ATA DMA auto mode */ reg = readl(ata_base + S3C_ATA_CFG); reg &= (~0x200); writel(reg, ata_base + S3C_ATA_CFG); } static void s3c_ide_dma_off_quietly (ide_drive_t * drive) { drive->using_dma = 0; s3c_ide_dma_host_off(drive); } static int s3c_ide_dma_lostirq (ide_drive_t * drive) { printk("%08x, %08x\n", readl(ata_base + S3C_ATA_IRQ), readl(ata_base + S3C_ATA_IRQ_MSK)); printk("left: %08x, %08x\n", readl(ata_base + S3C_ATA_XFR_CNT), readl(ata_base + S3C_ATA_XFR_NUM)); printk(KERN_ERR "%s: IRQ lost\n", drive->name); return 0; } #endif static int s3c_ide_irq_hook (void * data) { #ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA u32 stat = 0; ide_hwif_t *hwif = (ide_hwif_t *)data; s3c_ide_hwif_t *s3c_hwif = (s3c_ide_hwif_t *) hwif->hwif_data; #endif u32 reg = readl(ata_base + S3C_ATA_IRQ); writel(reg, ata_base + S3C_ATA_IRQ); DbgAta("##### %s, %08x\n", __FUNCTION__, reg); #ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA if (s3c_hwif->pseudo_dma) { uint i; i = s3c_hwif->index; if (reg & 0x10) { DbgAta("write: %08lx %08lx\n", s3c_hwif->table[i].addr, s3c_hwif->table[i].len); bus_fifo_status_check(PAUSER2); writel(s3c_hwif->table[i].len-0x1, ata_base + S3C_ATA_SBUF_SIZE); writel(s3c_hwif->table[i].addr, ata_base + S3C_ATA_SBUF_START); } else if (reg & 0x08) { DbgAta("read: %08lx %08lx\n", s3c_hwif->table[i].addr, s3c_hwif->table[i].len); stat = bus_fifo_status_check(PAUSEW); if (stat == 0x60000) { writel(s3c_hwif->table[i].len-0x1, ata_base + S3C_ATA_SBUF_SIZE); writel(s3c_hwif->table[i].addr, ata_base + S3C_ATA_SBUF_START); } writel(s3c_hwif->table[i].len-0x1, ata_base + S3C_ATA_TBUF_SIZE); writel(s3c_hwif->table[i].addr, ata_base + S3C_ATA_TBUF_START); } else { DbgAta("unexpected interrupt : 0x%x\n", reg); return 1; } s3c_hwif->index++; if (s3c_hwif->queue_size == s3c_hwif->index) { s3c_hwif->pseudo_dma = 0; DbgAta("UDMA close : s3c_hwif->queue_size == s3c_hwif->index\n"); } writel(ATA_CMD_CONTINUE, ata_base + S3C_ATA_CMD); return 1; } stat = readl(ata_base + S3C_BUS_FIFO_STATUS); if ( (stat >> 16) == 0x6 ) { /* in case of PAUSER2. */ writel(ATA_CMD_CONTINUE, ata_base + S3C_ATA_CMD); return 1; } else if ( (stat >> 16) == 0x5 ) { /* in case of PAUSEW. */ writel(ATA_CMD_CONTINUE, ata_base + S3C_ATA_CMD); } #endif return 0; } static void s3c_ide_setup_init_value (s3c_ide_hwif_t * s3c_hwif) { ulong t1, t2, teoc; ulong uTdvh1, uTdvs, uTrp, uTss, uTackenv; uint pio_t1[5] = {70, 50, 30, 30, 30}; uint pio_t2[5] = {290, 290, 290, 80, 70}; uint pio_teoc[5] = {20, 20, 10, 10, 10}; #if 0 uint uUdmaTdvh[5] = {10, 10, 10, 10, 10}; uint uUdmaTdvs[5] = {100, 60, 50, 35, 20}; uint uUdmaTrp[5] = {160, 125, 100, 100, 100}; uint uUdmaTss[5] = {50, 50, 50, 50, 50}; uint uUdmaTackenvMin[5] = {20, 20, 20, 20, 20}; #else u32 uUdmaTdvh[5] = { 7, 7, 7, 7, 7 }; u32 uUdmaTdvs[5] = { 70, 48, 31, 20, 7 }; u32 uUdmaTrp[5] = { 160, 125, 100, 100, 100 }; u32 uUdmaTss[5] = { 50, 50, 50, 50, 50 }; u32 uUdmaTackenvMin[5] = { 20, 20, 20, 20, 20 }; #endif ulong cycle_time = (uint) (1000000000 / clk_get_rate(s3c_hwif->clk)); uint i; for (i = 0; i < 5; i++) { t1 = (pio_t1[i] / cycle_time) & 0xff; t2 = (pio_t2[i] / cycle_time) & 0xff; teoc = (pio_teoc[i] / cycle_time) & 0x0f; s3c_hwif->piotime[i] = (teoc << 12) | (t2 << 4) | t1; DbgAta("PIO%dTIME = %08lx\n", i, s3c_hwif->piotime[i]); } for (i = 0; i < 5; i++) { uTdvh1 = (uUdmaTdvh[i] / cycle_time) & 0x0f; uTdvs = (uUdmaTdvs[i] / cycle_time) & 0xff; uTrp = (uUdmaTrp[i] / cycle_time) & 0xff; uTss = (uUdmaTss[i] / cycle_time) & 0x0f; uTackenv = (uUdmaTackenvMin[i] / cycle_time) & 0x0f; s3c_hwif->udmatime[i] = (uTdvh1 << 24) | (uTdvs << 16) | (uTrp << 8) | (uTss << 4) | uTackenv; DbgAta("UDMA%dTIME = %08lx\n", i, s3c_hwif->udmatime[i]); } } static void s3c_ide_setup_ports (hw_regs_t * hw, s3c_ide_hwif_t * s3c_hwif) { int i; unsigned long *ata_regs = hw->io_ports; for (i = 0; i < IDE_IRQ_OFFSET; i++) { *ata_regs++ = (u32)ata_base + 0x1954 + (i << 2); } } /*========================================================================= * ata controller register I/O fuctions *========================================================================= */ static void s3c_ide_OUTB (u8 addr, ulong reg) { wait_for_host_ready(); writeb(addr, reg); } static void s3c_ide_OUTBSYNC (ide_drive_t *drive, u8 addr, ulong port) { wait_for_host_ready(); writeb(addr, port); } static void s3c_ide_OUTW (u16 addr, ulong reg) { wait_for_host_ready(); writew(addr, reg); } static u8 s3c_ide_INB (ulong reg) { u8 temp; wait_for_host_ready(); temp = readb(reg); wait_for_host_ready(); temp = readb(ata_base + S3C_ATA_PIO_RDATA); return temp; } static u16 s3c_ide_INW (ulong reg) { u16 temp; wait_for_host_ready(); temp = readw(reg); wait_for_host_ready(); temp = readw(ata_base + S3C_ATA_PIO_RDATA); return temp; } #ifdef CONFIG_BLK_DEV_IDE_S3C_PDMA /* * check transfer status and return result */ static int check_dma_done (void) { u32 reg; uint i; for (i=0; i<0x10000000; i++) { reg = readl(ata_base + S3C_ATA_IRQ); if (reg & 0x01) break; } if (i > (0x10000000-5)) { printk("transmission timeout: %08x\n", reg); return -1; } reg = readl(ata_base + S3C_ATA_STATUS); if (reg & 3) { printk("\n\ntransmission failed: %08x\n", reg); printk("ata_irq: %08x\n", readl(ata_base + S3C_ATA_IRQ)); printk("ata_cfg: %08x\n", readl(ata_base + S3C_ATA_CFG)); return -1; } return 0; } static void s3c_ide_OUTSW (ulong port, void *addr, u32 count) { dma_addr_t dma_addr; /* half-word count. we must double it. by scsuh */ count = count << 1; DbgAta("@@@@@write: %p, %08x\n", addr, count); writel(0x000000ff, ata_base + S3C_ATA_IRQ_MSK); dma_addr = dma_map_single(NULL, addr, count, DMA_TO_DEVICE); writel(dma_addr, ata_base + S3C_ATA_SBUF_START); writel(count-1, ata_base + S3C_ATA_SBUF_SIZE); writel(count, ata_base + S3C_ATA_XFR_NUM); set_config_mode(PIO_DMA, 1); set_trans_command(ATA_CMD_START); check_dma_done(); set_config_mode(PIO_CPU, 0); dma_unmap_single(NULL, dma_addr, count, DMA_TO_DEVICE); writel(0x0000001b, ata_base + S3C_ATA_IRQ); writel(0x0000001b, ata_base + S3C_ATA_IRQ_MSK); } static void s3c_ide_INSW (ulong port, void *addr, u32 count) { dma_addr_t dma_addr; /* half-word count. we must double it. by scsuh */ count = count << 1; DbgAta("@@@@@read: %p, %08x\n", addr, count); writel(0x000000ff, ata_base + S3C_ATA_IRQ_MSK); dma_addr = dma_map_single(NULL, addr, count, DMA_FROM_DEVICE); writel(dma_addr, ata_base + S3C_ATA_TBUF_START); writel(count-1, ata_base + S3C_ATA_TBUF_SIZE); writel(count, ata_base + S3C_ATA_XFR_NUM); set_config_mode(PIO_DMA, 0); set_trans_command(ATA_CMD_START); check_dma_done(); set_config_mode(PIO_CPU, 0); dma_unmap_single(NULL, dma_addr, count, DMA_FROM_DEVICE); writel(0x0000001b, ata_base + S3C_ATA_IRQ); writel(0x0000001b, ata_base + S3C_ATA_IRQ_MSK); } #else static void s3c_ide_OUTSW(ulong port, void *addr, u32 count) { uint i; volatile u16 *temp_addr = (u16*)addr; for (i=0; idev = pdev; s3c_hwif->irq = platform_get_irq(pdev, 0); // s3c_hwif->regbase = (u32)S3C24XX_VA_CFATA; ata_base = ioremap(S3C_PA_CFATA, S3C_SZ_CFATA); s3c_hwif->clk = clk_get(&pdev->dev, "cfata"); if (IS_ERR(s3c_hwif->clk)) { printk("failed to find clock source.\n"); ret = PTR_ERR(s3c_hwif->clk); s3c_hwif->clk = NULL; goto out; } if ((ret = clk_enable(s3c_hwif->clk))) { printk("failed to enable clock source.\n"); goto out; } s3c_ide_setup_init_value(s3c_hwif); /* XXX: Because this code is 2443 specific * it will be moved to new functions * by scsuh. */ #if defined CONFIG_ARCH_S3C2443 || defined (CONFIG_CPU_S3C2450) { ulong *temp_adr; mdelay(1); // TDelay 1 unit = 10us // GPIO, EBI setting temp_adr = S3C24XX_VA_EBI+8; reg = readl(temp_adr); writel(reg | (1 << 10) | (1 << 9), temp_adr); reg = readl(S3C2410_GPGCON); writel(reg | (3 << 30) | (3 << 28) | (3 << 26) | (3 << 24) | (3 << 22), S3C2410_GPGCON); reg = readl(S3C2410_GPACON); writel(reg | (1 << 27) | (1 << 11) | (1 << 14) | (1 << 13), S3C2410_GPACON); reg = readl(S3C2410_MISCCR); writel(reg & (~(1 << 30)), S3C2410_MISCCR); init_buf_ctrl(); change_board_buf_mode(PIO_CPU, 0); /* CF controller - True IDE mode setting */ change_mode_to_ata(); /* Card configuration */ writel(0x1C238, ata_base + S3C_ATA_PIO_TIME); writel(0x20B1362, ata_base + S3C_ATA_UDMA_TIME); set_endian_mode(1); set_ata_enable(1); mdelay(400); // wait for 200ms, be positively necessary } #elif defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) { mdelay(1); // TDelay 1 unit = 10us reg = readl(S3C_MEM_SYS_CFG) & (~0x0000003f); writel(reg | 0x00000030, S3C_MEM_SYS_CFG); #ifdef CONFIG_BLK_DEV_IDE_S3C_DIRECT { /* direct mode */ u32 temp; printk(" * CF EBI MODE : Direct Mode\n\n"); reg = readl(S3C_MEM_SYS_CFG); writel(reg | 0x00004000, S3C_MEM_SYS_CFG); temp = readl(S3C_GPACON); // D: Set XhiDATA[15:0] pins as CF Data[15:0] writel(0x55555555, S3C_GPK0CON); writel(0x55555555, S3C_GPK1CON); // A: Set XhiADDR[2:0] pins as CF ADDR[2:0] reg = readl(S3C_GPL0CON) & (~0x00000fff); writel(reg | 0x00000666, S3C_GPL0CON); // C: Set Xhi control pins as CF control pins(IORDY, IOWR, IORD, CE[1], CE[0]) writel(0x00066666, S3C_GPMCON); /* This line must be here because Uart ports(GPIO_A) * are changed after setting GPIO_K. */ writel(temp, S3C_GPACON); } #else { /* indirect mode */ printk(" * CF EBI MODE : InDirect Mode\n\n"); reg = readl(S3C_MEM_SYS_CFG) & (~0x00004000); writel(reg, S3C_MEM_SYS_CFG); // C: Set Xhi control pins as CF control pins(IORDY, IOWR, IORD, CE[1], CE[0]) writel(0x00066666, S3C_GPMCON); } #endif change_mode_to_ata(); writel(0x1C238, ata_base + S3C_ATA_PIO_TIME); /* 0x1C238 */ writel(0x20B1362, ata_base + S3C_ATA_UDMA_TIME); /* 0x20B1362 */ set_endian_mode(1); set_ata_enable(1); mdelay(400); // wait for 200ms, be positively necessary #if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410) reg = readl(S3C_GPNCON) & ~(0x3<<(2*8)); writel(reg | (1<<(2*8)), S3C_GPNCON); reg = readl(S3C_GPNDAT); writel(reg | (1<<8), S3C_GPNDAT); mdelay(500); writel(reg & ~(1<<8), S3C_GPNDAT); #endif } #endif /* remove IRQ Status */ writel(0x1f, ata_base + S3C_ATA_IRQ); writel(0x1f, ata_base + S3C_ATA_IRQ); writel(0x1b, ata_base + S3C_ATA_IRQ_MSK); hwif = &ide_hwifs[pdev->id]; hw = &hwif->hw; hwif->irq = hw->irq = s3c_hwif->irq; hwif->chipset = ide_s3c; s3c_ide_setup_ports(hw, s3c_hwif); memcpy(hwif->io_ports, hw->io_ports, sizeof(hwif->io_ports)); hwif->ultra_mask = 0x0; /* Disable Ultra DMA */ hwif->mwdma_mask = 0x0; hwif->swdma_mask = 0x0; hwif->noprobe = 0; hwif->drives[0].unmask = 1; hwif->drives[1].unmask = 1; /* hold should be on in all cases */ hwif->hold = 1; hwif->mmio = 1; hwif->OUTB = &s3c_ide_OUTB; hwif->OUTBSYNC = &s3c_ide_OUTBSYNC; hwif->OUTW = &s3c_ide_OUTW; hwif->OUTSW = &s3c_ide_OUTSW; hwif->INB = &s3c_ide_INB; hwif->INW = &s3c_ide_INW; hwif->INSW = &s3c_ide_INSW; hwif->tuneproc = &s3c_ide_tune_drive; hwif->speedproc = &s3c_ide_tune_chipset; #ifdef CONFIG_IDE_HOOK_IRQ hwif->ide_irq_hook = &s3c_ide_irq_hook; #endif #if defined (CONFIG_BLK_DEV_IDE_S3C_UDMA) hwif->ultra_mask = 0x1f; /* enable Ultra DMA 0 ~ 4 */ hwif->dma_off_quietly = &s3c_ide_dma_off_quietly; hwif->ide_dma_timeout = &s3c_ide_dma_timeout; hwif->ide_dma_check = &s3c_ide_dma_check; hwif->dma_exec_cmd = &s3c_ide_dma_exec_cmd; hwif->dma_start = &s3c_ide_dma_start; hwif->ide_dma_end = &s3c_ide_dma_end; hwif->dma_setup = &s3c_ide_dma_setup; hwif->ide_dma_test_irq = &s3c_ide_dma_test_irq; hwif->dma_host_off = &s3c_ide_dma_host_off; hwif->dma_host_on = &s3c_ide_dma_host_on; hwif->ide_dma_lostirq = &s3c_ide_dma_lostirq; hwif->ide_dma_on = &s3c_ide_dma_on; hwif->autodma = 1; hwif->atapi_dma = 1; hwif->no_lba48 = 1; /* 1 = cannot do LBA48 */ hwif->no_lba48_dma = 1; /* 1 = cannot do LBA48 DMA */ #else hwif->autodma = 0; hwif->channel = 0; hwif->hold = 1; hwif->select_data = 0; /* no chipset-specific code */ hwif->config_data = 0; /* no chipset-specific code */ hwif->autodma = 0; #endif hwif->drives[0].autodma = hwif->autodma; hwif->drives[0].autotune = 1; /* 1=autotune, 2=noautotune, 0=default */ hwif->drives[0].no_io_32bit = 1; s3c_ide_hwif.hwif = hwif; hwif->hwif_data = &s3c_ide_hwif; probe_hwif_init(hwif); platform_set_drvdata(pdev, hwif); printk(KERN_INFO "S3C24XX IDE(builtin) configured for %s\n", mode); out: return ret; } static int s3c_ide_remove(struct platform_device *dev) { struct resource *res; ide_hwif_t *hwif = platform_get_drvdata(dev); ide_unregister(hwif - ide_hwifs); iounmap(ata_base); res = platform_get_resource(dev, IORESOURCE_MEM, 0); release_mem_region(res->start, res->end - res->start); return 0; } static struct platform_driver s3c_ide_driver = { .probe = s3c_ide_probe, .remove = s3c_ide_remove, .driver = { .name = "s3c-ide", }, }; static int __init s3c_ide_init(void) { return platform_driver_register(&s3c_ide_driver); } static void __exit s3c_ide_exit(void) { platform_driver_unregister(&s3c_ide_driver); } module_init(s3c_ide_init); module_exit(s3c_ide_exit); static int s3c_ata_proc_read ( char *buffer, char **buffer_location, off_t offset, int buffer_length, int *zero, void *ptr ) { #ifdef CONFIG_BLK_DEV_IDE_S3C_UDMA char *mode = "PIO/UDMA"; #elif defined (CONFIG_BLK_DEV_IDE_S3C_PDMA) char *mode = "PIO_ONLY(DMA)"; #else char *mode = "PIO_ONLY"; #endif printk("current mode is %s\n", mode); return 0; } static int s3c_ata_proc_write ( struct file *file, const char *buffer, unsigned long count, void *data ) { return 0; } static struct proc_dir_entry *evb_resource_dump; int __init s3c_ata_rw_proc (void) { evb_resource_dump = create_proc_entry("s3c-ata", 0666, &proc_root); evb_resource_dump->read_proc = s3c_ata_proc_read; evb_resource_dump->write_proc = s3c_ata_proc_write; evb_resource_dump->nlink = 1; return 0; } module_init(s3c_ata_rw_proc); MODULE_DESCRIPTION("S3C ATA in CF/ATA Driver"); MODULE_LICENSE("GPL");