Re: iq81340mc: oops with SATA VIA

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 3/6/07, Alan Cox <alan@xxxxxxxxxxxxxxxxxxx> wrote:
> Martin I am copying linux-ide because I am wondering if this issue is
> due to a case where the fact that iop13xx posts i/o writes is causing
> issues.  On IA the driver would be stalled until an i/o write completed.
> On iop13xx the driver is free to continue running.  Perhaps someone with
> more knowledge about the sata_via hardware can answer if this would be
> an issue, because everything thus far looks correct in the iop13xx code.

outb/w/l and friends are required not to post. If you are using mmio
operations then the MMIO ops code in libata does any posting of writes
required and handles it properly. If you are emulating outb/w/l without
avoiding posting then it wont work as with many other drivers.

I see... in this case the iop13xx board support code is indeed
violating the delayed assumption.

Martin can you give the attached patch a try to see if it resolves the
oops?  It has only been compile tested.  The patch spins the processor
on the ATU outbound-queue-busy bit whenever out*() is called.

Alan

Thanks for the help.

--
Dan
iop13xx: fix implementation of delayed pci i/o writes

From: Dan Williams <dan.j.williams@xxxxxxxxx>

iop13xx internally posts all write transactions including delayed
transcations to the ATU-E and ATU-X busses.  This causes a problem for
devices and drivers that rely on delayed actions for software/hardware
timing purposes.  This patch prevents out[bwl] transactions from completing
until the outbound transaction queue of the ATU is empty. 

Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx>
---

 arch/arm/mach-iop13xx/io.c        |  145 +++++++++++++++++++++++++++++++++++--
 include/asm-arm/arch-iop13xx/io.h |   63 +++++++++++++++-
 2 files changed, 198 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-iop13xx/io.c b/arch/arm/mach-iop13xx/io.c
index e79a1b6..d516878 100644
--- a/arch/arm/mach-iop13xx/io.c
+++ b/arch/arm/mach-iop13xx/io.c
@@ -21,25 +21,156 @@
 #include <asm/hardware.h>
 #include <asm/io.h>
 
-void * __iomem __iop13xx_io(unsigned long io_addr)
+static int select_atu_for_io(unsigned long io_addr)
 {
-	void __iomem * io_virt;
-
 	switch (io_addr) {
 	case IOP13XX_PCIE_LOWER_IO_PA ... IOP13XX_PCIE_UPPER_IO_PA:
-		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+		return IOP13XX_INIT_ATU_ATUE;
 		break;
 	case IOP13XX_PCIX_LOWER_IO_PA ... IOP13XX_PCIX_UPPER_IO_PA:
-		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+		return IOP13XX_INIT_ATU_ATUX;
 		break;
-	default:
-		BUG();
 	}
 
+	return IOP13XX_INIT_ATU_NONE;	
+}
+
+static void wait_for_atue_outq(void)
+{
+	while (__raw_readl(IOP13XX_ATUE_PCSR) & (IOP13XX_ATUE_PCSR_OUT_Q_BUSY))
+		cpu_relax();
+}
+
+static void wait_for_atux_outq(void)
+{
+	while (__raw_readl(IOP13XX_ATUX_PCSR) & (IOP13XX_ATUX_PCSR_OUT_Q_BUSY))
+		cpu_relax();
+}
+
+void * __iomem __iop13xx_io(unsigned long io_addr)
+{
+	void __iomem * io_virt;
+	int atu = select_atu_for_io(io_addr);
+
+	if (atu == IOP13XX_INIT_ATU_ATUE)
+		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+	else if (atu == IOP13XX_INIT_ATU_ATUX)
+		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+	else
+		BUG();
+
 	return io_virt;
 }
 EXPORT_SYMBOL(__iop13xx_io);
 
+void __iop13xx_outb(u8 v, unsigned long io_addr)
+{
+	int atu = select_atu_for_io(io_addr);
+	void __iomem * io_virt;
+
+	if (atu == IOP13XX_INIT_ATU_ATUE) {
+		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writeb(v, io_virt);
+		wait_for_atue_outq();
+	} else if (atu == IOP13XX_INIT_ATU_ATUX) {
+		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writeb(v, io_virt);
+		wait_for_atux_outq();
+	} else
+		BUG();
+}
+EXPORT_SYMBOL(__iop13xx_outb);
+
+void __iop13xx_outw(u16 v, unsigned long io_addr)
+{
+	int atu = select_atu_for_io(io_addr);
+	void __iomem * io_virt;
+
+	if (atu == IOP13XX_INIT_ATU_ATUE) {
+		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writew(cpu_to_le16(v), io_virt);
+		wait_for_atue_outq();
+	} else if (atu == IOP13XX_INIT_ATU_ATUX) {
+		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writew(cpu_to_le16(v), io_virt);
+		wait_for_atux_outq();
+	} else
+		BUG();
+}
+EXPORT_SYMBOL(__iop13xx_outw);
+
+void __iop13xx_outl(u32 v, unsigned long io_addr)
+{
+	int atu = select_atu_for_io(io_addr);
+	void __iomem * io_virt;
+
+	if (atu == IOP13XX_INIT_ATU_ATUE) {
+		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writel(cpu_to_le32(v), io_virt);
+		wait_for_atue_outq();
+	} else if (atu == IOP13XX_INIT_ATU_ATUX) {
+		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writel(cpu_to_le32(v), io_virt);
+		wait_for_atux_outq();
+	} else
+		BUG();
+}
+EXPORT_SYMBOL(__iop13xx_outl);
+
+void __iop13xx_outsb(unsigned long io_addr, void *d, unsigned long l)
+{
+	int atu = select_atu_for_io(io_addr);
+	void __iomem * io_virt;
+
+	if (atu == IOP13XX_INIT_ATU_ATUE) {
+		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writesb(io_virt, d, l);
+		wait_for_atue_outq();
+	} else if (atu == IOP13XX_INIT_ATU_ATUX) {
+		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writesb(io_virt, d, l);
+		wait_for_atux_outq();
+	} else
+		BUG();
+}
+EXPORT_SYMBOL(__iop13xx_outsb);
+
+void __iop13xx_outsw(unsigned long io_addr, void *d, unsigned long l)
+{
+	int atu = select_atu_for_io(io_addr);
+	void __iomem * io_virt;
+
+	if (atu == IOP13XX_INIT_ATU_ATUE) {
+		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writesw(io_virt, d, l);
+		wait_for_atue_outq();
+	} else if (atu == IOP13XX_INIT_ATU_ATUX) {
+		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writesw(io_virt, d, l);
+		wait_for_atux_outq();
+	} else
+		BUG();
+}
+EXPORT_SYMBOL(__iop13xx_outsw);
+
+void __iop13xx_outsl(unsigned long io_addr, void *d, unsigned long l)
+{
+	int atu = select_atu_for_io(io_addr);
+	void __iomem * io_virt;
+
+	if (atu == IOP13XX_INIT_ATU_ATUE) {
+		io_virt = (void *) IOP13XX_PCIE_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writesl(io_virt, d, l);
+		wait_for_atue_outq();
+	} else if (atu == IOP13XX_INIT_ATU_ATUX) {
+		io_virt = (void *) IOP13XX_PCIX_IO_PHYS_TO_VIRT(io_addr);
+		__raw_writesl(io_virt, d, l);
+		wait_for_atux_outq();
+	} else
+		BUG();
+}
+EXPORT_SYMBOL(__iop13xx_outsl);
+
 void * __iomem __iop13xx_ioremap(unsigned long cookie, size_t size,
 	unsigned long flags)
 {
diff --git a/include/asm-arm/arch-iop13xx/io.h b/include/asm-arm/arch-iop13xx/io.h
index 5a7bdb5..3fd5453 100644
--- a/include/asm-arm/arch-iop13xx/io.h
+++ b/include/asm-arm/arch-iop13xx/io.h
@@ -1,5 +1,5 @@
 /*
- * iop13xx custom ioremap implementation
+ * iop13xx custom ioremap and out[bsl] implementations
  * Copyright (c) 2005-2006, Intel Corporation.
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -21,11 +21,68 @@
 
 #define IO_SPACE_LIMIT 0xffffffff
 
-#define __io(a) __iop13xx_io(a)
+/* I/O write transactions are posted on the internal bus.
+ * In order to preserve the delayed transaction expectations
+ * of a device we will spin on the ATU* outbound-queue
+ * busy bit to busy-wait for completion.
+ */
+#define outb(v,p)		__iop13xx_outb(v,p)
+#define outw(v,p)		__iop13xx_outw((__force __u16) \
+                 		        cpu_to_le16(v),p)
+#define outl(v,p)		__iop13xx_outl((__force __u32) \
+                                        cpu_to_le32(v),p)
+#define outsb(p,d,l)		__iop13xx_outsb(p,d,l)
+#define outsw(p,d,l)		__iop13xx_outsw(p,d,l)
+#define outsl(p,d,l)		__iop13xx_outsl(p,d,l)
+
+/* I/O read macros mirror the default arm definitions */
+#define inb(p)	({ __u8 __v = __raw_readb(__iop13xx_io(p)); __v; })
+#define inw(p)	({ __u16 __v = le16_to_cpu((__force __le16) \
+                        __raw_readw(__iop13xx_io(p))); __v; })
+#define inl(p)	({ __u32 __v = le32_to_cpu((__force __le32) \
+                        __raw_readl(__iop13xx_io(p))); __v; })
+#define insb(p,d,l) 		__raw_readsb(__iop13xx_io(p),d,l)
+#define insw(p,d,l) 		__raw_readsw(__iop13xx_io(p),d,l)
+#define insl(p,d,l) 		__raw_readsl(__iop13xx_io(p),d,l)
+
+#define ioread8(p)	inb((unsigned long) (p)) 
+#define ioread16(p)	inw((unsigned long) (p))
+#define ioread32(p)	inl((unsigned long) (p))
+
+#define iowrite8(v,p)	outb(v,(unsigned long) (p))
+#define iowrite16(v,p)	outw(v,(unsigned long) (p))
+#define iowrite32(v,p)	outl(v,(unsigned long) (p))
+
+#define ioread8_rep(p,d,c)	insb((unsigned long) (p),d,c)
+#define ioread16_rep(p,d,c)	insw((unsigned long) (p),d,c)
+#define ioread32_rep(p,d,c)	insl((unsigned long) (p),d,c)
+
+#define iowrite8_rep(p,s,c)	outsb((unsigned long) (p),s,c)
+#define iowrite16_rep(p,s,c)	outsb((unsigned long) (p),s,c)
+#define iowrite32_rep(p,s,c)	outsl((unsigned long) (p),s,c)
+
+extern void __iomem * __iop13xx_io(unsigned long io_addr);
+static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
+{
+	/* do not translate */
+	return (void __iomem *) port;
+}
+
+static inline void ioport_unmap(void __iomem *addr)
+{
+	do { } while (0);
+}
+
 #define __mem_pci(a) (a)
 #define __mem_isa(a) (a)
 
-extern void __iomem * __iop13xx_io(unsigned long io_addr);
+extern void __iop13xx_outb(u8 v, unsigned long io_addr);
+extern void __iop13xx_outw(u16 v, unsigned long io_addr);
+extern void __iop13xx_outl(u32 v, unsigned long io_addr);
+extern void __iop13xx_outsb(unsigned long io_addr, void *d, unsigned long l);
+extern void __iop13xx_outsw(unsigned long io_addr, void *d, unsigned long l);
+extern void __iop13xx_outsl(unsigned long io_addr, void *d, unsigned long l);
+
 extern void __iomem * __ioremap(unsigned long, size_t, unsigned long);
 extern void __iomem *__iop13xx_ioremap(unsigned long cookie, size_t size,
 	unsigned long flags);

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux