[PATCH v5 2/3] PCI: Add tango PCIe host bridge support

Ard Biesheuvel ard.biesheuvel at linaro.org
Wed Jul 5 02:36:24 PDT 2017


On 31 May 2017 at 13:33, Marc Gonzalez <marc_gonzalez at sigmadesigns.com> wrote:
> This driver is required to work around several hardware bugs
> in the PCIe controller.
>
> NB: Revision 1 does not support legacy interrupts, or IO space.
>
> Signed-off-by: Marc Gonzalez <marc_gonzalez at sigmadesigns.com>
> ---
>  drivers/pci/host/Kconfig      |   8 +++
>  drivers/pci/host/Makefile     |   1 +
>  drivers/pci/host/pcie-tango.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/pci_ids.h       |   2 +
>  4 files changed, 175 insertions(+)
>
[...]
> diff --git a/drivers/pci/host/pcie-tango.c b/drivers/pci/host/pcie-tango.c
> new file mode 100644
> index 000000000000..67aaadcc1c5e
> --- /dev/null
> +++ b/drivers/pci/host/pcie-tango.c
> @@ -0,0 +1,164 @@
> +#include <linux/pci-ecam.h>
> +#include <linux/delay.h>
> +#include <linux/of.h>
> +
> +#define MSI_MAX 256
> +
> +#define SMP8759_MUX            0x48
> +#define SMP8759_TEST_OUT       0x74
> +
> +struct tango_pcie {
> +       void __iomem *mux;
> +};
> +
> +/*** HOST BRIDGE SUPPORT ***/
> +
> +static int smp8759_config_read(struct pci_bus *bus,
> +               unsigned int devfn, int where, int size, u32 *val)
> +{
> +       int ret;
> +       struct pci_config_window *cfg = bus->sysdata;
> +       struct tango_pcie *pcie = dev_get_drvdata(cfg->parent);
> +
> +       /*
> +        * QUIRK #1
> +        * Reads in configuration space outside devfn 0 return garbage.
> +        */
> +       if (devfn != 0)
> +               return PCIBIOS_FUNC_NOT_SUPPORTED;
> +

Does this mean multi-function devices are not supported? E.g., on my
system I have

-[0000:00]-+-00.0  Advanced Micro Devices, Inc. [AMD] Device 1a00
           +-02.0  Advanced Micro Devices, Inc. [AMD] Device 1a01
           +-02.1-[01]----00.0  Renesas Technology Corp. uPD720201 USB 3.0
           \-02.2-[02]--+-00.0  NVIDIA Corporation GT218 [GeForce 210]
                        \-00.1  NVIDIA Corporation HD Audio Controller

where the HDMI audio is on devfn 00.1 on bus 2


> +       /*
> +        * QUIRK #2
> +        * Unfortunately, config and mem spaces are muxed.
> +        * Linux does not support such a setting, since drivers are free
> +        * to access mem space directly, at any time.
> +        * Therefore, we can only PRAY that config and mem space accesses
> +        * NEVER occur concurrently.
> +        */
> +       writel_relaxed(1, pcie->mux);
> +       ret = pci_generic_config_read(bus, devfn, where, size, val);
> +       writel_relaxed(0, pcie->mux);
> +
> +       return ret;
> +}
> +
> +static int smp8759_config_write(struct pci_bus *bus,
> +               unsigned int devfn, int where, int size, u32 val)
> +{
> +       int ret;
> +       struct pci_config_window *cfg = bus->sysdata;
> +       struct tango_pcie *pcie = dev_get_drvdata(cfg->parent);
> +
> +       writel_relaxed(1, pcie->mux);
> +       ret = pci_generic_config_write(bus, devfn, where, size, val);
> +       writel_relaxed(0, pcie->mux);
> +
> +       return ret;
> +}
> +
> +static struct pci_ecam_ops smp8759_ecam_ops = {
> +       .bus_shift      = 20,
> +       .pci_ops        = {
> +               .map_bus        = pci_ecam_map_bus,
> +               .read           = smp8759_config_read,
> +               .write          = smp8759_config_write,
> +       }
> +};
> +
> +static const struct of_device_id tango_pcie_ids[] = {
> +       { .compatible = "sigma,smp8759-pcie" },
> +       { /* sentinel */ },
> +};
> +
> +static int tango_check_pcie_link(void __iomem *test_out)
> +{
> +       int i;
> +
> +       writel_relaxed(16, test_out);
> +       for (i = 0; i < 10; ++i) {
> +               u32 ltssm_state = readl_relaxed(test_out) >> 8;
> +               if ((ltssm_state & 0x1f) == 0xf) /* L0 */
> +                       return 0;
> +               usleep_range(3000, 4000);
> +       }
> +
> +       return -ENODEV;
> +}
> +
> +static int smp8759_init(struct tango_pcie *pcie, void __iomem *base)
> +{
> +       pcie->mux               = base + SMP8759_MUX;
> +
> +       return tango_check_pcie_link(base + SMP8759_TEST_OUT);
> +}
> +
> +static int tango_pcie_probe(struct platform_device *pdev)
> +{
> +       int ret = -EINVAL;
> +       void __iomem *base;
> +       struct resource *res;
> +       struct tango_pcie *pcie;
> +       struct device *dev = &pdev->dev;
> +
> +       pr_err("MAJOR ISSUE: PCIe config and mem spaces are muxed\n");
> +       pr_err("Tainting kernel... Use driver at your own risk\n");
> +       add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
> +
> +       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
> +       if (!pcie)
> +               return -ENOMEM;
> +
> +       platform_set_drvdata(pdev, pcie);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +       base = devm_ioremap_resource(&pdev->dev, res);
> +       if (IS_ERR(base))
> +               return PTR_ERR(base);
> +
> +       if (of_device_is_compatible(dev->of_node, "sigma,smp8759-pcie"))
> +               ret = smp8759_init(pcie, base);
> +
> +       if (ret)
> +               return ret;
> +
> +       return pci_host_common_probe(pdev, &smp8759_ecam_ops);
> +}
> +
> +static struct platform_driver tango_pcie_driver = {
> +       .probe  = tango_pcie_probe,
> +       .driver = {
> +               .name = KBUILD_MODNAME,
> +               .of_match_table = tango_pcie_ids,
> +       },
> +};
> +
> +builtin_platform_driver(tango_pcie_driver);
> +
> +/*
> + * QUIRK #3
> + * The root complex advertizes the wrong device class.
> + * Header Type 1 is for PCI-to-PCI bridges.
> + */
> +static void tango_fixup_class(struct pci_dev *dev)
> +{
> +       dev->class = PCI_CLASS_BRIDGE_PCI << 8;
> +}
> +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x24, tango_fixup_class);
> +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x28, tango_fixup_class);
> +
> +/*
> + * QUIRK #4
> + * The root complex exposes a "fake" BAR, which is used to filter
> + * bus-to-system accesses. Only accesses within the range defined
> + * by this BAR are forwarded to the host, others are ignored.
> + *
> + * By default, the DMA framework expects an identity mapping,
> + * and DRAM0 is mapped at 0x80000000.
> + */
> +static void tango_fixup_bar(struct pci_dev *dev)
> +{
> +       dev->non_compliant_bars = true;
> +       pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, 0x80000000);
> +}
> +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x24, tango_fixup_bar);
> +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SIGMA, 0x28, tango_fixup_bar);
> diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
> index f020ab4079d3..b577dbe46f8f 100644
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -1369,6 +1369,8 @@
>  #define PCI_DEVICE_ID_TTI_HPT374       0x0008
>  #define PCI_DEVICE_ID_TTI_HPT372N      0x0009  /* apparently a 372N variant? */
>
> +#define PCI_VENDOR_ID_SIGMA            0x1105
> +
>  #define PCI_VENDOR_ID_VIA              0x1106
>  #define PCI_DEVICE_ID_VIA_8763_0       0x0198
>  #define PCI_DEVICE_ID_VIA_8380_0       0x0204
> --
> 2.11.0
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list