All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT
@ 2007-03-30 11:00 Russell King
  2007-03-30 11:08 ` [RFC] pata_platform for ARM RiscPC Russell King
  2007-03-30 11:08 ` [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT Christoph Hellwig
  0 siblings, 2 replies; 19+ messages in thread
From: Russell King @ 2007-03-30 11:00 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton, Al Viro

Provide an dummy implementation of devm_ioport_map() and
devm_ioport_unmap() to allow drivers (eg, pata_platform) to build for
platforms where CONFIG_NO_IOPORT is selected.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 include/linux/io.h |   14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/include/linux/io.h b/include/linux/io.h
index c244a0c..8cf8df6 100644
--- a/include/linux/io.h
+++ b/include/linux/io.h
@@ -33,9 +33,22 @@ int ioremap_page_range(unsigned long addr, unsigned long end,
 /*
  * Managed iomap interface
  */
+#ifdef CONFIG_HAS_IOPORT
 void __iomem * devm_ioport_map(struct device *dev, unsigned long port,
 			       unsigned int nr);
 void devm_ioport_unmap(struct device *dev, void __iomem *addr);
+#else
+static inline void __iomem * devm_ioport_map(struct device *dev,
+					     unsigned long port,
+					     unsigned int nr)
+{
+	return NULL;
+}
+
+static inline void devm_ioport_unmap(struct device *dev, void __iomem *addr)
+{
+}
+#endif
 
 void __iomem * devm_ioremap(struct device *dev, unsigned long offset,
 			    unsigned long size);

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [RFC] pata_platform for ARM RiscPC
  2007-03-30 11:00 [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT Russell King
@ 2007-03-30 11:08 ` Russell King
  2007-04-08 10:18   ` [RFC] pata_icside driver Russell King
  2007-04-09 11:32   ` [RFC] pata_platform for ARM RiscPC Jeff Garzik
  2007-03-30 11:08 ` [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT Christoph Hellwig
  1 sibling, 2 replies; 19+ messages in thread
From: Russell King @ 2007-03-30 11:08 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton, Jeff Garzik

Add pata_platform device for RiscPC, thereby converting the primary
IDE channel on the machine to PATA.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---

Since this is dependent on the previous patch (to avoid build errors)
this needs to wait until the devm_ioport patch is merged.  I'll
therefore push this through the ARM tree at the appropriate time.

 arch/arm/mach-rpc/riscpc.c |   35 +++++++++++++++++++++++++++++++++++
 drivers/ata/Kconfig        |    2 +-
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/arch/arm/mach-rpc/riscpc.c b/arch/arm/mach-rpc/riscpc.c
index 208a2b5..711a2db 100644
--- a/arch/arm/mach-rpc/riscpc.c
+++ b/arch/arm/mach-rpc/riscpc.c
@@ -17,6 +17,7 @@
 #include <linux/sched.h>
 #include <linux/device.h>
 #include <linux/serial_8250.h>
+#include <linux/pata_platform.h>
 
 #include <asm/elf.h>
 #include <asm/io.h>
@@ -159,11 +160,45 @@ static struct plat_serial8250_port serial_platform_data[] = {
 	},
 };
 
+static struct pata_platform_info pata_platform_data = {
+	.ioport_shift		= 2,
+};
+
+static struct resource pata_resources[] = {
+	[0] = {
+		.start		= 0x030107c0,
+		.end		= 0x030107df,
+		.flags		= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start		= 0x03010fd8,
+		.end		= 0x03010fdb,
+		.flags		= IORESOURCE_MEM,
+	},
+	[2] = {
+		.start		= IRQ_HARDDISK,
+		.end		= IRQ_HARDDISK,
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device pata_device = {
+	.name			= "pata_platform",
+	.id			= -1,
+	.num_resources		= ARRAY_SIZE(pata_resources),
+	.resource		= pata_resources,
+	.dev			= {
+		.platform_data	= &pata_platform_data,
+		.coherent_dma_mask = ~0,	/* grumble */
+	},
+};
+
 static struct platform_device *devs[] __initdata = {
 	&iomd_device,
 	&kbd_device,
 	&serial_device,
 	&acornfb_device,
+	&pata_device,
 };
 
 static int __init rpc_init(void)
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index d16b5b0..d8a9758 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -545,7 +545,7 @@ config PATA_WINBOND_VLB
 
 config PATA_PLATFORM
 	tristate "Generic platform device PATA support"
-	depends on EMBEDDED
+	depends on EMBEDDED || ARCH_RPC
 	help
 	  This option enables support for generic directly connected ATA
 	  devices commonly found on embedded systems.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT
  2007-03-30 11:00 [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT Russell King
  2007-03-30 11:08 ` [RFC] pata_platform for ARM RiscPC Russell King
@ 2007-03-30 11:08 ` Christoph Hellwig
  1 sibling, 0 replies; 19+ messages in thread
From: Christoph Hellwig @ 2007-03-30 11:08 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton, Al Viro

On Fri, Mar 30, 2007 at 12:00:22PM +0100, Russell King wrote:
> Provide an dummy implementation of devm_ioport_map() and
> devm_ioport_unmap() to allow drivers (eg, pata_platform) to build for

Btw, I there a chance we can collect all the devm_* crap into a single
implementation and header file instead of spreading it all over the
tree?


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-03-30 11:08 ` [RFC] pata_platform for ARM RiscPC Russell King
@ 2007-04-08 10:18   ` Russell King
  2007-04-08 18:59     ` Alan Cox
                       ` (2 more replies)
  2007-04-09 11:32   ` [RFC] pata_platform for ARM RiscPC Jeff Garzik
  1 sibling, 3 replies; 19+ messages in thread
From: Russell King @ 2007-04-08 10:18 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton, Jeff Garzik

Below is an initial attempt at converting the ICS IDE driver to fit
into the PATA infrastructure.

There's a number of FIXMEs in there: due to the hardware missing
resistors on the interrupt signals from the drives, a port
without any drives attached results in spurious interrupts being
generated.

To prevent this, we need to disable the interrupts from the port
on the card if no drives are found, but unfortunately ATA doesn't
call the "port_disable" method in this circumstance.

The second FIXME area is ata_irq_ack - it is unconditionally coded
for SFF-type interfaces.  I believe that using this function in
non-BMDMA interfaces is wrong - it attempts to read from the BMDMA
registers irrespective of whether ap->ioaddr.bmdma_addr is set or
not.  The question this poses is: what should non-BMDMA implementations
use for this method?  Note that pata_platform also uses this
function despite not supporting BMDMA which seems even more suspicious.

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 7bdbe5a..9cd8a61 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -552,6 +552,14 @@ config PATA_PLATFORM
 
 	  If unsure, say N.
 
+config PATA_ICSIDE
+	tristate "Acorn ICS PATA support"
+	depends on ARM && ARCH_ACORN
+	help
+	  On Acorn systems, say Y here if you wish to use the ICS PATA
+	  interface card.  This is not required for ICS partition support.
+	  If you are unsure, say N to this.
+
 config PATA_IXP4XX_CF
 	tristate "IXP4XX Compact Flash support"
 	depends on ARCH_IXP4XX
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 13d7397..cc8798b 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_PATA_TRIFLEX)	+= pata_triflex.o
 obj-$(CONFIG_PATA_IXP4XX_CF)	+= pata_ixp4xx_cf.o
 obj-$(CONFIG_PATA_SCC)		+= pata_scc.o
 obj-$(CONFIG_PATA_PLATFORM)	+= pata_platform.o
+obj-$(CONFIG_PATA_ICSIDE)	+= pata_icside.o
 # Should be last but one libata driver
 obj-$(CONFIG_ATA_GENERIC)	+= ata_generic.o
 # Should be last libata driver
diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c
new file mode 100644
index 0000000..68eecfc
--- /dev/null
+++ b/drivers/ata/pata_icside.c
@@ -0,0 +1,679 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+
+#define DRV_NAME	"pata_icside"
+
+#define ICS_IDENT_OFFSET		0x2280
+
+#define ICS_ARCIN_V5_INTRSTAT		0x0000
+#define ICS_ARCIN_V5_INTROFFSET		0x0004
+
+#define ICS_ARCIN_V6_INTROFFSET_1	0x2200
+#define ICS_ARCIN_V6_INTRSTAT_1		0x2290
+#define ICS_ARCIN_V6_INTROFFSET_2	0x3200
+#define ICS_ARCIN_V6_INTRSTAT_2		0x3290
+
+struct portinfo {
+	unsigned int dataoffset;
+	unsigned int ctrloffset;
+	unsigned int stepping;
+};
+
+static const struct portinfo pata_icside_portinfo_v5 = {
+	.dataoffset	= 0x2800,
+	.ctrloffset	= 0x2b80,
+	.stepping	= 6,
+};
+
+static const struct portinfo pata_icside_portinfo_v6_1 = {
+	.dataoffset	= 0x2000,
+	.ctrloffset	= 0x2380,
+	.stepping	= 6,
+};
+
+static const struct portinfo pata_icside_portinfo_v6_2 = {
+	.dataoffset	= 0x3000,
+	.ctrloffset	= 0x3380,
+	.stepping	= 6,
+};
+
+#define PATA_ICSIDE_MAX_SG	128
+
+struct pata_icside_state {
+	void __iomem *irq_port;
+	void __iomem *ioc_base;
+	unsigned int type;
+	unsigned int dma;
+	struct {
+		u8 port_sel;
+		u8 disabled;
+		unsigned int speed[ATA_MAX_DEVICES];
+	} port[2];
+	struct scatterlist sg[PATA_ICSIDE_MAX_SG];
+};
+
+#define ICS_TYPE_A3IN	0
+#define ICS_TYPE_A3USER	1
+#define ICS_TYPE_V6	3
+#define ICS_TYPE_V5	15
+#define ICS_TYPE_NOTYPE	((unsigned int)-1)
+
+/* ---------------- Version 5 PCB Support Functions --------------------- */
+/* Prototype: pata_icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void pata_icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	writeb(0, state->irq_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+/* Prototype: pata_icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void pata_icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	readb(state->irq_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+static const expansioncard_ops_t pata_icside_ops_arcin_v5 = {
+	.irqenable	= pata_icside_irqenable_arcin_v5,
+	.irqdisable	= pata_icside_irqdisable_arcin_v5,
+};
+
+
+/* ---------------- Version 6 PCB Support Functions --------------------- */
+/* Prototype: pata_icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void pata_icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+	void __iomem *base = state->irq_port;
+
+	if (!state->port[0].disabled)
+		writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
+	if (!state->port[1].disabled)
+		writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: pata_icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void pata_icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
+	readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: pata_icside_irqprobe(struct expansion_card *ec)
+ * Purpose  : detect an active interrupt from card
+ */
+static int pata_icside_irqpending_arcin_v6(struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	return readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+	       readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+}
+
+static const expansioncard_ops_t pata_icside_ops_arcin_v6 = {
+	.irqenable	= pata_icside_irqenable_arcin_v6,
+	.irqdisable	= pata_icside_irqdisable_arcin_v6,
+	.irqpending	= pata_icside_irqpending_arcin_v6,
+};
+
+
+/*
+ * SG-DMA support.
+ *
+ * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers.
+ * There is only one DMA controller per card, which means that only
+ * one drive can be accessed at one time.  NOTE! We do not enforce that
+ * here, but we rely on the main IDE driver spotting that both
+ * interfaces use the same IRQ, which should guarantee this.
+ */
+
+/*
+ * Configure the IOMD to give the appropriate timings for the transfer
+ * mode being requested.  We take the advice of the ATA standards, and
+ * calculate the cycle time based on the transfer mode, and the EIDE
+ * MW DMA specs that the drive provides in the IDENTIFY command.
+ *
+ * We have the following IOMD DMA modes to choose from:
+ *
+ *	Type	Active		Recovery	Cycle
+ *	A	250 (250)	312 (550)	562 (800)
+ *	B	187 (200)	250 (550)	437 (750)
+ *	C	125 (125)	125 (375)	250 (500)
+ *	D	62  (50)	125 (375)	187 (425)
+ *
+ * (figures in brackets are actual measured timings on DIOR/DIOW)
+ *
+ * However, we also need to take care of the read/write active and
+ * recovery timings:
+ *
+ *			Read	Write
+ *  	Mode	Active	-- Recovery --	Cycle	IOMD type
+ *	MW0	215	50	215	480	A
+ *	MW1	80	50	50	150	C
+ *	MW2	70	25	25	120	C
+ */
+static void pata_icside_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+	struct pata_icside_state *state = ap->host->private_data;
+	struct ata_timing t;
+	unsigned int cycle;
+	char iomd_type;
+
+	/*
+	 * DMA is based on a 16MHz clock
+	 */
+	if (ata_timing_compute(adev, adev->dma_mode, &t, 1000, 1))
+		return;
+
+	/*
+	 * Now, properly adjust the timings.  If we have a 62.5ns clock
+	 * period and we ask for MWDMA2, it calculates the following
+	 * timings: active 125ns, recovery 62.5ns, cycle 125ns.
+	 * Quite obviously bogus.  Tweak the cycle time to be sufficiently
+	 * long to satisfy the active and recovery time.
+	 */
+	if (t.active + t.recover > t.cycle)
+		t.cycle = t.active + t.recover;
+
+	/*
+	 * Choose the IOMD cycle timing which ensure that the interface
+	 * satisfies the measured active, recovery and cycle times.
+	 */
+	if (t.active <= 50 && t.recover <= 375 && t.cycle <= 425)
+		iomd_type = 'D', cycle = 187;
+	else if (t.active <= 125 && t.recover <= 375 && t.cycle <= 500)
+		iomd_type = 'C', cycle = 250;
+	else if (t.active <= 200 && t.recover <= 550 && t.cycle <= 750)
+		iomd_type = 'B', cycle = 437;
+	else
+		iomd_type = 'A', cycle = 562;
+
+	ata_dev_printk(adev, KERN_INFO, "timings: act %dns rec %dns cyc %dns (%c)\n",
+		t.active, t.recover, t.cycle, iomd_type);
+
+	state->port[ap->port_no].speed[adev->devno] = cycle;
+}
+
+static void pata_icside_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct pata_icside_state *state = ap->host->private_data;
+	struct scatterlist *sg, *rsg = state->sg;
+	unsigned int write = qc->tf.flags & ATA_TFLAG_WRITE;
+
+	/*
+	 * We are simplex; BUG if we try to fiddle with DMA
+	 * while it's active.
+	 */
+	BUG_ON(dma_channel_active(state->dma));
+
+	/*
+	 * Copy ATAs scattered sg list into a contiguous array of sg
+	 */
+	ata_for_each_sg(sg, qc) {
+		memcpy(rsg, sg, sizeof(*sg));
+		rsg++;
+	}
+
+	/*
+	 * Route the DMA signals to the correct interface
+	 */
+	writeb(state->port[ap->port_no].port_sel, state->ioc_base);
+
+	set_dma_speed(state->dma, state->port[ap->port_no].speed[qc->dev->devno]);
+	set_dma_sg(state->dma, state->sg, rsg - state->sg);
+	set_dma_mode(state->dma, write ? DMA_MODE_WRITE : DMA_MODE_READ);
+
+	/* issue r/w command */
+	ap->ops->exec_command(ap, &qc->tf);
+}
+
+static void pata_icside_bmdma_start(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct pata_icside_state *state = ap->host->private_data;
+
+	BUG_ON(dma_channel_active(state->dma));
+	enable_dma(state->dma);
+}
+
+static void pata_icside_bmdma_stop(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct pata_icside_state *state = ap->host->private_data;
+
+	disable_dma(state->dma);
+
+	/* see ata_bmdma_stop */
+	ata_altstatus(ap);
+}
+
+static u8 pata_icside_bmdma_status(struct ata_port *ap)
+{
+	struct pata_icside_state *state = ap->host->private_data;
+	void __iomem *irq_port;
+
+	irq_port = state->irq_port + (ap->port_no ? ICS_ARCIN_V6_INTRSTAT_2 :
+						    ICS_ARCIN_V6_INTRSTAT_1);
+
+	return readb(irq_port) & 1 ? ATA_DMA_INTR : 0;
+}
+
+static int icside_dma_init(struct ata_probe_ent *ae, struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ae->private_data;
+	int i;
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		state->port[0].speed[i] = 480;
+		state->port[1].speed[i] = 480;
+	}
+
+	if (ec->dma != NO_DMA && !request_dma(ec->dma, DRV_NAME)) {
+		state->dma = ec->dma;
+		ae->mwdma_mask = 0x07;	/* MW0..2 */
+	}
+
+	return 0;
+}
+
+
+static int pata_icside_port_start(struct ata_port *ap)
+{
+	/* No PRD to alloc */
+	return ata_pad_alloc(ap, ap->dev);
+}
+
+static struct scsi_host_template pata_icside_sht = {
+	.module			= THIS_MODULE,
+	.name			= DRV_NAME,
+	.ioctl			= ata_scsi_ioctl,
+	.queuecommand		= ata_scsi_queuecmd,
+	.can_queue		= ATA_DEF_QUEUE,
+	.this_id		= ATA_SHT_THIS_ID,
+	.sg_tablesize		= PATA_ICSIDE_MAX_SG,
+	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
+	.emulated		= ATA_SHT_EMULATED,
+	.use_clustering		= ATA_SHT_USE_CLUSTERING,
+	.proc_name		= DRV_NAME,
+	.dma_boundary		= ~0, /* no dma boundaries */
+	.slave_configure	= ata_scsi_slave_config,
+	.slave_destroy		= ata_scsi_slave_destroy,
+	.bios_param		= ata_std_bios_param,
+};
+
+/* wish this was exported from libata-core */
+static void ata_dummy_noret(struct ata_port *port)
+{
+}
+
+/*
+ * We need to shut down unused ports to prevent spurious interrupts.
+ * FIXME: the libata core doesn't call this function for PATA interfaces.
+ */
+static void pata_icside_port_disable(struct ata_port *ap)
+{
+	struct pata_icside_state *state = ap->host->private_data;
+
+	ata_port_printk(ap, KERN_ERR, "disabling icside port\n");
+
+	ata_port_disable(ap);
+
+	state->port[ap->port_no].disabled = 1;
+
+	if (state->type == ICS_TYPE_V6) {
+		/*
+		 * Disable interrupts from this port, otherwise we
+		 * receive spurious interrupts from the floating
+		 * interrupt line.
+		 */
+		void __iomem *irq_port = state->irq_port +
+				(ap->port_no ? ICS_ARCIN_V6_INTROFFSET_2 : ICS_ARCIN_V6_INTROFFSET_1);
+		readb(irq_port);
+	}
+}
+
+static struct ata_port_operations pata_icside_port_ops = {
+	.port_disable		= pata_icside_port_disable,
+
+	.set_dmamode		= pata_icside_set_dmamode,
+
+	.tf_load		= ata_tf_load,
+	.tf_read		= ata_tf_read,
+	.exec_command		= ata_exec_command,
+	.check_status		= ata_check_status,
+	.dev_select		= ata_std_dev_select,
+
+	.bmdma_setup		= pata_icside_bmdma_setup,
+	.bmdma_start		= pata_icside_bmdma_start,
+
+	.data_xfer		= ata_data_xfer_noirq,
+
+	/* no need to build any PRD tables for DMA */
+	.qc_prep		= ata_noop_qc_prep,
+	.qc_issue		= ata_qc_issue_prot,
+
+	.freeze			= ata_bmdma_freeze,
+	.thaw			= ata_bmdma_thaw,
+	.error_handler		= ata_bmdma_error_handler,
+	.post_internal_cmd	= pata_icside_bmdma_stop,
+
+	.irq_handler		= ata_interrupt,
+	.irq_clear		= ata_dummy_noret,
+	.irq_on			= ata_irq_on,
+	.irq_ack		= ata_irq_ack, /* FIXME */
+
+	.port_start		= pata_icside_port_start,
+
+	.bmdma_stop		= pata_icside_bmdma_stop,
+	.bmdma_status		= pata_icside_bmdma_status,
+};
+
+static void
+pata_icside_add_port(struct ata_probe_ent *ae, void __iomem *base,
+		     const struct portinfo *info)
+{
+	struct ata_ioports *ioaddr = &ae->port[ae->n_ports++];
+	void __iomem *cmd = base + info->dataoffset;
+
+	ioaddr->cmd_addr	= cmd;
+	ioaddr->data_addr	= cmd + (ATA_REG_DATA    << info->stepping);
+	ioaddr->error_addr	= cmd + (ATA_REG_ERR     << info->stepping);
+	ioaddr->feature_addr	= cmd + (ATA_REG_FEATURE << info->stepping);
+	ioaddr->nsect_addr	= cmd + (ATA_REG_NSECT   << info->stepping);
+	ioaddr->lbal_addr	= cmd + (ATA_REG_LBAL    << info->stepping);
+	ioaddr->lbam_addr	= cmd + (ATA_REG_LBAM    << info->stepping);
+	ioaddr->lbah_addr	= cmd + (ATA_REG_LBAH    << info->stepping);
+	ioaddr->device_addr	= cmd + (ATA_REG_DEVICE  << info->stepping);
+	ioaddr->status_addr	= cmd + (ATA_REG_STATUS  << info->stepping);
+	ioaddr->command_addr	= cmd + (ATA_REG_CMD     << info->stepping);
+
+	ioaddr->ctl_addr	= base + info->ctrloffset;
+	ioaddr->altstatus_addr	= ioaddr->ctl_addr;
+}
+
+static int __init
+pata_icside_register_v5(struct ata_probe_ent *ae, struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ae->private_data;
+	void __iomem *base;
+
+	base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
+		       ecard_resource_len(ec, ECARD_RES_MEMC));
+	if (!base)
+		return -ENOMEM;
+
+	state->irq_port = base;
+
+	ec->irqaddr = base + ICS_ARCIN_V5_INTRSTAT;
+	ec->irqmask = 1;
+	ec->irq_data = state;
+	ec->ops = &pata_icside_ops_arcin_v5;
+
+	/*
+	 * Be on the safe side - disable interrupts
+	 */
+	ec->ops->irqdisable(ec, ec->irq);
+
+	pata_icside_add_port(ae, base, &pata_icside_portinfo_v5);
+
+	return 0;
+}
+
+static int __init
+pata_icside_register_v6(struct ata_probe_ent *ae, struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ae->private_data;
+	void __iomem *ioc_base, *easi_base;
+	unsigned int sel = 0;
+	int ret;
+
+	ioc_base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST),
+			   ecard_resource_len(ec, ECARD_RES_IOCFAST));
+	if (!ioc_base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	easi_base = ioc_base;
+
+	if (ecard_resource_flags(ec, ECARD_RES_EASI)) {
+		easi_base = ioremap(ecard_resource_start(ec, ECARD_RES_EASI),
+				    ecard_resource_len(ec, ECARD_RES_EASI));
+		if (!easi_base) {
+			ret = -ENOMEM;
+			goto unmap_slot;
+		}
+
+		/*
+		 * Enable access to the EASI region.
+		 */
+		sel = 1 << 5;
+	}
+
+	writeb(sel, ioc_base);
+
+	ec->irq_data = state;
+	ec->ops = &pata_icside_ops_arcin_v6;
+
+	state->irq_port = easi_base;
+	state->ioc_base = ioc_base;
+	state->port[0].port_sel = sel;
+	state->port[1].port_sel = sel | 1;
+
+	/*
+	 * Be on the safe side - disable interrupts
+	 */
+	ec->ops->irqdisable(ec, ec->irq);
+
+	/*
+	 * Find and register the interfaces.
+	 */
+	pata_icside_add_port(ae, easi_base, &pata_icside_portinfo_v6_1);
+	pata_icside_add_port(ae, easi_base, &pata_icside_portinfo_v6_2);
+
+	/*
+	 * FIXME: work around libata's aversion to calling port_disable.
+	 * This permanently disables interrupts on port 0 - bad luck if
+	 * you have a drive on that port.
+	 */
+	state->port[0].disabled = 1;
+
+	return icside_dma_init(ae, ec);
+
+ unmap_slot:
+	iounmap(ioc_base);
+ out:
+	return ret;
+}
+
+static int __devinit
+pata_icside_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+	struct pata_icside_state *state;
+	struct ata_probe_ent ae;
+	void __iomem *idmem;
+	int ret;
+
+	ret = ecard_request_resources(ec);
+	if (ret)
+		goto out;
+
+	state = kzalloc(sizeof(struct pata_icside_state), GFP_KERNEL);
+	if (!state) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	state->type = ICS_TYPE_NOTYPE;
+	state->dma = NO_DMA;
+
+	idmem = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST),
+			ecard_resource_len(ec, ECARD_RES_IOCFAST));
+	if (idmem) {
+		unsigned int type;
+
+		type = readb(idmem + ICS_IDENT_OFFSET) & 1;
+		type |= (readb(idmem + ICS_IDENT_OFFSET + 4) & 1) << 1;
+		type |= (readb(idmem + ICS_IDENT_OFFSET + 8) & 1) << 2;
+		type |= (readb(idmem + ICS_IDENT_OFFSET + 12) & 1) << 3;
+		iounmap(idmem);
+
+		state->type = type;
+	}
+
+	memset(&ae, 0, sizeof(ae));
+	INIT_LIST_HEAD(&ae.node);
+	ae.dev          = &ec->dev;
+	ae.port_ops     = &pata_icside_port_ops;
+	ae.sht          = &pata_icside_sht;
+	ae.pio_mask     = 0x1f;
+	ae.irq          = ec->irq;
+	ae.port_flags   = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
+	ae._host_flags  = ATA_HOST_SIMPLEX;
+	ae.private_data = state;
+
+	switch (state->type) {
+	case ICS_TYPE_A3IN:
+		dev_warn(&ec->dev, "A3IN unsupported\n");
+		ret = -ENODEV;
+		break;
+
+	case ICS_TYPE_A3USER:
+		dev_warn(&ec->dev, "A3USER unsupported\n");
+		ret = -ENODEV;
+		break;
+
+	case ICS_TYPE_V5:
+		ret = pata_icside_register_v5(&ae, ec);
+		break;
+
+	case ICS_TYPE_V6:
+		ret = pata_icside_register_v6(&ae, ec);
+		break;
+
+	default:
+		dev_warn(&ec->dev, "unknown interface type\n");
+		ret = -ENODEV;
+		break;
+	}
+
+	if (ret == 0)
+		ret = ata_device_add(&ae) == 0 ? -ENODEV : 0;
+
+	if (ret == 0)
+		goto out;
+
+	kfree(state);
+ release:
+	ecard_release_resources(ec);
+ out:
+	return ret;
+}
+
+static void pata_icside_shutdown(struct expansion_card *ec)
+{
+	struct ata_host *host = ecard_get_drvdata(ec);
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts from this card.  We need to do
+	 * this before disabling EASI since we may be accessing
+	 * this register via that region.
+	 */
+	local_irq_save(flags);
+	if (ec->ops)
+		ec->ops->irqdisable(ec, ec->irq);
+	local_irq_restore(flags);
+
+	/*
+	 * Reset the ROM pointer so that we can read the ROM
+	 * after a soft reboot.  This also disables access to
+	 * the IDE taskfile via the EASI region.
+	 */
+	if (host) {
+		struct pata_icside_state *state = host->private_data;
+		if (state->ioc_base)
+			writeb(0, state->ioc_base);
+	}
+}
+
+static void __devexit pata_icside_remove(struct expansion_card *ec)
+{
+	struct ata_host *host = ecard_get_drvdata(ec);
+	struct pata_icside_state *state = host->private_data;
+
+	ata_host_detach(host);
+
+	pata_icside_shutdown(ec);
+
+	/*
+	 * don't NULL out the drvdata - devres/libata wants it
+	 * to free the ata_host structure.
+	 */
+	ec->ops = NULL;
+	ec->irq_data = NULL;
+
+	if (state->dma != NO_DMA)
+		free_dma(state->dma);
+	if (state->ioc_base)
+		iounmap(state->ioc_base);
+	if (state->ioc_base != state->irq_port)
+		iounmap(state->irq_port);
+
+	kfree(state);
+	ecard_release_resources(ec);
+}
+
+static const struct ecard_id pata_icside_ids[] = {
+	{ MANU_ICS,  PROD_ICS_IDE  },
+	{ MANU_ICS2, PROD_ICS2_IDE },
+	{ 0xffff, 0xffff }
+};
+
+static struct ecard_driver pata_icside_driver = {
+	.probe		= pata_icside_probe,
+	.remove 	= __devexit_p(pata_icside_remove),
+	.shutdown	= pata_icside_shutdown,
+	.id_table	= pata_icside_ids,
+	.drv = {
+		.name	= DRV_NAME,
+	},
+};
+
+static int __init pata_icside_init(void)
+{
+	return ecard_register_driver(&pata_icside_driver);
+}
+
+static void __exit pata_icside_exit(void)
+{
+	ecard_remove_driver(&pata_icside_driver);
+}
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ICS PATA driver");
+
+module_init(pata_icside_init);
+module_exit(pata_icside_exit);

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-08 10:18   ` [RFC] pata_icside driver Russell King
@ 2007-04-08 18:59     ` Alan Cox
  2007-04-09  1:03       ` Jeff Garzik
  2007-04-08 20:09     ` Alan Cox
  2007-04-21 15:09     ` Russell King
  2 siblings, 1 reply; 19+ messages in thread
From: Alan Cox @ 2007-04-08 18:59 UTC (permalink / raw)
  To: Russell King; +Cc: linux-kernel, Andrew Morton, Jeff Garzik

> The second FIXME area is ata_irq_ack - it is unconditionally coded
> for SFF-type interfaces.  I believe that using this function in
> non-BMDMA interfaces is wrong - it attempts to read from the BMDMA
> registers irrespective of whether ap->ioaddr.bmdma_addr is set or
> not.  The question this poses is: what should non-BMDMA implementations
> use for this method?  Note that pata_platform also uses this
> function despite not supporting BMDMA which seems even more suspicious.

Thats a bug that has arrived again. The older code was corrected to
handle this properly but the fix appears to have become lost. The
ioread/iowrite code actually made quite a mess (all the address reporting
is also broken) and we do some iffy things like compare the iomap result
with zero and assume thats the same as checking for true bus zero
addresses.

ata_irq_ack is part of the SFF layer so its fine that it assumes SFF but
its wrong that it is used unconditionally and it shouldn't be used this
way. It just needs a (!ap->ioaddr.bmdma_addr) test adding (assuming thats
valid for iomap)


Alan

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-08 10:18   ` [RFC] pata_icside driver Russell King
  2007-04-08 18:59     ` Alan Cox
@ 2007-04-08 20:09     ` Alan Cox
  2007-04-09  8:18       ` Russell King
  2007-04-21 15:09     ` Russell King
  2 siblings, 1 reply; 19+ messages in thread
From: Alan Cox @ 2007-04-08 20:09 UTC (permalink / raw)
  To: Russell King; +Cc: linux-kernel, Andrew Morton, Jeff Garzik

> +	/*
> +	 * DMA is based on a 16MHz clock
> +	 */
> +	if (ata_timing_compute(adev, adev->dma_mode, &t, 1000, 1))
> +		return;

This seems strange for a 16MHz clock.

> +
> +	/*
> +	 * Now, properly adjust the timings.  If we have a 62.5ns clock
> +	 * period and we ask for MWDMA2, it calculates the following
> +	 * timings: active 125ns, recovery 62.5ns, cycle 125ns.
> +	 * Quite obviously bogus. 

NAK. 

At this point you need to work out why you are getting bogus results and
fix it or demonstrate a bug in the core code and fix that.


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-08 18:59     ` Alan Cox
@ 2007-04-09  1:03       ` Jeff Garzik
  2007-04-09  9:56         ` Alan Cox
  0 siblings, 1 reply; 19+ messages in thread
From: Jeff Garzik @ 2007-04-09  1:03 UTC (permalink / raw)
  To: Alan Cox; +Cc: Russell King, linux-kernel, Andrew Morton

Alan Cox wrote:
>> The second FIXME area is ata_irq_ack - it is unconditionally coded
>> for SFF-type interfaces.  I believe that using this function in
>> non-BMDMA interfaces is wrong - it attempts to read from the BMDMA
>> registers irrespective of whether ap->ioaddr.bmdma_addr is set or
>> not.  The question this poses is: what should non-BMDMA implementations
>> use for this method?  Note that pata_platform also uses this
>> function despite not supporting BMDMA which seems even more suspicious.
> 
> Thats a bug that has arrived again. The older code was corrected to
> handle this properly but the fix appears to have become lost. The
> ioread/iowrite code actually made quite a mess (all the address reporting
> is also broken) and we do some iffy things like compare the iomap result
> with zero and assume thats the same as checking for true bus zero
> addresses.
> 
> ata_irq_ack is part of the SFF layer so its fine that it assumes SFF but
> its wrong that it is used unconditionally and it shouldn't be used this
> way. It just needs a (!ap->ioaddr.bmdma_addr) test adding (assuming thats
> valid for iomap)

No.  It does not need such a test, as it requires BMDMA, not just an 
SFF-style Status register.  It is up to the driver to decide whether or 
not ata_irq_ack() is appropriate for your hardware.

pata_icside needs its own ata_irq_ack -- which may just be as simple as 
reading the Status register to clear the interrupt condition.

If others need this as well, ata_sff_irq_ack() would be a good generic 
function to create.

	Jeff




^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-08 20:09     ` Alan Cox
@ 2007-04-09  8:18       ` Russell King
  2007-04-09  8:24         ` Roland Dreier
  2007-04-09 10:25         ` Alan Cox
  0 siblings, 2 replies; 19+ messages in thread
From: Russell King @ 2007-04-09  8:18 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-kernel, Andrew Morton, Jeff Garzik

On Sun, Apr 08, 2007 at 09:09:17PM +0100, Alan Cox wrote:
> > +	/*
> > +	 * DMA is based on a 16MHz clock
> > +	 */
> > +	if (ata_timing_compute(adev, adev->dma_mode, &t, 1000, 1))
> > +		return;
> 
> This seems strange for a 16MHz clock.

We do this because, although the underlying clock is 16MHz, the DIOR
and DIOW signals go through a bit of logic and are not synchronised
to this clock.

As explained in the comments above:

+ *     Type    Active          Recovery        Cycle
+ *     A       250 (250)       312 (550)       562 (800)
+ *     B       187 (200)       250 (550)       437 (750)
+ *     C       125 (125)       125 (375)       250 (500)
+ *     D       62  (50)        125 (375)       187 (425)
+ *
+ * (figures in brackets are actual measured timings on DIOR/DIOW)

The figures outside the brackets are the documented timings on the host
bus, but these are not what the drive sees.  The timings which the drive
sees are those in brackets.

The timings the drive sees clearly are not based upon a 16MHz clock
period.  Therefore, I'd prefer to get the nanoseconds from the
calculation and work from that.

> > +
> > +	/*
> > +	 * Now, properly adjust the timings.  If we have a 62.5ns clock
> > +	 * period and we ask for MWDMA2, it calculates the following
> > +	 * timings: active 125ns, recovery 62.5ns, cycle 125ns.
> > +	 * Quite obviously bogus. 
> 
> NAK. 
> 
> At this point you need to work out why you are getting bogus results and
> fix it or demonstrate a bug in the core code and fix that.

Obviously I can demonstrate a bug. 8)

Lets say that we want to do MW DMA mode 2.  This has the minimum timing
of 70ns active, 25ns recovery, 120ns cycle time.

When you quantise those figures using a clock period of 62.5ns (16MHz)
you end up with: 2 clocks active (2*62.5 > 70), 1 clock recovery
(1*62.5 > 25) and 2 clocks cycle (2*62.5 > 120).

Last time I checked, active + recovery must always be equal to the cycle
time, and unless my math is failing me, 2 + 1 does not equal 2.

It's quite obvious that returning the active time _equal_ to the cycle
time is even more utterly bogus - that means you're asking for the signal
to remain active for the entire cycle without any recovery.

So, you probably need to incorporate that logic from pata_icside into the
libata core.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09  8:18       ` Russell King
@ 2007-04-09  8:24         ` Roland Dreier
  2007-04-09  8:44           ` Russell King
  2007-04-09 10:25         ` Alan Cox
  1 sibling, 1 reply; 19+ messages in thread
From: Roland Dreier @ 2007-04-09  8:24 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-kernel, Andrew Morton, Jeff Garzik

 > Lets say that we want to do MW DMA mode 2.  This has the minimum timing
 > of 70ns active, 25ns recovery, 120ns cycle time.
 > 
 > When you quantise those figures using a clock period of 62.5ns (16MHz)
 > you end up with: 2 clocks active (2*62.5 > 70), 1 clock recovery
 > (1*62.5 > 25) and 2 clocks cycle (2*62.5 > 120).
 > 
 > Last time I checked, active + recovery must always be equal to the cycle
 > time, and unless my math is failing me, 2 + 1 does not equal 2.

Do you mean active + recovery must be less than or equal to the cycle
time?  Because 70ns + 25ns does not equal 120ns either...

 - R.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09  8:24         ` Roland Dreier
@ 2007-04-09  8:44           ` Russell King
  0 siblings, 0 replies; 19+ messages in thread
From: Russell King @ 2007-04-09  8:44 UTC (permalink / raw)
  To: Roland Dreier; +Cc: Alan Cox, linux-kernel, Andrew Morton, Jeff Garzik

On Mon, Apr 09, 2007 at 01:24:45AM -0700, Roland Dreier wrote:
>  > Lets say that we want to do MW DMA mode 2.  This has the minimum timing
>  > of 70ns active, 25ns recovery, 120ns cycle time.
>  > 
>  > When you quantise those figures using a clock period of 62.5ns (16MHz)
>  > you end up with: 2 clocks active (2*62.5 > 70), 1 clock recovery
>  > (1*62.5 > 25) and 2 clocks cycle (2*62.5 > 120).
>  > 
>  > Last time I checked, active + recovery must always be equal to the cycle
>  > time, and unless my math is failing me, 2 + 1 does not equal 2.
> 
> Do you mean active + recovery must be less than or equal to the cycle
> time?  Because 70ns + 25ns does not equal 120ns either...

Now that I've had some breakfast, yes.

In any case, active + recovery must never be greater than the cycle time.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09  1:03       ` Jeff Garzik
@ 2007-04-09  9:56         ` Alan Cox
  2007-04-09 10:56           ` Jeff Garzik
  0 siblings, 1 reply; 19+ messages in thread
From: Alan Cox @ 2007-04-09  9:56 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Russell King, linux-kernel, Andrew Morton

On Sun, 08 Apr 2007 21:03:10 -0400
Jeff Garzik <jgarzik@pobox.com> wrote:
> > ata_irq_ack is part of the SFF layer so its fine that it assumes SFF but
> > its wrong that it is used unconditionally and it shouldn't be used this
> > way. It just needs a (!ap->ioaddr.bmdma_addr) test adding (assuming thats
> > valid for iomap)
> 
> No.  It does not need such a test, as it requires BMDMA, not just an 
> SFF-style Status register.  It is up to the driver to decide whether or 
> not ata_irq_ack() is appropriate for your hardware.

Then no SFF hardware can use ata_irq_ack. Not one card: Because in every
case it is permissible that BAR4 is not allocated and the device is
running non-DMA, or that the SFF hardware does not support DMA.

> pata_icside needs its own ata_irq_ack -- which may just be as simple as 
> reading the Status register to clear the interrupt condition.
> 
> If others need this as well, ata_sff_irq_ack() would be a good generic 
> function to create.

We should just rename ata_irq_ack(). It is in libata-sff, so it's either
wrong (missing a test), or in the wrong file completely.

Alan

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09  8:18       ` Russell King
  2007-04-09  8:24         ` Roland Dreier
@ 2007-04-09 10:25         ` Alan Cox
  2007-04-09 11:33           ` Russell King
  1 sibling, 1 reply; 19+ messages in thread
From: Alan Cox @ 2007-04-09 10:25 UTC (permalink / raw)
  To: Russell King; +Cc: linux-kernel, Andrew Morton, Jeff Garzik

> The timings the drive sees clearly are not based upon a 16MHz clock
> period.  Therefore, I'd prefer to get the nanoseconds from the
> calculation and work from that.

Makes sense.

> Obviously I can demonstrate a bug. 8)
> 
> Lets say that we want to do MW DMA mode 2.  This has the minimum timing
> of 70ns active, 25ns recovery, 120ns cycle time.

Well it depends on the drive id data but assuming the defaults yes

> When you quantise those figures using a clock period of 62.5ns (16MHz)
> you end up with: 2 clocks active (2*62.5 > 70), 1 clock recovery
> (1*62.5 > 25) and 2 clocks cycle (2*62.5 > 120).
> 
> Last time I checked, active + recovery must always be equal to the cycle
> time, and unless my math is failing me, 2 + 1 does not equal 2.

The libata code does the following:

        if (t->active + t->recover < t->cycle) {
                t->active += (t->cycle - (t->active + t->recover)) / 2;
                t->recover = t->cycle - t->active;
        }

which probably means for 16MHz you don't have enough resolution to be sure
you'll error in this direction in all cases. If so you just need to try
adding

	if (t->active + t->recover > t->cycle)
		t->cycle = t->active + t->recover

to stretch the cycle time to fit the resolution as well.

And we should get this tested/fixed by someone seeing the problem, before
and instead of putting hacks/notes in drivers that may then get lost

Alan

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09  9:56         ` Alan Cox
@ 2007-04-09 10:56           ` Jeff Garzik
  2007-04-09 11:13             ` Jeff Garzik
  0 siblings, 1 reply; 19+ messages in thread
From: Jeff Garzik @ 2007-04-09 10:56 UTC (permalink / raw)
  To: Alan Cox; +Cc: Russell King, linux-kernel, Andrew Morton

Alan Cox wrote:
> Then no SFF hardware can use ata_irq_ack. Not one card: Because in every

OK


> We should just rename ata_irq_ack(). It is in libata-sff, so it's either
> wrong (missing a test), or in the wrong file completely.

There is plenty of BMDMA-specific stuff in libata-sff, so neither 
assertion is correct.

It's fine to rename it, though.

	Jeff



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09 10:56           ` Jeff Garzik
@ 2007-04-09 11:13             ` Jeff Garzik
  2007-04-09 11:36               ` Russell King
  0 siblings, 1 reply; 19+ messages in thread
From: Jeff Garzik @ 2007-04-09 11:13 UTC (permalink / raw)
  To: Alan Cox, Russell King; +Cc: linux-kernel, Andrew Morton

It should be noted, mainly for russell's benefit, that the ->irq_ack() 
hook is only called if a debug define is manually enabled in libata.h, 
in a single callsite.

The number of people that actually use the hook -- kernel hackers 
debugging screaming interrupts -- can probably be counted on one hand.

	Jeff




^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_platform for ARM RiscPC
  2007-03-30 11:08 ` [RFC] pata_platform for ARM RiscPC Russell King
  2007-04-08 10:18   ` [RFC] pata_icside driver Russell King
@ 2007-04-09 11:32   ` Jeff Garzik
  1 sibling, 0 replies; 19+ messages in thread
From: Jeff Garzik @ 2007-04-09 11:32 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton

Russell King wrote:
> Add pata_platform device for RiscPC, thereby converting the primary
> IDE channel on the machine to PATA.
> 
> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

ACK

BTW, please CC linux-ide on anything related to old-IDE or libata.  Thanks.


> Since this is dependent on the previous patch (to avoid build errors)
> this needs to wait until the devm_ioport patch is merged.  I'll
> therefore push this through the ARM tree at the appropriate time.

Sounds good.

	Jeff



^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09 10:25         ` Alan Cox
@ 2007-04-09 11:33           ` Russell King
  0 siblings, 0 replies; 19+ messages in thread
From: Russell King @ 2007-04-09 11:33 UTC (permalink / raw)
  To: Alan Cox; +Cc: linux-kernel, Andrew Morton, Jeff Garzik

On Mon, Apr 09, 2007 at 11:25:06AM +0100, Alan Cox wrote:
> > When you quantise those figures using a clock period of 62.5ns (16MHz)
> > you end up with: 2 clocks active (2*62.5 > 70), 1 clock recovery
> > (1*62.5 > 25) and 2 clocks cycle (2*62.5 > 120).
> > 
> > Last time I checked, active + recovery must always be equal to the cycle
> > time, and unless my math is failing me, 2 + 1 does not equal 2.
> 
> The libata code does the following:
> 
>         if (t->active + t->recover < t->cycle) {
>                 t->active += (t->cycle - (t->active + t->recover)) / 2;
>                 t->recover = t->cycle - t->active;
>         }
> 
> which probably means for 16MHz you don't have enough resolution to be sure
> you'll error in this direction in all cases. If so you just need to try
> adding
> 
> 	if (t->active + t->recover > t->cycle)
> 		t->cycle = t->active + t->recover
> 
> to stretch the cycle time to fit the resolution as well.
> 
> And we should get this tested/fixed by someone seeing the problem, before
> and instead of putting hacks/notes in drivers that may then get lost

I agree; this is why I sent the patch out as an RFC before wanting it
merged.  My feeling at the time when I wrote this is it's something that
the libata timing calculation code should already be catering for.

I should've explicitly mentioned that in the comments, but forgot.

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09 11:13             ` Jeff Garzik
@ 2007-04-09 11:36               ` Russell King
  2007-04-09 12:02                 ` Jeff Garzik
  0 siblings, 1 reply; 19+ messages in thread
From: Russell King @ 2007-04-09 11:36 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Alan Cox, linux-kernel, Andrew Morton

On Mon, Apr 09, 2007 at 07:13:05AM -0400, Jeff Garzik wrote:
> It should be noted, mainly for russell's benefit, that the ->irq_ack() 
> hook is only called if a debug define is manually enabled in libata.h, 
> in a single callsite.
> 
> The number of people that actually use the hook -- kernel hackers 
> debugging screaming interrupts -- can probably be counted on one hand.

Ok, in that case it makes sense for me to set this to ata_dummy_irq_ack
since I don't have any additional status registers specific to the
interface.

Presumably a similar change to pata_platform.c would be appropriate?

-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-09 11:36               ` Russell King
@ 2007-04-09 12:02                 ` Jeff Garzik
  0 siblings, 0 replies; 19+ messages in thread
From: Jeff Garzik @ 2007-04-09 12:02 UTC (permalink / raw)
  To: Jeff Garzik, Alan Cox, linux-kernel, Andrew Morton

Russell King wrote:
> On Mon, Apr 09, 2007 at 07:13:05AM -0400, Jeff Garzik wrote:
>> It should be noted, mainly for russell's benefit, that the ->irq_ack() 
>> hook is only called if a debug define is manually enabled in libata.h, 
>> in a single callsite.
>>
>> The number of people that actually use the hook -- kernel hackers 
>> debugging screaming interrupts -- can probably be counted on one hand.
> 
> Ok, in that case it makes sense for me to set this to ata_dummy_irq_ack
> since I don't have any additional status registers specific to the
> interface.
> 
> Presumably a similar change to pata_platform.c would be appropriate?

I was just pointing out the practical reality.

The proper implementation of the hook is likely /not/ a dummy on your 
platform.

I presume you clear the interrupt condition by reading the ATA shadow 
register 'Status', like most other SFF-like controllers, so your 
implementation of ->irq_ack() should reflect that.

The proper implementation is basically what Alan described: 
ata_irq_ack() minus the BMDMA bitbanging.

	Jeff




^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [RFC] pata_icside driver
  2007-04-08 10:18   ` [RFC] pata_icside driver Russell King
  2007-04-08 18:59     ` Alan Cox
  2007-04-08 20:09     ` Alan Cox
@ 2007-04-21 15:09     ` Russell King
  2 siblings, 0 replies; 19+ messages in thread
From: Russell King @ 2007-04-21 15:09 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton, Jeff Garzik, linux-ide

On Sun, Apr 08, 2007 at 11:18:26AM +0100, Russell King wrote:
> Below is an initial attempt at converting the ICS IDE driver to fit
> into the PATA infrastructure.
> 
> There's a number of FIXMEs in there: due to the hardware missing
> resistors on the interrupt signals from the drives, a port
> without any drives attached results in spurious interrupts being
> generated.
> 
> To prevent this, we need to disable the interrupts from the port
> on the card if no drives are found, but unfortunately ATA doesn't
> call the "port_disable" method in this circumstance.

Here's an updated version.  I've removed the correction of the cycle
time - since we're checking whether all of active, recovery and cycle
periods fit the hardware, the correction becomes unnecessary.

I still suggest that the PATA core folk consider fixing their timing
calculation function in that respect though.

This driver continues to have the so far ignored issue concerning
port_disable.  It would be good to have some feedback on this instead
of this driver continuing to be crippled by the libata core code.  This
really needs resolving before this driver can be merged, though I'm not
sure how.

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 7bdbe5a..9cd8a61 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -552,6 +552,14 @@ config PATA_PLATFORM
 
 	  If unsure, say N.
 
+config PATA_ICSIDE
+	tristate "Acorn ICS PATA support"
+	depends on ARM && ARCH_ACORN
+	help
+	  On Acorn systems, say Y here if you wish to use the ICS PATA
+	  interface card.  This is not required for ICS partition support.
+	  If you are unsure, say N to this.
+
 config PATA_IXP4XX_CF
 	tristate "IXP4XX Compact Flash support"
 	depends on ARCH_IXP4XX
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 13d7397..cc8798b 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_PATA_TRIFLEX)	+= pata_triflex.o
 obj-$(CONFIG_PATA_IXP4XX_CF)	+= pata_ixp4xx_cf.o
 obj-$(CONFIG_PATA_SCC)		+= pata_scc.o
 obj-$(CONFIG_PATA_PLATFORM)	+= pata_platform.o
+obj-$(CONFIG_PATA_ICSIDE)	+= pata_icside.o
 # Should be last but one libata driver
 obj-$(CONFIG_ATA_GENERIC)	+= ata_generic.o
 # Should be last libata driver
diff --git a/drivers/ata/pata_icside.c b/drivers/ata/pata_icside.c
new file mode 100644
index 0000000..75b22da
--- /dev/null
+++ b/drivers/ata/pata_icside.c
@@ -0,0 +1,686 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <scsi/scsi_host.h>
+#include <linux/ata.h>
+#include <linux/libata.h>
+
+#include <asm/dma.h>
+#include <asm/ecard.h>
+
+#define DRV_NAME	"pata_icside"
+
+#define ICS_IDENT_OFFSET		0x2280
+
+#define ICS_ARCIN_V5_INTRSTAT		0x0000
+#define ICS_ARCIN_V5_INTROFFSET		0x0004
+
+#define ICS_ARCIN_V6_INTROFFSET_1	0x2200
+#define ICS_ARCIN_V6_INTRSTAT_1		0x2290
+#define ICS_ARCIN_V6_INTROFFSET_2	0x3200
+#define ICS_ARCIN_V6_INTRSTAT_2		0x3290
+
+struct portinfo {
+	unsigned int dataoffset;
+	unsigned int ctrloffset;
+	unsigned int stepping;
+};
+
+static const struct portinfo pata_icside_portinfo_v5 = {
+	.dataoffset	= 0x2800,
+	.ctrloffset	= 0x2b80,
+	.stepping	= 6,
+};
+
+static const struct portinfo pata_icside_portinfo_v6_1 = {
+	.dataoffset	= 0x2000,
+	.ctrloffset	= 0x2380,
+	.stepping	= 6,
+};
+
+static const struct portinfo pata_icside_portinfo_v6_2 = {
+	.dataoffset	= 0x3000,
+	.ctrloffset	= 0x3380,
+	.stepping	= 6,
+};
+
+#define PATA_ICSIDE_MAX_SG	128
+
+struct pata_icside_state {
+	void __iomem *irq_port;
+	void __iomem *ioc_base;
+	unsigned int type;
+	unsigned int dma;
+	struct {
+		u8 port_sel;
+		u8 disabled;
+		unsigned int speed[ATA_MAX_DEVICES];
+	} port[2];
+	struct scatterlist sg[PATA_ICSIDE_MAX_SG];
+};
+
+#define ICS_TYPE_A3IN	0
+#define ICS_TYPE_A3USER	1
+#define ICS_TYPE_V6	3
+#define ICS_TYPE_V5	15
+#define ICS_TYPE_NOTYPE	((unsigned int)-1)
+
+/* ---------------- Version 5 PCB Support Functions --------------------- */
+/* Prototype: pata_icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void pata_icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	writeb(0, state->irq_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+/* Prototype: pata_icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void pata_icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	readb(state->irq_port + ICS_ARCIN_V5_INTROFFSET);
+}
+
+static const expansioncard_ops_t pata_icside_ops_arcin_v5 = {
+	.irqenable	= pata_icside_irqenable_arcin_v5,
+	.irqdisable	= pata_icside_irqdisable_arcin_v5,
+};
+
+
+/* ---------------- Version 6 PCB Support Functions --------------------- */
+/* Prototype: pata_icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : enable interrupts from card
+ */
+static void pata_icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+	void __iomem *base = state->irq_port;
+
+	if (!state->port[0].disabled)
+		writeb(0, base + ICS_ARCIN_V6_INTROFFSET_1);
+	if (!state->port[1].disabled)
+		writeb(0, base + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: pata_icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+ * Purpose  : disable interrupts from card
+ */
+static void pata_icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_1);
+	readb(state->irq_port + ICS_ARCIN_V6_INTROFFSET_2);
+}
+
+/* Prototype: pata_icside_irqprobe(struct expansion_card *ec)
+ * Purpose  : detect an active interrupt from card
+ */
+static int pata_icside_irqpending_arcin_v6(struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ec->irq_data;
+
+	return readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 ||
+	       readb(state->irq_port + ICS_ARCIN_V6_INTRSTAT_2) & 1;
+}
+
+static const expansioncard_ops_t pata_icside_ops_arcin_v6 = {
+	.irqenable	= pata_icside_irqenable_arcin_v6,
+	.irqdisable	= pata_icside_irqdisable_arcin_v6,
+	.irqpending	= pata_icside_irqpending_arcin_v6,
+};
+
+
+/*
+ * SG-DMA support.
+ *
+ * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers.
+ * There is only one DMA controller per card, which means that only
+ * one drive can be accessed at one time.  NOTE! We do not enforce that
+ * here, but we rely on the main IDE driver spotting that both
+ * interfaces use the same IRQ, which should guarantee this.
+ */
+
+/*
+ * Configure the IOMD to give the appropriate timings for the transfer
+ * mode being requested.  We take the advice of the ATA standards, and
+ * calculate the cycle time based on the transfer mode, and the EIDE
+ * MW DMA specs that the drive provides in the IDENTIFY command.
+ *
+ * We have the following IOMD DMA modes to choose from:
+ *
+ *	Type	Active		Recovery	Cycle
+ *	A	250 (250)	312 (550)	562 (800)
+ *	B	187 (200)	250 (550)	437 (750)
+ *	C	125 (125)	125 (375)	250 (500)
+ *	D	62  (50)	125 (375)	187 (425)
+ *
+ * (figures in brackets are actual measured timings on DIOR/DIOW)
+ *
+ * However, we also need to take care of the read/write active and
+ * recovery timings:
+ *
+ *			Read	Write
+ *  	Mode	Active	-- Recovery --	Cycle	IOMD type
+ *	MW0	215	50	215	480	A
+ *	MW1	80	50	50	150	C
+ *	MW2	70	25	25	120	C
+ */
+static void pata_icside_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+	struct pata_icside_state *state = ap->host->private_data;
+	struct ata_timing t;
+	unsigned int cycle;
+	char iomd_type;
+
+	/*
+	 * DMA is based on a 16MHz clock
+	 */
+	if (ata_timing_compute(adev, adev->dma_mode, &t, 1000, 1))
+		return;
+
+	/*
+	 * Choose the IOMD cycle timing which ensure that the interface
+	 * satisfies the measured active, recovery and cycle times.
+	 */
+	if (t.active <= 50 && t.recover <= 375 && t.cycle <= 425)
+		iomd_type = 'D', cycle = 187;
+	else if (t.active <= 125 && t.recover <= 375 && t.cycle <= 500)
+		iomd_type = 'C', cycle = 250;
+	else if (t.active <= 200 && t.recover <= 550 && t.cycle <= 750)
+		iomd_type = 'B', cycle = 437;
+	else
+		iomd_type = 'A', cycle = 562;
+
+	ata_dev_printk(adev, KERN_INFO, "timings: act %dns rec %dns cyc %dns (%c)\n",
+		t.active, t.recover, t.cycle, iomd_type);
+
+	state->port[ap->port_no].speed[adev->devno] = cycle;
+}
+
+static void pata_icside_bmdma_setup(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct pata_icside_state *state = ap->host->private_data;
+	struct scatterlist *sg, *rsg = state->sg;
+	unsigned int write = qc->tf.flags & ATA_TFLAG_WRITE;
+
+	/*
+	 * We are simplex; BUG if we try to fiddle with DMA
+	 * while it's active.
+	 */
+	BUG_ON(dma_channel_active(state->dma));
+
+	/*
+	 * Copy ATAs scattered sg list into a contiguous array of sg
+	 */
+	ata_for_each_sg(sg, qc) {
+		memcpy(rsg, sg, sizeof(*sg));
+		rsg++;
+	}
+
+	/*
+	 * Route the DMA signals to the correct interface
+	 */
+	writeb(state->port[ap->port_no].port_sel, state->ioc_base);
+
+	set_dma_speed(state->dma, state->port[ap->port_no].speed[qc->dev->devno]);
+	set_dma_sg(state->dma, state->sg, rsg - state->sg);
+	set_dma_mode(state->dma, write ? DMA_MODE_WRITE : DMA_MODE_READ);
+
+	/* issue r/w command */
+	ap->ops->exec_command(ap, &qc->tf);
+}
+
+static void pata_icside_bmdma_start(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct pata_icside_state *state = ap->host->private_data;
+
+	BUG_ON(dma_channel_active(state->dma));
+	enable_dma(state->dma);
+}
+
+static void pata_icside_bmdma_stop(struct ata_queued_cmd *qc)
+{
+	struct ata_port *ap = qc->ap;
+	struct pata_icside_state *state = ap->host->private_data;
+
+	disable_dma(state->dma);
+
+	/* see ata_bmdma_stop */
+	ata_altstatus(ap);
+}
+
+static u8 pata_icside_bmdma_status(struct ata_port *ap)
+{
+	struct pata_icside_state *state = ap->host->private_data;
+	void __iomem *irq_port;
+
+	irq_port = state->irq_port + (ap->port_no ? ICS_ARCIN_V6_INTRSTAT_2 :
+						    ICS_ARCIN_V6_INTRSTAT_1);
+
+	return readb(irq_port) & 1 ? ATA_DMA_INTR : 0;
+}
+
+static int icside_dma_init(struct ata_probe_ent *ae, struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ae->private_data;
+	int i;
+
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		state->port[0].speed[i] = 480;
+		state->port[1].speed[i] = 480;
+	}
+
+	if (ec->dma != NO_DMA && !request_dma(ec->dma, DRV_NAME)) {
+		state->dma = ec->dma;
+		ae->mwdma_mask = 0x07;	/* MW0..2 */
+	}
+
+	return 0;
+}
+
+
+static int pata_icside_port_start(struct ata_port *ap)
+{
+	/* No PRD to alloc */
+	return ata_pad_alloc(ap, ap->dev);
+}
+
+static struct scsi_host_template pata_icside_sht = {
+	.module			= THIS_MODULE,
+	.name			= DRV_NAME,
+	.ioctl			= ata_scsi_ioctl,
+	.queuecommand		= ata_scsi_queuecmd,
+	.can_queue		= ATA_DEF_QUEUE,
+	.this_id		= ATA_SHT_THIS_ID,
+	.sg_tablesize		= PATA_ICSIDE_MAX_SG,
+	.cmd_per_lun		= ATA_SHT_CMD_PER_LUN,
+	.emulated		= ATA_SHT_EMULATED,
+	.use_clustering		= ATA_SHT_USE_CLUSTERING,
+	.proc_name		= DRV_NAME,
+	.dma_boundary		= ~0, /* no dma boundaries */
+	.slave_configure	= ata_scsi_slave_config,
+	.slave_destroy		= ata_scsi_slave_destroy,
+	.bios_param		= ata_std_bios_param,
+};
+
+/* wish this was exported from libata-core */
+static void ata_dummy_noret(struct ata_port *port)
+{
+}
+
+/*
+ * We need to shut down unused ports to prevent spurious interrupts.
+ * FIXME: the libata core doesn't call this function for PATA interfaces.
+ */
+static void pata_icside_port_disable(struct ata_port *ap)
+{
+	struct pata_icside_state *state = ap->host->private_data;
+
+	ata_port_printk(ap, KERN_ERR, "disabling icside port\n");
+
+	ata_port_disable(ap);
+
+	state->port[ap->port_no].disabled = 1;
+
+	if (state->type == ICS_TYPE_V6) {
+		/*
+		 * Disable interrupts from this port, otherwise we
+		 * receive spurious interrupts from the floating
+		 * interrupt line.
+		 */
+		void __iomem *irq_port = state->irq_port +
+				(ap->port_no ? ICS_ARCIN_V6_INTROFFSET_2 : ICS_ARCIN_V6_INTROFFSET_1);
+		readb(irq_port);
+	}
+}
+
+static u8 pata_icside_irq_ack(struct ata_port *ap, unsigned int chk_drq)
+{
+	unsigned int bits = chk_drq ? ATA_BUSY | ATA_DRQ : ATA_BUSY;
+	u8 status;
+
+	status = ata_busy_wait(ap, bits, 1000);
+	if (status & bits)
+		if (ata_msg_err(ap))
+			printk(KERN_ERR "abnormal status 0x%X\n", status);
+
+	if (ata_msg_intr(ap))
+		printk(KERN_INFO "%s: irq ack: drv_stat 0x%X\n",
+			__FUNCTION__, status);
+
+	return status;
+}
+
+static struct ata_port_operations pata_icside_port_ops = {
+	.port_disable		= pata_icside_port_disable,
+
+	.set_dmamode		= pata_icside_set_dmamode,
+
+	.tf_load		= ata_tf_load,
+	.tf_read		= ata_tf_read,
+	.exec_command		= ata_exec_command,
+	.check_status		= ata_check_status,
+	.dev_select		= ata_std_dev_select,
+
+	.bmdma_setup		= pata_icside_bmdma_setup,
+	.bmdma_start		= pata_icside_bmdma_start,
+
+	.data_xfer		= ata_data_xfer_noirq,
+
+	/* no need to build any PRD tables for DMA */
+	.qc_prep		= ata_noop_qc_prep,
+	.qc_issue		= ata_qc_issue_prot,
+
+	.freeze			= ata_bmdma_freeze,
+	.thaw			= ata_bmdma_thaw,
+	.error_handler		= ata_bmdma_error_handler,
+	.post_internal_cmd	= pata_icside_bmdma_stop,
+
+	.irq_handler		= ata_interrupt,
+	.irq_clear		= ata_dummy_noret,
+	.irq_on			= ata_irq_on,
+	.irq_ack		= pata_icside_irq_ack,
+
+	.port_start		= pata_icside_port_start,
+
+	.bmdma_stop		= pata_icside_bmdma_stop,
+	.bmdma_status		= pata_icside_bmdma_status,
+};
+
+static void
+pata_icside_add_port(struct ata_probe_ent *ae, void __iomem *base,
+		     const struct portinfo *info)
+{
+	struct ata_ioports *ioaddr = &ae->port[ae->n_ports++];
+	void __iomem *cmd = base + info->dataoffset;
+
+	ioaddr->cmd_addr	= cmd;
+	ioaddr->data_addr	= cmd + (ATA_REG_DATA    << info->stepping);
+	ioaddr->error_addr	= cmd + (ATA_REG_ERR     << info->stepping);
+	ioaddr->feature_addr	= cmd + (ATA_REG_FEATURE << info->stepping);
+	ioaddr->nsect_addr	= cmd + (ATA_REG_NSECT   << info->stepping);
+	ioaddr->lbal_addr	= cmd + (ATA_REG_LBAL    << info->stepping);
+	ioaddr->lbam_addr	= cmd + (ATA_REG_LBAM    << info->stepping);
+	ioaddr->lbah_addr	= cmd + (ATA_REG_LBAH    << info->stepping);
+	ioaddr->device_addr	= cmd + (ATA_REG_DEVICE  << info->stepping);
+	ioaddr->status_addr	= cmd + (ATA_REG_STATUS  << info->stepping);
+	ioaddr->command_addr	= cmd + (ATA_REG_CMD     << info->stepping);
+
+	ioaddr->ctl_addr	= base + info->ctrloffset;
+	ioaddr->altstatus_addr	= ioaddr->ctl_addr;
+}
+
+static int __init
+pata_icside_register_v5(struct ata_probe_ent *ae, struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ae->private_data;
+	void __iomem *base;
+
+	base = ioremap(ecard_resource_start(ec, ECARD_RES_MEMC),
+		       ecard_resource_len(ec, ECARD_RES_MEMC));
+	if (!base)
+		return -ENOMEM;
+
+	state->irq_port = base;
+
+	ec->irqaddr = base + ICS_ARCIN_V5_INTRSTAT;
+	ec->irqmask = 1;
+	ec->irq_data = state;
+	ec->ops = &pata_icside_ops_arcin_v5;
+
+	/*
+	 * Be on the safe side - disable interrupts
+	 */
+	ec->ops->irqdisable(ec, ec->irq);
+
+	pata_icside_add_port(ae, base, &pata_icside_portinfo_v5);
+
+	return 0;
+}
+
+static int __init
+pata_icside_register_v6(struct ata_probe_ent *ae, struct expansion_card *ec)
+{
+	struct pata_icside_state *state = ae->private_data;
+	void __iomem *ioc_base, *easi_base;
+	unsigned int sel = 0;
+	int ret;
+
+	ioc_base = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST),
+			   ecard_resource_len(ec, ECARD_RES_IOCFAST));
+	if (!ioc_base) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	easi_base = ioc_base;
+
+	if (ecard_resource_flags(ec, ECARD_RES_EASI)) {
+		easi_base = ioremap(ecard_resource_start(ec, ECARD_RES_EASI),
+				    ecard_resource_len(ec, ECARD_RES_EASI));
+		if (!easi_base) {
+			ret = -ENOMEM;
+			goto unmap_slot;
+		}
+
+		/*
+		 * Enable access to the EASI region.
+		 */
+		sel = 1 << 5;
+	}
+
+	writeb(sel, ioc_base);
+
+	ec->irq_data = state;
+	ec->ops = &pata_icside_ops_arcin_v6;
+
+	state->irq_port = easi_base;
+	state->ioc_base = ioc_base;
+	state->port[0].port_sel = sel;
+	state->port[1].port_sel = sel | 1;
+
+	/*
+	 * Be on the safe side - disable interrupts
+	 */
+	ec->ops->irqdisable(ec, ec->irq);
+
+	/*
+	 * Find and register the interfaces.
+	 */
+	pata_icside_add_port(ae, easi_base, &pata_icside_portinfo_v6_1);
+	pata_icside_add_port(ae, easi_base, &pata_icside_portinfo_v6_2);
+
+	/*
+	 * FIXME: work around libata's aversion to calling port_disable.
+	 * This permanently disables interrupts on port 0 - bad luck if
+	 * you have a drive on that port.
+	 */
+	state->port[0].disabled = 1;
+
+	return icside_dma_init(ae, ec);
+
+ unmap_slot:
+	iounmap(ioc_base);
+ out:
+	return ret;
+}
+
+static int __devinit
+pata_icside_probe(struct expansion_card *ec, const struct ecard_id *id)
+{
+	struct pata_icside_state *state;
+	struct ata_probe_ent ae;
+	void __iomem *idmem;
+	int ret;
+
+	ret = ecard_request_resources(ec);
+	if (ret)
+		goto out;
+
+	state = kzalloc(sizeof(struct pata_icside_state), GFP_KERNEL);
+	if (!state) {
+		ret = -ENOMEM;
+		goto release;
+	}
+
+	state->type = ICS_TYPE_NOTYPE;
+	state->dma = NO_DMA;
+
+	idmem = ioremap(ecard_resource_start(ec, ECARD_RES_IOCFAST),
+			ecard_resource_len(ec, ECARD_RES_IOCFAST));
+	if (idmem) {
+		unsigned int type;
+
+		type = readb(idmem + ICS_IDENT_OFFSET) & 1;
+		type |= (readb(idmem + ICS_IDENT_OFFSET + 4) & 1) << 1;
+		type |= (readb(idmem + ICS_IDENT_OFFSET + 8) & 1) << 2;
+		type |= (readb(idmem + ICS_IDENT_OFFSET + 12) & 1) << 3;
+		iounmap(idmem);
+
+		state->type = type;
+	}
+
+	memset(&ae, 0, sizeof(ae));
+	INIT_LIST_HEAD(&ae.node);
+	ae.dev          = &ec->dev;
+	ae.port_ops     = &pata_icside_port_ops;
+	ae.sht          = &pata_icside_sht;
+	ae.pio_mask     = 0x1f;
+	ae.irq          = ec->irq;
+	ae.port_flags   = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST;
+	ae._host_flags  = ATA_HOST_SIMPLEX;
+	ae.private_data = state;
+
+	switch (state->type) {
+	case ICS_TYPE_A3IN:
+		dev_warn(&ec->dev, "A3IN unsupported\n");
+		ret = -ENODEV;
+		break;
+
+	case ICS_TYPE_A3USER:
+		dev_warn(&ec->dev, "A3USER unsupported\n");
+		ret = -ENODEV;
+		break;
+
+	case ICS_TYPE_V5:
+		ret = pata_icside_register_v5(&ae, ec);
+		break;
+
+	case ICS_TYPE_V6:
+		ret = pata_icside_register_v6(&ae, ec);
+		break;
+
+	default:
+		dev_warn(&ec->dev, "unknown interface type\n");
+		ret = -ENODEV;
+		break;
+	}
+
+	if (ret == 0)
+		ret = ata_device_add(&ae) == 0 ? -ENODEV : 0;
+
+	if (ret == 0)
+		goto out;
+
+	kfree(state);
+ release:
+	ecard_release_resources(ec);
+ out:
+	return ret;
+}
+
+static void pata_icside_shutdown(struct expansion_card *ec)
+{
+	struct ata_host *host = ecard_get_drvdata(ec);
+	unsigned long flags;
+
+	/*
+	 * Disable interrupts from this card.  We need to do
+	 * this before disabling EASI since we may be accessing
+	 * this register via that region.
+	 */
+	local_irq_save(flags);
+	if (ec->ops)
+		ec->ops->irqdisable(ec, ec->irq);
+	local_irq_restore(flags);
+
+	/*
+	 * Reset the ROM pointer so that we can read the ROM
+	 * after a soft reboot.  This also disables access to
+	 * the IDE taskfile via the EASI region.
+	 */
+	if (host) {
+		struct pata_icside_state *state = host->private_data;
+		if (state->ioc_base)
+			writeb(0, state->ioc_base);
+	}
+}
+
+static void __devexit pata_icside_remove(struct expansion_card *ec)
+{
+	struct ata_host *host = ecard_get_drvdata(ec);
+	struct pata_icside_state *state = host->private_data;
+
+	ata_host_detach(host);
+
+	pata_icside_shutdown(ec);
+
+	/*
+	 * don't NULL out the drvdata - devres/libata wants it
+	 * to free the ata_host structure.
+	 */
+	ec->ops = NULL;
+	ec->irq_data = NULL;
+
+	if (state->dma != NO_DMA)
+		free_dma(state->dma);
+	if (state->ioc_base)
+		iounmap(state->ioc_base);
+	if (state->ioc_base != state->irq_port)
+		iounmap(state->irq_port);
+
+	kfree(state);
+	ecard_release_resources(ec);
+}
+
+static const struct ecard_id pata_icside_ids[] = {
+	{ MANU_ICS,  PROD_ICS_IDE  },
+	{ MANU_ICS2, PROD_ICS2_IDE },
+	{ 0xffff, 0xffff }
+};
+
+static struct ecard_driver pata_icside_driver = {
+	.probe		= pata_icside_probe,
+	.remove 	= __devexit_p(pata_icside_remove),
+	.shutdown	= pata_icside_shutdown,
+	.id_table	= pata_icside_ids,
+	.drv = {
+		.name	= DRV_NAME,
+	},
+};
+
+static int __init pata_icside_init(void)
+{
+	return ecard_register_driver(&pata_icside_driver);
+}
+
+static void __exit pata_icside_exit(void)
+{
+	ecard_remove_driver(&pata_icside_driver);
+}
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ICS PATA driver");
+
+module_init(pata_icside_init);
+module_exit(pata_icside_exit);


-- 
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:

^ permalink raw reply related	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2007-04-21 15:09 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-30 11:00 [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT Russell King
2007-03-30 11:08 ` [RFC] pata_platform for ARM RiscPC Russell King
2007-04-08 10:18   ` [RFC] pata_icside driver Russell King
2007-04-08 18:59     ` Alan Cox
2007-04-09  1:03       ` Jeff Garzik
2007-04-09  9:56         ` Alan Cox
2007-04-09 10:56           ` Jeff Garzik
2007-04-09 11:13             ` Jeff Garzik
2007-04-09 11:36               ` Russell King
2007-04-09 12:02                 ` Jeff Garzik
2007-04-08 20:09     ` Alan Cox
2007-04-09  8:18       ` Russell King
2007-04-09  8:24         ` Roland Dreier
2007-04-09  8:44           ` Russell King
2007-04-09 10:25         ` Alan Cox
2007-04-09 11:33           ` Russell King
2007-04-21 15:09     ` Russell King
2007-04-09 11:32   ` [RFC] pata_platform for ARM RiscPC Jeff Garzik
2007-03-30 11:08 ` [PATCH] Provide dummy devm_ioport_* if !HAS_IOPORT Christoph Hellwig

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.