1232 lines
30 KiB
C
1232 lines
30 KiB
C
/*
|
|
* $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 <sc.suh@samsung.com>
|
|
*
|
|
* 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 <linux/types.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/ide.h>
|
|
#include <linux/sysdev.h>
|
|
#include <linux/scatterlist.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/clk.h>
|
|
|
|
#include "../ide-timing.h"
|
|
|
|
#include <asm/dma.h>
|
|
#include <asm/dma-mapping.h>
|
|
|
|
#include <asm/arch/regs-ide.h>
|
|
#include <asm/arch/regs-gpio.h>
|
|
#include <asm/arch/regs-s3c6400-clock.h>
|
|
|
|
#include <asm/io.h>
|
|
#include "s3c-ide.h"
|
|
|
|
#define DRV_NAME "s3c-ide"
|
|
#define DRV_VERSION "0.7"
|
|
#define DRV_AUTHOR "Seung-Chull, Suh <sc.suh@samsung.com>"
|
|
|
|
#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; i<size && sg_dma_len(sg); i++, sg++) {
|
|
s3c_hwif->table[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; i<count; i++, temp_addr++) {
|
|
wait_for_host_ready();
|
|
writel(*temp_addr, port);
|
|
}
|
|
}
|
|
|
|
static void s3c_ide_INSW (ulong port, void *addr, u32 count)
|
|
{
|
|
uint i;
|
|
volatile u16 *temp_addr = (u16*)addr;
|
|
|
|
for (i=0; i<count; i++, temp_addr++) {
|
|
wait_for_host_ready();
|
|
*temp_addr = readw(port);
|
|
wait_for_host_ready();
|
|
*temp_addr = readw(S3C_ATA_PIO_RDATA);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void change_mode_to_ata (void)
|
|
{
|
|
/* TDelay 1 unit = 10us */
|
|
mdelay(1);
|
|
|
|
/* CF controller - True IDE mode setting */
|
|
|
|
/* Output pad disable, Card power off, ATA mode */
|
|
writel(0x07, ata_base + S3C_CFATA_MUX);
|
|
mdelay(10);
|
|
/* Output pad enable, Card power off, ATA mode */
|
|
writel(0x03, ata_base + S3C_CFATA_MUX);
|
|
mdelay(10);
|
|
/* Output pad enable, Card power on, ATA mode */
|
|
writel(0x01, ata_base + S3C_CFATA_MUX);
|
|
mdelay(500); /* wait for 500ms */
|
|
}
|
|
|
|
static int s3c_ide_probe (struct platform_device *pdev)
|
|
{
|
|
s3c_ide_hwif_t *s3c_hwif = &s3c_ide_hwif;
|
|
ide_hwif_t *hwif;
|
|
hw_regs_t *hw;
|
|
int ret = 0;
|
|
ulong reg;
|
|
|
|
#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
|
|
|
|
memset(&s3c_ide_hwif, 0, sizeof(s3c_ide_hwif));
|
|
|
|
s3c_hwif->dev = 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");
|