This is an alternative SH7751 PCI driver.
Existing driver (arch/sh/drivers/pci/pci-sh7751) uses SH specific interface.
But this driver uses common PCI interface. It is more modern and generic.

Signed-off-by: Yoshinori Sato <ys...@users.sourceforge.jp>
---
 .../devicetree/bindings/pci/sh7751-pci.txt         |  51 ++++
 arch/sh/boards/Kconfig                             |   1 +
 arch/sh/drivers/Makefile                           |   2 +
 arch/sh/include/asm/io.h                           |   6 +
 drivers/pci/host/Kconfig                           |   7 +
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-sh7751.c                      | 326 +++++++++++++++++++++
 7 files changed, 394 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/pci/sh7751-pci.txt
 create mode 100644 drivers/pci/host/pci-sh7751.c

diff --git a/Documentation/devicetree/bindings/pci/sh7751-pci.txt 
b/Documentation/devicetree/bindings/pci/sh7751-pci.txt
new file mode 100644
index 0000000..c3ec71a
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/sh7751-pci.txt
@@ -0,0 +1,51 @@
+* Renesas SH7751 PCI host interfaces
+
+Required properties:
+  - compatible: "renesas,sh7751-pci" is required.
+    And board specific compatible if fixup required.
+
+  - reg: base address and length of the PCI controller registers.
+  - #address-cells: set to <2>
+  - #size-cells: set to <1>
+  - bus-range: PCI bus numbers covered
+  - device_type: set to "pci"
+  - ranges: ranges for the PCI memory and I/O regions.
+  - interrupt-map-mask and interrupt-map: standard PCI properties
+       to define the mapping of the PCI interface to interrupt
+       numbers.
+
+Example:
+       pci: pci-controller@fe200000 {
+               compatible = "renesas,sh7751-pci", "iodata,landisk";
+               device_type = "pci";
+               bus-range = <0 0>;
+               #address-cells = <2>;
+               #size-cells = <1>;
+               ranges = <0x02000000 0x00000000 0xfd000000 0xfd000000 
0x00000000 0x01000000>,
+                        <0x01000000 0x00000000 0xfe240000 0x00000000 
0x00000000 0x00040000>;
+               reg = <0xfe200000 0x0400>,
+                     <0x0c000000 0x04000000>,
+                     <0xff800000 0x0030>;
+               #interrupt-cells = <1>;
+               interrupt-map-mask = <0x1800 0 7>;
+               interrupt-map = <0x0000 0 1 &cpldintc evt2irq(0x2a0) 0
+                                0x0000 0 2 &cpldintc evt2irq(0x2c0) 0
+                                0x0000 0 3 &cpldintc evt2irq(0x2e0) 0
+                                0x0000 0 4 &cpldintc evt2irq(0x300) 0
+
+                                0x0800 0 1 &cpldintc evt2irq(0x2c0) 0
+                                0x0800 0 2 &cpldintc evt2irq(0x2e0) 0
+                                0x0800 0 3 &cpldintc evt2irq(0x300) 0
+                                0x0800 0 4 &cpldintc evt2irq(0x2a0) 0
+
+                                0x1000 0 1 &cpldintc evt2irq(0x2e0) 0
+                                0x1000 0 2 &cpldintc evt2irq(0x300) 0
+                                0x1000 0 3 &cpldintc evt2irq(0x2a0) 0
+                                0x1000 0 4 &cpldintc evt2irq(0x2c0) 0
+
+                                0x1800 0 1 &cpldintc evt2irq(0x300) 0
+                                0x1800 0 2 &cpldintc evt2irq(0x2a0) 0
+                                0x1800 0 3 &cpldintc evt2irq(0x2c0) 0
+                                0x1800 0 4 &cpldintc evt2irq(0x2e0) 0>;
+       };
+};
diff --git a/arch/sh/boards/Kconfig b/arch/sh/boards/Kconfig
index b6ff9df..cfde921 100644
--- a/arch/sh/boards/Kconfig
+++ b/arch/sh/boards/Kconfig
@@ -14,6 +14,7 @@ config SH_DEVICE_TREE
        select GENERIC_CALIBRATE_DELAY
        select GENERIC_IOMAP
        select COMMON_CLK
+       select SYS_SUPPORTS_PCI
        help
          Select Board Described by Device Tree to build a kernel that
          does not hard-code any board-specific knowledge but instead uses
diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile
index e13f06b..382e86f 100644
--- a/arch/sh/drivers/Makefile
+++ b/arch/sh/drivers/Makefile
@@ -4,7 +4,9 @@
 
 obj-y          += dma/
 
+ifndef CONFIG_SH_DEVICE_TREE
 obj-$(CONFIG_PCI)              += pci/
+endif
 obj-$(CONFIG_SUPERHYWAY)       += superhyway/
 obj-$(CONFIG_PUSH_SWITCH)      += push-switch.o
 obj-$(CONFIG_HEARTBEAT)                += heartbeat.o
diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h
index 3280a6b..df62a9f 100644
--- a/arch/sh/include/asm/io.h
+++ b/arch/sh/include/asm/io.h
@@ -387,6 +387,12 @@ static inline int iounmap_fixed(void __iomem *addr) { 
return -EINVAL; }
 int valid_phys_addr_range(phys_addr_t addr, size_t size);
 int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
 
+#define PCI_IOBASE     0xfe240000UL
+
+#define HAVE_ARCH_PIO_SIZE
+#define PIO_OFFSET     PCI_IOBASE
+#define PIO_MASK       0x3ffffUL
+#define PIO_RESERVED   0x40000UL
 #endif /* __KERNEL__ */
 
 #endif /* __ASM_SH_IO_H */
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 5d2374e..df60505 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -245,4 +245,11 @@ config PCIE_ARMADA_8K
          Designware hardware and therefore the driver re-uses the
          Designware core functions to implement the driver.
 
+config PCI_SH7751
+       bool "Renesas SH7751 On-Chip PCI controller"
+       depends on OF && SUPERH
+       select PCI_HOST_COMMON
+       help
+         Say Y here if you want PCI support on SH7751.
+
 endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 9c8698e..4681e49 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -29,3 +29,4 @@ obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
 obj-$(CONFIG_PCI_HOST_THUNDER_ECAM) += pci-thunder-ecam.o
 obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o
 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
+obj-$(CONFIG_PCI_SH7751) += pci-sh7751.o
diff --git a/drivers/pci/host/pci-sh7751.c b/drivers/pci/host/pci-sh7751.c
new file mode 100644
index 0000000..d851fcf3
--- /dev/null
+++ b/drivers/pci/host/pci-sh7751.c
@@ -0,0 +1,326 @@
+/*
+ * SH7751 PCI driver
+ * Copyright (C) 2016 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.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include "../ecam.h"
+
+#define SH7751_PCICONF1            0x4           /* PCI Config Reg 1 */
+#define SH7751_PCICONF4            0x10          /* PCI Config Reg 4 */
+#define SH7751_PCICONF5            0x14          /* PCI Config Reg 5 */
+#define SH7751_PCICONF6            0x18          /* PCI Config Reg 6 */
+#define SH4_PCICR              0x100           /* PCI Control Register */
+  #define SH4_PCICR_PREFIX       0xA5000000    /* CR prefix for write */
+  #define SH4_PCICR_FTO                  BIT(10)       /* TRDY/IRDY Enable */
+  #define SH4_PCICR_TRSB         BIT(9)        /* Target Read Single */
+  #define SH4_PCICR_BSWP         BIT(8)        /* Target Byte Swap */
+  #define SH4_PCICR_PLUP         BIT(7)        /* Enable PCI Pullup */
+  #define SH4_PCICR_ARBM         BIT(6)        /* PCI Arbitration Mode */
+#define SH4_PCICR_MD             (BIT(4) | BIT(5))     /* MD9 and MD10 status 
*/
+  #define SH4_PCICR_SERR         BIT(3)        /* SERR output assert */
+  #define SH4_PCICR_INTA         BIT(2)        /* INTA output assert */
+  #define SH4_PCICR_PRST         BIT(1)        /* PCI Reset Assert */
+  #define SH4_PCICR_CFIN         BIT(0)        /* Central Fun. Init Done */
+#define SH4_PCILSR0            0x104           /* PCI Local Space Register0 */
+#define SH4_PCILSR1            0x108           /* PCI Local Space Register1 */
+#define SH4_PCILAR0            0x10C           /* PCI Local Addr Register1 */
+#define SH4_PCILAR1            0x110           /* PCI Local Addr Register1 */
+#define SH4_PCIINTM            0x118           /* PCI Interrupt Mask */
+  #define SH4_PCIINTM_TTADIM     BIT(14)       /* Target-target abort 
interrupt */
+  #define SH4_PCIINTM_TMTOIM     BIT(9)        /* Target retry timeout */
+  #define SH4_PCIINTM_MDEIM      BIT(8)        /* Master function disable 
error */
+  #define SH4_PCIINTM_APEDIM     BIT(7)        /* Address parity error 
detection */
+  #define SH4_PCIINTM_SDIM       BIT(6)        /* SERR detection */
+  #define SH4_PCIINTM_DPEITWM    BIT(5)        /* Data parity error for target 
write */
+  #define SH4_PCIINTM_PEDITRM    BIT(4)        /* PERR detection for target 
read */
+  #define SH4_PCIINTM_TADIMM     BIT(3)        /* Target abort for master */
+  #define SH4_PCIINTM_MADIMM     BIT(2)        /* Master abort for master */
+  #define SH4_PCIINTM_MWPDIM     BIT(1)        /* Master write data parity 
error */
+  #define SH4_PCIINTM_MRDPEIM    BIT(0)        /* Master read data parity 
error */
+#define SH4_PCIAINTM            0x134          /* Arbiter Int. Mask Register */
+#define SH4_PCIPAR             0x1C0           /* PIO Address Register */
+  #define SH4_PCIPAR_CFGEN       0x80000000    /* Configuration Enable */
+  #define SH4_PCIPAR_BUSNO       0x00FF0000    /* Config. Bus Number */
+  #define SH4_PCIPAR_DEVNO       0x0000FF00    /* Config. Device Number */
+  #define SH4_PCIPAR_REGAD       0x000000FC    /* Register Address Number */
+#define SH4_PCIPINT            0x1CC           /* Power Mgmnt Int. Register */
+  #define SH4_PCIPINT_D3         0x00000002    /* D3 Pwr Mgmt. Interrupt */
+  #define SH4_PCIPINT_D0         0x00000001    /* D0 Pwr Mgmt. Interrupt */
+#define SH4_PCICLKR            0x1D4           /* Clock Ctrl. Register */
+/* For definitions of BCR, MCR see ... */
+#define SH4_PCIBCR1            0x1E0           /* Memory BCR1 Register */
+  #define SH4_PCIMBR0          SH4_PCIBCR1
+#define SH4_PCIBCR2            0x1E4           /* Memory BCR2 Register */
+  #define SH4_PCIMBMR0         SH4_PCIBCR2
+#define SH4_PCIWCR1            0x1E8           /* Wait Control 1 Register */
+#define SH4_PCIWCR2            0x1EC           /* Wait Control 2 Register */
+#define SH4_PCIWCR3            0x1F0           /* Wait Control 3 Register */
+  #define SH4_PCIMBR2          SH4_PCIWCR3
+#define SH4_PCIMCR             0x1F4           /* Memory Control Register */
+#define SH4_PCIPDR             0x220           /* Port IO Data Register */
+
+/* Platform Specific Values */
+#define SH7751_VENDOR_ID             0x1054
+#define SH7751_DEVICE_ID             0x3505
+#define SH7751R_DEVICE_ID            0x350e
+
+/* Memory Control Registers */
+#define SH7751_BCR1                0x0000    /* Memory BCR1 Register */
+#define SH7751_BCR2                0x0004    /* Memory BCR2 Register */
+#define SH7751_BCR3                0x0050    /* Memory BCR3 Register */
+#define SH7751_WCR1                0x0008    /* Wait Control 1 Register */
+#define SH7751_WCR2                0x000C    /* Wait Control 2 Register */
+#define SH7751_WCR3                0x0010    /* Wait Control 3 Register */
+#define SH7751_MCR                 0x0014    /* Memory Control Register */
+
+#define pcic_writel(val, reg) iowrite32(val, pci_reg_base + (reg))
+#define pcic_readl(reg) ioread32(pci_reg_base + (reg))
+
+/*
+ * PCIC fixups
+ */
+
+#define PCIMCR_MRSET 0x40000000
+#define PCIMCR_RFSH  0x00000004
+
+static void __init landisk_fixup(void __iomem *pci_reg_base, void __iomem *bcr)
+{
+       unsigned long bcr1, mcr;
+
+       bcr1 = ioread32(bcr + SH7751_BCR1);
+       bcr1 |= 0x00080000;     /* Enable Bit 19 BREQEN, set PCIC to slave */
+       pcic_writel(bcr1, SH4_PCIBCR1);
+
+       mcr = ioread32(bcr + SH7751_MCR);
+       mcr &= (~PCIMCR_MRSET) & (~PCIMCR_RFSH);
+       pcic_writel(mcr, SH4_PCIMCR);
+
+       pcic_writel(0x0c000000, PCI_BASE_ADDRESS_1);
+       pcic_writel(0xd0000000, PCI_BASE_ADDRESS_2);
+       pcic_writel(0x0c000000, SH4_PCILAR0);
+       pcic_writel(0x00000000, SH4_PCILAR1);
+}
+
+static void __init r2dplus_fixup(void __iomem *pci_reg_base, void __iomem *bcr)
+{
+       unsigned long bcr1, mcr;
+
+       bcr1 = ioread32(bcr + SH7751_BCR1);
+       bcr1 |= 0x40080000;     /* Enable Bit 19 BREQEN, set PCIC to slave */
+       pcic_writel(bcr1, SH4_PCIBCR1);
+
+       /* Enable all interrupts, so we known what to fix */
+       pcic_writel(0x0000c3ff, SH4_PCIINTM);
+       pcic_writel(0x0000380f, SH4_PCIAINTM);
+
+       pcic_writel(0xfb900047, SH7751_PCICONF1);
+       pcic_writel(0xab000001, SH7751_PCICONF4);
+
+       mcr = ioread32(bcr + SH7751_MCR);
+       mcr &= (~PCIMCR_MRSET) & (~PCIMCR_RFSH);
+       pcic_writel(mcr, SH4_PCIMCR);
+
+       pcic_writel(0x0c000000, SH7751_PCICONF5);
+       pcic_writel(0xd0000000, SH7751_PCICONF6);
+       pcic_writel(0x0c000000, SH4_PCILAR0);
+       pcic_writel(0x00000000, SH4_PCILAR1);
+}
+
+/*
+ * Direct access to PCI hardware...
+ */
+#define CONFIG_CMD(bus, devfn, where) \
+       (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+static void __iomem *sh7751_map_bus(struct pci_bus *bus,
+                                  unsigned int devfn, int where)
+{
+       struct pci_config_window *cfg = bus->sysdata;
+       void __iomem *pci_reg_base = (void __iomem *)cfg->res.start;
+
+       pcic_writel(CONFIG_CMD(bus, devfn, where), SH4_PCIPAR);
+       return pci_reg_base + SH4_PCIPDR;
+}
+
+static const struct of_device_id fixup_of_match[] = {
+       { .compatible = "iodata,landisk-pci", .data = landisk_fixup, },
+       { .compatible = "renesas,r2dplus-pci", .data = r2dplus_fixup, },
+       { },
+};
+
+static const struct of_device_id sh7751_pci_of_match[] = {
+       { .compatible = "renesas,sh7751-pci", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sh7751_pci_of_match);
+
+static resource_size_t sh7751_align_resource(struct pci_dev *dev,
+                                            const struct resource *res,
+                                            resource_size_t start,
+                                            resource_size_t size,
+                                            resource_size_t align)
+{
+       if (res->flags & IORESOURCE_IO) {
+               if (start < PCIBIOS_MIN_IO + 0x1000)
+                       start = PCIBIOS_MIN_IO + 0x1000;
+
+               /*
+                * Put everything into 0x00-0xff region modulo 0x400.
+                */
+               if (start & 0x300)
+                       start = (start + 0x3ff) & ~0x3ff;
+       }
+
+       return start;
+}
+
+static void __init set_pci_bcr(void __iomem *pci_reg_base,
+                              void __iomem *bcr,
+                              unsigned int area)
+{
+       unsigned long word;
+
+       word = ioread32(bcr + SH7751_BCR1);
+       /* check BCR for SDRAM in area */
+       if (((word >> area) & 1) == 0) {
+               pr_info("PCI: Area %d is not configured for SDRAM. 
BCR1=0x%lx\n",
+                       area, word);
+               return;
+       }
+       pcic_writel(word, SH4_PCIBCR1);
+
+       word = ioread16(bcr + SH7751_BCR2);
+       /* check BCR2 for 32bit SDRAM interface*/
+       if (((word >> (area << 1)) & 0x3) != 0x3) {
+               pr_info("PCI: Area %d is not 32 bit SDRAM. BCR2=0x%lx\n",
+                       area, word);
+               return;
+       }
+       pcic_writel(word, SH4_PCIBCR2);
+}
+
+static __init int sh7751_cfg_init(struct device *dev,
+                                 struct pci_config_window *cfg)
+{
+       cfg->priv = sh7751_align_resource;
+       return 0;
+}
+
+static struct pci_ecam_ops ecm_ops __initdata = {
+       .init   = sh7751_cfg_init,
+       .pci_ops = {
+               .read   = pci_generic_config_read32,
+               .write  = pci_generic_config_write32,
+               .map_bus        = sh7751_map_bus,
+       }
+};
+
+static __init int sh7751_pci_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       u32 id;
+       u32 reg, word;
+       void __iomem *pci_reg_base;
+       void __iomem *bcr;
+       const struct of_device_id *match;
+       void (*fixup_fn)(void __iomem *pci_reg_base, void __iomem *bcr);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       pci_reg_base = ioremap(res->start, resource_size(res));
+       if (IS_ERR(pci_reg_base))
+               return PTR_ERR(pci_reg_base);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       bcr = ioremap(res->start, resource_size(res));
+       if (IS_ERR(bcr))
+               return PTR_ERR(bcr);
+
+       /* check for SH7751/SH7751R hardware */
+       id = pcic_readl(PCI_VENDOR_ID);
+       if (id != ((SH7751_DEVICE_ID << 16) | SH7751_VENDOR_ID) &&
+           id != ((SH7751R_DEVICE_ID << 16) | SH7751_VENDOR_ID)) {
+               pr_warn("PCI: This is not an SH7751(R)\n");
+               return -ENODEV;
+       }
+       dev_info(&pdev->dev, "PCI core found at %pR\n",
+               pci_reg_base);
+
+       /* Set the BCRs to enable PCI access */
+       reg = ioread32(bcr);
+       reg |= 0x80000;
+       iowrite32(reg, bcr);
+
+       /* Turn the clocks back on (not done in reset)*/
+       pcic_writel(0, SH4_PCICLKR);
+       /* Clear Powerdown IRQs (not done in reset) */
+       word = SH4_PCIPINT_D3 | SH4_PCIPINT_D0;
+       pcic_writel(word, SH4_PCIPINT);
+
+       /* set the command/status bits to:
+        * Wait Cycle Control + Parity Enable + Bus Master +
+        * Mem space enable
+        */
+       word = PCI_COMMAND_WAIT | PCI_COMMAND_PARITY |
+              PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+       pcic_writel(word, PCI_COMMAND);
+
+       /* define this host as the host bridge */
+       word = PCI_BASE_CLASS_BRIDGE << 24;
+       pcic_writel(word, PCI_CLASS_REVISION);
+
+       /* Set IO and Mem windows to local address
+        * Make PCI and local address the same for easy 1 to 1 mapping
+        */
+       word = memory_end - memory_start - 1;
+       pcic_writel(word, SH4_PCILSR0);
+       /* Set the values on window 0 PCI config registers */
+       word = P2SEGADDR(__pa(memory_start));
+       pcic_writel(word, SH4_PCILAR0);
+       pcic_writel(word, PCI_BASE_ADDRESS_1);
+
+       set_pci_bcr(pci_reg_base, bcr, (__pa(memory_start) >> 27) & 0x07);
+
+       /* configure the wait control registers */
+       word = ioread32(bcr + SH7751_WCR1);
+       pcic_writel(word, SH4_PCIWCR1);
+       word = ioread32(bcr + SH7751_WCR2);
+       pcic_writel(word, SH4_PCIWCR2);
+       word = ioread32(bcr + SH7751_WCR3);
+       pcic_writel(word, SH4_PCIWCR3);
+       word = ioread32(bcr + SH7751_MCR);
+       pcic_writel(word, SH4_PCIMCR);
+
+       match = of_match_node(fixup_of_match, pdev->dev.of_node);
+       if (match) {
+               fixup_fn = match->data;
+               fixup_fn(pci_reg_base, bcr);
+       }
+       /*
+        * SH7751 init done, set central function init complete
+        * use round robin mode to stop a device starving/overruning
+        */
+       word = SH4_PCICR_PREFIX | SH4_PCICR_CFIN | SH4_PCICR_ARBM;
+       pcic_writel(word, SH4_PCICR);
+
+       return pci_host_common_probe(pdev, &ecm_ops);
+}
+
+static __refdata struct platform_driver sh7751_pci_driver = {
+       .driver = {
+               .name = "sh7751-pci",
+               .of_match_table = sh7751_pci_of_match,
+       },
+       .probe = sh7751_pci_probe,
+};
+builtin_platform_driver(sh7751_pci_driver);
-- 
2.7.0

Reply via email to