linux/debian/patches/features/powerpc/efika/0008-Add-MPC5200-SDMA-PIO-d...

3974 lines
124 KiB
Diff

From e94e3e06dcf17bc739806a8a4cd8d732f7f45074 Mon Sep 17 00:00:00 2001
From: Nicolas DET <nd@bplan-gmbh.de>
Date: Fri, 24 Nov 2006 13:10:46 +0100
Subject: [PATCH] Add MPC5200 SDMA/PIO driver
Signed-off-by: Nicolas DET <nd@bplan-gmbh.de>
---
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 <hch@infradead.org>
+# 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 <linux/major.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/bitops.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+
+#include <asm/kmap_types.h>
+#include <asm/mpc52xx.h>
+
+/**************/
+/**************/
+/**************/
+//#define __DEBUG__
+
+/**************/
+/**************/
+/**************/
+#include <platforms/efika/bestcomm.h>
+
+#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 <tnt@246tNt.com>
+ * 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 <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <asm/types.h>
+#include <asm/io.h>
+
+#include <asm/mpc52xx.h>
+
+/* 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(&regs->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 <asm/io.h>
+
+/*
+ * 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 <linux/irq.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+
+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 <asm/prom.h>
+
+/**************/
+/**************/
+/**************/
+
+/**************/
+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 <linux/major.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/bitops.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+
+/**************/
+/**************/
+/**************/
+#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, &sectorcnt);
+ 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(&regs->pio1, timing->pio1);
+ out_be32(&regs->pio2, timing->pio2);
+ out_be32(&regs->mdma1, timing->mdma1);
+ out_be32(&regs->mdma2, timing->mdma2);
+ out_be32(&regs->udma1, timing->udma1);
+ out_be32(&regs->udma2, timing->udma2);
+ out_be32(&regs->udma3, timing->udma3);
+ out_be32(&regs->udma4, timing->udma4);
+ out_be32(&regs->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(&regs->dma_mode, MPC52xx_ATA_DMAMODE_FR);
+ udelay(10);
+
+ /* All sample code do this */
+ out_be32(&regs->share_cnt, 0);
+
+ /* Configure & Reset host */
+ out_be32(&regs->config,
+ MPC52xx_ATA_HOSTCONF_IE |
+ MPC52xx_ATA_HOSTCONF_IORDY |
+ MPC52xx_ATA_HOSTCONF_SMR |
+ MPC52xx_ATA_HOSTCONF_FR);
+ udelay(10);
+ out_be32(&regs->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(&regs->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, &sector, &sectorcnt);
+
+ 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, &sector, &sectorcnt);
+
+ 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