[PATCH v2 2/2] pci: host: Add Broadcom STB PCIE RC controller
Bharat Kumar Gogada
bharat.kumar.gogada at xilinx.com
Wed May 11 07:47:45 PDT 2016
> +
> +static struct pci_ops brcm_pcie_ops = {
> + .read = brcm_pcie_read_config,
> + .write = brcm_pcie_write_config,
> +};
> +
> +static int brcm_pcie_probe(struct platform_device *pdev) {
> + struct device_node *dn = pdev->dev.of_node;
> + const u32 *log2_scb_sizes, *dma_ranges;
> + const struct brcm_pcie_cfg_data *data;
> + const struct of_device_id *of_id;
> + struct brcm_pcie *pcie;
> + void __iomem *base;
> + struct resource *r;
> + int i, rlen, ret;
> + u32 tmp;
> +
> + pcie = devm_kzalloc(&pdev->dev, sizeof(struct brcm_pcie),
> GFP_KERNEL);
> + if (!pcie)
> + return -ENOMEM;
> +
> + of_id = of_match_node(brcm_pcie_match, dn);
> + if (!of_id)
> + return -EINVAL;
> +
> + data = of_id->data;
> + pcie->type = data->type;
> + pcie->ops = &data->ops;
> +
> + platform_set_drvdata(pdev, pcie);
> +
> + INIT_LIST_HEAD(&pcie->resource);
> +
> + r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + base = devm_ioremap_resource(&pdev->dev, r);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + ret = of_alias_get_id(dn, "pcie");
> + if (ret >= 0)
> + pcie->num = ret;
> +
> + pcie->clk = devm_clk_get(&pdev->dev, "pcie");
> + if (IS_ERR(pcie->clk)) {
> + dev_err(&pdev->dev, "could not get clock\n");
> + pcie->clk = NULL;
> + }
> +
> + ret = clk_prepare_enable(pcie->clk);
> + if (ret) {
> + dev_err(&pdev->dev, "could not enable clock\n");
> + return ret;
> + }
> +
> + pcie->dn = dn;
> + pcie->base = base;
> + pcie->dev = &pdev->dev;
> + pcie->dev->of_node = dn;
> + pcie->gen = 0;
> +
> + ret = of_property_read_u32(dn, "brcm,gen", &tmp);
> + if (ret == 0) {
> + if (tmp > 0 && tmp < 3)
> + pcie->gen = (int)tmp;
> + else
> + dev_warn(pcie->dev, "bad DT value for prop
> 'brcm,gen");
> + } else if (ret != -EINVAL) {
> + dev_warn(pcie->dev, "error reading DT prop 'brcm,gen");
> + }
> +
> + pcie->ssc = of_property_read_bool(dn, "brcm,ssc");
> +
> + /* Get the value for the log2 of the scb sizes. Subtract 15 from
> + * each because the target register field has 0==disabled and 1==6KB.
> + */
> + log2_scb_sizes = of_get_property(dn, "brcm,log2-scb-sizes", &rlen);
> + if (log2_scb_sizes) {
> + for (i = 0; i < rlen / sizeof(u32); i++) {
> + pcie->scb_size_vals[i]
> + = (int)of_read_number(log2_scb_sizes + i, 1)
> + - 15;
> + pcie->num_memc++;
> + }
> + }
In your device tree documentation this is required property, what if this property
is missing ?
> +
> + /* Look for the dma-ranges property. If it exists, issue a warning
> + * as PCIe drivers may not work. This is because the identity
> + * mapping between system memory and PCIe space is not
> preserved,
> + * and we need Linux to massage the dma_addr_t values it gets
> + * from dma memory allocation. This functionality will be added
> + * in the near future.
> + */
> + dma_ranges = of_get_property(dn, "dma-ranges", &rlen);
> + if (dma_ranges != NULL)
> + dev_warn(pcie->dev, "no identity map; PCI drivers may fail");
> +
> + if (IS_ENABLED(CONFIG_PCI_MSI)) {
> + ret = irq_of_parse_and_map(pdev->dev.of_node, 1);
> + if (ret == 0)
> + dev_warn(pcie->dev, "cannot get msi intr; MSI
> disabled\n");
> + else
> + pcie->msi_irq = ret;
> + }
> +
> + ret = of_pci_get_host_bridge_resources(dn, 0, 0xff,
> + &pcie->resource, NULL);
> + if (ret) {
> + dev_err(pcie->dev, "ranges parsing failed\n");
> + return ret;
> + }
> +
> + ret = brcm_pcie_setup_early(pcie);
> + if (ret)
> + goto out_err_clk;
> +
> + /* If setup bridge fails, it cleans up behind itself */
> + ret = brcm_setup_pcie_bridge(pcie);
> + if (ret)
> + goto out_err;
> +
> + pcie->bus = pci_scan_root_bus(pcie->dev, pcie->num,
> &brcm_pcie_ops,
> + pcie, &pcie->resource);
> + if (!pcie->bus) {
> + ret = -ENOMEM;
> + goto out_err_bus;
> + }
> +
> + if (IS_ENABLED(CONFIG_PCI_MSI))
> + brcm_pcie_msi_chip_set(pcie);
> +
> + pci_bus_size_bridges(pcie->bus);
> + pci_bus_assign_resources(pcie->bus);
> +
> + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
> + pci_bus_add_devices(pcie->bus);
> +
> + return 0;
> +
> +out_err_bus:
> + brcm_pcie_enter_l23(pcie);
> + brcm_pcie_turn_off(pcie);
> +out_err_clk:
> + clk_disable_unprepare(pcie->clk);
> +out_err:
> + return ret;
> +}
> +
> +static int brcm_pcie_remove(struct platform_device *pdev) {
> + return brcm_pcie_suspend(&pdev->dev);
> +}
> +
> +static struct platform_driver brcm_pcie_driver = {
> + .probe = brcm_pcie_probe,
> + .remove = brcm_pcie_remove,
> + .driver = {
> + .name = "brcm-pcie",
> + .owner = THIS_MODULE,
> + .of_match_table = brcm_pcie_match,
> + .pm = &brcm_pcie_pm_ops,
> + },
> +};
> +module_platform_driver(brcm_pcie_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Broadcom STB PCIE RC driver");
> +MODULE_AUTHOR("Broadcom");
> diff --git a/drivers/pci/host/pcie-brcmstb.h b/drivers/pci/host/pcie-
> brcmstb.h new file mode 100644 index 000000000000..b4a507423bb0
> --- /dev/null
> +++ b/drivers/pci/host/pcie-brcmstb.h
> @@ -0,0 +1,160 @@
> +#ifndef __PCIE_BRCMSTB_H
> +#define __PCIE_BRCMSTB_H
> +
> +#include <linux/io.h>
> +
> +/* Broadcom PCIE Offsets */
> +#define PCIE_RC_CFG_PCIE_LINK_CAPABILITY 0x00b8
> +#define PCIE_RC_CFG_PCIE_LINK_STATUS_CONTROL
> 0x00bc
> +#define PCIE_RC_CFG_PCIE_ROOT_CAP_CONTROL 0x00c8
> +#define PCIE_RC_CFG_PCIE_LINK_STATUS_CONTROL_2
> 0x00dc
> +#define PCIE_RC_CFG_VENDOR_VENDOR_SPECIFIC_REG1
> 0x0188
> +#define PCIE_RC_CFG_PRIV1_ID_VAL3 0x043c
> +#define PCIE_RC_DL_MDIO_ADDR 0x1100
> +#define PCIE_RC_DL_MDIO_WR_DATA
> 0x1104
> +#define PCIE_RC_DL_MDIO_RD_DATA
> 0x1108
> +#define PCIE_MISC_MISC_CTRL 0x4008
> +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO 0x400c
> +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_HI 0x4010
> +#define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c
> +#define PCIE_MISC_RC_BAR1_CONFIG_HI 0x4030
> +#define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034
> +#define PCIE_MISC_RC_BAR2_CONFIG_HI 0x4038
> +#define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
> +#define PCIE_MISC_RC_BAR3_CONFIG_HI 0x4040
> +#define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044
> +#define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048
> +#define PCIE_MISC_MSI_DATA_CONFIG 0x404c
> +#define PCIE_MISC_PCIE_CTRL 0x4064
> +#define PCIE_MISC_PCIE_STATUS 0x4068
> +#define PCIE_MISC_REVISION 0x406c
> +#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT 0x4070
> +#define PCIE_MISC_HARD_PCIE_HARD_DEBUG
> 0x4204
> +#define PCIE_INTR2_CPU_BASE 0x4300
> +#define PCIE_MSI_INTR2_BASE 0x4500
> +
> +#define PCIE_RGR1_SW_INIT_1 0x9210
> +#define PCIE_EXT_CFG_INDEX 0x9000
> +#define PCIE_EXT_CFG_DATA 0x9004
> +
> +/* BCM7425 specific register offsets */
> +#define BCM7425_PCIE_RGR1_SW_INIT_1 0x8010
> +#define BCM7425_PCIE_EXT_CFG_INDEX 0x8300
> +#define BCM7425_PCIE_EXT_CFG_DATA 0x8304
> +
> +#define PCI_BUSNUM_SHIFT 20
> +#define PCI_SLOT_SHIFT 15
> +#define PCI_FUNC_SHIFT 12
> +
> +#define BRCM_NUM_PCI_OUT_WINS 4
> +#define BRCM_MAX_SCB 4
> +
> +/* Offsets from PCIE_INTR2_CPU_BASE and PCIE_MSI_INTR2_BASE */
> +#define STATUS 0x0
> +#define SET 0x4
> +#define CLR 0x8
> +#define MASK_STATUS 0xc
> +#define MASK_SET 0x10
> +#define MASK_CLR 0x14
> +
> +enum brcm_pcie_type {
> + BCM7425,
> + BCM7435,
> + GENERIC,
> +};
> +
> +struct brcm_pcie;
> +
> +/* Chip-specific PCIe operations (read/write config and reset) */
> +struct brcm_pcie_ll_ops {
> + u32 (*read_config)(struct brcm_pcie *pcie, int cfg_idx);
> + void (*write_config)(struct brcm_pcie *pcie, int cfg_idx, u32 val);
> + void (*rgr1_sw_init)(struct brcm_pcie *pcie, u32 mask,
> + int shift, u32 val);
> +};
> +
> +struct brcm_pcie_cfg_data {
> + const enum brcm_pcie_type type;
> + const struct brcm_pcie_ll_ops ops;
> +};
> +
> +struct brcm_msi;
> +
> +/* Internal Bus Controller Information.*/ struct brcm_pcie {
> + void __iomem *base;
> + bool suspended;
> + struct clk *clk;
> + struct device_node *dn;
> + bool ssc;
> + int gen;
> + int scb_size_vals[BRCM_MAX_SCB];
> + struct pci_bus *bus;
> + struct device *dev;
> + struct list_head resource;
> + int msi_irq;
> + struct brcm_msi *msi;
> + unsigned int rev;
> + unsigned int num;
> + bool bridge_setup_done;
> + enum brcm_pcie_type type;
> + const struct brcm_pcie_ll_ops *ops;
> + unsigned int num_memc;
> +};
> +
> +/* Helper functions to access read/write config space and software init
> +which
> + * are chip-specific
> + */
> +static inline u32 brcm_pcie_ll_read_config(struct brcm_pcie *pcie, int
> +cfg_idx) {
> + return pcie->ops->read_config(pcie, cfg_idx); }
> +
> +static inline void brcm_pcie_ll_write_config(struct brcm_pcie *pcie,
> + int cfg_idx, u32 val)
> +{
> + pcie->ops->write_config(pcie, cfg_idx, val); }
> +
> +static inline void brcm_pcie_rgr1_sw_init(struct brcm_pcie *pcie, u32 mask,
> + int shift, u32 val)
> +{
> + pcie->ops->rgr1_sw_init(pcie, mask, shift, val); }
> +
> +/*
> + * 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))
> + return __raw_readl(base);
> + else
> + return readl(base);
> +}
> +
> +static inline void bpcie_writel(u32 val, void __iomem *base) {
> + if (IS_ENABLED(CONFIG_MIPS))
> + __raw_writel(val, base);
> + else
> + writel(val, base);
> +}
> +
> +#ifdef CONFIG_PCIE_BRCMSTB_MSI
> +int brcm_pcie_enable_msi(struct brcm_pcie *pcie, int nr); void
> +brcm_pcie_msi_chip_set(struct brcm_pcie *pcie); #else static inline int
> +brcm_pcie_enable_msi(struct brcm_pcie *pcie, int nr) {
> + return 0;
> +}
> +static inline void brcm_pcie_msi_chip_set(struct brcm_pcie *pcie) { }
> +#endif
> +
> +#endif /* __PCIE_BRCMSTB_H */
> --
> 2.1.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in the
> body of a message to majordomo at vger.kernel.org More majordomo info at
> http://vger.kernel.org/majordomo-info.html
More information about the linux-arm-kernel
mailing list