brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · d298664 Raw
343 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  pci-rcar-gen2: internal PCI bus support4 *5 * Copyright (C) 2013 Renesas Solutions Corp.6 * Copyright (C) 2013 Cogent Embedded, Inc.7 *8 * Author: Valentine Barshak <valentine.barshak@cogentembedded.com>9 */10 11#include <linux/delay.h>12#include <linux/init.h>13#include <linux/interrupt.h>14#include <linux/io.h>15#include <linux/kernel.h>16#include <linux/of_address.h>17#include <linux/of_pci.h>18#include <linux/pci.h>19#include <linux/platform_device.h>20#include <linux/pm_runtime.h>21#include <linux/sizes.h>22#include <linux/slab.h>23 24#include "../pci.h"25 26/* AHB-PCI Bridge PCI communication registers */27#define RCAR_AHBPCI_PCICOM_OFFSET	0x80028 29#define RCAR_PCIAHB_WIN1_CTR_REG	(RCAR_AHBPCI_PCICOM_OFFSET + 0x00)30#define RCAR_PCIAHB_WIN2_CTR_REG	(RCAR_AHBPCI_PCICOM_OFFSET + 0x04)31#define RCAR_PCIAHB_PREFETCH0		0x032#define RCAR_PCIAHB_PREFETCH4		0x133#define RCAR_PCIAHB_PREFETCH8		0x234#define RCAR_PCIAHB_PREFETCH16		0x335 36#define RCAR_AHBPCI_WIN1_CTR_REG	(RCAR_AHBPCI_PCICOM_OFFSET + 0x10)37#define RCAR_AHBPCI_WIN2_CTR_REG	(RCAR_AHBPCI_PCICOM_OFFSET + 0x14)38#define RCAR_AHBPCI_WIN_CTR_MEM		(3 << 1)39#define RCAR_AHBPCI_WIN_CTR_CFG		(5 << 1)40#define RCAR_AHBPCI_WIN1_HOST		(1 << 30)41#define RCAR_AHBPCI_WIN1_DEVICE		(1 << 31)42 43#define RCAR_PCI_INT_ENABLE_REG		(RCAR_AHBPCI_PCICOM_OFFSET + 0x20)44#define RCAR_PCI_INT_STATUS_REG		(RCAR_AHBPCI_PCICOM_OFFSET + 0x24)45#define RCAR_PCI_INT_SIGTABORT		(1 << 0)46#define RCAR_PCI_INT_SIGRETABORT	(1 << 1)47#define RCAR_PCI_INT_REMABORT		(1 << 2)48#define RCAR_PCI_INT_PERR		(1 << 3)49#define RCAR_PCI_INT_SIGSERR		(1 << 4)50#define RCAR_PCI_INT_RESERR		(1 << 5)51#define RCAR_PCI_INT_WIN1ERR		(1 << 12)52#define RCAR_PCI_INT_WIN2ERR		(1 << 13)53#define RCAR_PCI_INT_A			(1 << 16)54#define RCAR_PCI_INT_B			(1 << 17)55#define RCAR_PCI_INT_PME		(1 << 19)56#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT		| \57				RCAR_PCI_INT_SIGRETABORT	| \58				RCAR_PCI_INT_REMABORT		| \59				RCAR_PCI_INT_PERR		| \60				RCAR_PCI_INT_SIGSERR		| \61				RCAR_PCI_INT_RESERR		| \62				RCAR_PCI_INT_WIN1ERR		| \63				RCAR_PCI_INT_WIN2ERR)64 65#define RCAR_AHB_BUS_CTR_REG		(RCAR_AHBPCI_PCICOM_OFFSET + 0x30)66#define RCAR_AHB_BUS_MMODE_HTRANS	(1 << 0)67#define RCAR_AHB_BUS_MMODE_BYTE_BURST	(1 << 1)68#define RCAR_AHB_BUS_MMODE_WR_INCR	(1 << 2)69#define RCAR_AHB_BUS_MMODE_HBUS_REQ	(1 << 7)70#define RCAR_AHB_BUS_SMODE_READYCTR	(1 << 17)71#define RCAR_AHB_BUS_MODE		(RCAR_AHB_BUS_MMODE_HTRANS |	\72					RCAR_AHB_BUS_MMODE_BYTE_BURST |	\73					RCAR_AHB_BUS_MMODE_WR_INCR |	\74					RCAR_AHB_BUS_MMODE_HBUS_REQ |	\75					RCAR_AHB_BUS_SMODE_READYCTR)76 77#define RCAR_USBCTR_REG			(RCAR_AHBPCI_PCICOM_OFFSET + 0x34)78#define RCAR_USBCTR_USBH_RST		(1 << 0)79#define RCAR_USBCTR_PCICLK_MASK		(1 << 1)80#define RCAR_USBCTR_PLL_RST		(1 << 2)81#define RCAR_USBCTR_DIRPD		(1 << 8)82#define RCAR_USBCTR_PCIAHB_WIN2_EN	(1 << 9)83#define RCAR_USBCTR_PCIAHB_WIN1_256M	(0 << 10)84#define RCAR_USBCTR_PCIAHB_WIN1_512M	(1 << 10)85#define RCAR_USBCTR_PCIAHB_WIN1_1G	(2 << 10)86#define RCAR_USBCTR_PCIAHB_WIN1_2G	(3 << 10)87#define RCAR_USBCTR_PCIAHB_WIN1_MASK	(3 << 10)88 89#define RCAR_PCI_ARBITER_CTR_REG	(RCAR_AHBPCI_PCICOM_OFFSET + 0x40)90#define RCAR_PCI_ARBITER_PCIREQ0	(1 << 0)91#define RCAR_PCI_ARBITER_PCIREQ1	(1 << 1)92#define RCAR_PCI_ARBITER_PCIBP_MODE	(1 << 12)93 94#define RCAR_PCI_UNIT_REV_REG		(RCAR_AHBPCI_PCICOM_OFFSET + 0x48)95 96struct rcar_pci {97	struct device *dev;98	void __iomem *reg;99	struct resource mem_res;100	struct resource *cfg_res;101	int irq;102};103 104/* PCI configuration space operations */105static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn,106				       int where)107{108	struct rcar_pci *priv = bus->sysdata;109	int slot, val;110 111	if (!pci_is_root_bus(bus) || PCI_FUNC(devfn))112		return NULL;113 114	/* Only one EHCI/OHCI device built-in */115	slot = PCI_SLOT(devfn);116	if (slot > 2)117		return NULL;118 119	/* bridge logic only has registers to 0x40 */120	if (slot == 0x0 && where >= 0x40)121		return NULL;122 123	val = slot ? RCAR_AHBPCI_WIN1_DEVICE | RCAR_AHBPCI_WIN_CTR_CFG :124		     RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG;125 126	iowrite32(val, priv->reg + RCAR_AHBPCI_WIN1_CTR_REG);127	return priv->reg + (slot >> 1) * 0x100 + where;128}129 130#ifdef CONFIG_PCI_DEBUG131/* if debug enabled, then attach an error handler irq to the bridge */132 133static irqreturn_t rcar_pci_err_irq(int irq, void *pw)134{135	struct rcar_pci *priv = pw;136	struct device *dev = priv->dev;137	u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG);138 139	if (status & RCAR_PCI_INT_ALLERRORS) {140		dev_err(dev, "error irq: status %08x\n", status);141 142		/* clear the error(s) */143		iowrite32(status & RCAR_PCI_INT_ALLERRORS,144			  priv->reg + RCAR_PCI_INT_STATUS_REG);145		return IRQ_HANDLED;146	}147 148	return IRQ_NONE;149}150 151static void rcar_pci_setup_errirq(struct rcar_pci *priv)152{153	struct device *dev = priv->dev;154	int ret;155	u32 val;156 157	ret = devm_request_irq(dev, priv->irq, rcar_pci_err_irq,158			       IRQF_SHARED, "error irq", priv);159	if (ret) {160		dev_err(dev, "cannot claim IRQ for error handling\n");161		return;162	}163 164	val = ioread32(priv->reg + RCAR_PCI_INT_ENABLE_REG);165	val |= RCAR_PCI_INT_ALLERRORS;166	iowrite32(val, priv->reg + RCAR_PCI_INT_ENABLE_REG);167}168#else169static inline void rcar_pci_setup_errirq(struct rcar_pci *priv) { }170#endif171 172/* PCI host controller setup */173static void rcar_pci_setup(struct rcar_pci *priv)174{175	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(priv);176	struct device *dev = priv->dev;177	void __iomem *reg = priv->reg;178	struct resource_entry *entry;179	unsigned long window_size;180	unsigned long window_addr;181	unsigned long window_pci;182	u32 val;183 184	entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM);185	if (!entry) {186		window_addr = 0x40000000;187		window_pci = 0x40000000;188		window_size = SZ_1G;189	} else {190		window_addr = entry->res->start;191		window_pci = entry->res->start - entry->offset;192		window_size = resource_size(entry->res);193	}194 195	pm_runtime_enable(dev);196	pm_runtime_get_sync(dev);197 198	val = ioread32(reg + RCAR_PCI_UNIT_REV_REG);199	dev_info(dev, "PCI: revision %x\n", val);200 201	/* Disable Direct Power Down State and assert reset */202	val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD;203	val |= RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST;204	iowrite32(val, reg + RCAR_USBCTR_REG);205	udelay(4);206 207	/* De-assert reset and reset PCIAHB window1 size */208	val &= ~(RCAR_USBCTR_PCIAHB_WIN1_MASK | RCAR_USBCTR_PCICLK_MASK |209		 RCAR_USBCTR_USBH_RST | RCAR_USBCTR_PLL_RST);210 211	/* Setup PCIAHB window1 size */212	switch (window_size) {213	case SZ_2G:214		val |= RCAR_USBCTR_PCIAHB_WIN1_2G;215		break;216	case SZ_1G:217		val |= RCAR_USBCTR_PCIAHB_WIN1_1G;218		break;219	case SZ_512M:220		val |= RCAR_USBCTR_PCIAHB_WIN1_512M;221		break;222	default:223		pr_warn("unknown window size %ld - defaulting to 256M\n",224			window_size);225		window_size = SZ_256M;226		fallthrough;227	case SZ_256M:228		val |= RCAR_USBCTR_PCIAHB_WIN1_256M;229		break;230	}231	iowrite32(val, reg + RCAR_USBCTR_REG);232 233	/* Configure AHB master and slave modes */234	iowrite32(RCAR_AHB_BUS_MODE, reg + RCAR_AHB_BUS_CTR_REG);235 236	/* Configure PCI arbiter */237	val = ioread32(reg + RCAR_PCI_ARBITER_CTR_REG);238	val |= RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |239	       RCAR_PCI_ARBITER_PCIBP_MODE;240	iowrite32(val, reg + RCAR_PCI_ARBITER_CTR_REG);241 242	/* PCI-AHB mapping */243	iowrite32(window_addr | RCAR_PCIAHB_PREFETCH16,244		  reg + RCAR_PCIAHB_WIN1_CTR_REG);245 246	/* AHB-PCI mapping: OHCI/EHCI registers */247	val = priv->mem_res.start | RCAR_AHBPCI_WIN_CTR_MEM;248	iowrite32(val, reg + RCAR_AHBPCI_WIN2_CTR_REG);249 250	/* Enable AHB-PCI bridge PCI configuration access */251	iowrite32(RCAR_AHBPCI_WIN1_HOST | RCAR_AHBPCI_WIN_CTR_CFG,252		  reg + RCAR_AHBPCI_WIN1_CTR_REG);253	/* Set PCI-AHB Window1 address */254	iowrite32(window_pci | PCI_BASE_ADDRESS_MEM_PREFETCH,255		  reg + PCI_BASE_ADDRESS_1);256	/* Set AHB-PCI bridge PCI communication area address */257	val = priv->cfg_res->start + RCAR_AHBPCI_PCICOM_OFFSET;258	iowrite32(val, reg + PCI_BASE_ADDRESS_0);259 260	val = ioread32(reg + PCI_COMMAND);261	val |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |262	       PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;263	iowrite32(val, reg + PCI_COMMAND);264 265	/* Enable PCI interrupts */266	iowrite32(RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME,267		  reg + RCAR_PCI_INT_ENABLE_REG);268 269	rcar_pci_setup_errirq(priv);270}271 272static struct pci_ops rcar_pci_ops = {273	.map_bus = rcar_pci_cfg_base,274	.read	= pci_generic_config_read,275	.write	= pci_generic_config_write,276};277 278static int rcar_pci_probe(struct platform_device *pdev)279{280	struct device *dev = &pdev->dev;281	struct resource *cfg_res, *mem_res;282	struct rcar_pci *priv;283	struct pci_host_bridge *bridge;284	void __iomem *reg;285 286	bridge = devm_pci_alloc_host_bridge(dev, sizeof(*priv));287	if (!bridge)288		return -ENOMEM;289 290	priv = pci_host_bridge_priv(bridge);291	bridge->sysdata = priv;292 293	reg = devm_platform_get_and_ioremap_resource(pdev, 0, &cfg_res);294	if (IS_ERR(reg))295		return PTR_ERR(reg);296 297	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);298	if (!mem_res || !mem_res->start)299		return -ENODEV;300 301	if (mem_res->start & 0xFFFF)302		return -EINVAL;303 304	priv->mem_res = *mem_res;305	priv->cfg_res = cfg_res;306 307	priv->irq = platform_get_irq(pdev, 0);308	priv->reg = reg;309	priv->dev = dev;310 311	if (priv->irq < 0) {312		dev_err(dev, "no valid irq found\n");313		return priv->irq;314	}315 316	bridge->ops = &rcar_pci_ops;317 318	pci_add_flags(PCI_REASSIGN_ALL_BUS);319 320	rcar_pci_setup(priv);321 322	return pci_host_probe(bridge);323}324 325static const struct of_device_id rcar_pci_of_match[] = {326	{ .compatible = "renesas,pci-r8a7790", },327	{ .compatible = "renesas,pci-r8a7791", },328	{ .compatible = "renesas,pci-r8a7794", },329	{ .compatible = "renesas,pci-rcar-gen2", },330	{ .compatible = "renesas,pci-rzn1", },331	{ },332};333 334static struct platform_driver rcar_pci_driver = {335	.driver = {336		.name = "pci-rcar-gen2",337		.suppress_bind_attrs = true,338		.of_match_table = rcar_pci_of_match,339	},340	.probe = rcar_pci_probe,341};342builtin_platform_driver(rcar_pci_driver);343