diff options
author | kongzizaixian <xweikong@hotmail.com> | 2015-11-06 17:24:58 +0800 |
---|---|---|
committer | kongzizaixian <xweikong@hotmail.com> | 2015-11-06 17:24:58 +0800 |
commit | 6243f5478c8af97c4e1d7cc897ab7c2b018b2620 (patch) | |
tree | 81771ff4afb2be6b18fa7b3ef5b1051154b142d0 | |
parent | 25283cb218908a3cddbfcf695e4d2a89d92f9b7b (diff) | |
parent | 941ad0c06f826d6a49276312c7a36d935e41027d (diff) |
Merge pull request #8 from wangzhou/pcie_for_kongxinwei_estuary_v2.1
Pcie for kongxinwei estuary v2.1
-rw-r--r-- | Documentation/devicetree/bindings/pci/hisilicon-pcie.txt | 49 | ||||
-rw-r--r-- | arch/arm64/boot/dts/hisilicon/hip05-d02.dts | 1 | ||||
-rw-r--r-- | arch/arm64/boot/dts/hisilicon/hisi_p660_pcie_d02.dtsi | 37 | ||||
-rw-r--r-- | drivers/pci/host/Kconfig | 7 | ||||
-rw-r--r-- | drivers/pci/host/Makefile | 2 | ||||
-rw-r--r-- | drivers/pci/host/pci-dra7xx.c | 13 | ||||
-rw-r--r-- | drivers/pci/host/pci-keystone-dw.c | 2 | ||||
-rw-r--r-- | drivers/pci/host/pcie-designware.c | 404 | ||||
-rw-r--r-- | drivers/pci/host/pcie-designware.h | 15 | ||||
-rw-r--r-- | drivers/pci/host/pcie-hisi-hip05.c | 224 | ||||
-rw-r--r-- | drivers/pci/host/pcie-hisi-hip06.c | 175 | ||||
-rw-r--r-- | drivers/pci/host/pcie-hisi.c | 809 | ||||
-rw-r--r-- | drivers/pci/host/pcie-hisi.h | 193 |
13 files changed, 1164 insertions, 767 deletions
diff --git a/Documentation/devicetree/bindings/pci/hisilicon-pcie.txt b/Documentation/devicetree/bindings/pci/hisilicon-pcie.txt new file mode 100644 index 000000000000..789be59219a4 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/hisilicon-pcie.txt @@ -0,0 +1,49 @@ +HiSilicon PCIe host bridge DT description + +HiSilicon PCIe host controller is based on Designware PCI core. +It shares common functions with PCIe Designware core driver and inherits +common properties defined in +Documentation/devicetree/bindings/pci/designware-pci.txt. + +Additional properties are described here: + +Required properties: +- compatible: Should contain "hisilicon,hip05-pcie". +- reg: Should contain rc_dbi, subctrl, config registers location and length. +- reg-names: Must include the following entries: + "rc_dbi": controller configuration registers; + "subctrl": whole PCIe hosts configuration registers; + "pcs": pcs configuration registers; + "config": PCIe configuration space registers; + "serdes": serdes configuration registers. +- msi-parent: Should be its_pcie which is an ITS receiving MSI interrupts. +- port-id: Should be 0, 1, 2 or 3. + +Optional properties: +- status: Either "ok" or "disabled". +- dma-coherent: Present if DMA operations are coherent. + +Example: + pcie@0xb0080000 { + compatible = "hisilicon,hip05-pcie", "snps,dw-pcie"; + reg = <0 0xb0080000 0 0x10000>, <0 0xb0000000 0 0x10000>, + <0 0xb00d0000 0 0x10000>, <0x220 0x00000000 0 0x2000>, + <0 0xb2000000 0 0x40000>; + reg-names = "rc_dbi", "subctrl", "pcs", "config", "serdes"; + bus-range = <0 15>; + msi-parent = <&its_pcie>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + dma-coherent; + ranges = <0x82000000 0 0x00000000 0x220 0x00000000 0 0x10000000>; + num-lanes = <8>; + port-id = <1>; + #interrupts-cells = <1>; + interrupts-map-mask = <0xf800 0 0 7>; + interrupts-map = <0x0 0 0 1 &mbigen_pcie 1 10 + 0x0 0 0 2 &mbigen_pcie 2 11 + 0x0 0 0 3 &mbigen_pcie 3 12 + 0x0 0 0 4 &mbigen_pcie 4 13>; + status = "ok"; + }; diff --git a/arch/arm64/boot/dts/hisilicon/hip05-d02.dts b/arch/arm64/boot/dts/hisilicon/hip05-d02.dts index e69ab395d02b..7a8d4745e5a0 100644 --- a/arch/arm64/boot/dts/hisilicon/hip05-d02.dts +++ b/arch/arm64/boot/dts/hisilicon/hip05-d02.dts @@ -12,6 +12,7 @@ /dts-v1/; /include/ "hip05.dtsi" +/include/ "hisi_p660_pcie_d02.dtsi" / { model = "Hisilicon PhosphorV660 D02 Development Board"; diff --git a/arch/arm64/boot/dts/hisilicon/hisi_p660_pcie_d02.dtsi b/arch/arm64/boot/dts/hisilicon/hisi_p660_pcie_d02.dtsi new file mode 100644 index 000000000000..ae86082ddedb --- /dev/null +++ b/arch/arm64/boot/dts/hisilicon/hisi_p660_pcie_d02.dtsi @@ -0,0 +1,37 @@ +/ { + pcie@0xb0080000 { + compatible = "hisilicon,hip05-pcie", "snps,dw-pcie"; + reg = <0 0xb0080000 0 0x10000>, <0 0xb0000000 0 0x10000>, + <0 0xb00d0000 0 0x10000>, <0x220 0x00000000 0 0x2000>, + <0 0xb2000000 0 0x40000>; + reg-names = "rc_dbi", "subctrl", "pcs", "config", "serdes"; + bus-range = <0 127>; + msi-parent = <&its_pcie>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + dma-coherent; + ranges = <0x02000000 0 0xb0000000 0x220 0x00000000 0 0x10000000>; + num-lanes = <8>; + port-id = <1>; + status = "ok"; + }; + + pcie@0xb0090000 { + compatible = "hisilicon,hip05-pcie", "snps,dw-pcie"; + reg = <0 0xb0090000 0 0x10000>, <0 0xb0000000 0 0x10000>, + <0 0xb00e0000 0 0x10000>, <0x240 0x00000000 0 0x2000>, + <0 0xb2100000 0 0x40000>; + reg-names = "rc_dbi", "subctrl", "pcs", "config", "serdes"; + bus-range = <128 255>; + msi-parent = <&its_pcie>; + #address-cells = <3>; + #size-cells = <2>; + device_type = "pci"; + dma-coherent; + ranges = <0x02000000 0 0xb0000000 0x240 0x00000000 0 0x10000000>; + num-lanes = <8>; + port-id = <2>; + status = "ok"; + }; +}; diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index dddae320963a..e45fbaf57c33 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -126,8 +126,11 @@ config PCIE_IPROC_PLATFORM through the generic platform bus interface config PCI_HISI - depends on OF - bool "Hisilicon Soc HIP05 PCIe controller" + depends on OF && ARM64 + bool "HiSilicon SoC HIP05 PCIe controller" + select PCIEPORTBUS select PCIE_DW + help + Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 562142eadba7..1eae2fc156ec 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -15,4 +15,4 @@ obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o -obj-$(CONFIG_PCI_HISI) += pcie-hisi.o +obj-$(CONFIG_PCI_HISI) += pcie-hisi.o pcie-hisi-hip05.o pcie-hisi-hip06.o diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c index 2d57e19a2cd4..c50dbe936720 100644 --- a/drivers/pci/host/pci-dra7xx.c +++ b/drivers/pci/host/pci-dra7xx.c @@ -61,6 +61,7 @@ #define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C #define LINK_UP BIT(16) +#define CPU_TO_BUS_ADDR 0x0FFFFFFF struct dra7xx_pcie { void __iomem *base; @@ -144,6 +145,18 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) static void dra7xx_pcie_host_init(struct pcie_port *pp) { dw_pcie_setup_rc(pp); + + if (pp->io_base) + pp->io_base &= CPU_TO_BUS_ADDR; + + if (pp->mem_base) + pp->mem_base &= CPU_TO_BUS_ADDR; + + if (pp->cfg0_base) { + pp->cfg0_base &= CPU_TO_BUS_ADDR; + pp->cfg1_base &= CPU_TO_BUS_ADDR; + } + dra7xx_pcie_establish_link(pp); if (IS_ENABLED(CONFIG_PCI_MSI)) dw_pcie_msi_init(pp); diff --git a/drivers/pci/host/pci-keystone-dw.c b/drivers/pci/host/pci-keystone-dw.c index f34892e0edb4..b1e4135c8f1f 100644 --- a/drivers/pci/host/pci-keystone-dw.c +++ b/drivers/pci/host/pci-keystone-dw.c @@ -327,7 +327,7 @@ static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt) void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie) { struct pcie_port *pp = &ks_pcie->pp; - u32 start = pp->mem.start, end = pp->mem.end; + u32 start = pp->mem->start, end = pp->mem->end; int i, tr_size; /* Disable BARs for inbound access */ diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index 43e5ad35a11a..e2d1898ca8a7 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -11,6 +11,7 @@ * published by the Free Software Foundation. */ +#include <linux/hardirq.h> #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> @@ -22,7 +23,6 @@ #include <linux/pci_regs.h> #include <linux/platform_device.h> #include <linux/types.h> -#include <asm/hardirq.h> #include "pcie-designware.h" @@ -70,22 +70,8 @@ #define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) #define PCIE_ATU_UPPER_TARGET 0x91C -static struct hw_pci dw_pci; static struct pci_ops dw_pcie_ops; -static unsigned long global_io_offset; - -static inline struct pcie_port *sys_to_pcie(void *sys) -{ -#ifdef CONFIG_ARM - pci_sys_data *sys_data = (struct pci_sys_data *)sys; - - BUG_ON(!sys->private_data); - return sys_data->private_data; -#endif - return (struct pcie_port *)sys; -} - int dw_pcie_cfg_read(void __iomem *addr, int where, int size, u32 *val) { *val = readl(addr); @@ -158,6 +144,21 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return ret; } +static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index, + int type, u64 cpu_addr, u64 pci_addr, u32 size) +{ + dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index, + PCIE_ATU_VIEWPORT); + dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE); + dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE); + dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1), + PCIE_ATU_LIMIT); + dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET); + dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET); + dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1); + dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); +} + static struct irq_chip dw_msi_irq_chip = { .name = "PCI-MSI", .irq_enable = pci_msi_unmask_irq, @@ -246,7 +247,7 @@ static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq) static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int irq, pos0, i; - struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); + struct pcie_port *pp = desc->dev->bus->sysdata; pos0 = bitmap_find_free_region(pp->msi_irq_in_use, MAX_MSI_IRQS, order_base_2(no_irqs)); @@ -289,7 +290,7 @@ static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, { int irq, pos; struct msi_msg msg; - struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); + struct pcie_port *pp = pdev->bus->sysdata; if (desc->msi_attrib.is_msix) return -EINVAL; @@ -318,7 +319,7 @@ static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq) { struct irq_data *data = irq_get_irq_data(irq); struct msi_desc *msi = irq_data_get_msi(data); - struct pcie_port *pp = sys_to_pcie(msi->dev->bus->sysdata); + struct pcie_port *pp = msi->dev->bus->sysdata; clear_irq_range(pp, irq, 1, data->hwirq); } @@ -354,18 +355,12 @@ int dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; struct platform_device *pdev = to_platform_device(pp->dev); - struct of_pci_range range; - struct of_pci_range_parser parser; + struct pci_bus *bus; struct resource *cfg_res; - u32 val, na, ns; - const __be32 *addrp; - int i, index, ret; - - /* Find the address cell size and the number of cells in order to get - * the untranslated address. - */ - of_property_read_u32(np, "#address-cells", &na); - ns = of_n_size_cells(np); + LIST_HEAD(res); + u32 val; + int i, ret; + struct resource_entry *win; cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (cfg_res) { @@ -373,88 +368,60 @@ int dw_pcie_host_init(struct pcie_port *pp) pp->cfg1_size = resource_size(cfg_res)/2; pp->cfg0_base = cfg_res->start; pp->cfg1_base = cfg_res->start + pp->cfg0_size; - - /* Find the untranslated configuration space address */ - index = of_property_match_string(np, "reg-names", "config"); - addrp = of_get_address(np, index, NULL, NULL); - pp->cfg0_mod_base = of_read_number(addrp, ns); - pp->cfg1_mod_base = pp->cfg0_mod_base + pp->cfg0_size; } else { dev_err(pp->dev, "missing *config* reg space\n"); } - if (of_pci_range_parser_init(&parser, np)) { - dev_err(pp->dev, "missing ranges property\n"); - return -EINVAL; - } + ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base); + if (ret) + return ret; /* Get the I/O and memory ranges from DT */ - for_each_of_pci_range(&parser, &range) { - unsigned long restype = range.flags & IORESOURCE_TYPE_BITS; - - if (restype == IORESOURCE_IO) { - of_pci_range_to_resource(&range, np, &pp->io); - pp->io.name = "I/O"; - pp->io.start = max_t(resource_size_t, - PCIBIOS_MIN_IO, - range.pci_addr + global_io_offset); - pp->io.end = min_t(resource_size_t, - IO_SPACE_LIMIT, - range.pci_addr + range.size - + global_io_offset - 1); - pp->io_size = resource_size(&pp->io); - pp->io_bus_addr = range.pci_addr; - pp->io_base = range.cpu_addr; - - /* Find the untranslated IO space address */ - pp->io_mod_base = of_read_number(parser.range - - parser.np + na, ns); - } - if (restype == IORESOURCE_MEM) { - of_pci_range_to_resource(&range, np, &pp->mem); - pp->mem.name = "MEM"; - pp->mem_size = resource_size(&pp->mem); - pp->mem_bus_addr = range.pci_addr; - - /* Find the untranslated MEM space address */ - pp->mem_mod_base = of_read_number(parser.range - - parser.np + na, ns); - } - if (restype == 0) { - of_pci_range_to_resource(&range, np, &pp->cfg); - pp->cfg0_size = resource_size(&pp->cfg)/2; - pp->cfg1_size = resource_size(&pp->cfg)/2; - pp->cfg0_base = pp->cfg.start; - pp->cfg1_base = pp->cfg.start + pp->cfg0_size; - - /* Find the untranslated configuration space address */ - pp->cfg0_mod_base = of_read_number(parser.range - - parser.np + na, ns); - pp->cfg1_mod_base = pp->cfg0_mod_base + - pp->cfg0_size; + resource_list_for_each_entry(win, &res) { + switch (resource_type(win->res)) { + case IORESOURCE_IO: + pp->io = win->res; + pp->io->name = "I/O"; + pp->io_size = resource_size(pp->io); + pp->io_bus_addr = pp->io->start - win->offset; + ret = pci_remap_iospace(pp->io, pp->io_base); + if (ret) { + dev_warn(pp->dev, "error %d: failed to map resource %pR\n", + ret, pp->io); + continue; + } + break; + case IORESOURCE_MEM: + pp->mem = win->res; + pp->mem->name = "MEM"; + pp->mem_size = resource_size(pp->mem); + pp->mem_bus_addr = pp->mem->start - win->offset; + break; + case 0: + pp->cfg = win->res; + pp->cfg0_size = resource_size(pp->cfg)/2; + pp->cfg1_size = resource_size(pp->cfg)/2; + pp->cfg0_base = pp->cfg->start; + pp->cfg1_base = pp->cfg->start + pp->cfg0_size; + break; + case IORESOURCE_BUS: + pp->busn = win->res; + break; + default: + continue; } } - ret = of_pci_parse_bus_range(np, &pp->busn); - if (ret < 0) { - pp->busn.name = np->name; - pp->busn.start = 0; - pp->busn.end = 0xff; - pp->busn.flags = IORESOURCE_BUS; - dev_dbg(pp->dev, "failed to parse bus-range property: %d, using default %pR\n", - ret, &pp->busn); - } - if (!pp->dbi_base) { - pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start, - resource_size(&pp->cfg)); + pp->dbi_base = devm_ioremap(pp->dev, pp->cfg->start, + resource_size(pp->cfg)); if (!pp->dbi_base) { dev_err(pp->dev, "error with ioremap\n"); return -ENOMEM; } } - pp->mem_base = pp->mem.start; + pp->mem_base = pp->mem->start; if (!pp->va_cfg0_base) { pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base, @@ -501,169 +468,115 @@ int dw_pcie_host_init(struct pcie_port *pp) if (pp->ops->host_init) pp->ops->host_init(pp); + if (!pp->ops->rd_other_conf) + dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_MEM, pp->mem_base, + pp->mem_bus_addr, pp->mem_size); + dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0); /* program correct class for RC */ dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI); -#ifdef CONFIG_ARM64 - struct pci_bus *bus; - struct msi_controller *msi; - LIST_HEAD(res); - - ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &pp->io_base); - if (ret) - return ret; + dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val); + val |= PORT_LOGIC_SPEED_CHANGE; + dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val); + pp->root_bus_nr = pp->busn->start; bus = pci_create_root_bus(pp->dev, pp->root_bus_nr, &dw_pcie_ops, pp, &res); if (!bus) return -ENOMEM; - /* add msi_controller to root pci_bus */ - msi = kzalloc(sizeof(*msi), GFP_KERNEL); - if (!msi) - return -ENOMEM; - msi->domain = pp->domain; - bus->msi = msi; +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN + bus->msi = container_of(&pp->irq_domain, struct msi_controller, domain); +#else + dw_pcie_msi_chip.dev = pp->dev; + bus->msi = &dw_pcie_msi_chip; +#endif pci_scan_child_bus(bus); - pci_assign_unassigned_bus_resources(bus); - pci_bus_add_devices(bus); - - /* Configure PCI Express settings */ - if (!pci_has_flag(PCI_PROBE_ONLY)) { - struct pci_bus *child; - - list_for_each_entry(child, &bus->children, node) - pcie_bus_configure_settings(child); - } -#endif + if (pp->ops->scan_bus) + pp->ops->scan_bus(pp); #ifdef CONFIG_ARM -#ifdef CONFIG_PCI_MSI - dw_pcie_msi_chip.dev = pp->dev; - dw_pci.msi_ctrl = &dw_pcie_msi_chip; + /* support old dtbs that incorrectly describe IRQs */ + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); #endif - dw_pci.nr_controllers = 1; - dw_pci.private_data = (void **)&pp; - - pci_common_init_dev(pp->dev, &dw_pci); -#endif + pci_assign_unassigned_bus_resources(bus); + pci_bus_add_devices(bus); return 0; } -static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev) -{ - /* Program viewport 0 : OUTBOUND : CFG0 */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, pp->cfg0_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg0_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg0_mod_base + pp->cfg0_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -} - -static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev) -{ - /* Program viewport 1 : OUTBOUND : CFG1 */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->cfg1_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->cfg1_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->cfg1_mod_base + pp->cfg1_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -} - -static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp) -{ - /* Program viewport 0 : OUTBOUND : MEM */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->mem_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->mem_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->mem_mod_base + pp->mem_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->mem_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->mem_bus_addr), - PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -} - -static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp) -{ - /* Program viewport 1 : OUTBOUND : IO */ - dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, - PCIE_ATU_VIEWPORT); - dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1); - dw_pcie_writel_rc(pp, pp->io_mod_base, PCIE_ATU_LOWER_BASE); - dw_pcie_writel_rc(pp, (pp->io_mod_base >> 32), PCIE_ATU_UPPER_BASE); - dw_pcie_writel_rc(pp, pp->io_mod_base + pp->io_size - 1, - PCIE_ATU_LIMIT); - dw_pcie_writel_rc(pp, pp->io_bus_addr, PCIE_ATU_LOWER_TARGET); - dw_pcie_writel_rc(pp, upper_32_bits(pp->io_bus_addr), - PCIE_ATU_UPPER_TARGET); - dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); -} - static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, u32 devfn, int where, int size, u32 *val) { - int ret = PCIBIOS_SUCCESSFUL; - u32 address, busdev; + int ret, type; + u32 address, busdev, cfg_size; + u64 cpu_addr; + void __iomem *va_cfg_base; busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | PCIE_ATU_FUNC(PCI_FUNC(devfn)); address = where & ~0x3; if (bus->parent->number == pp->root_bus_nr) { - dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = dw_pcie_cfg_read(pp->va_cfg0_base + address, where, size, - val); - dw_pcie_prog_viewport_mem_outbound(pp); + type = PCIE_ATU_TYPE_CFG0; + cpu_addr = pp->cfg0_base; + cfg_size = pp->cfg0_size; + va_cfg_base = pp->va_cfg0_base; } else { - dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = dw_pcie_cfg_read(pp->va_cfg1_base + address, where, size, - val); - dw_pcie_prog_viewport_io_outbound(pp); + type = PCIE_ATU_TYPE_CFG1; + cpu_addr = pp->cfg1_base; + cfg_size = pp->cfg1_size; + va_cfg_base = pp->va_cfg1_base; } + dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, + type, cpu_addr, + busdev, cfg_size); + ret = dw_pcie_cfg_read(va_cfg_base + address, where, size, val); + dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); + return ret; } static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { - int ret = PCIBIOS_SUCCESSFUL; - u32 address, busdev; + int ret, type; + u32 address, busdev, cfg_size; + u64 cpu_addr; + void __iomem *va_cfg_base; busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) | PCIE_ATU_FUNC(PCI_FUNC(devfn)); address = where & ~0x3; if (bus->parent->number == pp->root_bus_nr) { - dw_pcie_prog_viewport_cfg0(pp, busdev); - ret = dw_pcie_cfg_write(pp->va_cfg0_base + address, where, size, - val); - dw_pcie_prog_viewport_mem_outbound(pp); + type = PCIE_ATU_TYPE_CFG0; + cpu_addr = pp->cfg0_base; + cfg_size = pp->cfg0_size; + va_cfg_base = pp->va_cfg0_base; } else { - dw_pcie_prog_viewport_cfg1(pp, busdev); - ret = dw_pcie_cfg_write(pp->va_cfg1_base + address, where, size, - val); - dw_pcie_prog_viewport_io_outbound(pp); + type = PCIE_ATU_TYPE_CFG1; + cpu_addr = pp->cfg1_base; + cfg_size = pp->cfg1_size; + va_cfg_base = pp->va_cfg1_base; } + dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, + type, cpu_addr, + busdev, cfg_size); + ret = dw_pcie_cfg_write(va_cfg_base + address, where, size, val); + dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); + return ret; } @@ -693,7 +606,7 @@ static int dw_pcie_valid_config(struct pcie_port *pp, static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 *val) { - struct pcie_port *pp = sys_to_pcie(bus->sysdata); + struct pcie_port *pp = bus->sysdata; int ret; if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) { @@ -717,7 +630,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn, int where, int size, u32 val) { - struct pcie_port *pp = sys_to_pcie(bus->sysdata); + struct pcie_port *pp = bus->sysdata; int ret; if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) @@ -741,66 +654,6 @@ static struct pci_ops dw_pcie_ops = { .write = dw_pcie_wr_conf, }; -#ifdef CONFIG_ARM -static int dw_pcie_setup(int nr, struct pci_sys_data *sys) -{ - struct pcie_port *pp; - - pp = sys_to_pcie(sys); - - if (global_io_offset < SZ_1M && pp->io_size > 0) { - sys->io_offset = global_io_offset - pp->io_bus_addr; - pci_ioremap_io(global_io_offset, pp->io_base); - global_io_offset += SZ_64K; - pci_add_resource_offset(&sys->resources, &pp->io, - sys->io_offset); - } - - sys->mem_offset = pp->mem.start - pp->mem_bus_addr; - pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset); - pci_add_resource(&sys->resources, &pp->busn); - - return 1; -} - -static struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys) -{ - struct pci_bus *bus; - struct pcie_port *pp = sys_to_pcie(sys); - - pp->root_bus_nr = sys->busnr; - bus = pci_create_root_bus(pp->dev, sys->busnr, - &dw_pcie_ops, sys, &sys->resources); - if (!bus) - return NULL; - - pci_scan_child_bus(bus); - - if (bus && pp->ops->scan_bus) - pp->ops->scan_bus(pp); - - return bus; -} - -static int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata); - int irq; - - irq = of_irq_parse_and_map_pci(dev, slot, pin); - if (!irq) - irq = pp->irq; - - return irq; -} - -static struct hw_pci dw_pci = { - .setup = dw_pcie_setup, - .scan = dw_pcie_scan_bus, - .map_irq = dw_pcie_map_irq, -}; -#endif - void dw_pcie_setup_rc(struct pcie_port *pp) { u32 val; @@ -843,13 +696,8 @@ void dw_pcie_setup_rc(struct pcie_port *pp) val |= PORT_LOGIC_LINK_WIDTH_8_LANES; break; } - - /* set the Directed Speed Change field of the - Link Width and Speed Change Control register */ - val |= PORT_LOGIC_SPEED_CHANGE; - dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL); - + /* setup RC BARs */ dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0); dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1); diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h index e37b6dc01b10..264c969eaea1 100644 --- a/drivers/pci/host/pcie-designware.h +++ b/drivers/pci/host/pcie-designware.h @@ -27,31 +27,26 @@ struct pcie_port { u8 root_bus_nr; void __iomem *dbi_base; u64 cfg0_base; - u64 cfg0_mod_base; void __iomem *va_cfg0_base; u32 cfg0_size; u64 cfg1_base; - u64 cfg1_mod_base; void __iomem *va_cfg1_base; u32 cfg1_size; - u64 io_base; - u64 io_mod_base; + resource_size_t io_base; phys_addr_t io_bus_addr; u32 io_size; u64 mem_base; - u64 mem_mod_base; phys_addr_t mem_bus_addr; u32 mem_size; - struct resource cfg; - struct resource io; - struct resource mem; - struct resource busn; + struct resource *cfg; + struct resource *io; + struct resource *mem; + struct resource *busn; int irq; u32 lanes; struct pcie_host_ops *ops; int msi_irq; struct irq_domain *irq_domain; - struct irq_domain *domain; unsigned long msi_data; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; diff --git a/drivers/pci/host/pcie-hisi-hip05.c b/drivers/pci/host/pcie-hisi-hip05.c new file mode 100644 index 000000000000..c25bc29a2b32 --- /dev/null +++ b/drivers/pci/host/pcie-hisi-hip05.c @@ -0,0 +1,224 @@ +/* + * PCIe host controller driver for HiSilicon Hip05 SoC + * + * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com + * + * Authors: Zhou Wang <wangzhou1@hisilicon.com> + * Gabriele Paoloni <gabriele.paoloni@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/irqreturn.h> +#include "pcie-designware.h" +#include "pcie-hisi.h" + +#define HIP05_PCIE_CLK_CTRL 0x3 +#define HIP05_PCS_SERDES_STATUS 0x8108 +#define HIP05_PCIE_CORE_RESET 0x1 + +/*Check if the link is up*/ +static int hisi_pcie_link_up_hip05(struct hisi_pcie *pcie) +{ + u32 val; + + val = hisi_pcie_subctrl_readl(pcie, + PCIE_SUBCTRL_SYS_STATE4_REG + 0x100 * pcie->port_id); + + return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); +} + +/* Configure vmid/asid table in PCIe host */ +static void hisi_pcie_config_context_hip05(struct hisi_pcie *pcie) +{ + int i; + u32 val; + + /* + * enable to clean vmid and asid tables though apb bus + * */ + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_SYSCTRL_MODE); + + val = hisi_pcie_apb_readl(pcie, PCIE_SYS_CTRL20_REG); + /* enable ar channel */ + val |= PCIE_RD_TAB_SEL | PCIE_RD_TAB_EN; + hisi_pcie_apb_writel(pcie, val, PCIE_SYS_CTRL20_REG); + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_CONTENT_MODE); + for (i = 0; i < 0x800; i++) + hisi_pcie_apb_writel(pcie, 0x0, i * 4); + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_SYSCTRL_MODE); + /* enable aw channel */ + val &= (~PCIE_RD_TAB_SEL); + val |= PCIE_RD_TAB_EN; + hisi_pcie_apb_writel(pcie, val, PCIE_SYS_CTRL20_REG); + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_CONTENT_MODE); + + /* + * init vmid and asid tables for all PCIe devices as 0 + * vmid table: 0 ~ 0x3ff, asid table: 0x400 ~ 0x7ff + */ + for (i = 0; i < 0x800; i++) + hisi_pcie_apb_writel(pcie, 0x0, i * 4); + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_SYSCTRL_MODE); + + val = hisi_pcie_apb_readl(pcie, PCIE_SYS_CTRL20_REG); + /* disable ar channel */ + val |= PCIE_RD_TAB_SEL; + val &= (~PCIE_RD_TAB_EN); + hisi_pcie_apb_writel(pcie, val, PCIE_SYS_CTRL20_REG); + /* disable aw channel */ + val &= ((~PCIE_RD_TAB_SEL) & (~PCIE_RD_TAB_EN)); + hisi_pcie_apb_writel(pcie, val, PCIE_SYS_CTRL20_REG); + + hisi_pcie_apb_writel(pcie, pcie->msi_addr & 0xffffffff, PCIE_MSI_LOW_ADDRESS); + hisi_pcie_apb_writel(pcie, pcie->msi_addr >> 32, PCIE_MSI_HIGH_ADDRESS); + hisi_pcie_apb_writel(pcie, PCIE_MSI_ASID_ENABLE | PCIE_MSI_ASID_VALUE, + PCIE_SLV_MSI_ASID); + hisi_pcie_apb_writel(pcie, PCIE_MSI_TRANS_ENABLE, PCIE_MSI_TRANS_REG); + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_DBI_MODE); +} + +static void hisi_pcie_enable_ltssm_hip05(struct hisi_pcie *pcie, bool on) +{ + u32 val; + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_SYSCTRL_MODE); + val = hisi_pcie_apb_readl(pcie, PCIE_CTRL_7_REG); + if (on) + val |= PCIE_LTSSM_ENABLE_SHIFT; + else + val &= ~(PCIE_LTSSM_ENABLE_SHIFT); + hisi_pcie_apb_writel(pcie, val, PCIE_CTRL_7_REG); + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_DBI_MODE); +} + +static void hisi_pcie_gen3_dfe_enable_hip05(struct hisi_pcie *pcie) +{ + u32 val; + u32 lane; + u32 port_id = pcie->port_id; + u32 current_speed; + + if (port_id == 3) + return; + val = hisi_pcie_subctrl_readl(pcie, + PCIE_SUBCTRL_SYS_STATE4_REG + + 0x100 * port_id); + current_speed = (val >> 6) & 0x3; + if (current_speed != PCIE_GEN3) + return; + for (lane = 0; lane < 8; lane++) + hisi_pcie_serdes_writel(pcie, + PCIE_DFE_ENABLE_VAL, DS_API(lane) + 4); + + dev_info(pcie->pp.dev, "enable DFE success!\n"); +} + +static void hisi_pcie_init_pcs_hip05(struct hisi_pcie *pcie) +{ + u32 i; + u32 lane_num = pcie->pp.lanes; + + if (pcie->port_id <= 2) { + hisi_pcie_serdes_writel(pcie, 0x212, 0xc088); + + hisi_pcie_pcs_writel(pcie, 0x2026044, 0x8020); + hisi_pcie_pcs_writel(pcie, 0x2126044, 0x8060); + hisi_pcie_pcs_writel(pcie, 0x2126044, 0x80c4); + hisi_pcie_pcs_writel(pcie, 0x2026044, 0x80e4); + hisi_pcie_pcs_writel(pcie, 0x4018, 0x80a0); + hisi_pcie_pcs_writel(pcie, 0x804018, 0x80a4); + hisi_pcie_pcs_writel(pcie, 0x11201100, 0x80c0); + hisi_pcie_pcs_writel(pcie, 0x3, 0x15c); + hisi_pcie_pcs_writel(pcie, 0x0, 0x158); + } else { + for (i = 0; i < lane_num; i++) + hisi_pcie_pcs_writel(pcie, 0x46e000, + PCIE_M_PCS_IN15_CFG + (i << 2)); + for (i = 0; i < lane_num; i++) + hisi_pcie_pcs_writel(pcie, 0x1001, + PCIE_M_PCS_IN13_CFG + (i << 2)); + + hisi_pcie_pcs_writel(pcie, 0xffff, PCIE_PCS_RXDETECTED); + } +} + +void pcie_equalization_hip05(struct hisi_pcie *pcie) +{ + u32 val; + + hisi_pcie_apb_writel(pcie, 0x1400, 0x890); + + pcie_equalization_common(pcie); + + val = hisi_pcie_apb_readl(pcie, 0x80); + val |= 0x80; + hisi_pcie_apb_writel(pcie, val, 0x80); + + hisi_pcie_apb_writel(pcie, 0x44444444, 0x184); + hisi_pcie_apb_writel(pcie, 0x44444444, 0x188); + hisi_pcie_apb_writel(pcie, 0x44444444, 0x18c); + hisi_pcie_apb_writel(pcie, 0x44444444, 0x190); +} + + +static void hisi_pcie_mode_set_hip05(struct hisi_pcie *pcie) +{ + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_SYSCTRL_MODE); + hisi_pcie_apb_writel(pcie, 0x4 << 28, PCIE_CORE_MODE_REG); + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_DBI_MODE); +} + +static struct pcie_soc_ops hip05_ops = { + &hisi_pcie_link_up_hip05, + &hisi_pcie_config_context_hip05, + &hisi_pcie_enable_ltssm_hip05, + &hisi_pcie_gen3_dfe_enable_hip05, + &hisi_pcie_init_pcs_hip05, + &pcie_equalization_hip05, + &hisi_pcie_mode_set_hip05, + NULL, + HIP05_PCIE_CORE_RESET, + HIP05_PCIE_CLK_CTRL, + HIP05_PCS_SERDES_STATUS, + PCIE_HOST_HIP05 +}; + +static const struct of_device_id hisi_pcie_of_match_hip05[] = { + { + .compatible = "hisilicon,hip05-pcie", + .data = (void *) &hip05_ops, + }, + {}, +}; + + +MODULE_DEVICE_TABLE(of, hisi_pcie_of_match); + +static struct platform_driver hisi_pcie_driver_hip05 = { + .driver = { + .name = "hisi-pcie-hip05", + .owner = THIS_MODULE, + .of_match_table = hisi_pcie_of_match_hip05, + }, +}; + +static int __init hisi_pcie_init_hip05(void) +{ + return platform_driver_probe(&hisi_pcie_driver_hip05, hisi_pcie_probe); +} +subsys_initcall(hisi_pcie_init_hip05); + +MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>"); +MODULE_AUTHOR("Dacai Zhu <zhudacai@hisilicon.com>"); +MODULE_AUTHOR("Gabriele Paoloni <gabriele.paoloni@huawei.com>"); +MODULE_LICENSE("GPL v2");
\ No newline at end of file diff --git a/drivers/pci/host/pcie-hisi-hip06.c b/drivers/pci/host/pcie-hisi-hip06.c new file mode 100644 index 000000000000..7deb2b4b3701 --- /dev/null +++ b/drivers/pci/host/pcie-hisi-hip06.c @@ -0,0 +1,175 @@ +/* + * PCIe host controller driver for HiSilicon Hip05 SoC + * + * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com + * + * Authors: Zhou Wang <wangzhou1@hisilicon.com> + * Gabriele Paoloni <gabriele.paoloni@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pci.h> +#include <linux/irqreturn.h> +#include "pcie-designware.h" +#include "pcie-hisi.h" + +#define HIP06_PCIE_CLK_CTRL 0x7 +#define HIP06_PCS_SERDES_STATUS 0x504 +#define HIP06_PCIE_CORE_RESET 0x3 + +#define PCIE_SYS_CTRL_13_REG 0x4 +#define PCIE_MST_BYPASS_SMMU_EN BIT(10) +#define PCIE_MST_AW_BYPASS_SMMU_EN BIT(12) +#define PCIE_MST_AR_BYPASS_SMMU_EN BIT(13) + +/*Check if the link is up*/ +static int hisi_pcie_link_up_hip06(struct hisi_pcie *pcie) +{ + u32 val; + + val = hisi_pcie_ctrl_readl(pcie, PCIE_SYS_STATE4); + + return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); +} + +/* Configure vmid/asid table in PCIe host */ +static void hisi_pcie_config_context_hip06(struct hisi_pcie *pcie) +{ + u32 val; + + hisi_pcie_ctrl_writel(pcie, pcie->msi_addr & 0xffffffff, PCIE_MSI_LOW_ADDRESS); + hisi_pcie_ctrl_writel(pcie, pcie->msi_addr >> 32, PCIE_MSI_HIGH_ADDRESS); + val = hisi_pcie_ctrl_readl(pcie, PCIE_MSI_TRANS_REG); + val |= PCIE_MSI_TRANS_ENABLE; + hisi_pcie_ctrl_writel(pcie, val, PCIE_MSI_TRANS_REG); +} + +static void hisi_pcie_enable_ltssm_hip06(struct hisi_pcie *pcie, bool on) +{ + u32 val; + + val = hisi_pcie_ctrl_readl(pcie, PCIE_CTRL_7_REG); + if (on) + /* In Hip06 host will fill all 1s in CFG cpu address space + * when host sends a read CFG TLP with wrong BDF if we set + * PCIE_RD_ERR_RESPONSE_EN as 1. And host will trigger a error + * interrupt when host sends a read/write CFG TLP with wrong + * BDF if we set PCIE_RD_ERR_RESPONSE_EN/PCIE_WR_ERR_RESPONSE_EN + * as 1 + */ + val |= PCIE_LTSSM_ENABLE_SHIFT | + PCIE_RD_ERR_RESPONSE_EN | + PCIE_WR_ERR_RESPONSE_EN; + else + val &= ~(PCIE_LTSSM_ENABLE_SHIFT); + hisi_pcie_ctrl_writel(pcie, val, PCIE_CTRL_7_REG); +} + +static void hisi_pcie_init_pcs_hip06(struct hisi_pcie *pcie) +{ + u32 i; + u32 lane_num = pcie->pp.lanes; + u32 val; + + for (i = 0; i < lane_num; i++) { + val = hisi_pcie_pcs_readl(pcie, 0x204 + i * 0x4); + val |= (1u << 20); + hisi_pcie_pcs_writel(pcie, val, 0x204 + i * 0x4); + } + hisi_pcie_pcs_writel(pcie, 0x4e20, 0x264); +} + +void pcie_equalization_hip06(struct hisi_pcie *pcie) +{ + u32 val; + + hisi_pcie_apb_writel(pcie, 0x1c00, 0x890); + + pcie_equalization_common(pcie); + + hisi_pcie_apb_writel(pcie, 0x44444444, 0x164); + hisi_pcie_apb_writel(pcie, 0x44444444, 0x168); + hisi_pcie_apb_writel(pcie, 0x44444444, 0x16c); + hisi_pcie_apb_writel(pcie, 0x44444444, 0x170); + + val = hisi_pcie_ctrl_readl(pcie, 0x2d0); + val &= (~0x3f); + val |= 0x5; + hisi_pcie_ctrl_writel(pcie, val, 0x2d0); +} + +static void hisi_pcie_mode_set_hip06(struct hisi_pcie *pcie) +{ + u32 val; + + hisi_pcie_ctrl_writel(pcie, 0x4 << 28, PCIE_CORE_MODE_REG); + + /* + * bypass SMMU. SMMU only allocates memory for stream ID 0, however, + * stream ID sent to SMMU by PCIe controller is BDF of PCIe device, + * which will bring error. + */ + val = hisi_pcie_ctrl_readl(pcie, PCIE_SYS_CTRL_13_REG); + val |= PCIE_MST_BYPASS_SMMU_EN | PCIE_MST_AW_BYPASS_SMMU_EN | + PCIE_MST_AR_BYPASS_SMMU_EN; + hisi_pcie_ctrl_writel(pcie, val, PCIE_SYS_CTRL_13_REG); +} + +static void hisi_pcie_portnum_set_hip06(struct hisi_pcie *pcie, u32 num) +{ + u32 val; + + val = hisi_pcie_ctrl_readl(pcie, PCIE_CTRL_19_REG); + val &= ~(0xff); + val |= num; + hisi_pcie_ctrl_writel(pcie, val, PCIE_CTRL_19_REG); +} + +static struct pcie_soc_ops hip06_ops = { + &hisi_pcie_link_up_hip06, + &hisi_pcie_config_context_hip06, + &hisi_pcie_enable_ltssm_hip06, + NULL, + &hisi_pcie_init_pcs_hip06, + &pcie_equalization_hip06, + &hisi_pcie_mode_set_hip06, + &hisi_pcie_portnum_set_hip06, + HIP06_PCIE_CORE_RESET, + HIP06_PCIE_CLK_CTRL, + HIP06_PCS_SERDES_STATUS, + PCIE_HOST_HIP06 +}; + +static const struct of_device_id hisi_pcie_of_match_hip06[] = { + { + .compatible = "hisilicon,hip06-pcie", + .data = (void *) &hip06_ops, + }, + {}, +}; + + +MODULE_DEVICE_TABLE(of, hisi_pcie_of_match); + +static struct platform_driver hisi_pcie_driver_hip06 = { + .driver = { + .name = "hisi-pcie-hip06", + .owner = THIS_MODULE, + .of_match_table = hisi_pcie_of_match_hip06, + }, +}; + +static int __init hisi_pcie_init_hip06(void) +{ + return platform_driver_probe(&hisi_pcie_driver_hip06, hisi_pcie_probe); +} +subsys_initcall(hisi_pcie_init_hip06); + +MODULE_AUTHOR("Zhou Wang <wangzhou1@hisilicon.com>"); +MODULE_AUTHOR("Dacai Zhu <zhudacai@hisilicon.com>"); +MODULE_AUTHOR("Gabriele Paoloni <gabriele.paoloni@huawei.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-hisi.c b/drivers/pci/host/pcie-hisi.c index 577f9faf9325..4c34977b0f00 100644 --- a/drivers/pci/host/pcie-hisi.c +++ b/drivers/pci/host/pcie-hisi.c @@ -1,212 +1,64 @@ /* - * PCIe host controller driver for Hisilicon Hip05 SoCs + * PCIe host controllers common functions for HiSilicon SoCs drivers * - * Copyright (C) 2014 Hisilicon Co., Ltd. http://www.hisilicon.com + * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com * - * Author: Zhou Wang <wangzhou1@hisilicon.com> - * Dacai Zhu <zhudacai@hisilicon.com> + * Authors: Zhou Wang <wangzhou1@hisilicon.com> + * Dacai Zhu <zhudacai@hisilicon.com> + * Gabriele Paoloni <gabriele.paoloni@huawei.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/delay.h> -#include <linux/gpio.h> #include <linux/interrupt.h> -#include <linux/kernel.h> +#include <linux/irqdomain.h> #include <linux/module.h> -#include <linux/of_gpio.h> +#include <linux/of.h> #include <linux/of_pci.h> -#include <linux/pci.h> #include <linux/platform_device.h> -#include <linux/resource.h> -#include <linux/signal.h> -#include <linux/types.h> +#include <linux/of_address.h> +#include <linux/of_device.h> #include "pcie-designware.h" +#include "pcie-hisi.h" -#define PCIE_SYS_CTRL20_REG (0x20) - -#define PCIE_SUBCTRL_RESET_REQ_REG (0xA00) -#define PCIE0_2_SUBCTRL_RESET_REQ_REG(port_id) \ - (PCIE_SUBCTRL_RESET_REQ_REG + (port_id << 3)) -#define PCIE3_SUBCTRL_RESET_REQ_REG (0xA68) - -#define PCIE_SUBCTRL_DRESET_REQ_REG (0xA04) -#define PCIE0_2_SUBCTRL_DRESET_REQ_REG(port_id) \ - (PCIE_SUBCTRL_DRESET_REQ_REG + (port_id << 3)) -#define PCIE3_SUBCTRL_DRESET_REQ_REG (0xA6C) - -#define PCIE_SUBCTRL_CLKREQ_REG (0x2800) - -#define PCIE_SUBCTRL_RESET_ST_REG (0x5A00) -#define PCIE0_2_SUBCTRL_RESET_ST_REG(port_id) \ - (PCIE_SUBCTRL_RESET_ST_REG + (port_id << 2)) -#define PCIE3_SUBCTRL_RESET_ST_REG (0x5A34) - -#define PCIE_SUBCTRL_SC_PCIE0_CLK_DIS_REG (0x304) -#define PCIE_SUBCTRL_SC_PCIE0_2_CLK_DIS_REG(port_id) \ - (PCIE_SUBCTRL_SC_PCIE0_CLK_DIS_REG + port_id * 0x8) -#define PCIE_SUBCTRL_SC_PCIE3_CLK_DIS_REG (0x324) - -#define PCIE_SUBCTRL_SC_PCIE0_CLK_ST_REG (0x5300) -#define PCIE_SUBCTRL_SC_PCIE0_2_CLK_ST_REG(port_id) \ - (PCIE_SUBCTRL_SC_PCIE0_CLK_ST_REG + port_id * 0x4) -#define PCIE_SUBCTRL_SC_PCIE3_CLK_ST_REG (0x5310) - -#define PCIE_SUBCTRL_SC_PCIE0_CLK_EN_REG (0x300) -#define PCIE_SUBCTRL_SC_PCIE0_2_CLK_EN_REG(port_id) \ - (PCIE_SUBCTRL_SC_PCIE0_CLK_EN_REG + port_id * 0x8) -#define PCIE_SUBCTRL_SC_PCIE3_CLK_EN_REG (0x320) - -#define PCIE_PCS_LOCAL_RESET_ST (0x5A60) - -#define PCIE_SUBCTRL_SYS_STATE4_REG (0x6818) - -#define PCIE_CTRL_7_REG (0x114) - -#define PCIE_SLV_DBI_ENABLE BIT(0) - -#define PCIE_SLV_DBI_MODE (0x0) -#define PCIE_SLV_SYSCTRL_MODE (0x1) -#define PCIE_SLV_CONTENT_MODE (0x2) - -#define PCIE_LTSSM_LINKUP_STATE (0x11) -#define PCIE_LTSSM_STATE_MASK (0x3F) -#define PCIE_LTSSM_ENABLE_SHIFT BIT(11) -#define PCIE_PCS_LOCAL_RESET_REQ (0xAC0) -#define PCIE_PCS_RESET_REQ_REG (0xA60) -#define PCIE_PCS_RESET_REG_ST (0x5A30) -#define PCIE_PCS_LOCAL_DRESET_REQ (0xAC4) -#define PCIE_PCS_LOCAL_DRESET_ST (0x5A60) -#define PCIE_PCS_DRESET_REQ_REG (0xA64) -#define PCIE_M_PCS_IN15_CFG (0x74) -#define PCIE_M_PCS_IN13_CFG (0x34) -#define PCIE_PCS_SERDES_STATUS (0x8108) -#define PCIE_PCS_RXDETECTED (0xE4) -#define PCIE_MSI_CONTEXT_VALUE (0x1011000) -#define PCIE_MSI_TRANS_ENABLE (0x1ff0) - -#define PCIE_ASSERT_RESET_ON (1) -#define PCIE_DEASSERT_RESET_ON (0) -#define PCIE_CLOCK_ON (1) -#define PCIE_CLOCK_OFF (0) - -#define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp) - -struct hisi_pcie { - void __iomem *subctrl_base; - void __iomem *reg_base; - void __iomem *phy_base; - struct msi_controller *msi; - u32 port_id; - struct pcie_port pp; -}; - -static inline void hisi_pcie_subctrl_writel(struct hisi_pcie *pcie, - u32 val, u32 reg) -{ - writel(val, pcie->subctrl_base + reg); -} - -static inline u32 hisi_pcie_subctrl_readl(struct hisi_pcie *pcie, u32 reg) -{ - return readl(pcie->subctrl_base + reg); -} - -static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie, - u32 val, u32 reg) -{ - writel(val, pcie->reg_base + reg); -} - -static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg) -{ - return readl(pcie->reg_base + reg); -} - -static inline void hisi_pcie_pcs_writel(struct hisi_pcie *pcie, - u32 val, u32 reg) -{ - writel(val, pcie->phy_base + reg); -} - -static inline u32 hisi_pcie_pcs_readl(struct hisi_pcie *pcie, u32 reg) -{ - return readl(pcie->phy_base + reg); -} - -/* Change reg_base to indicate base of PCIe host configure registers, - * RC cofigure space or vmid/asid context table +/* + * Change mode to indicate the same reg_base to base of PCIe host configure + * registers, base of RC configure space or base of vmid/asid context table */ -static void hisi_pcie_apb_slave_mode(struct pcie_port *pp, u32 mode) +void hisi_pcie_change_apb_mode(struct hisi_pcie *pcie, u32 mode) { u32 val; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); u32 bit_mask; u32 bit_shift; - u32 port_id = hisi_pcie->port_id; - u32 reg = PCIE_SUBCTRL_CLKREQ_REG + 0x100 * port_id; + u32 port_id = pcie->port_id; + u32 reg = PCIE_SUBCTRL_MODE_REG + 0x100 * port_id; - /* port0&3 use diff bit as port1&2 */ - if ((port_id > 0) && (port_id < 3)) { + if ((port_id == 1) || (port_id == 2)) { bit_mask = 0xc; bit_shift = 0x2; } else { bit_mask = 0x6; bit_shift = 0x1; } - do { - val = hisi_pcie_subctrl_readl(hisi_pcie, reg); - val = (val & (~bit_mask)) | (mode << bit_shift); - hisi_pcie_subctrl_writel(hisi_pcie, val, reg); - val = hisi_pcie_subctrl_readl(hisi_pcie, reg); - val = (val & bit_mask) >> bit_shift; - } while (val != mode); -} - -static int hisi_pcie_link_up(struct pcie_port *pp) -{ - u32 val; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - - val = hisi_pcie_subctrl_readl(hisi_pcie, PCIE_SUBCTRL_SYS_STATE4_REG + - 0x100 * hisi_pcie->port_id); - - return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); + val = hisi_pcie_subctrl_readl(pcie, reg); + val = (val & (~bit_mask)) | (mode << bit_shift); + hisi_pcie_subctrl_writel(pcie, val, reg); } -static void hisi_pcie_enable_ltssm(struct pcie_port *pp, bool on) -{ - u32 val; - - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - - hisi_pcie_apb_slave_mode(pp, PCIE_SLV_SYSCTRL_MODE); - - val = hisi_pcie_apb_readl(hisi_pcie, PCIE_CTRL_7_REG); - if (on) - val |= (PCIE_LTSSM_ENABLE_SHIFT); - else - val &= ~(PCIE_LTSSM_ENABLE_SHIFT); - hisi_pcie_apb_writel(hisi_pcie, val, PCIE_CTRL_7_REG); - hisi_pcie_apb_slave_mode(pp, PCIE_SLV_DBI_MODE); -} - -static void hisi_pcie_core_reset_ctrl(struct pcie_port *pp, bool reset_on) +static void hisi_pcie_core_reset_ctrl(struct hisi_pcie *pcie, bool reset_on) { u32 reg_reset_ctrl; u32 reg_dereset_ctrl; u32 reg_reset_status; - u32 reset_status; u32 reset_status_checked; - unsigned long timeout; - - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - u32 port_id = hisi_pcie->port_id; + u32 port_id = pcie->port_id; + u32 pcie_pcs_core_reset = pcie->soc_ops->pcie_pcs_core_reset; if (port_id == 3) { reg_reset_ctrl = PCIE3_SUBCTRL_RESET_REQ_REG; @@ -220,46 +72,44 @@ static void hisi_pcie_core_reset_ctrl(struct pcie_port *pp, bool reset_on) if (reset_on) { /* if core is link up, stop the ltssm state machine first */ - if (hisi_pcie_link_up(pp)) - hisi_pcie_enable_ltssm(pp, 0); + if (pcie->soc_ops->hisi_pcie_link_up(pcie)) + pcie->soc_ops->hisi_pcie_enable_ltssm(pcie, 0); - /* reset port */ - hisi_pcie_subctrl_writel(hisi_pcie, 0x1, reg_reset_ctrl); - } else { - /* dreset port */ - hisi_pcie_subctrl_writel(hisi_pcie, 0x1, reg_dereset_ctrl); - } + hisi_pcie_subctrl_writel(pcie, pcie_pcs_core_reset, + reg_reset_ctrl); + } else + hisi_pcie_subctrl_writel(pcie, pcie_pcs_core_reset, + reg_dereset_ctrl); - timeout = jiffies + HZ*1; + /* wait a delay for 1s */ + timeout = jiffies + HZ * 1; do { - reset_status = hisi_pcie_subctrl_readl(hisi_pcie, - reg_reset_status); - if (reset_on) - reset_status_checked = ((reset_status & 0x01) != 1); - else - reset_status_checked = ((reset_status & 0x01) != 0); - + reset_status = hisi_pcie_subctrl_readl(pcie, reg_reset_status); + if (reset_on) { + reset_status &= pcie_pcs_core_reset; + reset_status_checked = (reset_status != pcie_pcs_core_reset); + } else { + reset_status &= pcie_pcs_core_reset; + reset_status_checked = (reset_status != 0); + } } while ((reset_status_checked) && (time_before(jiffies, timeout))); /* get a timeout error */ if (reset_status_checked) - dev_err(pp->dev, "error:pcie core reset or dereset failed!\n"); + dev_err(pcie->pp.dev, "pcie core reset or dereset failed!\n"); } -static void hisi_pcie_clock_ctrl(struct pcie_port *pp, bool clock_on) +static void hisi_pcie_clock_ctrl(struct hisi_pcie *pcie, bool clock_on) { u32 reg_clock_disable; u32 reg_clock_enable; u32 reg_clock_status; - u32 clock_status; u32 clock_status_checked; - unsigned long timeout; - - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - u32 port_id = hisi_pcie->port_id; + u32 port_id = pcie->port_id; + u32 pcie_clk_ctrl = pcie->soc_ops->pcie_clk_ctrl; if (port_id == 3) { reg_clock_disable = PCIE_SUBCTRL_SC_PCIE3_CLK_DIS_REG; @@ -267,361 +117,390 @@ static void hisi_pcie_clock_ctrl(struct pcie_port *pp, bool clock_on) reg_clock_status = PCIE_SUBCTRL_SC_PCIE3_CLK_ST_REG; } else { reg_clock_disable = - PCIE_SUBCTRL_SC_PCIE0_2_CLK_DIS_REG(port_id); + PCIE_SUBCTRL_SC_PCIE0_2_CLK_DIS_REG(port_id); reg_clock_enable = PCIE_SUBCTRL_SC_PCIE0_2_CLK_EN_REG(port_id); reg_clock_status = PCIE_SUBCTRL_SC_PCIE0_2_CLK_ST_REG(port_id); } - if (clock_on) { - /* switch on pcie core clock */ - hisi_pcie_subctrl_writel(hisi_pcie, 0x3, reg_clock_enable); - } else { - /* switch off pcie core clock */ - hisi_pcie_subctrl_writel(hisi_pcie, 0x3, reg_clock_disable); - } - - timeout = jiffies + HZ*1; - + if (clock_on) + hisi_pcie_subctrl_writel(pcie, pcie_clk_ctrl, reg_clock_enable); + else + hisi_pcie_subctrl_writel(pcie, pcie_clk_ctrl, reg_clock_disable); + timeout = jiffies + HZ * 1; do { - clock_status = hisi_pcie_subctrl_readl(hisi_pcie, - reg_clock_status); + clock_status = hisi_pcie_subctrl_readl(pcie, reg_clock_status); if (clock_on) - clock_status_checked = ((clock_status & 0x03) != 0x3); + clock_status_checked = + ((clock_status & pcie_clk_ctrl) != pcie_clk_ctrl); else - clock_status_checked = ((clock_status & 0x03) != 0); - + clock_status_checked = ((clock_status & pcie_clk_ctrl) != 0); } while ((clock_status_checked) && (time_before(jiffies, timeout))); /* get a timeout error */ if (clock_status_checked) - dev_err(pp->dev, "error:clock operation failed!\n"); + dev_err(pcie->pp.dev, "clock operation failed!\n"); } -/* will implement in BIOS */ -static void hisi_pcie_assert_core_reset(struct pcie_port *pp) +static void hisi_pcie_assert_core_reset(struct hisi_pcie *pcie) { - hisi_pcie_core_reset_ctrl(pp, PCIE_ASSERT_RESET_ON); - hisi_pcie_clock_ctrl(pp, PCIE_CLOCK_OFF); + hisi_pcie_core_reset_ctrl(pcie, PCIE_ASSERT_RESET_ON); + hisi_pcie_clock_ctrl(pcie, PCIE_CLOCK_OFF); } -/* will implement in BIOS */ -static void hisi_pcie_deassert_core_reset(struct pcie_port *pp) +static void hisi_pcie_deassert_core_reset(struct hisi_pcie *pcie) { - hisi_pcie_core_reset_ctrl(pp, PCIE_DEASSERT_RESET_ON); - hisi_pcie_clock_ctrl(pp, PCIE_CLOCK_ON); + hisi_pcie_core_reset_ctrl(pcie, PCIE_DEASSERT_RESET_ON); } -/* will implement in BIOS */ -static void hisi_pcie_deassert_pcs_reset(struct pcie_port *pp) +static void hisi_pcie_deassert_pcs_reset(struct hisi_pcie *pcie) { - u32 val0; - + u32 val; u32 hilink_reset_status; u32 pcs_local_status; - u32 hilink_status_checked; u32 pcs_local_status_checked; - unsigned long timeout; + u32 port_id = pcie->port_id; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - u32 port_id = hisi_pcie->port_id; - - val0 = 1 << port_id; - hisi_pcie_subctrl_writel(hisi_pcie, val0, PCIE_PCS_LOCAL_DRESET_REQ); + val = 1 << port_id; + hisi_pcie_subctrl_writel(pcie, val, PCIE_PCS_LOCAL_DRESET_REQ); - val0 = 0xff << (port_id * 8); - hisi_pcie_subctrl_writel(hisi_pcie, val0, PCIE_PCS_DRESET_REQ_REG); + if (pcie->soc_ops->soc_type == PCIE_HOST_HIP06) + hisi_pcie_subctrl_writel(pcie, val, PCIE_PCS_APB_DRESET_REQ); - timeout = jiffies + HZ*1; + val = 0xff << (port_id << 3); + hisi_pcie_subctrl_writel(pcie, val, PCIE_PCS_DRESET_REQ_REG); - /*read reset status,make sure pcs is deassert */ + timeout = jiffies + HZ * 1; + /* read reset status, make sure pcs is deassert */ do { - pcs_local_status = hisi_pcie_subctrl_readl(hisi_pcie, - PCIE_PCS_LOCAL_RESET_ST); + pcs_local_status = hisi_pcie_subctrl_readl(pcie, + PCIE_PCS_LOCAL_RESET_ST); pcs_local_status_checked = (pcs_local_status & (1 << port_id)); } while ((pcs_local_status_checked) && (time_before(jiffies, timeout))); /* get a timeout error */ if (pcs_local_status_checked) - dev_err(pp->dev, "pcs deassert reset failed!\n"); + dev_err(pcie->pp.dev, "pcs deassert reset failed!\n"); - timeout = jiffies + HZ*1; + timeout = jiffies + HZ * 1; do { - hilink_reset_status = hisi_pcie_subctrl_readl(hisi_pcie, - PCIE_PCS_RESET_REG_ST); + hilink_reset_status = hisi_pcie_subctrl_readl(pcie, + PCIE_PCS_RESET_REG_ST); hilink_status_checked = (hilink_reset_status & - (0xff << (port_id * 8))); + (0xff << (port_id << 3))); } while ((hilink_status_checked) && (time_before(jiffies, timeout))); if (hilink_status_checked) - dev_err(pp->dev, "pcs deassert reset failed!\n"); + dev_err(pcie->pp.dev, "pcs deassert reset failed!\n"); } -/* will implement in BIOS */ -static void hisi_pcie_assert_pcs_reset(struct pcie_port *pp) +static void hisi_pcie_assert_pcs_reset(struct hisi_pcie *pcie) { u32 reg; u32 hilink_reset_status; u32 pcs_local_reset_status; - u32 hilink_status_checked; u32 pcs_local_status_checked; - unsigned long timeout; - - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - u32 port_id = hisi_pcie->port_id; + u32 port_id = pcie->port_id; reg = 1 << port_id; - hisi_pcie_subctrl_writel(hisi_pcie, reg, PCIE_PCS_LOCAL_RESET_REQ); + hisi_pcie_subctrl_writel(pcie, reg, PCIE_PCS_LOCAL_RESET_REQ); - reg = 0xff << (port_id * 8); - hisi_pcie_subctrl_writel(hisi_pcie, reg, PCIE_PCS_RESET_REQ_REG); + if (pcie->soc_ops->soc_type == PCIE_HOST_HIP06) + hisi_pcie_subctrl_writel(pcie, reg, PCIE_PCS_APB_RESET_REQ); - timeout = jiffies + HZ*1; + reg = 0xff << (port_id << 3); + hisi_pcie_subctrl_writel(pcie, reg, PCIE_PCS_RESET_REQ_REG); - /*read reset status,make sure pcs is reset */ + timeout = jiffies + HZ * 1; + /* read reset status, make sure pcs is reset */ do { - pcs_local_reset_status = hisi_pcie_subctrl_readl(hisi_pcie, - PCIE_PCS_LOCAL_RESET_ST); + pcs_local_reset_status = hisi_pcie_subctrl_readl(pcie, + PCIE_PCS_LOCAL_RESET_ST); pcs_local_status_checked = - ((pcs_local_reset_status & (1 << port_id)) != (1 << port_id)); + ((pcs_local_reset_status & (1 << port_id)) != + (1 << port_id)); } while ((pcs_local_status_checked) && (time_before(jiffies, timeout))); if (pcs_local_status_checked) - dev_err(pp->dev, "pcs local reset status read failed\n"); - - timeout = jiffies + HZ*1; + dev_err(pcie->pp.dev, "pcs local reset status read failed\n"); + timeout = jiffies + HZ * 1; do { - hilink_reset_status = hisi_pcie_subctrl_readl(hisi_pcie, - PCIE_PCS_RESET_REG_ST); + hilink_reset_status = hisi_pcie_subctrl_readl(pcie, + PCIE_PCS_RESET_REG_ST); hilink_status_checked = - ((hilink_reset_status & (0xff << (port_id << 3))) != - (0xff << (port_id << 3))); + ((hilink_reset_status & (0xff << (port_id << 3))) != + (0xff << (port_id << 3))); } while ((hilink_status_checked) && (time_before(jiffies, timeout))); if (hilink_status_checked) - dev_err(pp->dev, "error:pcs assert reset failed\n"); + dev_err(pcie->pp.dev, "error:pcs assert reset failed\n"); } -/* will implement in BIOS */ -static void hisi_pcie_init_pcs(struct pcie_port *pp) +void pcie_equalization_common(struct hisi_pcie *pcie) { - u32 lane_num = 8; - u32 i; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - - if (hisi_pcie->port_id <= 2) { - u32 *addr = ioremap_nocache(0xb200c088, 0x100); - u32 *addr1 = ioremap_nocache(0xb210c088, 0x100); - u32 *addr2 = ioremap_nocache(0xb218c088, 0x100); - u32 *addr3 = ioremap_nocache(0xb208c088, 0x100); - *addr = 0x212; - *addr1 = 0x212; - *addr2 = 0x212; - *addr3 = 0x212; - iounmap(addr); - iounmap(addr1); - iounmap(addr2); - iounmap(addr3); - - hisi_pcie_pcs_writel(hisi_pcie, 0x2026044, 0x8020); - hisi_pcie_pcs_writel(hisi_pcie, 0x2126044, 0x8060); - hisi_pcie_pcs_writel(hisi_pcie, 0x2126044, 0x80c4); - hisi_pcie_pcs_writel(hisi_pcie, 0x2026044, 0x80e4); - hisi_pcie_pcs_writel(hisi_pcie, 0x4018, 0x80a0); - hisi_pcie_pcs_writel(hisi_pcie, 0x804018, 0x80a4); - hisi_pcie_pcs_writel(hisi_pcie, 0x11201100, 0x80c0); - hisi_pcie_pcs_writel(hisi_pcie, 0x3, 0x15c); - hisi_pcie_pcs_writel(hisi_pcie, 0x0, 0x158); - } else { - for (i = 0; i < lane_num; i++) - hisi_pcie_pcs_writel(hisi_pcie, 0x46e000, - PCIE_M_PCS_IN15_CFG + (i << 2)); - for (i = 0; i < lane_num; i++) - hisi_pcie_pcs_writel(hisi_pcie, 0x1001, - PCIE_M_PCS_IN13_CFG + (i << 2)); - - hisi_pcie_pcs_writel(hisi_pcie, 0xffff, PCIE_PCS_RXDETECTED); - } + hisi_pcie_apb_writel(pcie, 0xfd7, 0x894); + + hisi_pcie_apb_writel(pcie, 0x0, 0x89c); + hisi_pcie_apb_writel(pcie, 0xfc00, 0x898); + hisi_pcie_apb_writel(pcie, 0x1, 0x89c); + hisi_pcie_apb_writel(pcie, 0xbd00, 0x898); + hisi_pcie_apb_writel(pcie, 0x2, 0x89c); + hisi_pcie_apb_writel(pcie, 0xccc0, 0x898); + hisi_pcie_apb_writel(pcie, 0x3, 0x89c); + hisi_pcie_apb_writel(pcie, 0x8dc0, 0x898); + hisi_pcie_apb_writel(pcie, 0x4, 0x89c); + hisi_pcie_apb_writel(pcie, 0xfc0, 0x898); + hisi_pcie_apb_writel(pcie, 0x5, 0x89c); + hisi_pcie_apb_writel(pcie, 0xe46, 0x898); + hisi_pcie_apb_writel(pcie, 0x6, 0x89c); + hisi_pcie_apb_writel(pcie, 0xdc8, 0x898); + hisi_pcie_apb_writel(pcie, 0x7, 0x89c); + hisi_pcie_apb_writel(pcie, 0xcb46, 0x898); + hisi_pcie_apb_writel(pcie, 0x8, 0x89c); + hisi_pcie_apb_writel(pcie, 0x8c07, 0x898); + hisi_pcie_apb_writel(pcie, 0x9, 0x89c); + hisi_pcie_apb_writel(pcie, 0xd0b, 0x898); + hisi_pcie_apb_writel(pcie, 0x103ff21, 0x8a8); } -static void hisi_pcie_config_context(struct pcie_port *pp) +static void hisi_pcie_spd_set(struct hisi_pcie *pcie, u32 spd) { - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - int i = 0; - - hisi_pcie_apb_slave_mode(pp, PCIE_SLV_CONTENT_MODE); - - for (i = 0; i < 0x400; i++) - hisi_pcie_apb_writel(hisi_pcie, 0x0, i * 4); - - for (i = 0x400; i < 0x800; i++) - hisi_pcie_apb_writel(hisi_pcie, 0x0, i * 4); - - hisi_pcie_apb_slave_mode(pp, PCIE_SLV_SYSCTRL_MODE); - - /* FIX ME! */ - hisi_pcie_apb_writel(hisi_pcie, 0xb7010040, 0x1b4); - hisi_pcie_apb_writel(hisi_pcie, 0x0, 0x1c4); - hisi_pcie_apb_writel(hisi_pcie, PCIE_MSI_CONTEXT_VALUE, 0x10); - hisi_pcie_apb_writel(hisi_pcie, PCIE_MSI_TRANS_ENABLE, 0x1c8); + u32 val; - hisi_pcie_apb_slave_mode(pp, PCIE_SLV_DBI_MODE); + val = hisi_pcie_apb_readl(pcie, 0xa0); + val &= ~(0xf); + val |= spd; + hisi_pcie_apb_writel(pcie, val, 0xa0); } -static void hisi_pcie_mask_link_up_int(struct pcie_port *pp) +static void hisi_pcie_spd_control(struct hisi_pcie *pcie) { u32 val; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - hisi_pcie_apb_slave_mode(pp, PCIE_SLV_SYSCTRL_MODE); - val = hisi_pcie_apb_readl(hisi_pcie, 0x1d0); - val |= 1 << 12; - hisi_pcie_apb_writel(hisi_pcie, val, 0x1d0); - hisi_pcie_apb_slave_mode(pp, PCIE_SLV_DBI_MODE); + /* set link width speed control register */ + val = hisi_pcie_apb_readl(pcie, PCIE_LINK_WIDTH_SPEED_CONTROL); + /* + * set the Directed Speed Change field of the Link Width and Speed + * Change Control register + */ + val |= PORT_LOGIC_SPEED_CHANGE; + hisi_pcie_apb_writel(pcie, val, PCIE_LINK_WIDTH_SPEED_CONTROL); } -void pcie_equalization(struct pcie_port *pp) +void hisi_pcie_establish_link(struct hisi_pcie *pcie) { u32 val = 0; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); + int count = 0; - if (hisi_pcie->port_id <= 2) { - hisi_pcie_apb_writel(hisi_pcie, 0x1400, 0x890); - hisi_pcie_apb_writel(hisi_pcie, 0xfd7, 0x894); - - val = hisi_pcie_apb_readl(hisi_pcie, 0x80); - val |= 0x80; - hisi_pcie_apb_writel(hisi_pcie, val, 0x80); - - hisi_pcie_apb_writel(hisi_pcie, 0x0, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0xfc00, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x1, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0xdb00, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x2, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0xccc0, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x3, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0x8dc0, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x4, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0xfc0, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x5, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0xe46, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x6, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0x7, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0xcb46, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x8, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0x8c07, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x9, 0x89c); - hisi_pcie_apb_writel(hisi_pcie, 0xd0b, 0x898); - hisi_pcie_apb_writel(hisi_pcie, 0x103ff21, 0x8a8); - - hisi_pcie_apb_writel(hisi_pcie, 0x44444444, 0x184); - hisi_pcie_apb_writel(hisi_pcie, 0x44444444, 0x188); - hisi_pcie_apb_writel(hisi_pcie, 0x44444444, 0x18c); - hisi_pcie_apb_writel(hisi_pcie, 0x44444444, 0x190); - } else { - hisi_pcie_apb_writel(hisi_pcie, 0x10e01, 0x890); + if (dw_pcie_link_up(&pcie->pp)) { + dev_info(pcie->pp.dev, "already Link up\n"); + return; + } + /* assert reset signals */ + hisi_pcie_assert_core_reset(pcie); + hisi_pcie_assert_pcs_reset(pcie); + + if (pcie->soc_ops->soc_type == PCIE_HOST_HIP05) { + /* de-assert phy reset */ + hisi_pcie_deassert_pcs_reset(pcie); + /* de-assert core reset */ + hisi_pcie_deassert_core_reset(pcie); + } else if (pcie->soc_ops->soc_type == PCIE_HOST_HIP06) { + /* de-assert core reset */ + hisi_pcie_deassert_core_reset(pcie); + /* de-assert phy reset */ + hisi_pcie_deassert_pcs_reset(pcie); } -} -/* will implement in BIOS */ -static int hisi_pcie_establish_link(struct pcie_port *pp) -{ - u32 val; - int count = 0; - struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); + /* enable clock */ + hisi_pcie_clock_ctrl(pcie, PCIE_CLOCK_ON); - if (dw_pcie_link_up(pp)) { - dev_err(pp->dev, "Link already up\n"); - return 0; - } + /* initialize phy */ + pcie->soc_ops->hisi_pcie_init_pcs(pcie); - /* assert reset signals */ - hisi_pcie_assert_core_reset(pp); - hisi_pcie_assert_pcs_reset(pp); + /* set controller to RC mode */ + pcie->soc_ops->hisi_pcie_mode_set(pcie); - /* de-assert phy reset */ - hisi_pcie_deassert_pcs_reset(pp); + /* set target link speed */ + hisi_pcie_spd_set(pcie, 3); - /* de-assert core reset */ - hisi_pcie_deassert_core_reset(pp); + /* set pcie port num 0 for all controller, must be set 0 here. + * + * there is a bug about ITS. ITS uses request_id(BDF) + MSI_vector to + * establish ITS table for PCIe devices. However, PCIe host send + * port_id + request_id + MSI_vector to ITS TRANSLATION register + */ + if (pcie->soc_ops->hisi_pcie_portnum_set) + pcie->soc_ops->hisi_pcie_portnum_set(pcie, 0); - /* initialize phy */ - hisi_pcie_init_pcs(pp); + /* set link speed control*/ + hisi_pcie_spd_control(pcie); /* setup root complex */ - dw_pcie_setup_rc(pp); - - /* disable link up interrupt */ - hisi_pcie_mask_link_up_int(pp); + dw_pcie_setup_rc(&pcie->pp); - pcie_equalization(pp); + pcie->soc_ops->pcie_equalization(pcie); /* assert LTSSM enable */ - hisi_pcie_enable_ltssm(pp, 1); + pcie->soc_ops->hisi_pcie_enable_ltssm(pcie, 1); /* check if the link is up or not */ - while (!dw_pcie_link_up(pp)) { + while (!dw_pcie_link_up(&pcie->pp)) { + u32 pcs_serdes_status = pcie->soc_ops->pcs_serdes_status; mdelay(100); count++; if (count == 10) { - while ((hisi_pcie_pcs_readl(hisi_pcie, - PCIE_PCS_SERDES_STATUS) & 0x3) == 0) { - val = hisi_pcie_pcs_readl(hisi_pcie, - PCIE_PCS_SERDES_STATUS); - dev_info(pp->dev, "PLL Locked: 0x%x\n", val); - } - - dev_err(pp->dev, "PCIe Link Fail\n"); - return -EINVAL; + val = hisi_pcie_pcs_readl(pcie, pcs_serdes_status); + + dev_info(pcie->pp.dev, + "PCIe Link Failed! PLL Locked: 0x%x\n", val); + return; } } - /*add a 1s delay between linkup and enumeration,make sure - the EP device'sconfiguration registers are prepared well */ - mdelay(999); - dev_info(pp->dev, "Link up\n"); + dev_info(pcie->pp.dev, "Link up\n"); + + /* dfe enable is just for 660 */ + if (pcie->soc_ops->hisi_pcie_gen3_dfe_enable) + pcie->soc_ops->hisi_pcie_gen3_dfe_enable(pcie); + /* + * add a 1s delay between linkup and enumeration, make sure + * the EP device's configuration registers are prepared well + */ + mdelay(1000); +} + +static void hisi_pcie_set_db2_enable(struct hisi_pcie *pcie, u32 enable) +{ + u32 dbi_ctrl; + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_SYSCTRL_MODE); + + dbi_ctrl = hisi_pcie_apb_readl(pcie, PCIE_SYS_CTRL20_REG); + if (enable) + dbi_ctrl |= PCIE_DB2_ENABLE_SHIFT; + else + dbi_ctrl &= ~PCIE_DB2_ENABLE_SHIFT; + hisi_pcie_apb_writel(pcie, dbi_ctrl, PCIE_SYS_CTRL20_REG); + + hisi_pcie_change_apb_mode(pcie, PCIE_SLV_DBI_MODE); +} + +static void hisi_pcie_disabled_bar0(struct hisi_pcie *pcie) +{ + hisi_pcie_set_db2_enable(pcie, PCIE_DBI_CS2_ENABLE); + hisi_pcie_apb_writel(pcie, 0, PCIE_CFG_BAR0BASE); + hisi_pcie_set_db2_enable(pcie, PCIE_DBI_CS2_DISABLE); +} + +static int hisi_pcie_msi_host_init(struct pcie_port *pp, + struct msi_controller *chip) +{ + struct device_node *msi_node; + struct irq_domain *irq_domain; + struct resource res; + struct device_node *np = pp->dev->of_node; + struct hisi_pcie *pcie = to_hisi_pcie(pp); + + + msi_node = of_parse_phandle(np, "msi-parent", 0); + if (!msi_node) { + dev_err(pp->dev, "failed to find msi-parent\n"); + return -ENODEV; + } + + of_address_to_resource(msi_node, 0, &res); + + pcie->msi_addr = res.start + PCIE_GITS_TRANSLATER; + + irq_domain = irq_find_host(msi_node); + if (!irq_domain) { + dev_err(pp->dev, "failed to find irq domain\n"); + return -ENODEV; + } + + pp->irq_domain = irq_domain; return 0; } static void hisi_pcie_host_init(struct pcie_port *pp) { - hisi_pcie_establish_link(pp); - hisi_pcie_config_context(pp); + struct hisi_pcie *pcie = to_hisi_pcie(pp); + + hisi_pcie_establish_link(pcie); + pcie->soc_ops->hisi_pcie_config_context(pcie); + /* + * The default size of BAR0 in p660 host bridge is 0x10000000, + * which will bring problem when most resource has been allocated + * to BAR0 in host bridge.However, we need not use BAR0 in host bridge + * in RC mode. Here we just disable it + */ + hisi_pcie_disabled_bar0(pcie); +} + +static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, + u32 val) +{ + u32 reg_val; + u32 reg; + struct hisi_pcie *pcie = to_hisi_pcie(pp); + void *walker = ®_val; + + walker += (where & 0x3); + reg = where & ~0x3; + if (size == 4) + hisi_pcie_apb_writel(pcie, val, reg); + else if (size == 2) { + reg_val = hisi_pcie_apb_readl(pcie, reg); + *(u16 __force *) walker = val; + hisi_pcie_apb_writel(pcie, reg_val, reg); + } else if (size == 1) { + reg_val = hisi_pcie_apb_readl(pcie, reg); + *(u8 __force *) walker = val; + hisi_pcie_apb_writel(pcie, reg_val, reg); + } else + return PCIBIOS_BAD_REGISTER_NUMBER; + + return PCIBIOS_SUCCESSFUL; +} + +int hisi_pcie_link_up(struct pcie_port *pp) +{ + struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); + return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie); } static struct pcie_host_ops hisi_pcie_host_ops = { .link_up = hisi_pcie_link_up, + .msi_host_init = hisi_pcie_msi_host_init, .host_init = hisi_pcie_host_init, + .wr_own_conf = hisi_pcie_cfg_write, }; -static int __init hisi_add_pcie_port(struct pcie_port *pp, - struct platform_device *pdev) +static int hisi_add_pcie_port(struct pcie_port *pp, + struct platform_device *pdev) { int ret; - struct resource busn; - + u32 port_id; struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); - if (of_property_read_u32(pdev->dev.of_node, "port-id", - &hisi_pcie->port_id)) { - dev_err(&pdev->dev, "failed to property port-id\n"); + if (of_property_read_u32(pdev->dev.of_node, "port-id", &port_id)) { + dev_err(&pdev->dev, "failed to read port-id\n"); return -EINVAL; } - - if (of_pci_parse_bus_range(pdev->dev.of_node, &busn)) { - dev_err(&pdev->dev, "fail to read bus-ranges\n"); - return -ENOMEM; + if (port_id > 3) { + dev_err(&pdev->dev, "Invalid port-id: %d\n", port_id); + return -EINVAL; } + hisi_pcie->port_id = port_id; - /* set root pcie tree base bus */ - pp->root_bus_nr = busn.start; pp->ops = &hisi_pcie_host_ops; ret = dw_pcie_host_init(pp); @@ -629,38 +508,19 @@ static int __init hisi_add_pcie_port(struct pcie_port *pp, dev_err(&pdev->dev, "failed to initialize host\n"); return ret; } - return 0; -} - -static -void hisi_pcie_msi_enable(struct device_node *np, struct hisi_pcie *hisi_pcie) -{ - struct device_node *msi_node; - struct irq_domain *irq_domain; - - msi_node = of_parse_phandle(np, "msi-parent", 0); - if (!msi_node) { - pr_err("failed to find msi-parent\n"); - return; - } - - irq_domain = irq_find_host(msi_node); - if (!irq_domain) { - pr_err("failed to find irq domain\n"); - return; - } - hisi_pcie->pp.domain = irq_domain; + return 0; } -static int hisi_pcie_probe(struct platform_device *pdev) +int hisi_pcie_probe(struct platform_device *pdev) { struct hisi_pcie *hisi_pcie; struct pcie_port *pp; - struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; struct resource *reg; struct resource *subctrl; struct resource *phy; + struct device_driver *driver; int ret; hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL); @@ -669,55 +529,54 @@ static int hisi_pcie_probe(struct platform_device *pdev) pp = &hisi_pcie->pp; pp->dev = &pdev->dev; + driver = (pdev->dev).driver; + + match = of_match_device(driver->of_match_table, &pdev->dev); + hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data; subctrl = platform_get_resource_byname(pdev, IORESOURCE_MEM, "subctrl"); hisi_pcie->subctrl_base = devm_ioremap_nocache(&pdev->dev, subctrl->start, resource_size(subctrl)); - if (IS_ERR(hisi_pcie->subctrl_base)) + if (IS_ERR(hisi_pcie->subctrl_base)) { + dev_err(pp->dev, "cannot get subctrl base\n"); return PTR_ERR(hisi_pcie->subctrl_base); + } reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); hisi_pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg); - if (IS_ERR(hisi_pcie->reg_base)) + if (IS_ERR(hisi_pcie->reg_base)) { + dev_err(pp->dev, "cannot get reg base\n"); return PTR_ERR(hisi_pcie->reg_base); + } - /* dbi slave use the common IO port with pcie's sys-state reg */ hisi_pcie->pp.dbi_base = hisi_pcie->reg_base; phy = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcs"); hisi_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy); - if (IS_ERR(hisi_pcie->phy_base)) + if (IS_ERR(hisi_pcie->phy_base)) { + dev_err(pp->dev, "cannot get phy base\n"); return PTR_ERR(hisi_pcie->phy_base); + } - hisi_pcie_msi_enable(np, hisi_pcie); + if (hisi_pcie->soc_ops->soc_type == PCIE_HOST_HIP05) { + struct resource *serdes; + serdes = platform_get_resource_byname(pdev, IORESOURCE_MEM, "serdes"); + hisi_pcie->serdes_base = devm_ioremap_resource(&pdev->dev, serdes); + if (IS_ERR(hisi_pcie->serdes_base)) { + dev_err(pp->dev, "cannot get serdes base\n"); + return PTR_ERR(hisi_pcie->serdes_base); + } + hisi_pcie->ctrl_base = NULL; + } else if (hisi_pcie->soc_ops->soc_type == PCIE_HOST_HIP06) + hisi_pcie->ctrl_base = hisi_pcie->reg_base + 0x1000; + else + dev_err(pp->dev, "unsopported soc type\n"); ret = hisi_add_pcie_port(pp, pdev); - if (ret < 0) + if (ret) return ret; platform_set_drvdata(pdev, hisi_pcie); - return ret; + return 0; } - -static const struct of_device_id hisi_pcie_of_match[] = { - {.compatible = "hisilicon,hip05-pcie",}, - {}, -}; - -MODULE_DEVICE_TABLE(of, hisi_pcie_of_match); - -static struct platform_driver hisi_pcie_driver = { - .probe = hisi_pcie_probe, - .driver = { - .name = "hisi-pcie", - .owner = THIS_MODULE, - .of_match_table = hisi_pcie_of_match, - }, -}; - -module_platform_driver(hisi_pcie_driver); - -MODULE_AUTHOR("Zhou Wang <wangzhou1@huawei.com>"); -MODULE_AUTHOR("Dacai Zhu <zhudacai@huawei.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-hisi.h b/drivers/pci/host/pcie-hisi.h new file mode 100644 index 000000000000..3e24e8bb2692 --- /dev/null +++ b/drivers/pci/host/pcie-hisi.h @@ -0,0 +1,193 @@ +/* + * PCIe host controller driver for Hisilicon Hip05 and Hip06 SoCs + * + * Copyright (C) 2015 Hisilicon Co., Ltd. http://www.hisilicon.com + * + * Authors: Zhou Wang <wangzhou1@hisilicon.com> + * Gabriele Paoloni <gabriele.paoloni@huawei.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _PCIE_HISI_H +#define _PCIE_HISI_H + +#define PCIE_HOST_HIP05 0x660 +#define PCIE_HOST_HIP06 0x1610 + +#define PCIE_SUBCTRL_MODE_REG 0x2800 +#define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 +#define PCIE_SLV_DBI_MODE 0x0 +#define PCIE_SLV_SYSCTRL_MODE 0x1 +#define PCIE_SLV_CONTENT_MODE 0x2 +#define PCIE_SLV_MSI_ASID 0x10 +#define PCIE_LTSSM_LINKUP_STATE 0x11 +#define PCIE_LTSSM_STATE_MASK 0x3F +#define PCIE_MSI_ASID_ENABLE (0x1 << 12) +#define PCIE_MSI_ASID_VALUE (0x1 << 16) +#define PCIE_MSI_TRANS_ENABLE (0x1 << 12) +#define PCIE_MSI_TRANS_REG 0x1c8 +#define PCIE_MSI_LOW_ADDRESS 0x1b4 +#define PCIE_MSI_HIGH_ADDRESS 0x1c4 +#define PCIE_GITS_TRANSLATER 0x10040 + +#define PCIE_SYS_CTRL20_REG 0x20 +#define PCIE_RD_TAB_SEL BIT(31) +#define PCIE_RD_TAB_EN BIT(30) +#define PCIE_CFG_BAR0BASE 0x10 +#define PCIE_DB2_ENABLE_SHIFT BIT(0) +#define PCIE_DBI_CS2_ENABLE 0x1 +#define PCIE_DBI_CS2_DISABLE 0x0 + +#define PCIE_CTRL_7_REG 0x114 +#define PCIE_LTSSM_ENABLE_SHIFT BIT(11) +#define PCIE_SUBCTRL_RESET_REQ_REG 0xA00 +#define PCIE0_2_SUBCTRL_RESET_REQ_REG(port_id) \ + (PCIE_SUBCTRL_RESET_REQ_REG + (port_id << 3)) +#define PCIE3_SUBCTRL_RESET_REQ_REG 0xA68 + +#define PCIE_SUBCTRL_DRESET_REQ_REG 0xA04 +#define PCIE0_2_SUBCTRL_DRESET_REQ_REG(port_id) \ + (PCIE_SUBCTRL_DRESET_REQ_REG + (port_id << 3)) +#define PCIE3_SUBCTRL_DRESET_REQ_REG 0xA6C + +#define PCIE_SUBCTRL_RESET_ST_REG 0x5A00 +#define PCIE0_2_SUBCTRL_RESET_ST_REG(port_id) \ + (PCIE_SUBCTRL_RESET_ST_REG + (port_id << 2)) +#define PCIE3_SUBCTRL_RESET_ST_REG 0x5A34 + +#define PCIE_SUBCTRL_SC_PCIE0_CLK_DIS_REG 0x304 +#define PCIE_SUBCTRL_SC_PCIE0_2_CLK_DIS_REG(port_id) \ + (PCIE_SUBCTRL_SC_PCIE0_CLK_DIS_REG + (port_id << 3)) +#define PCIE_SUBCTRL_SC_PCIE3_CLK_DIS_REG 0x324 + +#define PCIE_SUBCTRL_SC_PCIE0_CLK_ST_REG 0x5300 +#define PCIE_SUBCTRL_SC_PCIE0_2_CLK_ST_REG(port_id) \ + (PCIE_SUBCTRL_SC_PCIE0_CLK_ST_REG + (port_id << 2)) +#define PCIE_SUBCTRL_SC_PCIE3_CLK_ST_REG 0x5310 + +#define PCIE_SUBCTRL_SC_PCIE0_CLK_EN_REG 0x300 +#define PCIE_SUBCTRL_SC_PCIE0_2_CLK_EN_REG(port_id) \ + (PCIE_SUBCTRL_SC_PCIE0_CLK_EN_REG + (port_id << 3)) +#define PCIE_SUBCTRL_SC_PCIE3_CLK_EN_REG 0x320 + +#define PCIE_ASSERT_RESET_ON 1 +#define PCIE_DEASSERT_RESET_ON 0 +#define PCIE_CLOCK_ON 1 +#define PCIE_CLOCK_OFF 0 + +#define PCIE_PCS_LOCAL_RESET_REQ 0xAC0 +#define PCIE_PCS_LOCAL_DRESET_REQ 0xAC4 +#define PCIE_PCS_RESET_REQ_REG 0xA60 +#define PCIE_PCS_RESET_REG_ST 0x5A30 +#define PCIE_PCS_LOCAL_DRESET_ST 0x5A60 +#define PCIE_PCS_LOCAL_RESET_ST 0x5A60 +#define PCIE_PCS_DRESET_REQ_REG 0xA64 +#define PCIE_M_PCS_IN15_CFG 0x74 +#define PCIE_M_PCS_IN13_CFG 0x34 +#define PCIE_PCS_RXDETECTED 0xE4 +#define PCIE_CORE_MODE_REG 0xF8 +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) + +#define DS_API(lane) ((0x1FF6c + 8 * (15 - lane)) * 2) +#define PCIE_DFE_ENABLE_VAL 0x3851 + +#define PCIE_RD_ERR_RESPONSE_EN BIT(31) +#define PCIE_WR_ERR_RESPONSE_EN BIT(30) +#define PCIE_PCS_APB_RESET_REQ 0xAC8 +#define PCIE_PCS_APB_DRESET_REQ 0xACC +#define PCIE_CTRL_19_REG 0x1c +#define PCIE_SYS_STATE4 0x31c + +enum pcie_mac_phy_rate_e { + PCIE_GEN1 = 0, /* PCIE 1.0 */ + PCIE_GEN2 = 1, /* PCIE 2.0 */ + PCIE_GEN3 = 2 /* PCIE 3.0 */ +}; + +#define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp) + + struct hisi_pcie; + +struct pcie_soc_ops { + int (*hisi_pcie_link_up)(struct hisi_pcie *pcie); + void (*hisi_pcie_config_context)(struct hisi_pcie *pcie); + void (*hisi_pcie_enable_ltssm)(struct hisi_pcie *pcie, bool on); + void (*hisi_pcie_gen3_dfe_enable)(struct hisi_pcie *pcie); + void (*hisi_pcie_init_pcs)(struct hisi_pcie *pcie); + void (*pcie_equalization)(struct hisi_pcie *pcie); + void (*hisi_pcie_mode_set)(struct hisi_pcie *pcie); + void (*hisi_pcie_portnum_set)(struct hisi_pcie *pcie, u32 num); + u32 pcie_pcs_core_reset; + u32 pcie_clk_ctrl; + u32 pcs_serdes_status; + u32 soc_type; +}; + +struct hisi_pcie { + void __iomem *subctrl_base; + void __iomem *reg_base; + void __iomem *phy_base; + void __iomem *serdes_base; + void __iomem *ctrl_base; + u32 port_id; + struct pcie_port pp; + u64 msi_addr; + struct pcie_soc_ops *soc_ops; +}; + +static inline void hisi_pcie_subctrl_writel(struct hisi_pcie *pcie, + u32 val, u32 reg) +{ + writel(val, pcie->subctrl_base + reg); +} + +static inline u32 hisi_pcie_subctrl_readl(struct hisi_pcie *pcie, u32 reg) +{ + return readl(pcie->subctrl_base + reg); +} + +static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie, + u32 val, u32 reg) +{ + writel(val, pcie->reg_base + reg); +} + +static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg) +{ + return readl(pcie->reg_base + reg); +} +static inline void hisi_pcie_pcs_writel(struct hisi_pcie *pcie, + u32 val, u32 reg) +{ + writel(val, pcie->phy_base + reg); +} + +static inline u32 hisi_pcie_pcs_readl(struct hisi_pcie *pcie, u32 reg) +{ + return readl(pcie->phy_base + reg); +} + +static inline void hisi_pcie_serdes_writel(struct hisi_pcie *pcie, + u32 val, u32 reg) +{ + writel(val, pcie->serdes_base + reg); +} + +static inline void hisi_pcie_ctrl_writel(struct hisi_pcie *pcie, u32 val, u32 reg) +{ + return writel(val, pcie->ctrl_base + reg); +} + +static inline u32 hisi_pcie_ctrl_readl(struct hisi_pcie *pcie, u32 reg) +{ + return readl(pcie->ctrl_base + reg); +} + +void hisi_pcie_change_apb_mode(struct hisi_pcie *pcie, u32 mode); +void pcie_equalization_common(struct hisi_pcie *pcie); +int hisi_pcie_probe(struct platform_device *pdev); + +#endif /* _PCIE_HISI_H */
\ No newline at end of file |