[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