3974 lines
124 KiB
Diff
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(®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 <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, §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
|
|
|