[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: Vojtech Pavlik <vojtech@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
bc56b9e011
commit
4c0619add8
1 changed files with 58 additions and 24 deletions
|
@ -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)
|
||||||
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)
|
||||||
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;
|
||||||
|
@ -152,26 +171,41 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count)
|
||||||
size = dev->cfg_size - off;
|
size = dev->cfg_size - off;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (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 > 0) {
|
if (size) {
|
||||||
pci_write_config_byte(dev, off, buf[off - init_off]);
|
pci_write_config_byte(dev, off, data[off - init_off]);
|
||||||
off++;
|
off++;
|
||||||
--size;
|
--size;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue