📄 au1xxx-ide.c
字号:
/* * linux/drivers/ide/mips/au1xxx-ide.c version 01.30.00 Aug. 02 2005 * * BRIEF MODULE DESCRIPTION * AMD Alchemy Au1xxx IDE interface routines over the Static Bus * * Copyright (c) 2003-2005 AMD, Personal Connectivity Solutions * * 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. * * Note: for more information, please refer "AMD Alchemy Au1200/Au1550 IDE * Interface and Linux Device Driver" Application Note. */#undef REALLY_SLOW_IO /* most systems can safely undef this */#include <linux/types.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/init.h>#include <linux/ide.h>#include <linux/sysdev.h>#include <linux/dma-mapping.h>#include "ide-timing.h"#include <asm/io.h>#include <asm/mach-au1x00/au1xxx.h>#include <asm/mach-au1x00/au1xxx_dbdma.h>#include <asm/mach-au1x00/au1xxx_ide.h>#define DRV_NAME "au1200-ide"#define DRV_VERSION "1.0"#define DRV_AUTHOR "Enrico Walther <enrico.walther@amd.com> / Pete Popov <ppopov@embeddedalley.com>"/* enable the burstmode in the dbdma */#define IDE_AU1XXX_BURSTMODE 1static _auide_hwif auide_hwif;static int dbdma_init_done;#if defined(CONFIG_BLK_DEV_IDE_AU1XXX_PIO_DBDMA)void auide_insw(unsigned long port, void *addr, u32 count){ _auide_hwif *ahwif = &auide_hwif; chan_tab_t *ctp; au1x_ddma_desc_t *dp; if(!put_dest_flags(ahwif->rx_chan, (void*)addr, count << 1, DDMA_FLAGS_NOIE)) { printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__); return; } ctp = *((chan_tab_t **)ahwif->rx_chan); dp = ctp->cur_ptr; while (dp->dscr_cmd0 & DSCR_CMD0_V) ; ctp->cur_ptr = au1xxx_ddma_get_nextptr_virt(dp);}void auide_outsw(unsigned long port, void *addr, u32 count){ _auide_hwif *ahwif = &auide_hwif; chan_tab_t *ctp; au1x_ddma_desc_t *dp; if(!put_source_flags(ahwif->tx_chan, (void*)addr, count << 1, DDMA_FLAGS_NOIE)) { printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__); return; } ctp = *((chan_tab_t **)ahwif->tx_chan); dp = ctp->cur_ptr; while (dp->dscr_cmd0 & DSCR_CMD0_V) ; ctp->cur_ptr = au1xxx_ddma_get_nextptr_virt(dp);}#endifstatic void auide_tune_drive(ide_drive_t *drive, byte pio){ int mem_sttime; int mem_stcfg; u8 speed; /* get the best pio mode for the drive */ pio = ide_get_best_pio_mode(drive, pio, 4, NULL); printk(KERN_INFO "%s: setting Au1XXX IDE to PIO mode%d\n", drive->name, pio); mem_sttime = 0; mem_stcfg = au_readl(MEM_STCFG2); /* set pio mode! */ switch(pio) { case 0: mem_sttime = SBC_IDE_TIMING(PIO0); /* set configuration for RCS2# */ mem_stcfg |= TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_PIO0_TCSOE | SBC_IDE_PIO0_TOECS; break; case 1: mem_sttime = SBC_IDE_TIMING(PIO1); /* set configuration for RCS2# */ mem_stcfg |= TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_PIO1_TCSOE | SBC_IDE_PIO1_TOECS; break; case 2: mem_sttime = SBC_IDE_TIMING(PIO2); /* set configuration for RCS2# */ mem_stcfg &= ~TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_PIO2_TCSOE | SBC_IDE_PIO2_TOECS; break; case 3: mem_sttime = SBC_IDE_TIMING(PIO3); /* set configuration for RCS2# */ mem_stcfg &= ~TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_PIO3_TCSOE | SBC_IDE_PIO3_TOECS; break; case 4: mem_sttime = SBC_IDE_TIMING(PIO4); /* set configuration for RCS2# */ mem_stcfg &= ~TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_PIO4_TCSOE | SBC_IDE_PIO4_TOECS; break; } au_writel(mem_sttime,MEM_STTIME2); au_writel(mem_stcfg,MEM_STCFG2); speed = pio + XFER_PIO_0; ide_config_drive_speed(drive, speed);}static int auide_tune_chipset (ide_drive_t *drive, u8 speed){ int mem_sttime; int mem_stcfg; unsigned long mode;#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA if (ide_use_dma(drive)) mode = ide_dma_speed(drive, 0);#endif mem_sttime = 0; mem_stcfg = au_readl(MEM_STCFG2); if (speed >= XFER_PIO_0 && speed <= XFER_PIO_4) { auide_tune_drive(drive, speed - XFER_PIO_0); return 0; } switch(speed) {#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA case XFER_MW_DMA_2: mem_sttime = SBC_IDE_TIMING(MDMA2); /* set configuration for RCS2# */ mem_stcfg &= ~TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_MDMA2_TCSOE | SBC_IDE_MDMA2_TOECS; mode = XFER_MW_DMA_2; break; case XFER_MW_DMA_1: mem_sttime = SBC_IDE_TIMING(MDMA1); /* set configuration for RCS2# */ mem_stcfg &= ~TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_MDMA1_TCSOE | SBC_IDE_MDMA1_TOECS; mode = XFER_MW_DMA_1; break; case XFER_MW_DMA_0: mem_sttime = SBC_IDE_TIMING(MDMA0); /* set configuration for RCS2# */ mem_stcfg |= TS_MASK; mem_stcfg &= ~TCSOE_MASK; mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_MDMA0_TCSOE | SBC_IDE_MDMA0_TOECS; mode = XFER_MW_DMA_0; break;#endif default: return 1; } if (ide_config_drive_speed(drive, mode)) return 1; au_writel(mem_sttime,MEM_STTIME2); au_writel(mem_stcfg,MEM_STCFG2); return 0;}/* * Multi-Word DMA + DbDMA functions */#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMAstatic int auide_build_sglist(ide_drive_t *drive, struct request *rq){ ide_hwif_t *hwif = drive->hwif; _auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data; struct scatterlist *sg = hwif->sg_table; 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(ahwif->dev, sg, hwif->sg_nents, hwif->sg_dma_direction);}static int auide_build_dmatable(ide_drive_t *drive){ int i, iswrite, count = 0; ide_hwif_t *hwif = HWIF(drive); struct request *rq = HWGROUP(drive)->rq; _auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data; struct scatterlist *sg; iswrite = (rq_data_dir(rq) == WRITE); /* Save for interrupt context */ ahwif->drive = drive; /* Build sglist */ hwif->sg_nents = i = auide_build_sglist(drive, rq); if (!i) return 0; /* fill the descriptors */ sg = hwif->sg_table; while (i && sg_dma_len(sg)) { u32 cur_addr; u32 cur_len; cur_addr = sg_dma_address(sg); cur_len = sg_dma_len(sg); while (cur_len) { u32 flags = DDMA_FLAGS_NOIE; unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00; if (++count >= PRD_ENTRIES) { printk(KERN_WARNING "%s: DMA table too small\n", drive->name); goto use_pio_instead; } /* Lets enable intr for the last descriptor only */ if (1==i) flags = DDMA_FLAGS_IE; else flags = DDMA_FLAGS_NOIE; if (iswrite) { if(!put_source_flags(ahwif->tx_chan, (void*)(page_address(sg->page) + sg->offset), tc, flags)) { printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__); } } else { if(!put_dest_flags(ahwif->rx_chan, (void*)(page_address(sg->page) + sg->offset), tc, flags)) { printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__); } } cur_addr += tc; cur_len -= tc; } sg++; i--; } if (count) return 1; use_pio_instead: dma_unmap_sg(ahwif->dev, hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); return 0; /* revert to PIO for this request */}static int auide_dma_end(ide_drive_t *drive){ ide_hwif_t *hwif = HWIF(drive); _auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data; if (hwif->sg_nents) { dma_unmap_sg(ahwif->dev, hwif->sg_table, hwif->sg_nents, hwif->sg_dma_direction); hwif->sg_nents = 0; } return 0;}static void auide_dma_start(ide_drive_t *drive ){}static void auide_dma_exec_cmd(ide_drive_t *drive, u8 command){ /* issue cmd to drive */ ide_execute_command(drive, command, &ide_dma_intr, (2*WAIT_CMD), NULL);}static int auide_dma_setup(ide_drive_t *drive){ struct request *rq = HWGROUP(drive)->rq; if (!auide_build_dmatable(drive)) { ide_map_sg(drive, rq); return 1; } drive->waiting_for_dma = 1; return 0;}static int auide_dma_check(ide_drive_t *drive){ u8 speed;#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA if( dbdma_init_done == 0 ){ auide_hwif.white_list = ide_in_drive_list(drive->id, dma_white_list); auide_hwif.black_list = ide_in_drive_list(drive->id, dma_black_list); auide_hwif.drive = drive; auide_ddma_init(&auide_hwif); dbdma_init_done = 1; }#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -