Re: [PATCH v2] PCI: Max Payload Size BIOS workaround

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, May 28, 2011 at 3:09 AM, Jon Mason <jon.mason@xxxxxxxx> wrote:
> This is required to work around buggy BIOS revisions found on various
> whitebox motherboards which do not configure mps beyond one level below
> the root port.  Ensure that the max payload size of children bridges and
> devices are the same as the parent bus.
>
> Signed-off-by: Jon Mason <mason@xxxxxxxx>
> ---
>  drivers/pci/bus.c                |   63 ++++++++++++++++++++++++++++++++++++++
>  drivers/pci/hotplug/pcihp_slot.c |   45 ++-------------------------
>  2 files changed, 66 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
> index 1e2ad92..7dd3d24 100644
> --- a/drivers/pci/bus.c
> +++ b/drivers/pci/bus.c
> @@ -169,6 +169,59 @@ int pci_bus_add_child(struct pci_bus *bus)
>        return retval;
>  }
>
> +/* Program PCIE MaxPayload setting on device:
> + * ensure parent maxpayload <= device
> + */
> +int pci_set_payload(struct pci_dev *dev, bool hotplug)
> +{
> +       int pos, ppos;
> +       u16 pctl, psz;
> +       u16 dctl, dsz, dcap, dmax;
> +       struct pci_dev *parent;
> +
> +       parent = dev->bus->self;
> +       if (!parent)
> +               return 0;
> +
> +       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
> +       if (!pos)
> +               return 0;
> +
> +       /* Read Device MaxPayload capability and setting */
> +       pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &dctl);
> +       pci_read_config_word(dev, pos + PCI_EXP_DEVCAP, &dcap);
> +       dsz = (dctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
> +       dmax = (dcap & PCI_EXP_DEVCAP_PAYLOAD);
> +
> +       /* Read Parent MaxPayload setting */
> +       ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
> +       if (!ppos)
> +               return 0;
> +       pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl);
> +       psz = (pctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
> +
> +       /* If parent payload > device max payload -> error

Consider a Situation : RC->EP
RC's Max_Payload_Size is 256 bytes.
EP's Max_Payload_Size Supported is only 128 bytes.
Should this system not work? It must .
So the soultion should be "to align Max_Payload_Size for
all devices to the minimum of the downstream tree"

> +        * If parent payload > device payload -> set speed
> +        * If parent payload <= device payload -> do nothing
> +        */
> +       if (psz > dmax)
> +               return -1;
> +       else if (psz > dsz) {
> +               if (hotplug)
> +                       dev_info(&dev->dev, "Setting MaxPayload to %d\n",
> +                                128 << psz);
> +               else
> +                       dev_warn(&dev->dev, "Child MPS of %d != Parent MPS of "
> +                                "%d!  Most likely caused by bad BIOS.  Setting"
> +                                " Child MPS to %d\n", 128 << dsz, 128 << psz,
> +                                128 << psz);
> +
> +               pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, (psz << 5) |
> +                                     (dctl & ~PCI_EXP_DEVCTL_PAYLOAD));
> +       }
> +       return 0;
> +}
> +
>  /**
>  * pci_bus_add_devices - insert newly discovered PCI devices
>  * @bus: bus to check for new devices
> @@ -188,6 +241,16 @@ void pci_bus_add_devices(const struct pci_bus *bus)
>        int retval;
>
>        list_for_each_entry(dev, &bus->devices, bus_list) {
> +
> +               /*
> +                * This is required to work around buggy BIOS revisions found on
> +                * various whitebox motherboards which do not configure mps
> +                * beyond one level below the root port.  Ensure that the max
> +                * payload size of children bridges and devices are the same as
> +                * the parent bus.
> +                */

We should also consider about Max_Read_Request_Size.
Max_Read_Request_Size of any of the down stream device
should be less than or equal to  Device's Max_Payload_Size.
I mean, if an EP with  Max_Read_Request_Size  256 bytes generates an
outbound request to an RC with Max_Payload_Size 128 bytes, then RC will
not be able to respond. In this scenerio, EP's Max_Read_Request_Size  should be
set to 128.

Regards
Pratyush

> +               pci_set_payload(dev, false);
> +
>                /* Skip already-added devices */
>                if (dev->is_added)
>                        continue;
> diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c
> index 749fdf0..0038c40 100644
> --- a/drivers/pci/hotplug/pcihp_slot.c
> +++ b/drivers/pci/hotplug/pcihp_slot.c
> @@ -26,6 +26,8 @@
>  #include <linux/pci.h>
>  #include <linux/pci_hotplug.h>
>
> +extern int pci_set_payload(struct pci_dev *dev, bool hotplug);
> +
>  static struct hpp_type0 pci_default_type0 = {
>        .revision = 1,
>        .cache_line_size = 8,
> @@ -158,47 +160,6 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
>         */
>  }
>
> -/* Program PCIE MaxPayload setting on device: ensure parent maxpayload <= device */
> -static int pci_set_payload(struct pci_dev *dev)
> -{
> -       int pos, ppos;
> -       u16 pctl, psz;
> -       u16 dctl, dsz, dcap, dmax;
> -       struct pci_dev *parent;
> -
> -       parent = dev->bus->self;
> -       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
> -       if (!pos)
> -               return 0;
> -
> -       /* Read Device MaxPayload capability and setting */
> -       pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &dctl);
> -       pci_read_config_word(dev, pos + PCI_EXP_DEVCAP, &dcap);
> -       dsz = (dctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
> -       dmax = (dcap & PCI_EXP_DEVCAP_PAYLOAD);
> -
> -       /* Read Parent MaxPayload setting */
> -       ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
> -       if (!ppos)
> -               return 0;
> -       pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl);
> -       psz = (pctl &  PCI_EXP_DEVCTL_PAYLOAD) >> 5;
> -
> -       /* If parent payload > device max payload -> error
> -        * If parent payload > device payload -> set speed
> -        * If parent payload <= device payload -> do nothing
> -        */
> -       if (psz > dmax)
> -               return -1;
> -       else if (psz > dsz) {
> -               dev_info(&dev->dev, "Setting MaxPayload to %d\n", 128 << psz);
> -               pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
> -                                     (dctl & ~PCI_EXP_DEVCTL_PAYLOAD) +
> -                                     (psz << 5));
> -       }
> -       return 0;
> -}
> -
>  void pci_configure_slot(struct pci_dev *dev)
>  {
>        struct pci_dev *cdev;
> @@ -210,7 +171,7 @@ void pci_configure_slot(struct pci_dev *dev)
>                        (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)))
>                return;
>
> -       ret = pci_set_payload(dev);
> +       ret = pci_set_payload(dev, true);
>        if (ret)
>                dev_warn(&dev->dev, "could not set device max payload\n");
>
> --
> 1.7.4.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux