On Monday 02 May 2016 18:25:49 Florian Fainelli wrote:

> +/* Helper macros for reading registers varying from chip-to-chip */
> +#define IDX_ADDR(pcie)                       ((pcie->base) + \
> +                                      pcie->reg_offsets[EXT_CFG_INDEX])
> +#define DATA_ADDR(pcie)                      ((pcie->base) + \
> +                                      pcie->reg_offsets[EXT_CFG_DATA])
> +#define PCIE_RGR1_SW_INIT_1(pcie)    ((pcie->base) + \
> +                                      pcie->reg_offsets[RGR1_SW_INIT_1])
> +
> +static const int pcie_offset_bcm7425[] = {
> +     [RGR1_SW_INIT_1] = 0x8010,
> +     [EXT_CFG_INDEX]  = 0x8300,
> +     [EXT_CFG_DATA]   = 0x8304,
> +};
> +
> +static const struct pcie_cfg_data bcm7425_cfg = {
> +     .offsets        = pcie_offset_bcm7425,
> +     .type           = BCM7425,
> +};
> +
> +static const int pcie_offsets[] = {
> +     [RGR1_SW_INIT_1] = 0x9210,
> +     [EXT_CFG_INDEX]  = 0x9000,
> +     [EXT_CFG_DATA]   = 0x9004,
> +};
> +
> +static const struct pcie_cfg_data bcm7435_cfg = {
> +     .offsets        = pcie_offsets,
> +     .type           = BCM7435,
> +};
> +
> +static const struct pcie_cfg_data generic_cfg = {
> +     .offsets        = pcie_offsets,
> +     .type           = GENERIC,
> +};

The way you access the config space here seems very indirect. I'd
suggest instead writing two sets of pci_ops, with hardcoded registers
offsets in them, and a function pointer to access the RGR1_SW_INIT_1
register.

> +struct brcm_msi {
> +     struct irq_domain *domain;
> +     struct irq_chip irq_chip;
> +     struct msi_controller chip;
> +     struct mutex lock;
> +     int irq;
> +     /* intr_base is the base pointer for interrupt status/set/clr regs */
> +     void __iomem *intr_base;
> +     /* intr_legacy_mask indicates how many bits are MSI interrupts */
> +     u32 intr_legacy_mask;
> +     /* intr_legacy_offset indicates bit position of MSI_01 */
> +     u32 intr_legacy_offset;
> +     /* used indicates which MSI interrupts have been alloc'd */
> +     unsigned long used;
> +     /* working indicates that on boot we have brought up MSI */
> +     bool working;
> +};

I'd move the MSI controller stuff into a separate file, and add a way to
override it. It's likely that at some point the same PCIe implementation
will be used with a modern GICv3 or GICv2m, so you should be able to
look up the msi controller from a property.

> +struct brcm_window {
> +     u64 size;
> +     u64 cpu_addr;
> +     struct resource pcie_iomem_res;
> +};

This appears to duplicate things we already have. Try to get rid of
the structure and use what is already there.

> +struct brcm_dev_pwr_supply {
> +     struct list_head node;
> +     char name[32];
> +     struct regulator *regulator;
> +};

Same here: Just get all the regulators you know about by name
and put them into the main structure.

> +/* Internal Bus Controller Information.*/
> +struct brcm_pcie {
> +     struct list_head        list;
> +     void __iomem            *base;
> +     char                    name[8];
> +     bool                    suspended;
> +     struct clk              *clk;
> +     struct device_node      *dn;
> +     int                     pcie_irq[4];
> +     int                     irq;
> +     int                     num_out_wins;
> +     bool                    ssc;
> +     int                     gen;
> +     int                     scb_size_vals[BRCM_MAX_SCB];
> +     struct brcm_window      out_wins[BRCM_NUM_PCI_OUT_WINS];
> +     struct pci_bus          *bus;
> +     struct device           *dev;
> +     struct list_head        resource;
> +     struct list_head        pwr_supplies;
> +     struct brcm_msi         msi;
> +     unsigned int            rev;
> +     unsigned int            num;
> +     bool                    bridge_setup_done;
> +     const int               *reg_offsets;
> +     enum pcie_type          type;
> +};
> +
> +static int brcm_num_pci_controllers;
> +static int num_memc;

Remove the globals.

> +
> +/*
> + * MIPS endianness is configured by boot strap, which also reverses all
> + * bus endianness (i.e., big-endian CPU + big endian bus ==> native
> + * endian I/O).
> + *
> + * Other architectures (e.g., ARM) either do not support big endian, or
> + * else leave I/O in little endian mode.
> + */
> +static inline u32 bpcie_readl(void __iomem *base)
> +{
> +     if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
> +             return __raw_readl(base);
> +     else
> +             return readl_relaxed(base);
> +}

I think it would make more sense to only make this depend on the
architecture, not on endianess: If the __raw_ version works on MIPS
in big-endian mode, you should be able to also use it in little-endian
mode.

For the default (ARM) version, please use the non-relaxed accessor
by default, unless you can show a difference in performance and prove
that you don't need the implied barriers.

> +/* negative return value indicates error */
> +static int mdio_read(void __iomem *base, u8 phyad, u8 regad)
> +{
> +     u32 data = ((phyad & 0xf) << 16)
> +             | (regad & 0x1f)
> +             | 0x100000;
> +
> +     bpcie_writel(data, base + PCIE_RC_DL_MDIO_ADDR);
> +     bpcie_readl(base + PCIE_RC_DL_MDIO_ADDR);
> +
> +     data = bpcie_readl(base + PCIE_RC_DL_MDIO_RD_DATA);
> +     if (!(data & 0x80000000)) {
> +             mdelay(1);

Try to restructure the code so this is never called with spinlocks
held and then replace the mdelay() with an msleep().

> +             data = bpcie_readl(base + PCIE_RC_DL_MDIO_RD_DATA);
> +     }
> +     return (data & 0x80000000) ? (data & 0xffff) : -EIO;
> +}
> +
> +/* negative return value indicates error */
> +static int mdio_write(void __iomem *base, u8 phyad, u8 regad, u16 wrdata)
> +{
> +     u32 data = ((phyad & 0xf) << 16) | (regad & 0x1f);
> +
> +     bpcie_writel(data, base + PCIE_RC_DL_MDIO_ADDR);
> +     bpcie_readl(base + PCIE_RC_DL_MDIO_ADDR);
> +
> +     bpcie_writel(0x80000000 | wrdata, base + PCIE_RC_DL_MDIO_WR_DATA);
> +     data = bpcie_readl(base + PCIE_RC_DL_MDIO_WR_DATA);
> +     if (!(data & 0x80000000)) {
> +             mdelay(1);
> +             data = bpcie_readl(base + PCIE_RC_DL_MDIO_WR_DATA);
> +     }
> +     return (data & 0x80000000) ? 0 : -EIO;
> +}

Same here.

> +
> +     /* set up 4GB PCIE->SCB memory window on BAR2 */
> +     bpcie_writel(0x00000011, base + PCIE_MISC_RC_BAR2_CONFIG_LO);
> +     bpcie_writel(0x00000000, base + PCIE_MISC_RC_BAR2_CONFIG_HI);

Where does this window come from? It's not in the DT as far as I can see.

> +static int brcm_setup_pcie_bridge(struct brcm_pcie *pcie)
> +{
> +     static const char *link_speed[4] = { "???", "2.5", "5.0", "8.0" };
> +     void __iomem *base = pcie->base;
> +     const int limit = pcie->suspended ? 1000 : 100;
> +     struct clk *clk;
> +     unsigned int status;
> +     int i, j, ret;
> +     bool ssc_good = false;
> +
> +     /* Give the RC/EP time to wake up, before trying to configure RC.
> +      * Intermittently check status for link-up, up to a total of 100ms
> +      * when we don't know if the device is there, and up to 1000ms if
> +      * we do know the device is there.
> +      */
> +     for (i = 1, j = 0; j < limit && !is_pcie_link_up(pcie); j += i, i = i*2)
> +             mdelay(i + j > limit ? limit - j : i);

Again, try to use msleep() instead of mdelay(). Blocking the CPU for 1 second
seems highly suspicious.

> +     INIT_LIST_HEAD(&pcie->pwr_supplies);
> +     INIT_LIST_HEAD(&pcie->resource);
> +
> +     supplies = of_property_count_strings(dn, "supply-names");
> +     if (supplies <= 0)
> +             supplies = 0;
> +
> +     for (i = 0; i < supplies; i++) {
> +             if (of_property_read_string_index(dn, "supply-names", i,
> +                                               &name))
> +                     continue;
> +             supply = devm_kzalloc(&pdev->dev, sizeof(*supply), GFP_KERNEL);
> +             if (!supply)
> +                     return -ENOMEM;
> +
> +             strncpy(supply->name, name, sizeof(supply->name));
> +             supply->name[sizeof(supply->name) - 1] = '\0';
> +             supply->regulator = devm_regulator_get(&pdev->dev, name);
> +             if (IS_ERR(supply->regulator)) {
> +                     dev_err(&pdev->dev, "Unable to get %s supply, err=%d\n",
> +                             name, (int)PTR_ERR(supply->regulator));
> +                     continue;
> +             }
> +
> +             if (regulator_enable(supply->regulator))
> +                     dev_err(&pdev->dev, "Unable to enable %s supply.\n",
> +                             name);
> +             list_add_tail(&supply->node, &pcie->pwr_supplies);
> +     }

Don't parse the regulator properties yourself here, use the proper APIs.

> +     /* 'num_memc' will be set only by the first controller, and all
> +      * other controllers will use the value set by the first.
> +      */
> +     if (num_memc == 0)
> +             for_each_compatible_node(mdn, NULL, "brcm,brcmstb-memc")
> +                     if (of_device_is_available(mdn))
> +                             num_memc++;

Why is this?

> +     resource_list_for_each_entry(win, &res) {
> +             struct brcm_window *w = &pcie->out_wins[i];
> +
> +             r = win->res;
> +
> +             if (!r->flags)
> +                     continue;
> +
> +             switch (resource_type(r)) {
> +             case IORESOURCE_MEM:
> +                     w->cpu_addr = r->start;
> +                     w->size = resource_size(r);
> +                     w->pcie_iomem_res.name  = "External PCIe MEM";
> +                     w->pcie_iomem_res.flags = r->flags;
> +                     w->pcie_iomem_res.start = r->start;
> +                     w->pcie_iomem_res.end   = r->end;
> +                     pcie->num_out_wins++;
> +                     i++;
> +                     /* Request memory region resources. */
> +                     ret = devm_request_resource(&pdev->dev,
> +                                                 &iomem_resource,
> +                                                 &w->pcie_iomem_res);
> +                     if (ret) {
> +                             dev_err(&pdev->dev,
> +                                     "request PCIe memory resource 
> failed\n");
> +                             goto out_err_clk;
> +                     }
> +                     break;
> +
> +             default:
> +                     continue;
> +             }
> +     }

What about IORESOURCE_IO?

        Arnd

Reply via email to