Commit 4c0619ad authored by ssant@in.ibm.com's avatar ssant@in.ibm.com Committed by Greg KH

[PATCH] PCI: fix up word-aligned 16-bit PCI config access through sysfs

This patch adds the possibility to do word-aligned 16-bit atomic PCI
configuration space accesses via the sysfs PCI interface. As a result, problems
with Emulex LFPC on IBM PowerPC64 are fixed.

Patch is present in SLES 9 SP1.
Signed-off-by: default avatarVojtech Pavlik <vojtech@suse.cz>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent bc56b9e0
...@@ -91,6 +91,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) ...@@ -91,6 +91,7 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = 64; unsigned int size = 64;
loff_t init_off = off; loff_t init_off = off;
u8 *data = (u8*) buf;
/* Several chips lock up trying to read undefined config space */ /* Several chips lock up trying to read undefined config space */
if (capable(CAP_SYS_ADMIN)) { if (capable(CAP_SYS_ADMIN)) {
...@@ -108,30 +109,47 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) ...@@ -108,30 +109,47 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
size = count; size = count;
} }
while (off & 3) { if ((off & 1) && size) {
unsigned char val; u8 val;
pci_read_config_byte(dev, off, &val); pci_read_config_byte(dev, off, &val);
buf[off - init_off] = val; data[off - init_off] = val;
off++; off++;
if (--size == 0) size--;
break; }
if ((off & 3) && size > 2) {
u16 val;
pci_read_config_word(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
size -= 2;
} }
while (size > 3) { while (size > 3) {
unsigned int val; u32 val;
pci_read_config_dword(dev, off, &val); pci_read_config_dword(dev, off, &val);
buf[off - init_off] = val & 0xff; data[off - init_off] = val & 0xff;
buf[off - init_off + 1] = (val >> 8) & 0xff; data[off - init_off + 1] = (val >> 8) & 0xff;
buf[off - init_off + 2] = (val >> 16) & 0xff; data[off - init_off + 2] = (val >> 16) & 0xff;
buf[off - init_off + 3] = (val >> 24) & 0xff; data[off - init_off + 3] = (val >> 24) & 0xff;
off += 4; off += 4;
size -= 4; size -= 4;
} }
while (size > 0) { if (size >= 2) {
unsigned char val; u16 val;
pci_read_config_word(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
size -= 2;
}
if (size > 0) {
u8 val;
pci_read_config_byte(dev, off, &val); pci_read_config_byte(dev, off, &val);
buf[off - init_off] = val; data[off - init_off] = val;
off++; off++;
--size; --size;
} }
...@@ -145,6 +163,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) ...@@ -145,6 +163,7 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
unsigned int size = count; unsigned int size = count;
loff_t init_off = off; loff_t init_off = off;
u8 *data = (u8*) buf;
if (off > dev->cfg_size) if (off > dev->cfg_size)
return 0; return 0;
...@@ -153,25 +172,40 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) ...@@ -153,25 +172,40 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
count = size; count = size;
} }
while (off & 3) { if ((off & 1) && size) {
pci_write_config_byte(dev, off, buf[off - init_off]); pci_write_config_byte(dev, off, data[off - init_off]);
off++; off++;
if (--size == 0) size--;
break; }
if ((off & 3) && size > 2) {
u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
pci_write_config_word(dev, off, val);
off += 2;
size -= 2;
} }
while (size > 3) { while (size > 3) {
unsigned int val = buf[off - init_off]; u32 val = data[off - init_off];
val |= (unsigned int) buf[off - init_off + 1] << 8; val |= (u32) data[off - init_off + 1] << 8;
val |= (unsigned int) buf[off - init_off + 2] << 16; val |= (u32) data[off - init_off + 2] << 16;
val |= (unsigned int) buf[off - init_off + 3] << 24; val |= (u32) data[off - init_off + 3] << 24;
pci_write_config_dword(dev, off, val); pci_write_config_dword(dev, off, val);
off += 4; off += 4;
size -= 4; size -= 4;
} }
while (size > 0) { if (size >= 2) {
pci_write_config_byte(dev, off, buf[off - init_off]); u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
pci_write_config_word(dev, off, val);
off += 2;
size -= 2;
}
if (size) {
pci_write_config_byte(dev, off, data[off - init_off]);
off++; off++;
--size; --size;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment