Re: [PATCH,RFC] PCI: deal with device incorrectly reporting I/O decoding being enabled

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

 



On Sun, Nov 8, 2009 at 8:41 AM, Russell King - ARM Linux
<linux@xxxxxxxxxxxxxxxx> wrote:
> On Sun, Nov 08, 2009 at 08:23:10AM -0700, Matthew Wilcox wrote:
>> On Sun, Nov 08, 2009 at 09:04:51AM +0000, Russell King - ARM Linux wrote:
>> > On Sat, Nov 07, 2009 at 08:03:05AM -0700, Matthew Wilcox wrote:
>> > > I'm OK with this ... it might provoke bugs in some _other_ piece of hardware,
>> > > but that seems pretty unlikely.
>> >
>> > Please remember that some ARM hardware only has the capability to read
>> > and write full 32-bit quantities of configuration space, which means
>> > writing the control register can result in the status register being
>> > written as well.
>> >
>> > That shouldn't be a problem, but is merely something else to consider
>> > which isn't obvious.
>>
>> Wow, that's horrid.  You must have to special case each config register
>> that's accessed in a 16-bit way, since some bits are 'preserve' and others
>> are 'write 1 to clear'.  What a nightmare.
>
> Yes, it's horrid, and it can be found on all IOP ARM stuff, eg:
>
>                val = iop3xx_read(addr);
>                if (iop3xx_pci_status())
>                        return PCIBIOS_SUCCESSFUL;
>
>                where = (where & 3) * 8;
>
>                if (size == 1)
>                        val &= ~(0xff << where);
>                else
>                        val &= ~(0xffff << where);
>
>                *IOP3XX_OCCDR = val | value << where;
>
> This covers IOP32x, IOP33x, and there's similar code for IOP13xx.
>
> This does mean that if we make this change, we may do more accidental
> writes to the status register on such hardware.  Whether that matters
> in reality isn't something I can answer - I suspect we can get away
> with this without causing problems.
>
> CC'ing Dan for his info: the proposed change at the start of this thread
> is to make the write to the command register in pcibios_enable_device()
> unconditional.  This will guarantee that IOP platforms to copy-back the
> status register on to itself on pci_enable_device(), thereby causing
> "write 1 to clear" status bits to be cleared.
>

Thanks for the CC.  I am hoping that this is just an initial
misreading of the IOP specification that was blindly forward ported
from the iop32x days.  In my interpretation it says that accesses may
not *cross* a dword boundary, it says nothing about sub-dword.

>From the developer's manual:
"Configuration cycles are non-burst and restricted to a single 32-bit
word cycle"
...
"The user should designate the memory region containing the OCCDR as
non-cacheable and non-bufferable from the Intel XScale® core.  This
guarantees that all load/stores to the OCCDR is only of DWORD
quantities. In event the user inadvertently issues a read to the OCCDR
that crosses a DWORD address boundary, the ATU target aborts the
transaction. All writes is terminated with a Single-Phase-Disconnect
and only bytes 3:0 is relevant."

I believe sub-dword access were once verified to work, but I no longer
have access to those test results.  However, the following at least
boots on my iop13xx, so I'll look into this a bit more.

Thanks,
Dan

diff --git a/arch/arm/mach-iop13xx/pci.c b/arch/arm/mach-iop13xx/pci.c
index 4873f26..1b6a65a 100644
--- a/arch/arm/mach-iop13xx/pci.c
+++ b/arch/arm/mach-iop13xx/pci.c
@@ -190,7 +190,7 @@ static u32 iop13xx_atux_cfg_address(struct pci_bus
*bus, int devfn, int where)
        else
                addr = bus->number << 16 | PCI_SLOT(devfn) << 11 | 1;

-       addr |= PCI_FUNC(devfn) << 8 | ((where & 0xff) & ~3);
+       addr |= PCI_FUNC(devfn) << 8 | (where & 0xff);
        addr |= ((where & 0xf00) >> 8) << 24; /* upper register number */

        return addr;
@@ -264,7 +264,7 @@ static u32 iop13xx_atux_read(unsigned long addr)
 {
        u32 val;

-       __asm__ __volatile__(
+       asm volatile(
                "str    %1, [%2]\n\t"
                "ldr    %0, [%3]\n\t"
                "mov    %0, %0\n\t"
@@ -281,7 +281,7 @@ static int
 iop13xx_atux_read_config(struct pci_bus *bus, unsigned int devfn, int where,
                int size, u32 *value)
 {
-       unsigned long addr = iop13xx_atux_cfg_address(bus, devfn, where);
+       unsigned long addr = iop13xx_atux_cfg_address(bus, devfn, where & ~3);
        u32 val = iop13xx_atux_read(addr) >> ((where & 3) * 8);

        if (iop13xx_atux_pci_status(1) || is_atux_occdr_error()) {
@@ -300,25 +300,14 @@ iop13xx_atux_write_config(struct pci_bus *bus,
unsigned int devfn, int where,
                int size, u32 value)
 {
        unsigned long addr = iop13xx_atux_cfg_address(bus, devfn, where);
-       u32 val;
-
-       if (size != 4) {
-               val = iop13xx_atux_read(addr);
-               if (!iop13xx_atux_pci_status(1) == 0)
-                       return PCIBIOS_SUCCESSFUL;
-
-               where = (where & 3) * 8;

-               if (size == 1)
-                       val &= ~(0xff << where);
-               else
-                       val &= ~(0xffff << where);
-
-               __raw_writel(val | value << where, IOP13XX_ATUX_OCCDR);
-       } else {
-               __raw_writel(addr, IOP13XX_ATUX_OCCAR);
+       __raw_writel(addr, IOP13XX_ATUX_OCCAR);
+       if (size == 1)
+               __raw_writeb(value, IOP13XX_ATUX_OCCDR);
+       else if (size == 2)
+               __raw_writew(value, IOP13XX_ATUX_OCCDR);
+       else
                __raw_writel(value, IOP13XX_ATUX_OCCDR);
-       }

        return PCIBIOS_SUCCESSFUL;
 }
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux