summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkongzizaixian <xweikong@hotmail.com>2015-11-06 17:24:58 +0800
committerkongzizaixian <xweikong@hotmail.com>2015-11-06 17:24:58 +0800
commit6243f5478c8af97c4e1d7cc897ab7c2b018b2620 (patch)
tree81771ff4afb2be6b18fa7b3ef5b1051154b142d0
parent25283cb218908a3cddbfcf695e4d2a89d92f9b7b (diff)
parent941ad0c06f826d6a49276312c7a36d935e41027d (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.txt49
-rw-r--r--arch/arm64/boot/dts/hisilicon/hip05-d02.dts1
-rw-r--r--arch/arm64/boot/dts/hisilicon/hisi_p660_pcie_d02.dtsi37
-rw-r--r--drivers/pci/host/Kconfig7
-rw-r--r--drivers/pci/host/Makefile2
-rw-r--r--drivers/pci/host/pci-dra7xx.c13
-rw-r--r--drivers/pci/host/pci-keystone-dw.c2
-rw-r--r--drivers/pci/host/pcie-designware.c404
-rw-r--r--drivers/pci/host/pcie-designware.h15
-rw-r--r--drivers/pci/host/pcie-hisi-hip05.c224
-rw-r--r--drivers/pci/host/pcie-hisi-hip06.c175
-rw-r--r--drivers/pci/host/pcie-hisi.c809
-rw-r--r--drivers/pci/host/pcie-hisi.h193
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 = &reg_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