Message ID | 8c44b3e28da65cf47ff6bd53cf8e9cf30f2b4cb0.1712205900.git.ysato@users.sourceforge.jp (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Geert Uytterhoeven |
Headers | show |
Series | Device Tree support for SH7751 based board | expand |
Hi, On 4/3/2024 9:59 PM, Yoshinori Sato wrote: > Renesas SH7751 CPU Internal PCI Controller driver. > > Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp> > --- > drivers/pci/controller/Kconfig | 9 + > drivers/pci/controller/Makefile | 1 + > drivers/pci/controller/pci-sh7751.c | 342 ++++++++++++++++++++++++++++ > 3 files changed, 352 insertions(+) > create mode 100644 drivers/pci/controller/pci-sh7751.c > > diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig > index e534c02ee34f..a2fd917a2e03 100644 > --- a/drivers/pci/controller/Kconfig > +++ b/drivers/pci/controller/Kconfig > @@ -353,6 +353,15 @@ config PCIE_XILINX_CPM > Say 'Y' here if you want kernel support for the > Xilinx Versal CPM host bridge. > > +config PCI_SH7751 > + bool "Renesas SH7751 PCI controller" > + depends on OF > + depends on CPU_SUBTYPE_SH7751 || CPU_SUBTYPE_SH7751R || COMPILE_TEST > + select PCI_HOST_COMMON > + help > + Say 'Y' here if you want kernel to support the Renesas SH7751 PCI > + Host Bridge driver. > + > source "drivers/pci/controller/cadence/Kconfig" > source "drivers/pci/controller/dwc/Kconfig" > source "drivers/pci/controller/mobiveil/Kconfig" > diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile > index f2b19e6174af..aa97e5d74e58 100644 > --- a/drivers/pci/controller/Makefile > +++ b/drivers/pci/controller/Makefile > @@ -40,6 +40,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o > obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o > obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o > obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o > +obj-$(CONFIG_PCI_SH7751) += pci-sh7751.o > > # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW > obj-y += dwc/ > diff --git a/drivers/pci/controller/pci-sh7751.c b/drivers/pci/controller/pci-sh7751.c > new file mode 100644 > index 000000000000..a5340689f737 > --- /dev/null > +++ b/drivers/pci/controller/pci-sh7751.c > @@ -0,0 +1,342 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * SH7751 PCI driver > + * Copyright (C) 2023 Yoshinori Sato > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of_address.h> > +#include <linux/of_pci.h> > +#include <linux/of_platform.h> > +#include <linux/pci-ecam.h> > +#include <linux/platform_device.h> > +#include <linux/io.h> > +#include <linux/pci.h> > +#include <linux/dma-direct.h> > +#include <asm/addrspace.h> can you consider to rearranging headers into alphabetically sorted order ? > + > +/* PCICR and PCICLKCR write enable magic key */ > +#define PCIC_WE_KEY (0xa5 << 24) > + > +/* PCIC registers */ > +/* 0x0000 - 0x00ff mapped to PCI device configuration space */ > +#define PCIC_PCICR 0x100 /* PCI Control Register */ > +#define PCIC_PCICR_TRSB BIT(9) /* Target Read Single */ > +#define PCIC_PCICR_BSWP BIT(8) /* Target Byte Swap */ > +#define PCIC_PCICR_PLUP BIT(7) /* Enable PCI Pullup */ > +#define PCIC_PCICR_ARBM BIT(6) /* PCI Arbitration Mode */ > +#define PCIC_PCICR_MD10 BIT(5) /* MD10 status */ > +#define PCIC_PCICR_MD9 BIT(4) /* MD9 status */ > +#define PCIC_PCICR_SERR BIT(3) /* SERR output assert */ > +#define PCIC_PCICR_INTA BIT(2) /* INTA output assert */ > +#define PCIC_PCICR_PRST BIT(1) /* PCI Reset Assert */ > +#define PCIC_PCICR_CFIN BIT(0) /* Central Fun. Init Done */ > + > +#define PCIC_PCILSR0 0x104 /* PCI Local Space Register0 */ > +#define PCIC_PCILSR1 0x108 /* PCI Local Space Register1 */ > +#define PCIC_PCILAR0 0x10c /* PCI Local Addr Register1 */ > +#define PCIC_PCILAR1 0x110 /* PCI Local Addr Register1 */ > +#define PCIC_PCIINT 0x114 /* PCI Interrupt Register */ > +#define PCIC_PCIINTM 0x118 /* PCI Interrupt Mask */ > +#define PCIC_PCIALR 0x11c /* Error Address Register */ > +#define PCIC_PCICLR 0x120 /* Error Command/Data */ > +#define PCIC_PCIAINT 0x130 /* Arbiter Interrupt Register */ > +#define PCIC_PCIAINTM 0x134 /* Arbiter Int. Mask Register */ > +#define PCIC_PCIBMLR 0x138 /* Error Bus Master Register */ > +#define PCIC_PCIDMABT 0x140 /* DMA Transfer Arb. Register */ > +#define PCIC_PCIPAR 0x1c0 /* PIO Address Register */ > +#define PCIC_PCIMBR 0x1c4 /* Memory Base Address */ > +#define PCIC_PCIIOBR 0x1c8 /* I/O Base Address Register */ > + > +#define PCIC_PCIPINT 0x1cc /* Power Mgmnt Int. Register */ > +#define PCIC_PCIPINT_D3 BIT(1) /* D3 Pwr Mgmt. Interrupt */ > +#define PCIC_PCIPINT_D0 BIT(0) /* D0 Pwr Mgmt. Interrupt */ > + > +#define PCIC_PCIPINTM 0x1d0 /* Power Mgmnt Mask Register */ > +#define PCIC_PCICLKR 0x1d4 /* Clock Ctrl. Register */ > +#define PCIC_PCIBCR1 0x1e0 /* Memory BCR1 Register */ > +#define PCIC_PCIBCR2 0x1e4 /* Memory BCR2 Register */ > +#define PCIC_PCIWCR1 0x1e8 /* Wait Control 1 Register */ > +#define PCIC_PCIWCR2 0x1ec /* Wait Control 2 Register */ > +#define PCIC_PCIWCR3 0x1f0 /* Wait Control 3 Register */ > +#define PCIC_PCIMCR 0x1f4 /* Memory Control Register */ > +#define PCIC_PCIBCR3 0x1f8 /* Memory BCR3 Register */ > +#define PCIC_PCIPDR 0x220 /* Port IO Data Register */ > + > +/* PCI IDs */ > +/* Hitachi is the company that led to Renesas. */ > +/* The SH7751 was designed by Hitachi, so it has a Hitachi ID. */ multi-line comments way ? > +#define PCI_VENDOR_ID_HITACHI 0x1054 > +#define PCI_DEVICE_ID_SH7751 0x3505 > +#define PCI_DEVICE_ID_SH7751R 0x350e > + > +/* BSC registers */ > +/* Copy BSC setting to PCI BSC */ > +#define BSC_BCR1 0x0000 > +#define BSC_BCR1_SLAVE BIT(30) > +#define BSC_BCR1_BRQEN BIT(19) > +#define BSC_BCR2 0x0004 > +#define BSC_BCR3 0x0050 > +#define BSC_WCR1 0x0008 > +#define BSC_WCR2 0x000c > +#define BSC_WCR3 0x0010 > +#define BSC_MCR 0x0014 > +#define BSC_MCR_MRSET BIT(30) > +#define BSC_MCR_RFSH BIT(2) > + > +/* PCIC access wrapper */ > +#define pcic_writel(val, base, reg) writel(val, base + (reg)) > +#define pcic_readl(base, reg) readl(base + (reg)) Do you really needed these new macros ? Is it for better readability ? > +/* > + * We need to avoid collisions with `mirrored' VGA ports > + * and other strange ISA hardware, so we always want the > + * addresses to be allocated in the 0x000-0x0ff region > + * modulo 0x400. > + */ > +#define IO_REGION_BASE 0x1000 > +resource_size_t pcibios_align_resource(void *data, const struct resource *res, > + resource_size_t size, resource_size_t align) > +{ > + resource_size_t start = res->start; > + > + if (res->flags & IORESOURCE_IO) { > + if (start < PCIBIOS_MIN_IO + IO_REGION_BASE) > + start = PCIBIOS_MIN_IO + IO_REGION_BASE; > + > + /* > + * Put everything into 0x00-0xff region modulo 0x400. > + */ single line comment would work. no ? > + if (start & 0x300) > + start = (start + 0x3ff) & ~0x3ff; > + } > + > + return start; > +} > + > +static int setup_pci_bsc(struct device *dev, void __iomem *pcic, > + void __iomem *bsc, unsigned int area, bool bcr3) > +{ > + u32 word; > + > + word = __raw_readl(bsc + BSC_BCR1); > + /* check BCR for SDRAM in area */ > + if (((word >> area) & 1) == 0) { > + dev_err(dev, "Area %u is not configured for SDRAM. BCR1=0x%x\n", > + area, word); > + return -EINVAL; > + } > + word |= BSC_BCR1_SLAVE; /* PCIC BSC is slave only */ > + pcic_writel(word, pcic, PCIC_PCIBCR1); > + > + word = __raw_readw(bsc + BSC_BCR2); > + /* check BCR2 for 32bit SDRAM interface*/ > + if (((word >> (area << 1)) & 0x3) != 0x3) { > + dev_err(dev, "Area %u is not 32 bit SDRAM. BCR2=0x%x\n", > + area, word); > + return -EINVAL; > + } > + pcic_writel(word, pcic, PCIC_PCIBCR2); > + > + if (bcr3) { > + /* BCR3 have only SH7751R */ > + word = __raw_readw(bsc + BSC_BCR3); > + pcic_writel(word, pcic, PCIC_PCIBCR3); > + } > + > + /* configure the wait control registers */ > + word = __raw_readl(bsc + BSC_WCR1); > + pcic_writel(word, pcic, PCIC_PCIWCR1); > + word = __raw_readl(bsc + BSC_WCR2); > + pcic_writel(word, pcic, PCIC_PCIWCR2); > + word = __raw_readl(bsc + BSC_WCR3); > + pcic_writel(word, pcic, PCIC_PCIWCR3); > + word = __raw_readl(bsc + BSC_MCR); > + /* Clear MRSET and RFSH bit */ > + word &= ~(BSC_MCR_MRSET | BSC_MCR_RFSH); > + pcic_writel(word, pcic, PCIC_PCIMCR); > + > + return 0; > +} > + > +#define NUM_AREA 7 > +static int set_pci_ranges(struct device *dev, > + void __iomem *pcic, void __iomem *bsc, bool bcr3) > +{ > + struct resource_entry *dma, *tmp; > + struct pci_host_bridge *bridge; > + u32 bsc_done[NUM_AREA]; > + unsigned int la; > + > + bridge = dev_get_drvdata(dev); > + pcic_writel(0, pcic, PCIC_PCILAR0); > + pcic_writel(0, pcic, PCIC_PCILAR1); > + la = 0; > + memset(&bsc_done, 0, sizeof(bsc_done)); > + resource_list_for_each_entry_safe(dma, tmp, &bridge->dma_ranges) { > + struct resource *res = dma->res; > + unsigned int area; > + u32 word; > + > + switch (resource_type(res)) { > + case IORESOURCE_IO: > + /* BAR0 is I/O space */ > + word = res->start | 1; > + pcic_writel(word, pcic, PCI_BASE_ADDRESS_0); > + word = pcic_readl(pcic, PCI_COMMAND); > + word |= PCI_COMMAND_IO; > + pcic_writel(word, pcic, PCI_COMMAND); > + break; > + case IORESOURCE_MEM: > + if (la > 4) { > + dev_err(dev, "Invalid range definition.\n"); > + return -EINVAL; > + } > + area = (res->start >> 26) & 0x07; > + word = res->end - res->start; > + if (area >= NUM_AREA) { > + /* Area 7 is reserved. */ > + dev_info(dev, "Invalid local address 0x%08x. Ignore it.\n", > + res->start); > + break; > + } > + pcic_writel(res->start, pcic, PCI_BASE_ADDRESS_1 + la); > + /* if dummy entry, skip BSC setup */ > + if (word < 4) > + break; > + /* BAR1 is local area 0, BAR2 is local area 1 */ > + pcic_writel(word, pcic, PCIC_PCILSR0 + la); > + word = P2SEGADDR(res->start); > + pcic_writel(word, pcic, PCIC_PCILAR0 + la); > + la += 4; > + if (!bsc_done[area]) { > + /* check BCR for SDRAM in specified area. And setup PCI BSC. */ > + if (setup_pci_bsc(dev, pcic, bsc, area, bcr3)) > + return -EINVAL; > + bsc_done[area] = 1; > + } > + break; > + } > + } > + return 0; > +} > + > +static int sh7751_pci_probe(struct platform_device *pdev) > +{ > + struct resource *res, *bscres; > + void __iomem *pcic; > + void __iomem *bsc; > + u16 vid, did; > + u32 word; > + int ret; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (IS_ERR(res)) > + return PTR_ERR(res); > + pcic = ioremap(res->start, res->end - res->start + 1); Can you consider using devm_platform_ioremap_resource() API ? > + bscres = platform_get_resource(pdev, IORESOURCE_MEM, 1); > + bsc = devm_ioremap_resource(&pdev->dev, bscres); > + if (IS_ERR(bsc)) > + return PTR_ERR(bsc); Same as above. > + /* check for SH7751/SH7751R hardware */ > + word = pcic_readl(pcic, PCI_VENDOR_ID); > + vid = word & 0xffff; > + did = word >> 16; > + if ((vid != PCI_VENDOR_ID_HITACHI) || > + ((did != PCI_DEVICE_ID_SH7751) && > + (did != PCI_DEVICE_ID_SH7751R))) { > + dev_err(&pdev->dev, "This is not an SH7751(R)\n"); error handling missing iounmap(pcic) > + return -ENODEV; > + } > + dev_info(&pdev->dev, "PCI core found at %pR\n", res); > + > + /* Set the BCR's to enable PCI access */ > + word = __raw_readl(bsc + BSC_BCR1); > + word |= BSC_BCR1_BRQEN; > + __raw_writel(word, bsc + BSC_BCR1); > + > + /* Turn the clocks back on (not done in reset)*/ > + pcic_writel(PCIC_WE_KEY | 0, pcic, PCIC_PCICLKR); > + /* Clear Powerdown IRQ's (not done in reset) */ > + word = PCIC_PCIPINT_D3 | PCIC_PCIPINT_D0; > + pcic_writel(word, pcic, PCIC_PCIPINT); > + > + /* set the command/status */ > + word = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | > + PCI_COMMAND_PARITY | PCI_COMMAND_WAIT; > + pcic_writel(word, pcic, PCI_COMMAND); > + > + /* define this host as the host bridge */ > + word = PCI_BASE_CLASS_BRIDGE << 24; > + pcic_writel(word, pcic, PCI_CLASS_REVISION); > + > + ret = pci_host_common_probe(pdev); > + if (ret) { > + dev_err(&pdev->dev, "Initialize failed (%d)\n", ret); > + return ret; > + } > + > + /* Set IO and Mem windows to local address */ > + if (set_pci_ranges(&pdev->dev, pcic, bsc, > + did == PCI_DEVICE_ID_SH7751R)) > + return -EINVAL; error handling to call pci_host_common_remove() ? > + pcic_writel(0, pcic, PCIC_PCIIOBR); > + > + if (of_property_read_bool(pdev->dev.of_node, "renesas,bus-arbit-round-robin")) > + word |= BIT(0); > + else > + word = 0; > + pcic_writel(word, pcic, PCIC_PCIDMABT); > + > + /* SH7751 init done, set central function init complete */ > + /* use round robin mode to stop a device starving/overrunning */ multi-line comment ? > + word = PCIC_PCICR_CFIN | PCIC_PCICR_ARBM; > + pcic_writel(PCIC_WE_KEY | word, pcic, PCIC_PCICR); > + > + return 0; > +} > + > +/* > + * Direct access to PCI hardware... > + */ > +#define CONFIG_CMD(bus, devfn, where) \ > + (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) > + > +static void __iomem *sh4_pci_map_bus(struct pci_bus *bus, > + unsigned int devfn, int where) > +{ > + struct pci_config_window *cfg = bus->sysdata; > + void __iomem *pcic = (void __iomem *)cfg->res.start; > + > + pcic_writel(CONFIG_CMD(bus, devfn, where), pcic, PCIC_PCIPAR); > + return pcic + PCIC_PCIPDR; > +} > + > +static const struct pci_ecam_ops pci_sh7751_bus_ops = { > + .pci_ops = { > + .map_bus = sh4_pci_map_bus, > + .read = pci_generic_config_read32, > + .write = pci_generic_config_write32, > + } > +}; > + > +static const struct of_device_id sh7751_pci_of_match[] = { > + { .compatible = "renesas,sh7751-pci", > + .data = &pci_sh7751_bus_ops }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, sh7751_pci_of_match); > + > +static struct platform_driver sh7751_pci_driver = { > + .driver = { > + .name = "sh7751-pci", > + .of_match_table = sh7751_pci_of_match, > + }, > + .probe = sh7751_pci_probe, > +}; > +module_platform_driver(sh7751_pci_driver); > + > +MODULE_DESCRIPTION("SH7751 PCI driver");
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index e534c02ee34f..a2fd917a2e03 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -353,6 +353,15 @@ config PCIE_XILINX_CPM Say 'Y' here if you want kernel support for the Xilinx Versal CPM host bridge. +config PCI_SH7751 + bool "Renesas SH7751 PCI controller" + depends on OF + depends on CPU_SUBTYPE_SH7751 || CPU_SUBTYPE_SH7751R || COMPILE_TEST + select PCI_HOST_COMMON + help + Say 'Y' here if you want kernel to support the Renesas SH7751 PCI + Host Bridge driver. + source "drivers/pci/controller/cadence/Kconfig" source "drivers/pci/controller/dwc/Kconfig" source "drivers/pci/controller/mobiveil/Kconfig" diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index f2b19e6174af..aa97e5d74e58 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o +obj-$(CONFIG_PCI_SH7751) += pci-sh7751.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/pci-sh7751.c b/drivers/pci/controller/pci-sh7751.c new file mode 100644 index 000000000000..a5340689f737 --- /dev/null +++ b/drivers/pci/controller/pci-sh7751.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SH7751 PCI driver + * Copyright (C) 2023 Yoshinori Sato + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> +#include <linux/pci-ecam.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/pci.h> +#include <linux/dma-direct.h> +#include <asm/addrspace.h> + +/* PCICR and PCICLKCR write enable magic key */ +#define PCIC_WE_KEY (0xa5 << 24) + +/* PCIC registers */ +/* 0x0000 - 0x00ff mapped to PCI device configuration space */ +#define PCIC_PCICR 0x100 /* PCI Control Register */ +#define PCIC_PCICR_TRSB BIT(9) /* Target Read Single */ +#define PCIC_PCICR_BSWP BIT(8) /* Target Byte Swap */ +#define PCIC_PCICR_PLUP BIT(7) /* Enable PCI Pullup */ +#define PCIC_PCICR_ARBM BIT(6) /* PCI Arbitration Mode */ +#define PCIC_PCICR_MD10 BIT(5) /* MD10 status */ +#define PCIC_PCICR_MD9 BIT(4) /* MD9 status */ +#define PCIC_PCICR_SERR BIT(3) /* SERR output assert */ +#define PCIC_PCICR_INTA BIT(2) /* INTA output assert */ +#define PCIC_PCICR_PRST BIT(1) /* PCI Reset Assert */ +#define PCIC_PCICR_CFIN BIT(0) /* Central Fun. Init Done */ + +#define PCIC_PCILSR0 0x104 /* PCI Local Space Register0 */ +#define PCIC_PCILSR1 0x108 /* PCI Local Space Register1 */ +#define PCIC_PCILAR0 0x10c /* PCI Local Addr Register1 */ +#define PCIC_PCILAR1 0x110 /* PCI Local Addr Register1 */ +#define PCIC_PCIINT 0x114 /* PCI Interrupt Register */ +#define PCIC_PCIINTM 0x118 /* PCI Interrupt Mask */ +#define PCIC_PCIALR 0x11c /* Error Address Register */ +#define PCIC_PCICLR 0x120 /* Error Command/Data */ +#define PCIC_PCIAINT 0x130 /* Arbiter Interrupt Register */ +#define PCIC_PCIAINTM 0x134 /* Arbiter Int. Mask Register */ +#define PCIC_PCIBMLR 0x138 /* Error Bus Master Register */ +#define PCIC_PCIDMABT 0x140 /* DMA Transfer Arb. Register */ +#define PCIC_PCIPAR 0x1c0 /* PIO Address Register */ +#define PCIC_PCIMBR 0x1c4 /* Memory Base Address */ +#define PCIC_PCIIOBR 0x1c8 /* I/O Base Address Register */ + +#define PCIC_PCIPINT 0x1cc /* Power Mgmnt Int. Register */ +#define PCIC_PCIPINT_D3 BIT(1) /* D3 Pwr Mgmt. Interrupt */ +#define PCIC_PCIPINT_D0 BIT(0) /* D0 Pwr Mgmt. Interrupt */ + +#define PCIC_PCIPINTM 0x1d0 /* Power Mgmnt Mask Register */ +#define PCIC_PCICLKR 0x1d4 /* Clock Ctrl. Register */ +#define PCIC_PCIBCR1 0x1e0 /* Memory BCR1 Register */ +#define PCIC_PCIBCR2 0x1e4 /* Memory BCR2 Register */ +#define PCIC_PCIWCR1 0x1e8 /* Wait Control 1 Register */ +#define PCIC_PCIWCR2 0x1ec /* Wait Control 2 Register */ +#define PCIC_PCIWCR3 0x1f0 /* Wait Control 3 Register */ +#define PCIC_PCIMCR 0x1f4 /* Memory Control Register */ +#define PCIC_PCIBCR3 0x1f8 /* Memory BCR3 Register */ +#define PCIC_PCIPDR 0x220 /* Port IO Data Register */ + +/* PCI IDs */ +/* Hitachi is the company that led to Renesas. */ +/* The SH7751 was designed by Hitachi, so it has a Hitachi ID. */ +#define PCI_VENDOR_ID_HITACHI 0x1054 +#define PCI_DEVICE_ID_SH7751 0x3505 +#define PCI_DEVICE_ID_SH7751R 0x350e + +/* BSC registers */ +/* Copy BSC setting to PCI BSC */ +#define BSC_BCR1 0x0000 +#define BSC_BCR1_SLAVE BIT(30) +#define BSC_BCR1_BRQEN BIT(19) +#define BSC_BCR2 0x0004 +#define BSC_BCR3 0x0050 +#define BSC_WCR1 0x0008 +#define BSC_WCR2 0x000c +#define BSC_WCR3 0x0010 +#define BSC_MCR 0x0014 +#define BSC_MCR_MRSET BIT(30) +#define BSC_MCR_RFSH BIT(2) + +/* PCIC access wrapper */ +#define pcic_writel(val, base, reg) writel(val, base + (reg)) +#define pcic_readl(base, reg) readl(base + (reg)) + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + */ +#define IO_REGION_BASE 0x1000 +resource_size_t pcibios_align_resource(void *data, const struct resource *res, + resource_size_t size, resource_size_t align) +{ + resource_size_t start = res->start; + + if (res->flags & IORESOURCE_IO) { + if (start < PCIBIOS_MIN_IO + IO_REGION_BASE) + start = PCIBIOS_MIN_IO + IO_REGION_BASE; + + /* + * Put everything into 0x00-0xff region modulo 0x400. + */ + if (start & 0x300) + start = (start + 0x3ff) & ~0x3ff; + } + + return start; +} + +static int setup_pci_bsc(struct device *dev, void __iomem *pcic, + void __iomem *bsc, unsigned int area, bool bcr3) +{ + u32 word; + + word = __raw_readl(bsc + BSC_BCR1); + /* check BCR for SDRAM in area */ + if (((word >> area) & 1) == 0) { + dev_err(dev, "Area %u is not configured for SDRAM. BCR1=0x%x\n", + area, word); + return -EINVAL; + } + word |= BSC_BCR1_SLAVE; /* PCIC BSC is slave only */ + pcic_writel(word, pcic, PCIC_PCIBCR1); + + word = __raw_readw(bsc + BSC_BCR2); + /* check BCR2 for 32bit SDRAM interface*/ + if (((word >> (area << 1)) & 0x3) != 0x3) { + dev_err(dev, "Area %u is not 32 bit SDRAM. BCR2=0x%x\n", + area, word); + return -EINVAL; + } + pcic_writel(word, pcic, PCIC_PCIBCR2); + + if (bcr3) { + /* BCR3 have only SH7751R */ + word = __raw_readw(bsc + BSC_BCR3); + pcic_writel(word, pcic, PCIC_PCIBCR3); + } + + /* configure the wait control registers */ + word = __raw_readl(bsc + BSC_WCR1); + pcic_writel(word, pcic, PCIC_PCIWCR1); + word = __raw_readl(bsc + BSC_WCR2); + pcic_writel(word, pcic, PCIC_PCIWCR2); + word = __raw_readl(bsc + BSC_WCR3); + pcic_writel(word, pcic, PCIC_PCIWCR3); + word = __raw_readl(bsc + BSC_MCR); + /* Clear MRSET and RFSH bit */ + word &= ~(BSC_MCR_MRSET | BSC_MCR_RFSH); + pcic_writel(word, pcic, PCIC_PCIMCR); + + return 0; +} + +#define NUM_AREA 7 +static int set_pci_ranges(struct device *dev, + void __iomem *pcic, void __iomem *bsc, bool bcr3) +{ + struct resource_entry *dma, *tmp; + struct pci_host_bridge *bridge; + u32 bsc_done[NUM_AREA]; + unsigned int la; + + bridge = dev_get_drvdata(dev); + pcic_writel(0, pcic, PCIC_PCILAR0); + pcic_writel(0, pcic, PCIC_PCILAR1); + la = 0; + memset(&bsc_done, 0, sizeof(bsc_done)); + resource_list_for_each_entry_safe(dma, tmp, &bridge->dma_ranges) { + struct resource *res = dma->res; + unsigned int area; + u32 word; + + switch (resource_type(res)) { + case IORESOURCE_IO: + /* BAR0 is I/O space */ + word = res->start | 1; + pcic_writel(word, pcic, PCI_BASE_ADDRESS_0); + word = pcic_readl(pcic, PCI_COMMAND); + word |= PCI_COMMAND_IO; + pcic_writel(word, pcic, PCI_COMMAND); + break; + case IORESOURCE_MEM: + if (la > 4) { + dev_err(dev, "Invalid range definition.\n"); + return -EINVAL; + } + area = (res->start >> 26) & 0x07; + word = res->end - res->start; + if (area >= NUM_AREA) { + /* Area 7 is reserved. */ + dev_info(dev, "Invalid local address 0x%08x. Ignore it.\n", + res->start); + break; + } + pcic_writel(res->start, pcic, PCI_BASE_ADDRESS_1 + la); + /* if dummy entry, skip BSC setup */ + if (word < 4) + break; + /* BAR1 is local area 0, BAR2 is local area 1 */ + pcic_writel(word, pcic, PCIC_PCILSR0 + la); + word = P2SEGADDR(res->start); + pcic_writel(word, pcic, PCIC_PCILAR0 + la); + la += 4; + if (!bsc_done[area]) { + /* check BCR for SDRAM in specified area. And setup PCI BSC. */ + if (setup_pci_bsc(dev, pcic, bsc, area, bcr3)) + return -EINVAL; + bsc_done[area] = 1; + } + break; + } + } + return 0; +} + +static int sh7751_pci_probe(struct platform_device *pdev) +{ + struct resource *res, *bscres; + void __iomem *pcic; + void __iomem *bsc; + u16 vid, did; + u32 word; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (IS_ERR(res)) + return PTR_ERR(res); + pcic = ioremap(res->start, res->end - res->start + 1); + + bscres = platform_get_resource(pdev, IORESOURCE_MEM, 1); + bsc = devm_ioremap_resource(&pdev->dev, bscres); + if (IS_ERR(bsc)) + return PTR_ERR(bsc); + + /* check for SH7751/SH7751R hardware */ + word = pcic_readl(pcic, PCI_VENDOR_ID); + vid = word & 0xffff; + did = word >> 16; + if ((vid != PCI_VENDOR_ID_HITACHI) || + ((did != PCI_DEVICE_ID_SH7751) && + (did != PCI_DEVICE_ID_SH7751R))) { + dev_err(&pdev->dev, "This is not an SH7751(R)\n"); + return -ENODEV; + } + dev_info(&pdev->dev, "PCI core found at %pR\n", res); + + /* Set the BCR's to enable PCI access */ + word = __raw_readl(bsc + BSC_BCR1); + word |= BSC_BCR1_BRQEN; + __raw_writel(word, bsc + BSC_BCR1); + + /* Turn the clocks back on (not done in reset)*/ + pcic_writel(PCIC_WE_KEY | 0, pcic, PCIC_PCICLKR); + /* Clear Powerdown IRQ's (not done in reset) */ + word = PCIC_PCIPINT_D3 | PCIC_PCIPINT_D0; + pcic_writel(word, pcic, PCIC_PCIPINT); + + /* set the command/status */ + word = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | + PCI_COMMAND_PARITY | PCI_COMMAND_WAIT; + pcic_writel(word, pcic, PCI_COMMAND); + + /* define this host as the host bridge */ + word = PCI_BASE_CLASS_BRIDGE << 24; + pcic_writel(word, pcic, PCI_CLASS_REVISION); + + ret = pci_host_common_probe(pdev); + if (ret) { + dev_err(&pdev->dev, "Initialize failed (%d)\n", ret); + return ret; + } + + /* Set IO and Mem windows to local address */ + if (set_pci_ranges(&pdev->dev, pcic, bsc, + did == PCI_DEVICE_ID_SH7751R)) + return -EINVAL; + pcic_writel(0, pcic, PCIC_PCIIOBR); + + if (of_property_read_bool(pdev->dev.of_node, "renesas,bus-arbit-round-robin")) + word |= BIT(0); + else + word = 0; + pcic_writel(word, pcic, PCIC_PCIDMABT); + + /* SH7751 init done, set central function init complete */ + /* use round robin mode to stop a device starving/overrunning */ + word = PCIC_PCICR_CFIN | PCIC_PCICR_ARBM; + pcic_writel(PCIC_WE_KEY | word, pcic, PCIC_PCICR); + + return 0; +} + +/* + * Direct access to PCI hardware... + */ +#define CONFIG_CMD(bus, devfn, where) \ + (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) + +static void __iomem *sh4_pci_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) +{ + struct pci_config_window *cfg = bus->sysdata; + void __iomem *pcic = (void __iomem *)cfg->res.start; + + pcic_writel(CONFIG_CMD(bus, devfn, where), pcic, PCIC_PCIPAR); + return pcic + PCIC_PCIPDR; +} + +static const struct pci_ecam_ops pci_sh7751_bus_ops = { + .pci_ops = { + .map_bus = sh4_pci_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, + } +}; + +static const struct of_device_id sh7751_pci_of_match[] = { + { .compatible = "renesas,sh7751-pci", + .data = &pci_sh7751_bus_ops }, + { } +}; +MODULE_DEVICE_TABLE(of, sh7751_pci_of_match); + +static struct platform_driver sh7751_pci_driver = { + .driver = { + .name = "sh7751-pci", + .of_match_table = sh7751_pci_of_match, + }, + .probe = sh7751_pci_probe, +}; +module_platform_driver(sh7751_pci_driver); + +MODULE_DESCRIPTION("SH7751 PCI driver");
Renesas SH7751 CPU Internal PCI Controller driver. Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp> --- drivers/pci/controller/Kconfig | 9 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/pci-sh7751.c | 342 ++++++++++++++++++++++++++++ 3 files changed, 352 insertions(+) create mode 100644 drivers/pci/controller/pci-sh7751.c