pci_read_legacy_io() passes the sysfs buffer directly to pci_legacy_read():

  return pci_legacy_read(bus, off, (u32 *)buf, count);

The PowerPC implementation stores the result as a native-endian integer:

  *((u16 *)val) = in_le16(addr);

On big-endian PowerPC this stores the bytes in the wrong order, so
a 2-byte read of a device register returns different bytes than two
1-byte reads at the same addresses.  The same applies to 4-byte
reads.  On little-endian the native byte order already matches PCI
I/O port byte order, so the conversion is a no-op.

Thus, let pci_legacy_read() store into a local u32 variable, then
copy the I/O port value to the sysfs buffer using put_unaligned_le16()
and put_unaligned_le32() for the 2 and 4 byte cases, converting from
the native integer to little-endian byte order matching PCI I/O port
space.

No changes are needed for the Alpha platform.

The legacy_io file is root-only and exists only on Alpha and PowerPC,
the two architectures that define HAVE_PCI_LEGACY.

Cc: [email protected]
Signed-off-by: Krzysztof Wilczyński <[email protected]>
---
 drivers/pci/pci-sysfs.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index b56000ba3a33..2354d09fd3fa 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -908,12 +908,30 @@ static ssize_t pci_read_legacy_io(struct file *filp, 
struct kobject *kobj,
                                  char *buf, loff_t off, size_t count)
 {
        struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
+       u32 val = 0;
+       int ret;
 
        /* Only support 1, 2 or 4 byte accesses */
        if (count != 1 && count != 2 && count != 4)
                return -EINVAL;
 
-       return pci_legacy_read(bus, off, (u32 *)buf, count);
+       ret = pci_legacy_read(bus, off, &val, count);
+       if (ret < 0)
+               return ret;
+
+       switch (count) {
+       case 1:
+               buf[0] = *(u8 *)&val;
+               break;
+       case 2:
+               put_unaligned_le16(*(u16 *)&val, buf);
+               break;
+       case 4:
+               put_unaligned_le32(val, buf);
+               break;
+       }
+
+       return ret;
 }
 
 /**
-- 
2.54.0


Reply via email to