From e94e3e06dcf17bc739806a8a4cd8d732f7f45074 Mon Sep 17 00:00:00 2001 From: Nicolas DET Date: Fri, 24 Nov 2006 13:10:46 +0100 Subject: [PATCH] Add MPC5200 SDMA/PIO driver Signed-off-by: Nicolas DET --- drivers/block/Kconfig | 25 + drivers/block/Makefile | 1 + drivers/block/mpc52xx/Makefile | 9 + drivers/block/mpc52xx/ata.c | 216 +++++++ drivers/block/mpc52xx/dodrivecmd.c | 138 ++++ drivers/block/mpc52xx/hwmisc.c | 577 +++++++++++++++++ drivers/block/mpc52xx/mpc52xx_blockata.h | 311 +++++++++ drivers/block/mpc52xx/mpc52xx_ide.h | 131 ++++ drivers/block/mpc52xx/piofunc_inline.h | 250 ++++++++ drivers/block/mpc52xx/protos.h | 107 ++++ drivers/block/mpc52xx/sdmatask.c | 142 ++++ drivers/block/mpc52xx/skel.c | 1024 ++++++++++++++++++++++++++++++ drivers/block/mpc52xx/transfer.c | 932 +++++++++++++++++++++++++++ 13 files changed, 3863 insertions(+), 0 deletions(-) diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 17dc222..0d9d9c1 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -63,6 +63,31 @@ config AMIGA_Z2RAM To compile this driver as a module, choose M here: the module will be called z2ram. +config BLK_DEV_MPC52XX_ATAPIO + tristate "MPC52xx ATA/PIO support" + depends on PPC_EFIKA + help + Selects this one if you are running on Efika 5k2 + +config BLK_DEV_MPC52XX_ATAPIO_SDMA + bool "Use DMA driven PIO transfert" + depends on BLK_DEV_MPC52XX_ATAPIO + help + take advantage of the Bestcom SDMA engine on the MPC52xx to transfer + data to and from the PIO Fifo. + This offload the CPU core and allow higher transfer rate. + +config BLK_DEV_MPC52XX_ATAPIO_MAXPIO + bool "Probe and set up the best PIO mode when setting the drive up" + depends on BLK_DEV_MPC52XX_ATAPIO + help + Probe and set up the best PIO mode available for the drive on + driver startup + +config BLK_DEV_MPC52XX_ATAPIO_VERBOSE + bool "Print out loads of verbose information" + depends on BLK_DEV_MPC52XX_ATAPIO + config ATARI_ACSI tristate "Atari ACSI support" depends on ATARI && BROKEN diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 410f259..72a5e66 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_VIODASD) += viodasd.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o obj-$(CONFIG_BLK_DEV_UB) += ub.o +obj-$(CONFIG_BLK_DEV_MPC52XX_ATAPIO) += mpc52xx/ diff --git a/drivers/block/mpc52xx/Makefile b/drivers/block/mpc52xx/Makefile new file mode 100644 index 0000000..3f20c67 --- /dev/null +++ b/drivers/block/mpc52xx/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the kernel block device drivers. +# +# 12 June 2000, Christoph Hellwig +# Rewritten to use lists instead of if-statements. +# + +obj-$(CONFIG_BLK_DEV_MPC52XX_ATAPIO) += skel.o sdmatask.o ata.o transfer.o hwmisc.o dodrivecmd.o + diff --git a/drivers/block/mpc52xx/ata.c b/drivers/block/mpc52xx/ata.c new file mode 100644 index 0000000..dced8b1 --- /dev/null +++ b/drivers/block/mpc52xx/ata.c @@ -0,0 +1,216 @@ +/* + * mpc52xx_atablock / ata.c + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + + +/**************/ +/**************/ +/**************/ + +#include "mpc52xx_blockata.h" + +/**************/ +/**************/ +/**************/ +int mpc52xx_ata_dodrivereset(struct mpc52xx_blockata_priv *priv) +{ + int ret; + + write_cmd(priv, ATA_CMD_RESET); + + ret = wait_not_busy(priv); + if (ret<0) + return ret; + + if ( ATASTS_GOT_ERR(read_altstatus(priv)) ) + return -1; + + return mpc52xx_ata_doidentify(priv); +} + + +/**************/ +int mpc52xx_ata_regcheck(struct mpc52xx_blockata_priv *priv) +{ + u8 seccnt; + u8 sector; + + write_sectorcnt(priv, 0x55); + write_sectornum(priv, 0xaa); + write_sectorcnt(priv, 0xaa); + write_sectornum(priv, 0x55); + write_sectorcnt(priv, 0x55); + write_sectornum(priv, 0xaa); + + seccnt = read_sectorcnt(priv); + sector = read_sectornum(priv); + + NPRINTK("%s: seccnt=%x, sector=%x\n", __func__, seccnt, sector ); + + if + ( + ( (seccnt == 0x55) || (seccnt == 0x01) ) + && (sector == 0xaa) + ) + return 0; + + return -1; +} + +/**************/ +/**************/ +static int ata_docpupollread( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len) +{ + u16 *buffer16 = (u16 *) buffer; + int local_len = len; + + while(local_len--) + *buffer16++ = read_data(priv); + + return len - local_len; +} + +/**************/ +static int ata_docpupollwrite( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len) +{ + u16 *buffer16 = (u16 *) buffer; + int local_len = len; + + while(local_len--) + write_data(priv, *buffer16++); + + return len - local_len; + +} + + +/**************/ +int mpc52xx_ata_setupsector( + struct mpc52xx_blockata_priv *priv, + sector_t sector, + int sector_num, + int is_write) +{ + u8 seccnt, secnum, cyl_lo, cyl_hi, devhead; + u16 cyl16; + + if (priv->drive_canlba) { + if ( sector & (0xfffff0000000LL) ) { + if (!priv->drive_canlba48) + return -1; + + write_sectornum(priv, (sector >> 24) & 0xff); + + write_cyllo(priv, (sector >> 32) & 0xff); + + write_cylhi(priv, (sector >> 40) & 0xff); + + write_sectorcnt(priv, sector_num & 0xff00); + sector_num &= 0xff; + + sector &= 0xffffff; + + if ( (priv->multi_available) && (sector_num > 1) ) + priv->curio_atacmd = is_write ? ATA_CMD_WRITE_MULTI_EXT : ATA_CMD_READ_MULTI_EXT; + else + priv->curio_atacmd = is_write ? ATA_CMD_PIO_WRITE_EXT : ATA_CMD_PIO_READ_EXT; + + } else { + if ( (priv->multi_available) && (sector_num > 1) ) + priv->curio_atacmd = is_write ? ATA_CMD_WRITE_MULTI : ATA_CMD_READ_MULTI; + else + priv->curio_atacmd = is_write ? ATA_CMD_PIO_WRITE : ATA_CMD_PIO_READ; + } + + secnum = sector & 0xff; + cyl16 = (sector >> 8) & 0xffff; + + devhead = (sector >> 24) & 0xf; + devhead |= ATA_LBA; + } else { + unsigned long blkno = (unsigned long) sector; + int sectorspertrack = priv->drive_chs_sectorspertrack; + int cylinders = priv->drive_chs_cylinders; + int heads = priv->drive_chs_heads; + + secnum = (blkno % sectorspertrack) + 1; + blkno -= secnum - 1; + + blkno /= sectorspertrack; + + devhead = blkno / cylinders; + + cyl16 = blkno % cylinders; + + if (devhead > (heads-1) ) { + return -1; + } + } + + seccnt = (u8) sector_num; + cyl_hi = (cyl16 >> 8) & 0xff; + cyl_lo = cyl16 & 0xff; + + write_devhead(priv, devhead); + write_sectornum(priv, secnum); + write_cyllo(priv, cyl_lo); + write_cylhi(priv, cyl_hi); + write_sectorcnt(priv, seccnt); + + NPRINTK("lba=%d, sector=%lld, seccnt=%x sector=0x%x (%d), cyl_lo=%x, cyl_hi=%x, cyl16=0x%x, devhead=%x\n", + priv->drive_canlba, sector, seccnt, secnum, secnum, cyl_lo, cyl_hi, cyl16, devhead); + + return 0; +} + + + +int mpc52xx_ata_doreset(struct mpc52xx_blockata_priv *priv) +{ + int ret = 0; + + ret = wait_not_busy(priv); + if (ret!=0) + return -1; + + NPRINTK("%s: set reset %x\n", __func__, read_altstatus(priv) ); + + write_ctl(priv, ATA_SRST | ATA_NIEN); + ata_sleep(priv); + + NPRINTK("%s: release reset, %x\n", __func__, read_altstatus(priv)); + + write_ctl(priv, ATA_NIEN); + + + ata_sleep(priv); + + ret = wait_not_busy(priv); + if (ret<0) + return ret; + + write_devhead(priv, 0x00); + ata_sleep(priv); + + return wait_not_busy(priv); +} + +/* + * Export stuff +*/ + +EXPORT_SYMBOL(mpc52xx_ata_setupsector); +EXPORT_SYMBOL(mpc52xx_ata_doreset); +EXPORT_SYMBOL(mpc52xx_ata_regcheck); diff --git a/drivers/block/mpc52xx/dodrivecmd.c b/drivers/block/mpc52xx/dodrivecmd.c new file mode 100644 index 0000000..afbfded --- /dev/null +++ b/drivers/block/mpc52xx/dodrivecmd.c @@ -0,0 +1,138 @@ +/* + * mpc52xx_atablock / dodrivecmd.c + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* + * handle the ioctl HDIO_DRIVE_CMD command +*/ + +/**************/ +/**************/ +/**************/ +#include "mpc52xx_blockata.h" + +/**************/ +/**************/ +/**************/ +irqreturn_t ata_drivecmd_handler(struct mpc52xx_blockata_priv *priv, u8 ata_status) +{ + NPRINTK("%s: status=0x%2.2x\n", __func__, ata_status); + + priv->ata_handler = ata_void_handler; + priv->io_inprogress = 0; + wake_up_interruptible(&priv->my_waitqueue); + + return IRQ_HANDLED; +} + + int mpc52xx_ata_dodrivecmd( + struct mpc52xx_blockata_priv *priv, + unsigned long *irqflags, + u8 *args) + { + int ret; + u8 status; + u8 cmd; + + NPRINTK("arg [0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x],\n", args[0], args[1], args[2], args[3]); + + cmd = args[0]; + if ( (cmd==ATA_CMD_ID_ATAPI) || (cmd==ATA_CMD_ID_ATA) ) + { + int i; + u16 *iddest_u16; + u16 *drive_idendify; + + drive_idendify = priv->drive_identify; + iddest_u16 = (u16*) (&args[4]); + ret = 0; + + if (!priv->drive_identify_valid) { + if (priv->io_inprogress) { + VPRINTK("IO already in-progress, can not do more!\n"); + ret = -EBUSY; + goto end; + } + + priv->io_inprogress = 1; + ret = mpc52xx_ata_doidentify(priv); + priv->io_inprogress = 0; + + if (ret!=0) + goto end; + } + + for(i=0; i < 256; i++) + iddest_u16[i] = cpu_to_le16(drive_idendify[i]); + + goto end; + } + + if (priv->io_inprogress) + { + VPRINTK("IO already in-progress, can not do more!\n"); + ret = -EBUSY; + goto end; + } + + priv->io_inprogress = 1; + + // a drive cmd looks very simple + // simply copy the stuff in the drive reg and wait for some interrupt + + // Get preprepatre to receive an interrupt + priv->ata_handler = ata_drivecmd_handler; + + write_devfeature(priv, args[2]); + write_sectorcnt(priv, args[3]); + write_sectornum(priv, args[1]); + + write_cmd(priv, cmd); + + /* + * I'm 100% (well not even 10%) happy and confortable with this IRQ/wait stuff + * People should really write an how to "How to wait for an event in atomic section?" + */ + spin_unlock_irqrestore(&priv->lock, *irqflags); + ret = wait_event_interruptible_timeout(priv->my_waitqueue, priv->io_inprogress==0, 5*HZ); + spin_lock_irqsave(&priv->lock, *irqflags); + + if (ret<0) + { + ret = -ERESTARTSYS; + goto end; + } + + // Whatever happen, we can read back the status to users mess + //args[0] = read_devfeature(priv); + + status = read_status(priv); + if ( ATASTS_GOT_ERR(status) ) + ret = -EIO; + + // Not sure for arg0 + args[0] = status; + args[1] = read_devfeature(priv); + args[2] = read_sectorcnt(priv); + args[3] = read_sectornum(priv); + + NPRINTK("arg cmd=0x%2.2x, [0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x]\n", cmd, args[0], args[1], args[2], args[3]); + NPRINTK("return %d\n", ret); + + if(ret>0) + ret = 0; + + end: + priv->io_inprogress = 0; + priv->ata_handler = ata_void_handler; + return ret; + } + + +EXPORT_SYMBOL(mpc52xx_ata_dodrivecmd); diff --git a/drivers/block/mpc52xx/hwmisc.c b/drivers/block/mpc52xx/hwmisc.c new file mode 100644 index 0000000..862729a --- /dev/null +++ b/drivers/block/mpc52xx/hwmisc.c @@ -0,0 +1,577 @@ +/* + * mpc52xx_atablock / hwmisc.c + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* + * misc hw func (pio mode ...) +*/ + +/**************/ +/**************/ +/**************/ +#include "mpc52xx_blockata.h" + +/**************/ +/**************/ +/**************/ +static int ata_doidentify_atapi(struct mpc52xx_blockata_priv *priv) +{ + u16 * drive_idendify; + int ret; + u16 OneShort; + + NPRINTK("\n"); + + ret = 0; + drive_idendify = priv->drive_identify; + + NPRINTK("id[00]=0x%2.2x\n", drive_idendify[0]); + NPRINTK("id[85]=0x%2.2x\n", drive_idendify[85]); + + OneShort = drive_idendify[0]; + if ( (OneShort & 0xc000) != 0x8000) + return -1; + + if (OneShort & (1<<7) ) + priv->IsRemovable = 1; + else + priv->IsRemovable = 0; + + priv->drive_canlba = 0; + priv->IsATAPI = 1; + + priv->drive_identify_valid = 1; + + return ret; +} + +static int ata_doidentify(struct mpc52xx_blockata_priv *priv) +{ + int ret; + + priv->drive_identify_valid = 0; + + ret = wait_ready(priv); + if (ret!=0) + goto error; + + write_cmd(priv, priv->UsePacket ? ATA_CMD_ID_ATAPI : ATA_CMD_ID_ATA); + ret = wait_drq(priv); + if (ret!=0) + goto error; + + ret = mpc52xx_ata_docpupollread(priv, priv->drive_identify, 256); + if (ret<0) + goto error; + + priv->drive_identify[49] = priv->drive_identify[49] & ~(1<<8); + + memcpy(priv->drive_model, (char *) & priv->drive_identify[27], 40); + priv->drive_model[39] = '\0'; + memcpy(priv->drive_firmware, (char *) & priv->drive_identify[23], 8); + priv->drive_firmware[7] = '\0'; + + if (priv->UsePacket) + return ata_doidentify_atapi(priv); + + priv->drive_heads = priv->drive_identify[3]; + priv->drive_cylinders = priv->drive_identify[1]; + priv->drive_sectorspertrack = priv->drive_identify[6]; + priv->drive_cap = priv->drive_identify[49]; + + priv->drive_canlba = priv->drive_cap & CAPF_LBA; + priv->drive_canlba48 = ((priv->drive_identify[83]) & (1 << 10)) == (1 << 10) ; + + if (priv->drive_canlba) { + priv->drive_sectors = (sector_t) + ( (priv->drive_identify[60] & 0xffff) + | ( (priv->drive_identify[61] << 16) & 0xffff0000) ); + } else { + priv->drive_sectors = (sector_t) + ( (priv->drive_identify[57] & 0xffff) + | ( (priv->drive_identify[58] << 16) & 0xffff0000) ); + } + + + NPRINTK("%s: id[0]=%x\n", __func__, priv->drive_identify[0]); + NPRINTK("%s: id[1]=%x\n", __func__, priv->drive_identify[1]); + NPRINTK("%s: id[3]=%x\n", __func__, priv->drive_identify[3]); + NPRINTK("%s: id[6]=%x\n", __func__, priv->drive_identify[6]); + NPRINTK("%s: id[53]=%x\n", __func__, priv->drive_identify[53]); + NPRINTK("%s: id[54]=%x\n", __func__, priv->drive_identify[54]); + NPRINTK("%s: id[55]=%x\n", __func__, priv->drive_identify[55]); + NPRINTK("%s: id[56]=%x\n", __func__, priv->drive_identify[56]); + + NPRINTK("%s: id[57/58]=%x %x\n", __func__, priv->drive_identify[57], priv->drive_identify[58]); + NPRINTK("%s: id[60/61]=%x %x\n", __func__, priv->drive_identify[57], priv->drive_identify[58]); + NPRINTK("%s: id[82]=%x\n", __func__, priv->drive_identify[82]); + NPRINTK("%s: id[83]=%x\n", __func__, priv->drive_identify[83]); + + + NPRINTK("%s: model=%s, fw=%s. %d heads, %d cylinders, %lld sectors. LBA=%d\n", + __func__, + priv->drive_model, priv->drive_firmware, + priv->drive_heads, priv->drive_cylinders, priv->drive_sectors, priv->drive_canlba ); + + priv->drive_identify_valid = 1; + return 0; + +error: + return -1; +} + +int mpc52xx_ata_doidentify(struct mpc52xx_blockata_priv *priv) +{ + return ata_doidentify(priv); +} + +/**************/ +/**************/ +int mpc52xx_ata_setpiomode(struct mpc52xx_blockata_priv *priv, int pio_mode) +{ + u8 setfeature_val; + int ret; + + NPRINTK("%s: pio_mode=%d\n", __func__, pio_mode); + + switch(pio_mode) { + case 0: + setfeature_val = XFER_PIO_0; + break; + case 1: + setfeature_val = XFER_PIO_1; + break; + case 2: + setfeature_val = XFER_PIO_2; + break; + case 3: + setfeature_val = XFER_PIO_3; + break; + case 4: + setfeature_val = XFER_PIO_4; + break; + default: + return -1; + } + + write_sectorcnt(priv, setfeature_val); + write_devfeature(priv, SETFEATURES_XFER); + + ret = wait_ready(priv); + if (ret !=0) + return ret; + + write_cmd(priv, ATA_CMD_SET_FEATURES); + + return wait_not_busy(priv); +} + +/* + * if pio_arg is <0 then, return the best pio mode available +*/ +int mpc52xx_ata_isthispiovalid(struct mpc52xx_blockata_priv *priv, int pio_arg) +{ + int ret; + int mode; + + u16 id_fieldval; + u16 id_advpio; + + NPRINTK("pio_arg=%d\n", pio_arg); + + if (!priv->drive_identify_valid) { + ret = ata_doidentify(priv); + if (ret!=0) + return -1; + } + + id_advpio = priv->drive_identify[64]; + id_fieldval = priv->drive_identify[53]; + + NPRINTK("id_advpio=%x, id_fieldval=%x\n",id_advpio, id_fieldval); + + if (pio_arg<0) { + pio_arg = 2; + + if (id_fieldval & 0x0002) { + if (id_advpio&0x2) + pio_arg = 4; + else if (id_advpio&0x1) + pio_arg = 3; + } + } + + NPRINTK("pio_arg=%d\n", pio_arg); + + switch(pio_arg) { + case 0: + case 1: + case 2: + mode = pio_arg; + break; + + case 3: + mode = ( (id_fieldval&0x0002) && (id_advpio&0x1) )? pio_arg : -1; + break; + + case 4: + mode = ( (id_fieldval&0x0002) && (id_advpio&0x2) )? pio_arg : -1; + break; + + default: + mode = -1; + } + + return mode; +} + + +/**************/ +#define IDENTIFY_MULTI_MAXSECMASK 0x00ff +#define IDENTIFY_MULTI_OKFLAG 0x00100 + +int mpx52xx_ata_dosetmulti(struct mpc52xx_blockata_priv *priv, u16 val) +{ + int ret; + + NPRINTK("set multi to %d\n", val); + + if (priv->UsePacket) + return -1; + + write_sectorcnt(priv, val&0xff); + + ret = wait_ready(priv); + if (ret !=0) + return -1; + + ATA_DUMPREG + write_cmd(priv, ATA_CMD_SETMULTI); + + ata_sleep(priv); + ret = wait_not_busy(priv); + if (ret !=0) + return -1; + + return 0; +} + +#define IS_MULTIOK(__identify_multi__) \ +( \ + (__identify_multi__ & IDENTIFY_MULTI_OKFLAG) \ + && (__identify_multi__ & IDENTIFY_MULTI_MAXSECMASK) \ +) + +static int ata_multi_probeandset(struct mpc52xx_blockata_priv *priv) +{ + int ret; + u16 identify_multi; + u16 identify_multimax; + + priv->multi_available = 0; + + if (priv->UsePacket) + return -1; + + NPRINTK("id[59]=0x%x, id[47]=0x%x\n", priv->drive_identify[59], priv->drive_identify[47]); + + identify_multi = priv->drive_identify[59]; + if ( IS_MULTIOK(identify_multi) ) { + priv->multi_available = 1; + priv->multi_secpercmd = identify_multi & IDENTIFY_MULTI_MAXSECMASK; + } else { + identify_multimax = priv->drive_identify[47] & IDENTIFY_MULTI_MAXSECMASK; + ret = mpx52xx_ata_dosetmulti(priv, identify_multimax); + if (ret<0) + goto error; + + ret = ata_doidentify(priv); + if (ret!=0) + goto error; + + NPRINTK("idenditify updated, identify_multimax=%d, s=%x, id[59]=0x%x, id[47]=0x%x\n", + identify_multimax, read_altstatus(priv), priv->drive_identify[59], priv->drive_identify[47]); + + identify_multi = priv->drive_identify[59]; + + if ( IS_MULTIOK(identify_multi) ) { + priv->multi_available = 1; + priv->multi_secpercmd = identify_multi & IDENTIFY_MULTI_MAXSECMASK; + } else { + priv->multi_secpercmd = MAX_SECPERREQ; + } + + } + + return 0; + +error: + priv->multi_secpercmd = MAX_SECPERREQ; + priv->multi_available = 0; + + return ret; +} + + +/**************/ +static int ata_dodiag(struct mpc52xx_blockata_priv *priv) +{ + int ret; + u8 diagcode; + u8 sectorcount; + u8 lbah; + u8 lbal; + u8 lbam; + + NPRINTK("1\n"); + + ret = wait_not_busy(priv); + if (ret!=0) + goto error; + + NPRINTK("cmd\n"); + write_cmd(priv, ATA_CMD_EDD); + + wait_not_busy(priv); + diagcode = read_devfeature(priv); + + NPRINTK("diagcode=%x\n", diagcode); + + /* Check if this device implement the PACKET feature set + * see ATAPI6 spec section 9.12 + * Non Packet has this signature: + * Sector Count 01h + * LBA Low 01h + * LBA Mid 00h + * LBA High 00h + * Device 00h + * + * Packet has this signature: + * Sector Count 01h + * LBA Low 01h + * LBA Mid 14h + * LBA High EBh + * + * + */ + sectorcount = read_sectorcnt(priv); + lbal = read_sectornum(priv); + lbam = read_cyllo(priv); + lbah = read_cylhi(priv); + + NPRINTK("sc=%2.2x lbal=%2.2x lbam=%2.2x lbah=%2.2x\n", sectorcount, lbal, lbam, lbah); + + if ( (sectorcount != 0x01) && (lbal != 0x01) ) + goto error; + + if ( (lbam==0) && (lbah==0) ) + priv->UsePacket = 0; + else if ((lbam==0x14) && (lbah==0xeb) ) + priv->UsePacket = 1; + else + goto error; + + return ! (diagcode & 0x01); + +error: + NPRINTK("error %d\n", ret); + ATA_DUMPREG + + return -1; +} +/**************/ +void mpc52xx_ata_dumpreg(struct mpc52xx_blockata_priv *priv) +{ + u8 seccnt, sector, cyl_lo, cyl_hi, error, status; + + seccnt = read_sectorcnt(priv); + sector = read_sectornum(priv); + cyl_lo = read_cyllo(priv); + cyl_hi = read_cylhi(priv); + error = read_error(priv); + status = read_altstatus(priv); + + VPRINTK("seccnt=%x sector=%x, cyl_lo=%x, cyl_hi=%x, error=%x, status =%x \n", + seccnt, sector, cyl_lo, cyl_hi, error, status); +} + +static int ata_doreset(struct mpc52xx_blockata_priv *priv) +{ + int ret = 0; + + ret = wait_not_busy(priv); + if (ret!=0) + return -1; + + NPRINTK("set reset %x\n", read_altstatus(priv) ); + write_ctl(priv, ATA_SRST | ATA_NIEN); + ata_sleep(priv); + + NPRINTK("release reset, %x\n", read_altstatus(priv)); + write_ctl(priv, ATA_NIEN); + + ata_sleep(priv); + + ret = wait_not_busy(priv); + if (ret<0) + return ret; + + write_devhead(priv, 0x00); + ata_sleep(priv); + + wait_not_busy(priv); + if (ret<0) + return ret; + + return ATASTS_GOT_ERR( read_altstatus(priv) ); +} + +/**************/ +static int ata_setupchs(struct mpc52xx_blockata_priv *priv) +{ + int ret = 0; + + NPRINTK("s=0x%2.2x, lba=%d, secper=%d, heads=%dn", + read_altstatus(priv), + priv->drive_canlba, + priv->drive_sectorspertrack, + priv->drive_heads); + + priv->drive_chs_ok = 0; + + if + ( + (priv->drive_sectorspertrack == 0) + || priv->drive_canlba + ) + return -1; + + if (!priv->drive_identify_valid) { + ret = ata_doidentify(priv); + if (ret!=0) + return -1; + } + + NPRINTK("wait1 s=%2.2x\n", read_altstatus(priv) ); + ret = wait_not_busy(priv); + if (ret<0) + return ret; + + write_sectorcnt(priv, priv->drive_sectorspertrack ); + write_devhead(priv, priv->drive_heads - 1); + + NPRINTK("wait2 s=%2.2x\n", read_altstatus(priv) ); + ret = wait_ready(priv); + if (ret<0) { + ATA_DUMPREG + return ret; + } + + write_cmd(priv, ATA_CMD_INIT_DEV_PARAMS); + + NPRINTK("wait3 s=%2.2x\n", read_altstatus(priv) ); + ret = wait_not_busy(priv); + if (ret<0) { + ATA_DUMPREG + return ret; + } + + ret = ata_doidentify(priv); + if (ret!=0) + return ret; + + if (!(priv->drive_identify[53] & 0x0001)) + return -1; + + priv->drive_chs_ok = 1; + + priv->drive_chs_heads = priv->drive_identify[54]; + priv->drive_chs_cylinders = priv->drive_identify[55]; + priv->drive_chs_sectorspertrack = priv->drive_identify[56]; + + NPRINTK("ok. CHS=%d/%d/%ds=0x%2.2x\n", + read_altstatus(priv), + priv->drive_chs_cylinders, + priv->drive_chs_heads, + priv->drive_chs_sectorspertrack); + + return 0; +} + +/**************/ +/**************/ +/**************/ + +/* + * Simple and dump drive init func + * reset, sanity check, drive presence, get drive info +*/ +int mpc52xx_ata_init_drive(struct mpc52xx_blockata_priv *priv) +{ + int ret; + + ret = wait_not_busy(priv); + if (ret!=0) + goto error; + + write_ctl(priv, ATA_NIEN); + + NPRINTK("select dev 0\n"); + write_devhead(priv, 0x00); + + ata_sleep(priv); + + ata_doreset(priv); + + /* Some check ... */ + NPRINTK("regcheck %x\n", read_altstatus(priv)); + ret = mpc52xx_ata_regcheck(priv); + if (ret!=0) + goto error; + + /* diagnostic */ + NPRINTK("diag %x\n", read_altstatus(priv)); + ret = ata_dodiag(priv); + if (ret!=0) + goto error; + + /* Get information from the drive */ + NPRINTK("do id %x, UsePacket=%d\n", read_altstatus(priv), priv->UsePacket); + if (!priv->drive_identify_valid) { + ret = ata_doidentify(priv); + if (ret!=0) + goto error; + } + + ata_setupchs(priv); + ata_multi_probeandset(priv); + + return ret; + +error: + NPRINTK("%s: error %d, altsts=%x, err=%x\n", __func__, ret, read_altstatus(priv), read_error(priv)); + ATA_DUMPREG; + + return -1; +} + +u32 mpc52xx_ata_getregisterbase(struct mpc52xx_blockata_priv *priv) +{ return 0xf0003a00; } + +/* + * Export stuff +*/ + +EXPORT_SYMBOL(mpc52xx_ata_init_drive); +EXPORT_SYMBOL(mpc52xx_ata_getregisterbase); +EXPORT_SYMBOL(mpc52xx_ata_doidentify); +EXPORT_SYMBOL(mpc52xx_ata_setpiomode); + +EXPORT_SYMBOL(mpc52xx_ata_isthispiovalid); +EXPORT_SYMBOL(mpx52xx_ata_dosetmulti); diff --git a/drivers/block/mpc52xx/mpc52xx_blockata.h b/drivers/block/mpc52xx/mpc52xx_blockata.h new file mode 100644 index 0000000..69a6a88 --- /dev/null +++ b/drivers/block/mpc52xx/mpc52xx_blockata.h @@ -0,0 +1,311 @@ +/* + * mpc52xx_blockata.h + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + + +#ifndef __MPC52xx_BLOCKATA_H__ +#define __MPC52xx_BLOCKATA_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/**************/ +/**************/ +/**************/ +//#define __DEBUG__ + +/**************/ +/**************/ +/**************/ +#include + +#include "protos.h" +#include "mpc52xx_ide.h" + + +/**************/ +/**************/ +/**************/ + +#ifdef __DEBUG__ +#define DEBUG +#define NPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +//#define NPRINTK2(fmt, args...) printk(KERN_ERR "%s: " fmt, __FUNCTION__, ## args) +#define NPRINTK2(fmt, args...) + +#define ATA_DUMPREG mpc52xx_ata_dumpreg(priv); + +#else +#define NPRINTK(fmt, args...) +#define NPRINTK2(fmt, args...) +#define ATA_DUMPREG +#endif + +/**************/ +/**************/ +/**************/ + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_VERBOSE +#define VPRINTK(fmt, args...) if ( printk_ratelimit() ) { printk(KERN_ERR DEVSKEL_DEVICENAME ": " fmt, ## args); } +#else +#define VPRINTK(fmt, args...) +#endif + +#define DEVICE_USERLIMIT 1024 +#define KERNEL_SECTOR_SIZE 512 + +// I notice 8 gives some good result +#define DRIVER_MAXHWSEG 8 + +/**************/ +/**************/ +/**************/ + + +#ifndef ATA_CMD_RESET +#define ATA_CMD_RESET 0x08 +#endif + +#ifndef ATA_CMD_SETMULTI +#define ATA_CMD_SETMULTI 0xc6 +#endif + +/**************/ +/**************/ +/**************/ + +#ifndef bio_cur_sectors_idx +#define bio_cur_sectors_idx(__bio__,__idx__) (bio_iovec_idx((__bio__), (__idx__))->bv_len >> 9) +#endif + +#ifndef bio_getnext +#define bio_getnext(__bio__) ( (__bio__)->bi_next ) +#endif + + +#ifndef bio_getsector +#define bio_getsector(__bio__) ( (__bio__)->bi_sector ) +#endif + +#ifndef bio_getcuridx +#define bio_getcuridx(__bio__) ( (__bio__)->bi_idx ) +#endif + +#ifndef bio_islastidx +#define bio_islastidx(__bio__, __idx__) ( (__idx__) >= (__bio__)->bi_vcnt ) +#endif + + + +/**************/ +/**************/ +/**************/ + +/* + * + * TO DO: + * - better error handling + * - Transaction timeout/reset + * - test and fix LBA48 addressing + * - code, test and fix CHS addressing + * + * History + * - 0.1. 27.06.2006 + * Initial revision + * PIO func, SDMA/CPU polling... + * Reliable operation (1 or 2 MB/s) + * + * - 0.2. 28.06.2006 + * Interrupt based driver! + * IRQ handlers, handles better bio, bio vec, req... + * Reliable operation (write 1.5MB/s, read 3.5MB/s to 6.5MB/s) + * + * - 0.3b1. 29.06.2006 + * Cleanup and small fixes + * Wait for DRQ interrupt instead of polling for read + * Add user count feature + * + * - 0.3b2. 30.06.2006 + * Fixed the nopen/nrelease -> release was disabling IRQ even if + * there was still some people using it. I don't even speak about open! + * Cleanup and small fixes + * + * - 0.4 02.07.2006 + * Driver totaly cleaned up and spread other severals sources file + * Implemented LBA48 but untested due to lack of drive (you've been warmed!) + * + * - 0.5 07.07.2006 + * General cleanup + * Better SDMA task configuration + * Try to adjust the block queue config + * + * - 0.6b1 30.07.2006 + * General cleanup + * Add HIDIO_DRIVE_CMD support + * Fixed (a bit) spinlock_* + * Primilary ATAPI/Packet support + * + * - 0.6 31.07.2006 + * General cleanup + * Nicely failed when ATAPI/Packet command is needed + * Fixed HDIO_DRIVE_CMD/Identify + * + * - 0.7 18.10.2006 + * General cleanup + * fix text layout for Linux patch + * First try to init the drive and then the Linux block system becase + * the block code BUG() on free +*/ + + +/**************/ +/**************/ +/**************/ + +#define MAX_DMA_BUFFERS 4 +#define MAX_DMA_BUFFER_SIZE 512*256 + +#define DEVSKEL_DRIVERVERSION "0.7" +#define DEVSKEL_DRIVERNAME "MPC52xx ATA/PIO" +#define DEVSKEL_DEVICENAME "mpc52xx_ata" + +#define MAX_SECPERREQ DRIVER_MAXHWSEG + + +/**************/ +/**************/ +/**************/ + +#ifndef MPC52xx_ATA_OFFSET +#define MPC52xx_ATA_OFFSET (0x3a00) +#endif + +#define ATAFIFO_BUSADDR ( (u32) 0xf0003a60 ) + + +/**************/ +/**************/ +/**************/ + +/* Helper to compute timing parameters */ +#define CALC_CLK_VALUE_UP(c,v) (((v) + c - 1) / c) + +/**************/ +/**************/ +/**************/ +/* Private structures used by the driver */ +struct mpc52xx_ata_timings +{ + u32 pio1; + u32 pio2; + u32 mdma1; + u32 mdma2; + u32 udma1; + u32 udma2; + u32 udma3; + u32 udma4; + u32 udma5; + int using_udma; +}; + +/**************/ +/**************/ +/**************/ +struct mpc52xx_blockata_priv +{ + struct gendisk *device_gendisk; + struct request_queue *device_queue; + int major; + + spinlock_t lock; + + wait_queue_head_t my_waitqueue; + + int drive_inited; + int usercnt; + + int drive_identify_valid; + u16 drive_identify[256]; + + char drive_model[40]; + char drive_firmware[8]; + + sector_t drive_sectors; + int drive_sectorspertrack; + int drive_cylinders; + int drive_heads; + + int drive_chs_ok; + int drive_chs_sectorspertrack; + int drive_chs_cylinders; + int drive_chs_heads; + +#define CAPF_LBA (1<<9) + u16 drive_cap; + int drive_canlba; + int drive_canlba48; + + u8 curio_atacmd; + + int io_inprogress; + + unsigned int ipb_period; /* in ps */ + + struct mpc52xx_ata __iomem *ata_regs; + u32 ata_regs_bus; + struct mpc52xx_ata_timings timings[2]; + + struct bestcomm_taskhandle taskhandle; + struct bestcomm_taskhandle *sdma; + + int ata_irq; + int sdma_irq; + + int multi_secpercmd; + int multi_available; + + irqreturn_t (*sdma_handler) (struct mpc52xx_blockata_priv *priv); + irqreturn_t (*ata_handler) (struct mpc52xx_blockata_priv *priv, u8 ata_status); + + int curio_sectodo; + + int curio_secidx; + int curio_secpershot; + u16 *curio_buffer; + + struct request *curio_req; + struct bio *curio_bio; + int curio_bioidx; + sector_t curio_sector; + + int piomode; + + int UsePacket; + int IsATAPI; + int IsRemovable; +}; + + +/**************/ +/**************/ +/**************/ + +#include "piofunc_inline.h" + +#endif diff --git a/drivers/block/mpc52xx/mpc52xx_ide.h b/drivers/block/mpc52xx/mpc52xx_ide.h new file mode 100644 index 0000000..6cd35eb --- /dev/null +++ b/drivers/block/mpc52xx/mpc52xx_ide.h @@ -0,0 +1,131 @@ +/* + * drivers/ide/ppc/mpc52xx_ide.h + * + * Definitions for the Freescale MPC52xx on-chip IDE interface + * + * + * Copyright (C) 2006 Sylvain Munaut + * Copyright (C) 2003 Mipsys - Benjamin Herrenschmidt + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef __MPC52xx_IDE_H__ +#define __MPC52xx_IDE_H__ + + +#include +#include +#include +#include +#include + +#include + +/* Bit definitions inside the registers */ + +#define MPC52xx_ATA_HOSTCONF_SMR 0x80000000UL /* State machine reset */ +#define MPC52xx_ATA_HOSTCONF_FR 0x40000000UL /* FIFO Reset */ +#define MPC52xx_ATA_HOSTCONF_IE 0x02000000UL /* Enable interrupt in PIO */ +#define MPC52xx_ATA_HOSTCONF_IORDY 0x01000000UL /* Drive supports IORDY protocol */ + +#define MPC52xx_ATA_HOSTSTAT_TIP 0x80000000UL /* Transaction in progress */ +#define MPC52xx_ATA_HOSTSTAT_UREP 0x40000000UL /* UDMA Read Extended Pause */ +#define MPC52xx_ATA_HOSTSTAT_RERR 0x02000000UL /* Read Error */ +#define MPC52xx_ATA_HOSTSTAT_WERR 0x01000000UL /* Write Error */ + +#define MPC52xx_ATA_FIFOSTAT_EMPTY 0x01 /* FIFO Empty */ + +#define MPC52xx_ATA_DMAMODE_WRITE 0x01 /* Write DMA */ +#define MPC52xx_ATA_DMAMODE_READ 0x02 /* Read DMA */ +#define MPC52xx_ATA_DMAMODE_UDMA 0x04 /* UDMA enabled */ +#define MPC52xx_ATA_DMAMODE_IE 0x08 /* Enable drive interrupt to CPU in DMA mode */ +#define MPC52xx_ATA_DMAMODE_FE 0x10 /* FIFO Flush enable in Rx mode */ +#define MPC52xx_ATA_DMAMODE_FR 0x20 /* FIFO Reset */ +#define MPC52xx_ATA_DMAMODE_HUT 0x40 /* Host UDMA burst terminate */ + + +/* Structure of the hardware registers */ +struct mpc52xx_ata +{ + + /* Host interface registers */ + u32 config; /* ATA + 0x00 Host configuration */ + u32 host_status; /* ATA + 0x04 Host controller status */ + u32 pio1; /* ATA + 0x08 PIO Timing 1 */ + u32 pio2; /* ATA + 0x0c PIO Timing 2 */ + u32 mdma1; /* ATA + 0x10 MDMA Timing 1 */ + u32 mdma2; /* ATA + 0x14 MDMA Timing 2 */ + u32 udma1; /* ATA + 0x18 UDMA Timing 1 */ + u32 udma2; /* ATA + 0x1c UDMA Timing 2 */ + u32 udma3; /* ATA + 0x20 UDMA Timing 3 */ + u32 udma4; /* ATA + 0x24 UDMA Timing 4 */ + u32 udma5; /* ATA + 0x28 UDMA Timing 5 */ + u32 share_cnt; /* ATA + 0x2c ATA share counter */ + u32 reserved0[3]; + + /* FIFO registers */ + u32 fifo_data; /* ATA + 0x3c */ + u8 fifo_status_frame; /* ATA + 0x40 */ + u8 fifo_status; /* ATA + 0x41 */ + u16 reserved7[1]; + u8 fifo_control; /* ATA + 0x44 */ + u8 reserved8[5]; + u16 fifo_alarm; /* ATA + 0x4a */ + u16 reserved9; + u16 fifo_rdp; /* ATA + 0x4e */ + u16 reserved10; + u16 fifo_wrp; /* ATA + 0x52 */ + u16 reserved11; + u16 fifo_lfrdp; /* ATA + 0x56 */ + u16 reserved12; + u16 fifo_lfwrp; /* ATA + 0x5a */ + + /* Drive TaskFile registers */ + u8 tf_control; /* ATA + 0x5c TASKFILE Control/Alt Status */ + u8 reserved13[3]; + u16 tf_data; /* ATA + 0x60 TASKFILE Data */ + u16 reserved14; + u8 tf_features; /* ATA + 0x64 TASKFILE Features/Error */ + u8 reserved15[3]; + u8 tf_sec_count; /* ATA + 0x68 TASKFILE Sector Count */ + u8 reserved16[3]; + u8 tf_sec_num; /* ATA + 0x6c TASKFILE Sector Number */ + u8 reserved17[3]; + u8 tf_cyl_low; /* ATA + 0x70 TASKFILE Cylinder Low */ + u8 reserved18[3]; + u8 tf_cyl_high; /* ATA + 0x74 TASKFILE Cylinder High */ + u8 reserved19[3]; + u8 tf_dev_head; /* ATA + 0x78 TASKFILE Device/Head */ + u8 reserved20[3]; + u8 tf_command; /* ATA + 0x7c TASKFILE Command/Status */ + u8 dma_mode; /* ATA + 0x7d ATA Host DMA Mode configuration */ + u8 reserved21[2]; +}; + + +/* Function definition */ + + +static inline void +mpc52xx_ide_wait_tip_bit_clear(struct mpc52xx_ata __iomem *regs) +{ + int timeout = 1000; + + while (in_be32(®s->host_status) & MPC52xx_ATA_HOSTSTAT_TIP) + if (timeout-- == 0) + { + printk(KERN_ERR + "mpc52xx-ide: Timeout waiting for TIP clear\n"); + break; + } + udelay(10); /* FIXME: Necessary ??? */ +} + +extern void mpc52xx_ide_setup_hwif_iops(ide_hwif_t *hwif); + + +#endif /* __MPC52xx_IDE_H__ */ + diff --git a/drivers/block/mpc52xx/piofunc_inline.h b/drivers/block/mpc52xx/piofunc_inline.h new file mode 100644 index 0000000..ca89dec --- /dev/null +++ b/drivers/block/mpc52xx/piofunc_inline.h @@ -0,0 +1,250 @@ +/* + * mpc52xx_atablock / piofunc_inline.h + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + + +#ifndef __MPC52xx_BLOCKATA_PIOFUNC_H__ +#define __MPC52xx_BLOCKATA_PIOFUNC_H__ + + +/**************/ +/**************/ +/**************/ + +#include "mpc52xx_blockata.h" + +/**************/ +/**************/ +/**************/ + +#include + +/* + * Pio helper +*/ + +static inline u8 read_altstatus(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readb(&ata_regs->tf_control); +} + +static inline u8 read_status(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readb(&ata_regs->tf_command); +} + +static inline u8 read_sectorcnt(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readb(&ata_regs->tf_sec_count); +} + +static inline u8 read_sectornum(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readb(&ata_regs->tf_sec_num); +} + +static inline u8 read_cylhi(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readb(&ata_regs->tf_cyl_high); +} + +static inline u8 read_cyllo(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readb(&ata_regs->tf_cyl_low); +} + +static inline u8 read_devfeature(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readb(&ata_regs->tf_features); +} +static inline u8 read_error(struct mpc52xx_blockata_priv *priv) +{ + return read_devfeature(priv); +} + +static inline u16 read_data(struct mpc52xx_blockata_priv *priv) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + return readw(&ata_regs->tf_data); +} + +/**************/ +static inline void write_data(struct mpc52xx_blockata_priv *priv, u16 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writew(val, &ata_regs->tf_data); +} + +static inline void write_devfeature(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_features); +} + + +static inline void write_cyllo(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_cyl_low); +} + +static inline void write_cylhi(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_cyl_high); +} + +static inline void write_cmd(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_command); +} + +static inline void write_ctl(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_control); +} + +static inline void write_sectornum(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_sec_num); +} + +static inline void write_sectorcnt(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_sec_count); +} + + +static inline void write_devhead(struct mpc52xx_blockata_priv *priv, u8 val) +{ + struct mpc52xx_ata __iomem *ata_regs = priv->ata_regs; + writeb(val, &ata_regs->tf_dev_head); +} + + +/**************/ +static inline void ata_sleep(struct mpc52xx_blockata_priv *priv) +{ + read_altstatus(priv); + udelay(500); +} + +static inline void ata_sleep2(struct mpc52xx_blockata_priv *priv) +{ + read_altstatus(priv); + udelay(1); +} + +/**************/ +#define ATASTS_IS_NOTBUSY(__status__) ( ! ( (__status__) & ATA_BUSY) ) +#define ATASTS_IS_READY(__status__) ( ( (__status__) & (ATA_DRDY|ATA_BUSY) ) == ATA_DRDY ) +#define ATASTS_IS_DRQ(__status__) ( (__status__) & ATA_DRQ) +#define ATASTS_GOT_ERR(__status__) ( (__status__) & ATA_ERR) + +#define WAIT_TIMEOUT (1000*1000) + + +static inline u8 read_mystatus(struct mpc52xx_blockata_priv *priv) +{ + return read_altstatus(priv); +} + +static inline int wait_not_busy(struct mpc52xx_blockata_priv *priv) +{ + int timeout; + u8 status; + + timeout = WAIT_TIMEOUT; + + status = read_mystatus(priv); + if ( ATASTS_IS_NOTBUSY(status) ) + goto end; + + while(timeout--) { + status = read_mystatus(priv); + if ( ATASTS_IS_NOTBUSY(status) ) + goto end; + + ata_sleep2(priv); + } + + NPRINTK("%s: timeout, %x\n", __func__, status); + + return -1; + +end: + return 0; +} + + +static inline int wait_ready(struct mpc52xx_blockata_priv *priv) +{ + int timeout; + u8 status; + + timeout = WAIT_TIMEOUT; + + status = read_mystatus(priv); + if ( ATASTS_IS_READY(status) ) + goto end; + + while(timeout--) { + status = read_mystatus(priv); + if ( ATASTS_IS_READY(status) ) + goto end; + + ata_sleep2(priv); + } + + NPRINTK("%s: timeout, %x\n", __func__, status); + + return -1; + +end: + return ATASTS_GOT_ERR(status) ? -1 : 0; +} + +static inline int wait_drq(struct mpc52xx_blockata_priv *priv) +{ + int timeout; + u8 status; + + timeout = WAIT_TIMEOUT; + + status = read_mystatus(priv); + if ( ATASTS_IS_DRQ(status) ) + goto end; + + while(timeout--) { + status = read_mystatus(priv); + if ( ATASTS_IS_DRQ(status) ) + goto end; + + ata_sleep2(priv); + } + + NPRINTK("%s: timeout, %x\n", __func__, status); + return -1; + +end: + return ATASTS_GOT_ERR(status) ? -1 : 0; +} + +#endif diff --git a/drivers/block/mpc52xx/protos.h b/drivers/block/mpc52xx/protos.h new file mode 100644 index 0000000..aaee5b6 --- /dev/null +++ b/drivers/block/mpc52xx/protos.h @@ -0,0 +1,107 @@ +/* + * protos.h + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + + +/* + * Simply summaries every protos we use over the driver +*/ + +#ifndef __MPC52xx_BLOCKATA_PROTOS_H__ +#define __MPC52xx_BLOCKATA_PROTOS_H__ + +#include +#include +#include + +struct mpc52xx_blockata_priv; + +/**********/ +/* HWMISC */ +/**********/ +int mpc52xx_ata_setpiomode(struct mpc52xx_blockata_priv *priv, int pio_mode); +int mpc52xx_ata_init_drive(struct mpc52xx_blockata_priv *priv); +int mpc52xx_ata_isthispiovalid(struct mpc52xx_blockata_priv *priv, int pio_arg); +int mpc52xx_ata_doidentify(struct mpc52xx_blockata_priv *priv); +u32 mpc52xx_ata_getregisterbase(struct mpc52xx_blockata_priv *priv); +int mpx52xx_ata_dosetmulti(struct mpc52xx_blockata_priv *priv, u16 val); + + +/**********/ +/* ATA */ +/**********/ +int mpc52xx_ata_setupsector(struct mpc52xx_blockata_priv *priv, sector_t sector, int sector_num, int is_write); +int mpc52xx_ata_isthispiovalid(struct mpc52xx_blockata_priv *priv, int pio_arg); +void mpc52xx_ata_dumpreg(struct mpc52xx_blockata_priv *priv); +int mpc52xx_ata_doreset(struct mpc52xx_blockata_priv *priv); +int mpc52xx_ata_regcheck(struct mpc52xx_blockata_priv *priv); +int mpc52xx_ata_dodrivereset(struct mpc52xx_blockata_priv *priv); + +/************/ +/*DODRIVECMD*/ +/************/ +int mpc52xx_ata_dodrivecmd( + struct mpc52xx_blockata_priv *priv, + unsigned long *irqflags, + u8 *arg); + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + + +/**********/ +/* SDMA */ +/**********/ +int mpc52xx_ata_sdma_setup(struct mpc52xx_blockata_priv *priv); + +void sdma_ata_rx_init(struct bestcomm_taskhandle *mytaskhandle); +void sdma_ata_tx_init(struct bestcomm_taskhandle *mytaskhandle); +void sdma_ata_reset(struct bestcomm_taskhandle *mytaskhandle); +int sdma_ata_getirq(struct bestcomm_taskhandle *mytaskhandle); +void sdma_ata_clear_irq(struct bestcomm_taskhandle *mytaskhandle); +void sdma_ata_disable(struct bestcomm_taskhandle *mytaskhandle); +void sdma_ata_enable(struct bestcomm_taskhandle *mytaskhandle); + +void sdma_ata_submit_buffer( + struct bestcomm_taskhandle *mytaskhandle, + void *cookie, + void *data1, + void *data2, + int length); + +#endif /* CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA */ + +/**********/ +/*TRANSFER*/ +/**********/ +int mpc52xx_ata_docpupollread( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len); + +int mpc52xx_ata_docpupollwrite( + struct mpc52xx_blockata_priv*priv, + void *buffer, + int len); + +int mpc52xx_ata_dotransfer( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + char *buffer, + int is_write); + +irqreturn_t sdma_void_handler(struct mpc52xx_blockata_priv *priv); +irqreturn_t ata_void_handler(struct mpc52xx_blockata_priv *priv, u8 ata_status); + +void mpc52xx_ata_ack_blkreq(struct mpc52xx_blockata_priv *priv, int retval); + +#endif diff --git a/drivers/block/mpc52xx/sdmatask.c b/drivers/block/mpc52xx/sdmatask.c new file mode 100644 index 0000000..eb321e8 --- /dev/null +++ b/drivers/block/mpc52xx/sdmatask.c @@ -0,0 +1,142 @@ +/* + * mpc52xx_atablock / piofunc.c + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + + +/**************/ +/**************/ +/**************/ + +#include "mpc52xx_blockata.h" + + +/**************/ +/**************/ +/**************/ + + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + +#include + +/**************/ +/**************/ +/**************/ + +/**************/ +void sdma_ata_rx_init(struct bestcomm_taskhandle *mytaskhandle) +{ + struct sdma_ata_inc *inc; + inc = (struct sdma_ata_inc *)bestcomm_taskget_inctable(mytaskhandle); + + NPRINTK("inc = %p\n", inc); + + inc->incr_bytes = -(s16)sizeof(u16); + inc->incr_src = 0; + inc->incr_dst = sizeof(u16); +} + +/**************/ +/* + * Initialize ATA transmit task. + */ +void sdma_ata_tx_init(struct bestcomm_taskhandle *mytaskhandle) +{ + struct sdma_ata_inc *inc; + inc = (struct sdma_ata_inc *)bestcomm_taskget_inctable(mytaskhandle); + + NPRINTK("inc = %p\n", inc); + + inc->incr_bytes = -(s16)sizeof(u16); + inc->incr_src = sizeof(u16); + inc->incr_dst = 0; +} + +/**************/ +void sdma_ata_reset(struct bestcomm_taskhandle *mytaskhandle) +{ + struct sdma_ata_var *var; + var = (struct sdma_ata_var *)bestcomm_taskget_vartable(mytaskhandle); + + NPRINTK("var = %p\n", var); + + sdma_reset_buffers2((struct sdma *) mytaskhandle); + + var->bd_start = var->bd_base; +} + + +/**************/ +int sdma_ata_getirq(struct bestcomm_taskhandle *mytaskhandle) +{ + return bestcomm_taskget_irq(mytaskhandle); +} + +/**************/ +void sdma_ata_clear_irq(struct bestcomm_taskhandle *mytaskhandle) +{ + bestcomm_taskclear_irq(mytaskhandle); +} + +/**************/ +void sdma_ata_disable(struct bestcomm_taskhandle *mytaskhandle) +{ + bestcomm_taskdisable(mytaskhandle); +} + +/**************/ +void sdma_ata_enable(struct bestcomm_taskhandle *mytaskhandle) +{ + bestcomm_taskenable(mytaskhandle); +} + +int mpc52xx_ata_sdma_setup(struct mpc52xx_blockata_priv *priv) +{ + struct sdma *s; + + s = sdma_ata_preinit(MAX_DMA_BUFFERS); + if (!s) + return -1; + + priv->sdma = (struct bestcomm_taskhandle *)s; + return sdma_ata_init((struct bestcomm_taskhandle *)s, MAX_DMA_BUFFER_SIZE); +} + +/**************/ +void sdma_ata_submit_buffer( + struct bestcomm_taskhandle *mytaskhandle, + void *cookie, + void *data1, + void *data2, + int length) +{ + NPRINTK(" d1=%p, d2=%p, len=%d\n", data1, data2, length); + + sdma_submit_buffer2( + (struct sdma *)mytaskhandle, + cookie, data1, data2, length); +} + +/* + * Export stuff +*/ + +EXPORT_SYMBOL(sdma_ata_rx_init); +EXPORT_SYMBOL(sdma_ata_tx_init); +EXPORT_SYMBOL(sdma_ata_disable); +EXPORT_SYMBOL(sdma_ata_enable); +EXPORT_SYMBOL(sdma_ata_submit_buffer); +EXPORT_SYMBOL(sdma_ata_reset); +EXPORT_SYMBOL(sdma_ata_getirq); +EXPORT_SYMBOL(sdma_ata_clear_irq); +EXPORT_SYMBOL(mpc52xx_ata_sdma_setup); + + +#endif // CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + diff --git a/drivers/block/mpc52xx/skel.c b/drivers/block/mpc52xx/skel.c new file mode 100644 index 0000000..85b20ad --- /dev/null +++ b/drivers/block/mpc52xx/skel.c @@ -0,0 +1,1024 @@ +/* + * mpc52xx.c + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* + * Here we "translate" Linux API mess to our funcs +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************/ +/**************/ +/**************/ +#include "mpc52xx_blockata.h" + +/**************/ +/**************/ +/**************/ +static int local_setpiomode(struct mpc52xx_blockata_priv *priv, int piomode_arg); + +/**************/ +/**************/ +/**************/ + +static void dump_config(struct mpc52xx_blockata_priv *priv) +{ + VPRINTK(" configuration:\n"); + + if (priv->multi_available) + VPRINTK("\t * Multi-PIO commands: available (%d sectors per shot)\n", priv->multi_secpercmd ); + VPRINTK("\t * PIO Mode: %d\n", priv->piomode < 0 ? 0 : priv->piomode); + + if (priv->UsePacket) + VPRINTK("\t * Packet feature set supported\n"); + + if (priv->IsATAPI) + VPRINTK("\t * %s ATAPI device detected\n", priv->IsRemovable ? "Removable" : ""); + + VPRINTK("\t * LBA48 supported: %s\n", priv->drive_canlba48 ? "Yes" : "No"); + VPRINTK("\t * CHS: %s (%d/%d/%d)\n", priv->drive_chs_ok ? "Ok" : "Ko", priv->drive_chs_cylinders, priv->drive_chs_heads , priv->drive_chs_sectorspertrack); + VPRINTK("\t * SDMA Engine: %s\n", +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + "Enable"); +#else + "Disable" + ); +#endif +} + +/**************/ +/**************/ +/**************/ + + + +/* ATAPI-4 PIO specs (arranged for the 5200, cfr User Manual) */ +/* numbers in ns, extrapolation done by code */ +static int ataspec_t0[5] = {600, 383, 240, 180, 120}; +static int ataspec_t1[5] = { 70, 50, 30, 30, 25}; +static int ataspec_t2_8[5] = {290, 290, 290, 80, 70}; +static int ataspec_t2_16[5] = {165, 125, 100, 80, 70}; +static int ataspec_t2i[5] = { 0, 0, 0, 70, 25}; +static int ataspec_t4[5] = { 30, 20, 15, 10, 10}; +static int ataspec_ta[5] = { 35, 35, 35, 35, 35}; + + + +/**************/ +/**************/ +/**************/ +static int device_handlebio( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int *sectorcnt_ptr) +{ + int i; + struct bio_vec *bvec; + sector_t sector; + int sectorcnt_biovec; + int sectorcnt; + int is_write; + int ret; + + ret = -1; + is_write = bio_data_dir(bio); + sector = bio_getsector(bio); + + sectorcnt = 0; + + NPRINTK("bio=%p\n", bio); + + bio_for_each_segment(bvec, bio, i) + { + char *buffer; + + sectorcnt_biovec = bio_cur_sectors_idx(bio, i); + buffer = __bio_kmap_atomic(bio, i, KM_USER0); + ret = mpc52xx_ata_dotransfer( + priv, + req, bio, i, + sector, sectorcnt_biovec, + buffer, is_write); + + if (ret==0) + goto end; + + // Success or error, we kunmap the buffer -> it's done! + __bio_kunmap_atomic(buffer, KM_USER0); + + if (ret<0) + goto end; + + /* Ret > 0 -> done, do the next one */ + sector += ret; + sectorcnt += ret; + } + + ret = sector; + +end: + *sectorcnt_ptr = sectorcnt; + return ret; +} + +/**************/ +/**************/ +/**************/ +/* + * The main challenge! +*/ + +static int device_handlereq( + struct mpc52xx_blockata_priv *priv, + struct request *req, + int *sectorcnt_ptr) +{ + struct bio *bio; + int ret; + int nsect; + int nsect_thisbio; + + ret = 0; + nsect = 0; + + if (priv->UsePacket) + return -ENOTSUPP; + + if (priv->io_inprogress) + { + VPRINTK("IO already in-progress, can not do more!\n"); + ret = -EBUSY; + goto end; + } + + + rq_for_each_bio(bio, req) + { + ret = device_handlebio(priv, req, bio, &nsect_thisbio); + + if (ret<=0) + goto end; + + nsect += nsect_thisbio; + } + + ret = nsect; + +end: + *sectorcnt_ptr = nsect; + return ret; +} + +/**************/ +static void device_request(request_queue_t *q) +{ + struct mpc52xx_blockata_priv *priv = (struct mpc52xx_blockata_priv *) q->queuedata; + struct request *req; + int ret=0; + int sectorcnt; + + while ((req = elv_next_request(q)) != NULL) { + + if (! blk_fs_request(req)) { + printk (KERN_NOTICE "Skip non-fs request\n"); + end_request(req, 0); + continue; + } + + ret = device_handlereq(priv, req, §orcnt); + if (ret==0) { + NPRINTK("stop the queue\n"); + blk_stop_queue(q); + break; + } else if (ret>0) + end_request(req, 1); + else + end_request(req, 0); + } + + NPRINTK("end, ret=%d\n", ret); +} + + +/**************/ +/**************/ +/**************/ + +static int device_doreset( + struct mpc52xx_blockata_priv *priv, + int do_drivereset) +{ + int ret = 0; + + NPRINTK("priv->io_inprogress = %d, do_drivereset=%d\n", + priv->io_inprogress, do_drivereset ); + + if (priv->io_inprogress != 0) + mpc52xx_ata_ack_blkreq(priv, 0); + + ret = mpc52xx_ata_doreset(priv); + if(ret<0) + ret = -EIO; + + return ret; +} + +/* + * The device open/close func +*/ + +/**************/ +static int device_open(struct inode *inode, struct file *filp) +{ + unsigned long flags; + struct mpc52xx_blockata_priv *priv = inode->i_bdev->bd_disk->private_data; + int ret = 0; + int force_enableirq; + + NPRINTK("enter. sectors=%lld, usercnt=%d\n", priv->drive_sectors, priv->usercnt); + + spin_lock_irqsave(&priv->lock, flags); + force_enableirq = 0; + + if (priv->usercnt >= DEVICE_USERLIMIT) + { + VPRINTK("Error! Too much openers (%d)!\n", priv->usercnt); + ret = -1; + goto end; + } + + // device not inited ? -> try to init it a gain + if (!priv->drive_inited) + { + priv->drive_sectors = 0; + priv->drive_inited = 0; + + ret = mpc52xx_ata_init_drive(priv); + if (ret>=0) + priv->drive_inited = 1; + + ret = 0; + } + + NPRINTK("drive inited ok. s=%x, ret=%d\n", read_altstatus(priv), ret); + NPRINTK("drive looks ok. s=%x\n", read_altstatus(priv)); + + if ( (priv->usercnt==0) || force_enableirq ) + { + priv->ata_handler = ata_void_handler; + priv->sdma_handler = sdma_void_handler; + + write_ctl(priv, 0); + + priv->curio_bio = NULL; + priv->curio_req = NULL; + priv->io_inprogress = 0; + } + + priv->usercnt++; + +end: + spin_unlock_irqrestore(&priv->lock, flags); + + NPRINTK("end. ret=%d\n", ret); + return ret; +} + +/**************/ +static int device_release(struct inode *inode, struct file *filp) +{ + unsigned long flags; + int ret; + struct mpc52xx_blockata_priv *priv = inode->i_bdev->bd_disk->private_data; + + ret = 0; + + NPRINTK("enter usercnt=%d\n", priv->usercnt); + + spin_lock_irqsave(&priv->lock, flags); + priv->usercnt--; + + if (priv->usercnt==0) + { + write_ctl(priv, ATA_NIEN); + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + sdma_ata_disable(priv->sdma); + sdma_ata_clear_irq(priv->sdma); + sdma_ata_reset(priv->sdma); +#endif + } + + if (priv->usercnt<0) + VPRINTK("Warning! _release and _open count doesn't match!\n"); + + NPRINTK("end, ret=%d\n", ret); + + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +/**************/ +/**************/ +/**************/ +/* + * The device ioctl +*/ + +#ifndef HDIO_GETGEO_BIG + +/* + * Let's define GETGEO big because it's used! + * they removed it from recent linux kernel + * but already compiled software use it (eg: hdparm) + * + * this define and struct has been picked from linux 2.4.32 +*/ + +#define HDIO_GETGEO_BIG 0x330 +struct hd_big_geometry +{ + unsigned char heads; + unsigned char sectors; + unsigned int cylinders; + unsigned long start; +}; +#endif + +static int device_ioctl( + struct inode *inode, + struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + int ret; + unsigned long flags; + struct mpc52xx_blockata_priv *priv = inode->i_bdev->bd_disk->private_data; + + NPRINTK("%s: cmd=0x%x\n", __func__, cmd); + + spin_lock_irqsave(&priv->lock, flags); + + switch(cmd) + { + case HDIO_SET_MULTCOUNT: { + u16 multicount; + + NPRINTK("HDIO_SET_PIO_MODE. Try to set the multicount value to %ld\n", arg); + + if (priv->UsePacket) { + ret = -EIO; + break; + } + + multicount = (u16) arg; + + ret = mpx52xx_ata_dosetmulti(priv, multicount); + if (ret<0) + ret = -EFAULT; + } + break; + + case HDIO_GET_MULTCOUNT: + if (priv->UsePacket) { + ret = -EIO; + break; + } + + ret = put_user(priv->multi_secpercmd , (long __user *) arg); + break; + + case HDIO_DRIVE_CMD: { + u8 drivecmd_args[4+512]; + u8 drivecmd; + int lentocopy; + + if (NULL == (void *)arg) { + ret = -EINVAL; + break; + } + + if (copy_from_user(drivecmd_args, (void __user *) arg, sizeof(drivecmd_args) )) { + ret = -EFAULT; + break; + } + + drivecmd = drivecmd_args[0]; + ret = mpc52xx_ata_dodrivecmd(priv, &flags, drivecmd_args); + + lentocopy = + ( (drivecmd==ATA_CMD_ID_ATAPI) || (drivecmd==ATA_CMD_ID_ATA) ) + ? (512 + 4): 4; + + if (copy_to_user((void __user *)arg, drivecmd_args, lentocopy)) + ret = -EFAULT; + } + break; + + case HDIO_SET_PIO_MODE: { + int piomode; + NPRINTK("HDIO_SET_PIO_MODE. Try to set PIO %ld\n", arg); + + if (priv->io_inprogress) { + VPRINTK("IO already in-progress, can not do more!\n"); + ret = -EBUSY; + break; + } + + priv->io_inprogress = 1; + piomode = local_setpiomode(priv, (int) arg); + priv->io_inprogress = 0; + + if (piomode>0) + VPRINTK("PIO mode %d sat\n", piomode); + + ret = 0; + } + break; + + case HDIO_GET_IDENTITY: { + NPRINTK("HDIO_GET_IDENTITY:\n" ); + + if (priv->drive_identify_valid) { + if (copy_to_user((void __user *) arg, priv->drive_identify, sizeof(priv->drive_identify) )) + ret = -EFAULT; + else + ret = 0; + } else + ret = -1; + } + break; + + case HDIO_GETGEO: { + struct hd_geometry geo; + + NPRINTK("HDIO_GETGEO\n"); + + geo.cylinders = priv->drive_cylinders; + geo.heads = priv->drive_heads; + geo.sectors = priv->drive_sectorspertrack; + geo.start = 0; + if (copy_to_user((void __user *) arg, &geo, sizeof(geo))) + ret = -EFAULT; + else + ret = 0; + + NPRINTK("HDIO_GETGEO, ret=%d\n", ret); + } + break; + + case HDIO_GETGEO_BIG: { + struct hd_big_geometry biggeo; + + NPRINTK("HDIO_GETGEO_BIG\n"); + + biggeo.cylinders = priv->drive_cylinders; + biggeo.heads = priv->drive_heads; + biggeo.sectors = priv->drive_sectorspertrack; + biggeo.start = 0; + + if (copy_to_user((void __user *) arg, &biggeo, sizeof(biggeo))) + ret = -EFAULT; + else + ret = 0; + + NPRINTK("HDIO_GETGEO_BIG, ret=%d\n", ret); + } + break; + + case HDIO_DRIVE_RESET: { + VPRINTK("Issue a controller and drive reset\n"); + ret = device_doreset(priv, 1); + if(ret<0) + ret = -EIO; + + break; + } + + default: + ret = -EINVAL; + } + + NPRINTK("%s: end, ret=%d\n", __func__, ret); + + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +/**************/ +/**************/ +/**************/ +/* + * The device media changes/revalidate +*/ +static int device_media_changed(struct gendisk *gd) +{ + /* Our media won't move! */ + NPRINTK("\n"); + return 0; +} + +static int device_revalidate_disk(struct gendisk *gd) +{ + NPRINTK("\n"); + return 0; +} + +/**************/ +/**************/ +/**************/ +/* + * Modules prove/init +*/ + +static struct block_device_operations device_fops = + { + .owner = THIS_MODULE, + .open = device_open, + .release = device_release, + .ioctl = device_ioctl, + .media_changed = device_media_changed, + .revalidate_disk = device_revalidate_disk, + }; + +/**************/ +static void module_free(struct mpc52xx_blockata_priv *priv) +{ + NPRINTK("Enter. priv=%p\n", priv); + if (priv) + { + NPRINTK("free private private\n"); + + if (priv->ata_irq>=0) + free_irq(priv->ata_irq, priv); + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + if (priv->sdma_irq>=0) + free_irq(priv->sdma_irq, priv); +#endif + + NPRINTK("priv->device_queue=%p\n", priv->device_queue); + if (priv->device_queue) + blk_cleanup_queue(priv->device_queue); + + NPRINTK("priv->device_gendisk=%p\n", priv->device_gendisk); + if (priv->device_gendisk) + del_gendisk(priv->device_gendisk); + + NPRINTK("priv->major=%d\n", priv->major); + if (priv->major>0) + unregister_blkdev(priv->major, DEVSKEL_DEVICENAME); + + NPRINTK("free priv. p=%p\n", priv); + kfree(priv); + } + + NPRINTK("End\n"); +} + +static void module_remove(struct mpc52xx_blockata_priv *priv) +{ + printk(KERN_INFO DEVSKEL_DRIVERNAME ": Tchuss!\n"); + module_free(priv); +} + + + +/**************/ +/**************/ +/**************/ + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + +irqreturn_t generalsdma_handler(int irq, void *host) + { + struct mpc52xx_blockata_priv *priv = (struct mpc52xx_blockata_priv *) host; + irqreturn_t ret = IRQ_NONE; + + NPRINTK2("%s: irq=%d, sdma_handler %p\n", __func__, irq, priv->sdma_handler); + + if (irq == priv->sdma_irq) + { + ret = + (priv->sdma_handler) + ? priv->sdma_handler(priv) + : IRQ_HANDLED; + } else + { + ret = IRQ_NONE; + } + + return ret; + } + + +#endif + + irqreturn_t generalata_handler(int irq, void *host) + { + struct mpc52xx_blockata_priv *priv = (struct mpc52xx_blockata_priv *) host; + irqreturn_t ret; + + if (irq == priv->ata_irq) + { + u8 status; + status = read_status(priv); + + ret = + (priv->ata_handler) + ? priv->ata_handler(priv, status) + : IRQ_HANDLED; + } else + { + ret = IRQ_NONE; + } + + return ret; + } + + + static void + mpc52xx_ide_apply_timing(struct mpc52xx_ata __iomem *regs, struct mpc52xx_ata_timings *timing) + { + out_be32(®s->pio1, timing->pio1); + out_be32(®s->pio2, timing->pio2); + out_be32(®s->mdma1, timing->mdma1); + out_be32(®s->mdma2, timing->mdma2); + out_be32(®s->udma1, timing->udma1); + out_be32(®s->udma2, timing->udma2); + out_be32(®s->udma3, timing->udma3); + out_be32(®s->udma4, timing->udma4); + out_be32(®s->udma5, timing->udma5); + } + + static void + mpc52xx_ide_compute_pio_timing( struct mpc52xx_ata_timings *timing, unsigned int ipb_period, u8 pio) + { + u32 t0, t2_8, t2_16, t2i, t4, t1, ta; + + /* We add 1 as a 'margin' */ + t0 = 1 + CALC_CLK_VALUE_UP(ipb_period, 1000*ataspec_t0[pio]); + t2_8 = 1 + CALC_CLK_VALUE_UP(ipb_period, 1000*ataspec_t2_8[pio]); + t2_16 = 1 + CALC_CLK_VALUE_UP(ipb_period, 1000*ataspec_t2_16[pio]); + t2i = 1 + CALC_CLK_VALUE_UP(ipb_period, 1000*ataspec_t2i[pio]); + t4 = 1 + CALC_CLK_VALUE_UP(ipb_period, 1000*ataspec_t4[pio]); + t1 = 1 + CALC_CLK_VALUE_UP(ipb_period, 1000*ataspec_t1[pio]); + ta = 1 + CALC_CLK_VALUE_UP(ipb_period, 1000*ataspec_ta[pio]); + + timing->pio1 = (t0 << 24) | (t2_8 << 16) | (t2_16 << 8) | (t2i); + timing->pio2 = (t4 << 24) | (t1 << 16) | (ta << 8); + } + + static int local_setpiomode(struct mpc52xx_blockata_priv *priv, int piomode_arg) + { + int piomode; + int ret; + + NPRINTK(KERN_DEBUG DEVSKEL_DEVICENAME "pio mode arg=%d\n", piomode_arg); + + piomode = mpc52xx_ata_isthispiovalid(priv, piomode_arg); + if (piomode<0) + return -1; + + NPRINTK(KERN_DEBUG DEVSKEL_DEVICENAME "pio mode=%d\n", piomode); + + ret = mpc52xx_ata_setpiomode(priv, piomode); + if (ret<0) + return -1; + + mpc52xx_ide_compute_pio_timing(&priv->timings[0], priv->ipb_period, piomode); + mpc52xx_ide_apply_timing(priv->ata_regs, &priv->timings[0]); + + return piomode; + } + + static int + mpc52xx_ide_setup( + struct mpc52xx_ata __iomem *regs, + struct mpc52xx_blockata_priv *priv) + { + +#define MPC52xx_IPBFREQ (132*1000*1000) + + /* Vars */ + + int tslot; + + NPRINTK("%s: enter\n", __func__); + + out_8(®s->dma_mode, MPC52xx_ATA_DMAMODE_FR); + udelay(10); + + /* All sample code do this */ + out_be32(®s->share_cnt, 0); + + /* Configure & Reset host */ + out_be32(®s->config, + MPC52xx_ATA_HOSTCONF_IE | + MPC52xx_ATA_HOSTCONF_IORDY | + MPC52xx_ATA_HOSTCONF_SMR | + MPC52xx_ATA_HOSTCONF_FR); + udelay(10); + out_be32(®s->config, + MPC52xx_ATA_HOSTCONF_IE | + MPC52xx_ATA_HOSTCONF_IORDY); + + /* Get IPB bus period */ + priv->ipb_period = 1000000000 / (MPC52xx_IPBFREQ/1000); + + /* Try to set the time slot to around 1us = 1000000 ps */ + tslot = CALC_CLK_VALUE_UP(priv->ipb_period, 1000000); + out_be32(®s->share_cnt, tslot << 16); + + + /* Init imings to PIO0 (safest) */ + memset(priv->timings, 0x00, 2*sizeof(struct mpc52xx_ata_timings)); + + mpc52xx_ide_compute_pio_timing(&priv->timings[0], priv->ipb_period, 0); + mpc52xx_ide_compute_pio_timing(&priv->timings[1], priv->ipb_period, 0); + + mpc52xx_ide_apply_timing(regs, &priv->timings[0]); + + return 0; + } + + + + + static int some_hwinit(struct mpc52xx_blockata_priv *priv) + { + struct mpc52xx_gpio __iomem *gpio_regs; + struct mpc52xx_ata __iomem *ata_regs; + struct device_node *of_dev; + int ata_irq; + u32 res_mem; + int sdma_irqnum; + int ret; + + ata_irq = -1; + sdma_irqnum = -1; + + ata_regs = NULL; + gpio_regs = NULL; + res_mem = 0; + + + + priv->ata_irq = -1; + priv->sdma_irq = -1; + + of_dev = of_find_compatible_node(NULL, "ata", "mpc5200-ata"); + if (of_dev == NULL) + return -ENODEV; + + + /* Get the resources of this device */ + ata_irq = irq_of_parse_and_map(of_dev, 0); + if (ata_irq<0) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Invalid resource set!\n"); + return -EINVAL; + } + + ret = request_irq(ata_irq, generalata_handler, SA_INTERRUPT, DEVSKEL_DRIVERNAME " ATA interrupt", priv); + if (ret) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can not allocate interrupt for the ATA controller\n"); + ata_irq=-1; + goto error; + } + priv->ata_irq = ata_irq; + + res_mem = mpc52xx_ata_getregisterbase(priv); + if (!res_mem) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Unable to locate ATA registers\n"); + ret = -ENOMEM; + goto error; + } + + ata_regs = ioremap(res_mem, sizeof(struct mpc52xx_ata)); + if (!ata_regs) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Unable to ioremap ATA registers\n"); + ret = -ENOMEM; + goto error; + } + + priv->ata_regs = ata_regs; + priv->ata_regs_bus = res_mem; + + /* Setup the ATA controller */ + ret = mpc52xx_ide_setup(ata_regs, priv); + if (ret) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Controller setup failed !\n"); + goto error; + } + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + ret = mpc52xx_ata_sdma_setup(priv); + if (ret) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": SDMA setup failed !\n"); + goto error; + } + + sdma_irqnum = sdma_ata_getirq(priv->sdma); + ret = request_irq(sdma_irqnum, generalsdma_handler, SA_INTERRUPT, DEVSKEL_DRIVERNAME " SDMA interrupt", priv); + if (ret) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can not allocate interrupt for the SDMA Task\n"); + goto error; + } + + priv->sdma_irq = sdma_irqnum; + + NPRINTK("%s: ATA irq=%d, SDMA IRQ=%d\n", __func__, ata_irq, priv->sdma_irq); + +#endif + + return 0; + + error: + return ret; + } + + /**************/ + static struct mpc52xx_blockata_priv *module_probe(int *retcode) + { + int ret; + struct mpc52xx_blockata_priv *priv; + struct gendisk *device_gendisk; + struct request_queue *device_queue; + + ret = 0; + priv = NULL; + device_gendisk = NULL; + device_queue = NULL; + + NPRINTK("enter\n"); + + + /* Setup private structure */ + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (!priv) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can't allocate private structure !\n"); + ret = -ENOMEM; + goto error; + } + memset(priv, 0, sizeof (*priv) ); + + + spin_lock_init(&priv->lock); + priv->major = -1; + + NPRINTK("device privata data ok. p=%p\n", priv); + + ret = some_hwinit(priv); + if(ret<0) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can't allcoate some private stuff !\n"); + goto error; + } + + priv->drive_sectors = 0; + priv->drive_inited = 0; + + /* + * I prefer to the drive init here. Indeed, in case of failure + * (for example no drive present), the linux block code generally exploses + * + */ + ret = mpc52xx_ata_init_drive(priv); + if (ret<0) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can not init the ATA drive\n"); + goto error; + } + + ret = register_blkdev(0, DEVSKEL_DEVICENAME); + if (ret<0) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can not register the block device\n"); + goto error; + } + priv->major = ret; + + NPRINTK("block device registered with major %d\n", ret); + + // minors must be >1 for partition mess (?) + device_gendisk = alloc_disk(32); + if (!device_gendisk) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can not allocate a block disk\n"); + goto error; + } + priv->device_gendisk = device_gendisk; + + device_gendisk->major = priv->major; + device_gendisk->first_minor = 0; + device_gendisk->fops = &device_fops; + sprintf(device_gendisk->disk_name, DEVSKEL_DEVICENAME); + + NPRINTK("device disk allocated. p=%p\n", device_gendisk); + + device_queue = blk_init_queue(device_request, &priv->lock); + if (!device_queue) + { + printk(KERN_ERR DEVSKEL_DRIVERNAME ": Can not init the block queue\n"); + ret = -1; + goto error; + } + + priv->device_queue = device_queue; + device_gendisk->queue = device_queue; + + NPRINTK("block queue ok. p=%p\n", device_queue); + + device_queue->queuedata = priv; + device_gendisk->private_data = priv; + + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_MAXPIO + NPRINTK("Set pio mode to max\n"); + priv->piomode = local_setpiomode( priv, -1); +#endif + + priv->drive_inited = 1; + + /* Only add the disk as very last step */ + blk_queue_max_phys_segments(device_queue, DRIVER_MAXHWSEG); + blk_queue_max_sectors(priv->device_queue, DRIVER_MAXHWSEG); + + // This should be the multi pio value + blk_queue_max_hw_segments(device_queue, priv->multi_secpercmd); + + set_capacity(priv->device_gendisk, priv->drive_sectors); + add_disk(device_gendisk); + + init_waitqueue_head(&priv->my_waitqueue); + + printk(KERN_INFO DEVSKEL_DRIVERNAME ": %s (Version %s - Compiled date %s at %s)\n", DEVSKEL_DRIVERNAME, DEVSKEL_DRIVERVERSION, __DATE__, __TIME__); + dump_config(priv); + + *retcode = 0; + return priv; + + error: + + module_free(priv); + + *retcode = (ret == 0) ? -1 : ret; + return NULL; + } + + /**************/ + /**************/ + /**************/ + /* + * Kernel call for module load/remove + */ + struct mpc52xx_blockata_priv *global_priv = NULL; + +static int __init kernelcall_init(void) +{ + int retcode; + + global_priv = module_probe(&retcode); + return retcode; +} + +static void __exit kernelcall_exit(void) +{ + module_remove(global_priv); + global_priv = NULL; +} + + +module_init(kernelcall_init); +module_exit(kernelcall_exit); + +MODULE_AUTHOR("bplan GmbH"); +MODULE_DESCRIPTION(DEVSKEL_DRIVERNAME); +MODULE_LICENSE("GPL"); + diff --git a/drivers/block/mpc52xx/transfer.c b/drivers/block/mpc52xx/transfer.c new file mode 100644 index 0000000..50f5f3a --- /dev/null +++ b/drivers/block/mpc52xx/transfer.c @@ -0,0 +1,932 @@ +/* + * mpc52xx_atablock / transfer.c + * + * Copyright 2006 bplan GmbH + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* + * real brain! +*/ + + +#include "mpc52xx_blockata.h" + + +/**************/ +/**************/ +/**************/ +static int ata_dopollwrite( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len); +static int ata_dopollread( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len); + +/**************/ +/**************/ +/**************/ +/* + * We could check for unexpected interrupt or status here +*/ + +irqreturn_t sdma_void_handler(struct mpc52xx_blockata_priv *priv) + { + NPRINTK("%s: \n", __func__); + return IRQ_HANDLED; + } + + irqreturn_t ata_void_handler(struct mpc52xx_blockata_priv *priv, u8 ata_status) + { + NPRINTK("%s: status=0x%2.2x\n", __func__, ata_status); + return IRQ_HANDLED; + } + + + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + + /**************/ + static int start_writerequest( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + u16 * buffer); +static void submit_writebuffer( + struct mpc52xx_blockata_priv *priv, + u16 *buffer, + int curio_secidx); + +static void submit_readbuffer( + struct mpc52xx_blockata_priv *priv, + u16 *buffer, + int curio_secidx); +static int submitandenable_readbuffer( + struct mpc52xx_blockata_priv *priv, + u16 *buffer); +static int start_readrequest( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + u16 * buffer); + +/**************/ +void mpc52xx_ata_ack_blkreq(struct mpc52xx_blockata_priv *priv, int retval) +{ + struct request *req; + + req = priv->curio_req; + + priv->curio_bio = NULL; + priv->curio_req = NULL; + priv->sdma_handler = sdma_void_handler; + priv->ata_handler = ata_void_handler; + + priv->io_inprogress = 0; + + // Ack the req if any + if (req) + end_request(req, retval); + + // Make sure to restart the queue + blk_start_queue(priv->device_queue); +} + +/**************/ +static int inhandlercheck_atastatus( + struct mpc52xx_blockata_priv *priv, + u8 ata_status) +{ + if ( ATASTS_GOT_ERR(ata_status) ) + { + VPRINTK("ATA Error, transfer aborted"); + ATA_DUMPREG + + mpc52xx_ata_ack_blkreq(priv, 0); + + // taut! + return -1; + } + + return 0; +} + + +/**************/ +/* + * This func will do the necessary operation when a bio iovec is done + * -> end the transfer if there are no more job + * -> start a new io vec if any + * -> start a new bio if any + * + * This is the same for RX and TX buffer that's why I wrote this static + * (and hopefully inlined !) func + * +*/ +static inline u16 *handle_nextbio( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio **ptr_bio, + int *ptr_bio_idx, + sector_t *ptr_sector, + int *ptr_sectorcnt) +{ + struct bio *bio; + int bio_idx; + int lastvec_sectorcnt; + sector_t sector; + + bio = priv->curio_bio; + + // need to do! + NPRINTK2(KERN_DEBUG "One iovec done. bio idx=%d. cnt=%d\n", bio->bi_idx, bio->bi_vcnt); + + /* Unmap the previous one + * kmap stuff is a bit strange + * the LDD manual pass the bio, but the + * macro seen to accept the buffer... + * maybe API change between the LDD (3rd release) + * and this Linux kernel (2.6.16.15 but also in 2.6.17.1). + * anyway, kmap* seens void. + */ + __bio_kunmap_atomic( bio_data(bio), KM_USER0); + + bio_idx = priv->curio_bioidx; + lastvec_sectorcnt = bio_cur_sectors_idx(bio, bio_idx); + + bio_idx++; + if (bio_islastidx(bio, bio_idx) ) + { + // Finish with this bio + + // Let's see if there is another bio in this req + bio = bio_getnext(bio); + if (!bio) { + // we have done! + NPRINTK(KERN_DEBUG "req (%p, bio=%p) over, ack it and start the queue\n", + req, bio); + + mpc52xx_ata_ack_blkreq(priv, 1); + + // vertig + return NULL; + } + + NPRINTK(KERN_DEBUG "bio over,but new one %p\n", bio); + + // Set up stuff for the new bio + bio_idx = bio_getcuridx(bio); + sector = bio_getsector(bio); + } else + { + // Set up stuff for the next bio iovec + NPRINTK(KERN_DEBUG "bio iovec over,but new one %d\n", bio_idx); + + sector = priv->curio_sector + lastvec_sectorcnt; + } + + *ptr_bio = bio; + *ptr_sector = sector; + *ptr_bio_idx = bio_idx; + *ptr_sectorcnt = bio_cur_sectors_idx(bio, bio_idx); + + // Small notes, I'm really not sure kmap atomix is smart here + // We should prolly use kmap_irq as we will prolly be called from an interrupt + return __bio_kmap_atomic(bio, bio_idx, KM_USER0); +} + +/**************/ + +irqreturn_t ata_complete_txtransfer_handler(struct mpc52xx_blockata_priv *priv, u8 ata_status) + { + int secidx; + int sectodo; + int secpershot; + u16 *buffer; + + NPRINTK2("curio_secidx=%d, curio_sectodo=%d. bio=%p, req=%p. s=0x%x\n", + priv->curio_secidx, priv->curio_sectodo, priv->curio_bio, priv->curio_req, ata_status ); + + // Make sure to kill the task first + sdma_ata_disable(priv->sdma); + + // Sanity check! + if ( ! priv->io_inprogress ) + goto end; + + if ( inhandlercheck_atastatus(priv, ata_status ) ) + goto end; + + // grab some data + secidx = priv->curio_secidx; + sectodo = priv->curio_sectodo; + secpershot = priv->curio_secpershot; + + secidx += secpershot; + if (secidx < sectodo) + { + if ( ATASTS_IS_READY(ata_status) ) { + // Update the buffer to the position of the new sector to write + // buffer is a u16* + buffer = ( (u16 *) priv->curio_buffer) + (priv->curio_secpershot * 256); + + submit_writebuffer(priv, buffer, secidx); + + sdma_ata_enable(priv->sdma); + } else { + // Error here, DRQ should be set + goto end; + } + } else + { + struct bio *bio; + struct request *req; + int ret; + int bio_idx; + int sectorcnt; + u16 *buffer; + sector_t sector; + + req = priv->curio_req; + buffer = handle_nextbio(priv, req, &bio, &bio_idx, §or, §orcnt); + + ret = + buffer + ? start_writerequest(priv, req, bio, bio_idx, sector, sectorcnt, buffer) + : -1; + } + + end: + return IRQ_HANDLED; + } + + + + /**************/ + irqreturn_t sdma_complete_rxtransfer_handler(struct mpc52xx_blockata_priv *priv); + +irqreturn_t ata_wait_rxready_handler(struct mpc52xx_blockata_priv *priv, u8 ata_status) + { + NPRINTK2("%s: \n", __func__); + + // Sanity check! + if (!priv->io_inprogress) + { + // Should report some erro rhere! + goto end; + } + + if ( inhandlercheck_atastatus(priv, ata_status ) ) + goto end; + + if ( ATASTS_IS_DRQ(ata_status) ) + { + // Set up the new handlers + priv->sdma_handler = sdma_complete_rxtransfer_handler; + priv->ata_handler = ata_void_handler; + + // Now start the transfer + submitandenable_readbuffer(priv, priv->curio_buffer); + } + + end: + return IRQ_HANDLED; + } + + irqreturn_t sdma_complete_rxtransfer_handler(struct mpc52xx_blockata_priv *priv) + { + int secidx; + int sectodo; + int secpershot; + u16 *buffer; + + NPRINTK("curio_secidx=%d, curio_sectodo=%d. bio=%p, req=%p.\n", + priv->curio_secidx, priv->curio_sectodo, priv->curio_bio, priv->curio_req ); + + // Make sure to kill the task first + sdma_ata_disable(priv->sdma); + + // Sanity check! + if ( ! priv->io_inprogress ) + goto end; + + // grab + secidx = priv->curio_secidx; + sectodo = priv->curio_sectodo; + secpershot = priv->curio_secpershot; + + if ( inhandlercheck_atastatus(priv, read_altstatus(priv) ) ) + goto end; + + secidx += secpershot; + if (secidx < sectodo) + { +#if 0 + u8 ata_status; + + // Rx transfer for this sector over, wait for intr for the new one + priv->sdma_handler = sdma_void_handler; + priv->ata_handler = ata_wait_rxready_handler; + + // reading the status reg should acknowledge this transfer + // and start the next one + ata_status = read_status(priv); +#else + // Stuff data to transfer for this iovec + //if ( IS_READY( read_altstatus(priv) ) ) + { + buffer = ( (u16 *) priv->curio_buffer) + (secpershot * 256); + + NPRINTK("%s: ATA ready -> read task started. Sector=%lld\n", __func__, priv->curio_sector); + + submit_readbuffer(priv, buffer, secidx); + + // I don't like to wait here + wait_drq(priv); + sdma_ata_enable(priv->sdma); + } +#endif + } else + { + struct bio *bio; + struct request *req; + int ret; + int bio_idx; + int sectorcnt; + u16 *buffer; + sector_t sector; + + req = priv->curio_req; + buffer = handle_nextbio(priv, req, &bio, &bio_idx, §or, §orcnt); + + ret = + buffer + ? start_readrequest(priv, req, bio, bio_idx, sector, sectorcnt, buffer) + : -1; + } + + end: + return IRQ_HANDLED; + } + + + /* + * This func will setup the ATA and the SDMA buffers + * and install the ATA handler + */ + /**************/ + static void submit_writebuffer( + struct mpc52xx_blockata_priv *priv, + u16 *buffer, + int curio_secidx) + { + u32 port_BusAddr; + u32 addr_BusAddr; + + port_BusAddr = ATAFIFO_BUSADDR; + addr_BusAddr = virt_to_phys( (void*) buffer); + + NPRINTK("priv=%p, , port bus=0x%8.8x, addr bus =0x%8.8x, buf=%p, idx=%d\n", + priv, port_BusAddr, addr_BusAddr, buffer, curio_secidx); + + // reset the buf + sdma_ata_reset(priv->sdma); + + sdma_ata_submit_buffer( + priv->sdma, + (void *) buffer, + (void *) addr_BusAddr, + (void *) port_BusAddr, + 512*priv->curio_secpershot); + + priv->curio_secidx = curio_secidx; + priv->curio_buffer = buffer; + } + + + static int start_writerequest( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + u16 *buffer) + { + + int ret; + int secperreq; + int sectodo; + int secpershot; + + NPRINTK("%s: sector=%ld, sectorcnt=%d, buffer=%p\n", __func__, (long) sector, sectorcnt, buffer); + + secperreq = priv->multi_secpercmd; + sectodo = sectorcnt > secperreq ? secperreq : sectorcnt; + secpershot = sectodo ; + + ret = wait_not_busy(priv); + if (ret!=0) + return ret; + mpc52xx_ata_setupsector(priv, sector, sectodo, 1); + + // I don't like to call wait here + ret = wait_ready(priv); + if (ret!=0) + { + NPRINTK("can't wait rdy=%d. 0x%x\n", ret, read_altstatus(priv)); + ATA_DUMPREG + return ret; + } + + priv->curio_sector = sector; + priv->curio_secpershot = secpershot; + priv->curio_sectodo = sectodo; + priv->curio_req = req; + priv->curio_bio = bio; + priv->curio_bioidx = bio_index; + + priv->sdma_handler = sdma_void_handler; + priv->ata_handler = ata_complete_txtransfer_handler; + + priv->io_inprogress = 1; + + write_cmd(priv, priv->curio_atacmd); + ret = wait_drq(priv); + if (ret!=0) + { + ATA_DUMPREG + return ret; + } + + submit_writebuffer(priv, (u16 *) buffer, 0); + sdma_ata_enable(priv->sdma); + + return 0; + } + + + /**************/ + + /**************/ + static void submit_readbuffer( + struct mpc52xx_blockata_priv *priv, + u16 *buffer, + int curio_secidx) + { + u32 port_BusAddr; + u32 addr_BusAddr; + + port_BusAddr = ATAFIFO_BUSADDR; + addr_BusAddr = virt_to_phys( (void*) buffer); + + NPRINTK2("priv=%p, , port bus=0x%8.8x, addr bus =0x%8.8x, buf=%p, idx=%d\n", + priv, port_BusAddr, addr_BusAddr, buffer, curio_secidx); + + // reset the buf + sdma_ata_reset(priv->sdma); + + sdma_ata_submit_buffer( + priv->sdma, + (void *) buffer, + (void *) port_BusAddr, + (void *) addr_BusAddr, + 512*priv->curio_secpershot); + + priv->curio_secidx = curio_secidx; + priv->curio_buffer = buffer; + } + + static int submitandenable_readbuffer( + struct mpc52xx_blockata_priv *priv, + u16 *buffer) + { + + submit_readbuffer(priv, (u16 *) buffer, 0); + sdma_ata_enable(priv->sdma); + + return 0; + } + + static int start_readrequest( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + u16 *buffer) + { + int ret; + int secperreq; + int sectodo; + int secpershot; + + NPRINTK2("%s: sector=%ld, sectorcnt=%d, buffer=%p, cmd=0%2.2x\n", __func__, (long) sector, sectorcnt, buffer, priv->curio_atacmd); + + secperreq = priv->multi_secpercmd; + sectodo = sectorcnt > secperreq ? secperreq : sectorcnt; + secpershot = sectodo; + + ret = wait_not_busy(priv); + if (ret!=0) + return ret; + + mpc52xx_ata_setupsector(priv, sector, sectodo, 0); + + ret = wait_ready(priv); + if (ret!=0) + { + NPRINTK("can't wait rdy=%d. 0x%x\n", ret, read_altstatus(priv)); + ATA_DUMPREG + return ret; + } + + priv->curio_sector = sector; + priv->curio_secpershot = secpershot; + priv->curio_sectodo = sectodo; + priv->curio_req = req; + priv->curio_bio = bio; + priv->curio_bioidx = bio_index; + + priv->sdma_handler = sdma_void_handler; + priv->ata_handler = ata_wait_rxready_handler; + + priv->io_inprogress = 1; + + /* + * We only start the task when we got the drive int + */ + priv->curio_secidx = 0; + priv->curio_buffer = buffer; + write_cmd(priv, priv->curio_atacmd); + + return 0; + } +#endif + + + +#ifndef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA + /**************/ + /**************/ + /**************/ + /* + * This do_read func use polling -> not interrupt! + */ + + static int do_readrequest( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + u16 * buffer) + { + int ret; + int secperreq; + int sectodo; + int sectorcnt_original = sectorcnt; + + NPRINTK("%s: sector=%ld, sectorcnt=%d, buffer=%p\n", __func__, (long) sector, sectorcnt, buffer); + + secperreq = priv->multi_secpercmd; + + while(sectorcnt>0) + { + int i; + sectodo = sectorcnt > secperreq ? secperreq : sectorcnt; + + ret = wait_not_busy(priv); + if (ret!=0) + return -1; + mpc52xx_ata_setupsector(priv, sector, sectodo, 0); + + ret = wait_ready(priv); + if (ret!=0) { + ATA_DUMPREG + return ret; + } + + if (priv->multi_available) { + write_cmd(priv, ATA_CMD_READ_MULTI); + ret = wait_drq(priv); + if (ret!=0) { + ATA_DUMPREG + return ret; + } + ret = ata_dopollread(priv, buffer, 256*sectodo ); + buffer = ( ((u16*) buffer) + 256*sectodo ); + } else { + write_cmd(priv, ATA_CMD_PIO_READ); + + for(i=0; i < sectodo; i++) { + ret = wait_drq(priv); + if (ret!=0) { + ATA_DUMPREG + return ret; + } + + ret = ata_dopollread(priv, buffer, 256); + + // Check! + + buffer =( ((u16*) buffer) + 256 ); + } + } + + sector += sectodo; + sectorcnt -= sectodo; + } + + return sectorcnt_original ; + } + + /**************/ + static int do_writerequest( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + u16 *buffer) + { + int ret; + int secperreq; + int sectodo; + int sectorcnt_original = sectorcnt; + + NPRINTK("%s: sector=%ld, sectorcnt=%d, buffer=%p\n", __func__, (long) sector, sectorcnt, buffer); + + secperreq = priv->multi_secpercmd; + + while(sectorcnt) + { + int i; + sectodo = sectorcnt > secperreq ? secperreq : sectorcnt; + + ret = wait_not_busy(priv); + if (ret!=0) + return ret; + mpc52xx_ata_setupsector(priv, sector, sectodo, 1); + + ret = wait_ready(priv); + if (ret!=0) { + NPRINTK("can't wait rdy=%d. 0x%x\n", ret, read_altstatus(priv)); + ATA_DUMPREG + return ret; + } + + if (priv->multi_available) { + /* If the drive support the multi write, let's go! + * should be really faster as we don't have to wait for DRQ + * However, either I make something wrong, either I did not find + * any disk supporting this! + */ + write_cmd(priv, ATA_CMD_WRITE_MULTI); + ret = wait_drq(priv); + if (ret!=0) { + NPRINTK("can't wait drq %d\n", ret); + ATA_DUMPREG + return ret; + } + + ret = ata_dopollwrite(priv, buffer, 256*sectodo); + buffer = ( (u16*) buffer + 256*sectodo ); + } else { + write_cmd(priv, ATA_CMD_PIO_WRITE); + for(i=0; i < sectodo; i++) { + ret = wait_drq(priv); + if (ret!=0) { + ATA_DUMPREG + return ret; + } + + ret = ata_dopollwrite(priv, buffer, 256 ); + + // Check! + + buffer = ( ((u16*) buffer) + 256 ); + } + } + + sector += sectodo; + sectorcnt-= sectodo; + } + + //NPRINTK("return %d\n", sectorcnt_original); + return sectorcnt_original; + } +#endif /* CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA */ + + /**************/ + /**************/ + /**************/ + /* + * This is call by nicoskel + */ + + int mpc52xx_ata_dotransfer( + struct mpc52xx_blockata_priv *priv, + struct request *req, + struct bio *bio, + int bio_index, + sector_t sector, + int sectorcnt, + char *buffer, + int is_write) + { +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA +#warning Using interrupt based SDMA + int ret; + + if (is_write) + { + /* + * for TX beter wait the drive interrupt + * because we first write into drive buffer and the drive do the real stuff + */ + sdma_ata_tx_init(priv->sdma); + + ret = start_writerequest(priv, req, bio, bio_index, sector, sectorcnt, (u16 *) buffer); + } else + { + /* + * for RX better write for sdma int + * because the drive will first to the read and then the SDMA task fetch + * the data from the FIFO + */ + sdma_ata_rx_init(priv->sdma); + + // Wait for int before starting the task + ret = start_readrequest(priv, req, bio, bio_index, sector, sectorcnt, (u16 *) buffer); + } + + return ret; + +#else /* CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA */ +#warning Using poll based SDMA -> Slow and obsolet! + return + is_write + ? do_writerequest(priv, req, bio, bio_index, sector, sectorcnt, (u16 *) buffer) + : do_readrequest(priv, req, bio, bio_index, sector, sectorcnt, (u16 *) buffer); +#endif /* CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA */ + } + + + + /**************/ + /**************/ + int mpc52xx_ata_docpupollread( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len) + { + u16 *buffer16 = (u16 *) buffer; + int local_len = len; + + while(local_len--) + *buffer16++ = read_data(priv); + + return len - local_len; + } + +#ifdef CONFIG_BLK_DEV_MPC52XX_ATAPIO_SDMA +#if 0 + static int ata_dosdmapollwrite( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len) + { + int ret = 0; + u32 port_BusAddr; + u32 addr_BusAddr; + int taskirq; + int timeout = 10*1000; + + //NPRINTK("%s: have to read %d , from port 0x%8.8lx to mem %p\n", __func__, count, port, addr); + + taskirq = priv->sdma_irq; + port_BusAddr = ATAFIFO_BUSADDR; + + addr_BusAddr = virt_to_phys( (void*) buffer); + + //NPRINTK("%s:priv=%p, irq=%d, port bus=0x%8.8x, addr bus =0x%8.8x\n", + // __func__, priv, taskirq, port_BusAddr, addr_BusAddr) + + // Turn the SDMA into RX + sdma_ata_tx_init(priv->sdma); + + sdma_ata_submit_buffer(priv->sdma, (void *)buffer, (void *) addr_BusAddr, (void *)port_BusAddr, len*2); + + sdma_ata_clear_irq(priv->sdma); + sdma_ata_enable(priv->sdma); + + for(;;) + { + + u32 val; + val = in_be32(&sdma.io->IntPend); + if ( (val & (1 << priv->sdma->tasknum) ) ) + break; + + if (timeout--<=0) { + ret = -1; + printk("timeout 0x%x\n", read_altstatus(priv) ); + break; + } + + udelay(1); + } + + sdma_ata_disable(priv->sdma); + sdma_ata_reset(priv->sdma); + + return 0; + } + + static int ata_dosdmapollread( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len) + { + int ret = 0; + u32 port_BusAddr; + u32 addr_BusAddr; + int taskirq; + int timeout = 10*1000; + + taskirq = priv->sdma_irq; + port_BusAddr = ATAFIFO_BUSADDR; + addr_BusAddr = virt_to_phys( (void*) buffer); + + // Turn the SDMA into RX + sdma_ata_rx_init(priv->sdma); + + sdma_ata_submit_buffer(priv->sdma, (void *)buffer, (void *)port_BusAddr, (void *) addr_BusAddr, len*2); + + sdma_ata_clear_irq(priv->sdma); + sdma_ata_enable(priv->sdma); + + for(;;) + { + u32 val; + val = in_be32(&sdma.io->IntPend); + if ( (val & (1 << priv->sdma->tasknum) ) ) + break; + + if (timeout--<=0) { + printk("timeout 0x%x\n", read_altstatus(priv) ); + ret = -1; + break; + } + + udelay(1); + } + + sdma_ata_disable(priv->sdma); + sdma_ata_reset(priv->sdma); + + return ret; + } +#endif +#endif + + /**************/ + int mpc52xx_ata_docpupollwrite( + struct mpc52xx_blockata_priv *priv, + void *buffer, + int len) + { + u16 *buffer16 = (u16 *) buffer; + int local_len = len; + + while(local_len--) + write_data(priv, *buffer16++); + + return len - local_len; + } + +/* + * +*/ + +EXPORT_SYMBOL(mpc52xx_ata_docpupollread); +EXPORT_SYMBOL(mpc52xx_ata_docpupollwrite); +EXPORT_SYMBOL(mpc52xx_ata_dotransfer); +EXPORT_SYMBOL(sdma_void_handler); +EXPORT_SYMBOL(ata_void_handler); +EXPORT_SYMBOL(mpc52xx_ata_ack_blkreq); -- 1.4.3.2