Message ID | 1304624030-1922-4-git-send-email-jbarnes@virtuousgeek.org (mailing list archive) |
---|---|
State | Superseded, archived |
Headers | show |
On Thu, May 05, 2011 at 12:33:50PM -0700, Jesse Barnes wrote: > +bool pci_ltr_supported(struct pci_dev *dev) > +{ > + int pos; > + u32 cap; > + > + if (!pci_is_pcie(dev)) > + return false; > + > + pos = pci_pcie_cap(dev); > + if (!pos) > + return false; > + > + pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap); > + > + return cap & PCI_EXP_DEVCAP2_LTR; > +} Missing EXPORT_SYMBOL? (throughout) > +/** > + * pci_set_ltr - set LTR latency values > + * @dev: PCI device > + * @latencies: LTR latency values & scaling > + * > + * Set the LTR cap registers to the values provided by @latencies. > + */ > +int pci_set_ltr(struct pci_dev *dev, struct pci_ltr_latencies *latencies) > +{ I don't like this API. Why not just pass a u32 snoop_lat and nosnoop_lat (in ns) and do: int scale = 0; while (latency > 1023) { latency = (latency + 31) / 32; scale++; } (a u32 latency gives you up to 4 seconds of latency, which is surely more than any device can tolerate ... and doesn't let you max out scale) > + int pos, ret; > + u32 val; > + > + if (!pci_ltr_supported(dev)) > + return -ENOTSUPP; > + > + if (latencies->max_snoop_value > PCI_LTR_VALUE_MASK || > + latencies->max_nosnoop_value > PCI_LTR_VALUE_MASK) > + return -EINVAL; > + > + if ((latencies->max_snoop_scale > > + (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)) || > + (latencies->max_nosnoop_scale > > + (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT))) > + return -EINVAL; > + > + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); > + if (!pos) > + return -ENOTSUPP; > + > + val = (latencies->max_snoop_scale << PCI_LTR_SCALE_SHIFT) | > + latencies->max_snoop_value; > + ret = pci_write_config_dword(dev, pos + PCI_LTR_MAX_SNOOP_LAT, val); > + if (ret != 4) > + return -EIO; Spec indicates these are word values ... can probably hit both registers with a single dword write though. > + val = (latencies->max_nosnoop_scale << PCI_LTR_SCALE_SHIFT) | > + latencies->max_nosnoop_value; > + ret = pci_write_config_dword(dev, pos + PCI_LTR_MAX_NOSNOOP_LAT, val); > + if (ret != 4) > + return -EIO; > + > + return 0; > +} > + > static int pci_acs_enable; > > /** > diff --git a/include/linux/pci.h b/include/linux/pci.h > index 45a035c..c5b2d9c 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -840,6 +840,17 @@ enum pci_obff_signal_type { > int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); > void pci_disable_obff(struct pci_dev *dev); > > +bool pci_ltr_supported(struct pci_dev *dev); > +int pci_enable_ltr(struct pci_dev *dev); > +void pci_disable_ltr(struct pci_dev *dev); > +struct pci_ltr_latencies { > + u16 max_snoop_value; > + u8 max_snoop_scale; > + u16 max_nosnoop_value; > + u8 max_nosnoop_scale; > +}; > +int pci_set_ltr(struct pci_dev *dev, struct pci_ltr_latencies *latencies); > + > /* For use by arch with custom probe code */ > void set_pcie_port_type(struct pci_dev *pdev); > void set_pcie_hotplug_bridge(struct pci_dev *pdev); > diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h > index aa42026..e884096 100644 > --- a/include/linux/pci_regs.h > +++ b/include/linux/pci_regs.h > @@ -508,6 +508,7 @@ > #define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ > #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ > #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ > +#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ > #define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ > #define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ > #define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ > @@ -515,6 +516,7 @@ > #define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ > #define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ > #define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ > +#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ > #define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ > #define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ > #define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ > @@ -535,6 +537,7 @@ > #define PCI_EXT_CAP_ID_ARI 14 > #define PCI_EXT_CAP_ID_ATS 15 > #define PCI_EXT_CAP_ID_SRIOV 16 > +#define PCI_EXT_CAP_ID_LTR 24 > > /* Advanced Error Reporting */ > #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ > @@ -691,6 +694,12 @@ > #define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ > #define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ > > +#define PCI_LTR_MAX_SNOOP_LAT 0x4 > +#define PCI_LTR_MAX_NOSNOOP_LAT 0x6 > +#define PCI_LTR_VALUE_MASK 0x000003ff > +#define PCI_LTR_SCALE_MASK 0x00001c00 > +#define PCI_LTR_SCALE_SHIFT 10 > + > /* Access Control Service */ > #define PCI_ACS_CAP 0x04 /* ACS Capability Register */ > #define PCI_ACS_SV 0x01 /* Source Validation */ > -- > 1.7.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, 5 May 2011 13:51:58 -0600 Matthew Wilcox <matthew@wil.cx> wrote: > On Thu, May 05, 2011 at 12:33:50PM -0700, Jesse Barnes wrote: > > +bool pci_ltr_supported(struct pci_dev *dev) > > +{ > > + int pos; > > + u32 cap; > > + > > + if (!pci_is_pcie(dev)) > > + return false; > > + > > + pos = pci_pcie_cap(dev); > > + if (!pos) > > + return false; > > + > > + pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap); > > + > > + return cap & PCI_EXP_DEVCAP2_LTR; > > +} > > Missing EXPORT_SYMBOL? (throughout) Oops, will fix. I guess drivers would have run into that pretty quickly. :) > > > +/** > > + * pci_set_ltr - set LTR latency values > > + * @dev: PCI device > > + * @latencies: LTR latency values & scaling > > + * > > + * Set the LTR cap registers to the values provided by @latencies. > > + */ > > +int pci_set_ltr(struct pci_dev *dev, struct pci_ltr_latencies *latencies) > > +{ > > I don't like this API. Why not just pass a u32 snoop_lat and nosnoop_lat > (in ns) and do: > > int scale = 0; > while (latency > 1023) { > latency = (latency + 31) / 32; > scale++; > } > > (a u32 latency gives you up to 4 seconds of latency, which is surely > more than any device can tolerate ... and doesn't let you max out scale) Agreed, not sure why I thought this was a good API when I coded it up. Taking simple ns is much easier from a caller perspective. > > + int pos, ret; > > + u32 val; > > + > > + if (!pci_ltr_supported(dev)) > > + return -ENOTSUPP; > > + > > + if (latencies->max_snoop_value > PCI_LTR_VALUE_MASK || > > + latencies->max_nosnoop_value > PCI_LTR_VALUE_MASK) > > + return -EINVAL; > > + > > + if ((latencies->max_snoop_scale > > > + (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)) || > > + (latencies->max_nosnoop_scale > > > + (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT))) > > + return -EINVAL; > > + > > + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); > > + if (!pos) > > + return -ENOTSUPP; > > + > > + val = (latencies->max_snoop_scale << PCI_LTR_SCALE_SHIFT) | > > + latencies->max_snoop_value; > > + ret = pci_write_config_dword(dev, pos + PCI_LTR_MAX_SNOOP_LAT, val); > > + if (ret != 4) > > + return -EIO; > > Spec indicates these are word values ... can probably hit both registers > with a single dword write though. May as well split the writes up just to avoid confusion. Thanks a lot for checking it out.
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 44c1d3d..56f073e 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1977,6 +1977,140 @@ void pci_disable_obff(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl); } +/** + * pci_ltr_supported - check whether a device supports LTR + * @dev: PCI device + * + * RETURNS: + * True if @dev supports latency tolerance reporting, false otherwise. + */ +bool pci_ltr_supported(struct pci_dev *dev) +{ + int pos; + u32 cap; + + if (!pci_is_pcie(dev)) + return false; + + pos = pci_pcie_cap(dev); + if (!pos) + return false; + + pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap); + + return cap & PCI_EXP_DEVCAP2_LTR; +} + +/** + * pci_enable_ltr - enable latency tolerance reporting + * @dev: PCI device + * + * Enable LTR on @dev if possible, which means enabling it first on + * upstream ports. + * + * RETURNS: + * Zero on success, errno on failure. + */ +int pci_enable_ltr(struct pci_dev *dev) +{ + int pos; + u16 ctrl; + int ret; + + if (!pci_ltr_supported(dev)) + return -ENOTSUPP; + + pos = pci_pcie_cap(dev); + if (!pos) + return -ENOTSUPP; + + /* Only primary function can enable/disable LTR */ + if (PCI_FUNC(dev->devfn) != 0) + return -EINVAL; + + /* Enable upstream ports first */ + if (dev->bus) { + ret = pci_enable_ltr(dev->bus->self); + if (ret) + return ret; + } + + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl); + ctrl |= PCI_EXP_LTR_EN; + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl); + + return 0; +} + +/** + * pci_disable_ltr - disable latency tolerance reporting + * @dev: PCI device + */ +void pci_disable_ltr(struct pci_dev *dev) +{ + int pos; + u16 ctrl; + + if (!pci_ltr_supported(dev)) + return; + + pos = pci_pcie_cap(dev); + if (!pos) + return; + + /* Only primary function can enable/disable LTR */ + if (PCI_FUNC(dev->devfn) != 0) + return; + + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl); + ctrl &= ~PCI_EXP_LTR_EN; + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl); +} + +/** + * pci_set_ltr - set LTR latency values + * @dev: PCI device + * @latencies: LTR latency values & scaling + * + * Set the LTR cap registers to the values provided by @latencies. + */ +int pci_set_ltr(struct pci_dev *dev, struct pci_ltr_latencies *latencies) +{ + int pos, ret; + u32 val; + + if (!pci_ltr_supported(dev)) + return -ENOTSUPP; + + if (latencies->max_snoop_value > PCI_LTR_VALUE_MASK || + latencies->max_nosnoop_value > PCI_LTR_VALUE_MASK) + return -EINVAL; + + if ((latencies->max_snoop_scale > + (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT)) || + (latencies->max_nosnoop_scale > + (PCI_LTR_SCALE_MASK >> PCI_LTR_SCALE_SHIFT))) + return -EINVAL; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_LTR); + if (!pos) + return -ENOTSUPP; + + val = (latencies->max_snoop_scale << PCI_LTR_SCALE_SHIFT) | + latencies->max_snoop_value; + ret = pci_write_config_dword(dev, pos + PCI_LTR_MAX_SNOOP_LAT, val); + if (ret != 4) + return -EIO; + + val = (latencies->max_nosnoop_scale << PCI_LTR_SCALE_SHIFT) | + latencies->max_nosnoop_value; + ret = pci_write_config_dword(dev, pos + PCI_LTR_MAX_NOSNOOP_LAT, val); + if (ret != 4) + return -EIO; + + return 0; +} + static int pci_acs_enable; /** diff --git a/include/linux/pci.h b/include/linux/pci.h index 45a035c..c5b2d9c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -840,6 +840,17 @@ enum pci_obff_signal_type { int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type); void pci_disable_obff(struct pci_dev *dev); +bool pci_ltr_supported(struct pci_dev *dev); +int pci_enable_ltr(struct pci_dev *dev); +void pci_disable_ltr(struct pci_dev *dev); +struct pci_ltr_latencies { + u16 max_snoop_value; + u8 max_snoop_scale; + u16 max_nosnoop_value; + u8 max_nosnoop_scale; +}; +int pci_set_ltr(struct pci_dev *dev, struct pci_ltr_latencies *latencies); + /* For use by arch with custom probe code */ void set_pcie_port_type(struct pci_dev *pdev); void set_pcie_hotplug_bridge(struct pci_dev *pdev); diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index aa42026..e884096 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -508,6 +508,7 @@ #define PCI_EXP_RTSTA_PENDING 0x20000 /* PME pending */ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ #define PCI_EXP_DEVCAP2_ARI 0x20 /* Alternative Routing-ID */ +#define PCI_EXP_DEVCAP2_LTR 0x800 /* Latency tolerance reporting */ #define PCI_EXP_OBFF_MASK 0xc0000 /* OBFF support mechanism */ #define PCI_EXP_OBFF_MSG 0x40000 /* New message signaling */ #define PCI_EXP_OBFF_WAKE 0x80000 /* Re-use WAKE# for OBFF */ @@ -515,6 +516,7 @@ #define PCI_EXP_DEVCTL2_ARI 0x20 /* Alternative Routing-ID */ #define PCI_EXP_IDO_REQ_EN 0x100 /* ID-based ordering request enable */ #define PCI_EXP_IDO_CMP_EN 0x200 /* ID-based ordering completion enable */ +#define PCI_EXP_LTR_EN 0x400 /* Latency tolerance reporting */ #define PCI_EXP_OBFF_MSGA_EN 0x2000 /* OBFF enable with Message type A */ #define PCI_EXP_OBFF_MSGB_EN 0x4000 /* OBFF enable with Message type B */ #define PCI_EXP_OBFF_WAKE_EN 0x6000 /* OBFF using WAKE# signaling */ @@ -535,6 +537,7 @@ #define PCI_EXT_CAP_ID_ARI 14 #define PCI_EXT_CAP_ID_ATS 15 #define PCI_EXT_CAP_ID_SRIOV 16 +#define PCI_EXT_CAP_ID_LTR 24 /* Advanced Error Reporting */ #define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ @@ -691,6 +694,12 @@ #define PCI_SRIOV_VFM_MO 0x2 /* Active.MigrateOut */ #define PCI_SRIOV_VFM_AV 0x3 /* Active.Available */ +#define PCI_LTR_MAX_SNOOP_LAT 0x4 +#define PCI_LTR_MAX_NOSNOOP_LAT 0x6 +#define PCI_LTR_VALUE_MASK 0x000003ff +#define PCI_LTR_SCALE_MASK 0x00001c00 +#define PCI_LTR_SCALE_SHIFT 10 + /* Access Control Service */ #define PCI_ACS_CAP 0x04 /* ACS Capability Register */ #define PCI_ACS_SV 0x01 /* Source Validation */
Latency tolerance reporting allows devices to send messages to the root complex indicating their latency tolerance for snooped & unsnooped memory transactions. Add support for enabling & disabling this feature, along with a routine to set the max latencies a device should send upstream. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> --- drivers/pci/pci.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 11 ++++ include/linux/pci_regs.h | 9 +++ 3 files changed, 154 insertions(+), 0 deletions(-)