Skip to content

Commit

Permalink
PCI: add PCI-X/PCI-Express read control interfaces
Browse files Browse the repository at this point in the history
This patch introduces an interface to read and write PCI-X / PCI-Express 
maximum read byte count values from PCI config space. There is a second 
function that returns the maximum _designed_ read byte count, which marks the 
maximum value for a device, since some drivers try to set MMRBC to the 
highest allowed value and rely on such a function.

Based on patch set by Stephen Hemminger <shemminger@linux-foundation.org>

Cc: Stephen Hemminger <shemminger@linux-foundation.org>
Signed-off-by: Peter Oruba <peter.oruba@amd.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Peter Oruba authored and Greg Kroah-Hartman committed Jul 11, 2007
1 parent e4585da commit d556ad4
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 1 deletion.
160 changes: 160 additions & 0 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1374,6 +1374,166 @@ pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
}
#endif

/**
* pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
* @dev: PCI device to query
*
* Returns mmrbc: maximum designed memory read count in bytes
* or appropriate error value.
*/
int pcix_get_max_mmrbc(struct pci_dev *dev)
{
int ret, err, cap;
u32 stat;

cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!cap)
return -EINVAL;

err = pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat);
if (err)
return -EINVAL;

ret = (stat & PCI_X_STATUS_MAX_READ) >> 12;

return ret;
}
EXPORT_SYMBOL(pcix_get_max_mmrbc);

/**
* pcix_get_mmrbc - get PCI-X maximum memory read byte count
* @dev: PCI device to query
*
* Returns mmrbc: maximum memory read count in bytes
* or appropriate error value.
*/
int pcix_get_mmrbc(struct pci_dev *dev)
{
int ret, cap;
u32 cmd;

cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!cap)
return -EINVAL;

ret = pci_read_config_dword(dev, cap + PCI_X_CMD, &cmd);
if (!ret)
ret = 512 << ((cmd & PCI_X_CMD_MAX_READ) >> 2);

return ret;
}
EXPORT_SYMBOL(pcix_get_mmrbc);

/**
* pcix_set_mmrbc - set PCI-X maximum memory read byte count
* @dev: PCI device to query
* @mmrbc: maximum memory read count in bytes
* valid values are 512, 1024, 2048, 4096
*
* If possible sets maximum memory read byte count, some bridges have erratas
* that prevent this.
*/
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc)
{
int cap, err = -EINVAL;
u32 stat, cmd, v, o;

if (mmrbc < 512 || mmrbc > 4096 || (mmrbc & (mmrbc-1)))
goto out;

v = ffs(mmrbc) - 10;

cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!cap)
goto out;

err = pci_read_config_dword(dev, cap + PCI_X_STATUS, &stat);
if (err)
goto out;

if (v > (stat & PCI_X_STATUS_MAX_READ) >> 21)
return -E2BIG;

err = pci_read_config_dword(dev, cap + PCI_X_CMD, &cmd);
if (err)
goto out;

o = (cmd & PCI_X_CMD_MAX_READ) >> 2;
if (o != v) {
if (v > o && dev->bus &&
(dev->bus->bus_flags & PCI_BUS_FLAGS_NO_MMRBC))
return -EIO;

cmd &= ~PCI_X_CMD_MAX_READ;
cmd |= v << 2;
err = pci_write_config_dword(dev, cap + PCI_X_CMD, cmd);
}
out:
return err;
}
EXPORT_SYMBOL(pcix_set_mmrbc);

/**
* pcie_get_readrq - get PCI Express read request size
* @dev: PCI device to query
*
* Returns maximum memory read request in bytes
* or appropriate error value.
*/
int pcie_get_readrq(struct pci_dev *dev)
{
int ret, cap;
u16 ctl;

cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (!cap)
return -EINVAL;

ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
if (!ret)
ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);

return ret;
}
EXPORT_SYMBOL(pcie_get_readrq);

/**
* pcie_set_readrq - set PCI Express maximum memory read request
* @dev: PCI device to query
* @count: maximum memory read count in bytes
* valid values are 128, 256, 512, 1024, 2048, 4096
*
* If possible sets maximum read byte count
*/
int pcie_set_readrq(struct pci_dev *dev, int rq)
{
int cap, err = -EINVAL;
u16 ctl, v;

if (rq < 128 || rq > 4096 || (rq & (rq-1)))
goto out;

v = (ffs(rq) - 8) << 12;

cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
if (!cap)
goto out;

err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
if (err)
goto out;

if ((ctl & PCI_EXP_DEVCTL_READRQ) != v) {
ctl &= ~PCI_EXP_DEVCTL_READRQ;
ctl |= v;
err = pci_write_config_dword(dev, cap + PCI_EXP_DEVCTL, ctl);
}

out:
return err;
}
EXPORT_SYMBOL(pcie_set_readrq);

/**
* pci_select_bars - Make BAR mask from the type of resource
* @dev: the PCI device for which BAR mask is made
Expand Down
16 changes: 16 additions & 0 deletions drivers/pci/quirks.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,22 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_
DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
#endif /* CONFIG_X86_IO_APIC */

/*
* Some settings of MMRBC can lead to data corruption so block changes.
* See AMD 8131 HyperTransport PCI-X Tunnel Revision Guide
*/
static void __init quirk_amd_8131_mmrbc(struct pci_dev *dev)
{
unsigned char revid;

pci_read_config_byte(dev, PCI_REVISION_ID, &revid);
if (dev->subordinate && revid <= 0x12) {
printk(KERN_INFO "AMD8131 rev %x detected, disabling PCI-X MMRBC\n",
revid);
dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MMRBC;
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_mmrbc);

/*
* FIXME: it is questionable that quirk_via_acpi
Expand Down
7 changes: 6 additions & 1 deletion include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ enum pcie_reset_state {

typedef unsigned short __bitwise pci_bus_flags_t;
enum pci_bus_flags {
PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1,
PCI_BUS_FLAGS_NO_MSI = (__force pci_bus_flags_t) 1,
PCI_BUS_FLAGS_NO_MMRBC = (__force pci_bus_flags_t) 2,
};

struct pci_cap_saved_state {
Expand Down Expand Up @@ -549,6 +550,10 @@ void pci_intx(struct pci_dev *dev, int enable);
void pci_msi_off(struct pci_dev *dev);
int pci_set_dma_mask(struct pci_dev *dev, u64 mask);
int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask);
int pcix_get_max_mmrbc(struct pci_dev *dev);
int pcix_get_mmrbc(struct pci_dev *dev);
int pcix_set_mmrbc(struct pci_dev *dev, int mmrbc);
int pcie_set_readrq(struct pci_dev *dev, int rq);
void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno);
int __must_check pci_assign_resource(struct pci_dev *dev, int i);
int __must_check pci_assign_resource_fixed(struct pci_dev *dev, int i);
Expand Down

0 comments on commit d556ad4

Please sign in to comment.