diff options
-rw-r--r-- | Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt | 17 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/pci/hisilicon-pcie.txt | 44 | ||||
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | drivers/pci/host/Kconfig | 8 | ||||
-rw-r--r-- | drivers/pci/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/host/pcie-hisi.c | 198 |
6 files changed, 275 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt index c733e28e18e5..764c738bb3ba 100644 --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt | |||
@@ -167,6 +167,23 @@ Example: | |||
167 | }; | 167 | }; |
168 | 168 | ||
169 | ----------------------------------------------------------------------- | 169 | ----------------------------------------------------------------------- |
170 | Hisilicon HiP05 PCIe-SAS system controller | ||
171 | |||
172 | Required properties: | ||
173 | - compatible : "hisilicon,pcie-sas-subctrl", "syscon"; | ||
174 | - reg : Register address and size | ||
175 | |||
176 | The HiP05 PCIe-SAS system controller is shared by PCIe and SAS controllers in | ||
177 | HiP05 Soc to implement some basic configurations. | ||
178 | |||
179 | Example: | ||
180 | /* for HiP05 PCIe-SAS system */ | ||
181 | pcie_sas: system_controller@0xb0000000 { | ||
182 | compatible = "hisilicon,pcie-sas-subctrl", "syscon"; | ||
183 | reg = <0xb0000000 0x10000>; | ||
184 | }; | ||
185 | |||
186 | ----------------------------------------------------------------------- | ||
170 | Hisilicon CPU controller | 187 | Hisilicon CPU controller |
171 | 188 | ||
172 | Required properties: | 189 | Required properties: |
diff --git a/Documentation/devicetree/bindings/pci/hisilicon-pcie.txt b/Documentation/devicetree/bindings/pci/hisilicon-pcie.txt new file mode 100644 index 000000000000..17c6ed9c6059 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/hisilicon-pcie.txt | |||
@@ -0,0 +1,44 @@ | |||
1 | HiSilicon PCIe host bridge DT description | ||
2 | |||
3 | HiSilicon PCIe host controller is based on Designware PCI core. | ||
4 | It shares common functions with PCIe Designware core driver and inherits | ||
5 | common properties defined in | ||
6 | Documentation/devicetree/bindings/pci/designware-pci.txt. | ||
7 | |||
8 | Additional properties are described here: | ||
9 | |||
10 | Required properties: | ||
11 | - compatible: Should contain "hisilicon,hip05-pcie". | ||
12 | - reg: Should contain rc_dbi, config registers location and length. | ||
13 | - reg-names: Must include the following entries: | ||
14 | "rc_dbi": controller configuration registers; | ||
15 | "config": PCIe configuration space registers. | ||
16 | - msi-parent: Should be its_pcie which is an ITS receiving MSI interrupts. | ||
17 | - port-id: Should be 0, 1, 2 or 3. | ||
18 | |||
19 | Optional properties: | ||
20 | - status: Either "ok" or "disabled". | ||
21 | - dma-coherent: Present if DMA operations are coherent. | ||
22 | |||
23 | Example: | ||
24 | pcie@0xb0080000 { | ||
25 | compatible = "hisilicon,hip05-pcie", "snps,dw-pcie"; | ||
26 | reg = <0 0xb0080000 0 0x10000>, <0x220 0x00000000 0 0x2000>; | ||
27 | reg-names = "rc_dbi", "config"; | ||
28 | bus-range = <0 15>; | ||
29 | msi-parent = <&its_pcie>; | ||
30 | #address-cells = <3>; | ||
31 | #size-cells = <2>; | ||
32 | device_type = "pci"; | ||
33 | dma-coherent; | ||
34 | ranges = <0x82000000 0 0x00000000 0x220 0x00000000 0 0x10000000>; | ||
35 | num-lanes = <8>; | ||
36 | port-id = <1>; | ||
37 | #interrupts-cells = <1>; | ||
38 | interrupts-map-mask = <0xf800 0 0 7>; | ||
39 | interrupts-map = <0x0 0 0 1 &mbigen_pcie 1 10 | ||
40 | 0x0 0 0 2 &mbigen_pcie 2 11 | ||
41 | 0x0 0 0 3 &mbigen_pcie 3 12 | ||
42 | 0x0 0 0 4 &mbigen_pcie 4 13>; | ||
43 | status = "ok"; | ||
44 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 7ba7ab749c85..944a229bb01e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -8047,6 +8047,13 @@ S: Maintained | |||
8047 | F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt | 8047 | F: Documentation/devicetree/bindings/pci/xgene-pci-msi.txt |
8048 | F: drivers/pci/host/pci-xgene-msi.c | 8048 | F: drivers/pci/host/pci-xgene-msi.c |
8049 | 8049 | ||
8050 | PCIE DRIVER FOR HISILICON | ||
8051 | M: Zhou Wang <wangzhou1@hisilicon.com> | ||
8052 | L: linux-pci@vger.kernel.org | ||
8053 | S: Maintained | ||
8054 | F: Documentation/devicetree/bindings/pci/hisilicon-pcie.txt | ||
8055 | F: drivers/pci/host/pcie-hisi.c | ||
8056 | |||
8050 | PCMCIA SUBSYSTEM | 8057 | PCMCIA SUBSYSTEM |
8051 | P: Linux PCMCIA Team | 8058 | P: Linux PCMCIA Team |
8052 | L: linux-pcmcia@lists.infradead.org | 8059 | L: linux-pcmcia@lists.infradead.org |
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index d5e58bae95cf..ae873beaff2b 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig | |||
@@ -145,4 +145,12 @@ config PCIE_IPROC_BCMA | |||
145 | Say Y here if you want to use the Broadcom iProc PCIe controller | 145 | Say Y here if you want to use the Broadcom iProc PCIe controller |
146 | through the BCMA bus interface | 146 | through the BCMA bus interface |
147 | 147 | ||
148 | config PCI_HISI | ||
149 | depends on OF && ARM64 | ||
150 | bool "HiSilicon SoC HIP05 PCIe controller" | ||
151 | select PCIEPORTBUS | ||
152 | select PCIE_DW | ||
153 | help | ||
154 | Say Y here if you want PCIe controller support on HiSilicon HIP05 SoC | ||
155 | |||
148 | endmenu | 156 | endmenu |
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 140d66f796e4..ea1dbf28ae41 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile | |||
@@ -17,3 +17,4 @@ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o | |||
17 | obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o | 17 | obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o |
18 | obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o | 18 | obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o |
19 | obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o | 19 | obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o |
20 | obj-$(CONFIG_PCI_HISI) += pcie-hisi.o | ||
diff --git a/drivers/pci/host/pcie-hisi.c b/drivers/pci/host/pcie-hisi.c new file mode 100644 index 000000000000..35457ecd8e70 --- /dev/null +++ b/drivers/pci/host/pcie-hisi.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * PCIe host controller driver for HiSilicon Hip05 SoC | ||
3 | * | ||
4 | * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com | ||
5 | * | ||
6 | * Author: Zhou Wang <wangzhou1@hisilicon.com> | ||
7 | * Dacai Zhu <zhudacai@hisilicon.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/mfd/syscon.h> | ||
16 | #include <linux/of_address.h> | ||
17 | #include <linux/of_pci.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/regmap.h> | ||
20 | |||
21 | #include "pcie-designware.h" | ||
22 | |||
23 | #define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 | ||
24 | #define PCIE_LTSSM_LINKUP_STATE 0x11 | ||
25 | #define PCIE_LTSSM_STATE_MASK 0x3F | ||
26 | |||
27 | #define to_hisi_pcie(x) container_of(x, struct hisi_pcie, pp) | ||
28 | |||
29 | struct hisi_pcie { | ||
30 | struct regmap *subctrl; | ||
31 | void __iomem *reg_base; | ||
32 | u32 port_id; | ||
33 | struct pcie_port pp; | ||
34 | }; | ||
35 | |||
36 | static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie, | ||
37 | u32 val, u32 reg) | ||
38 | { | ||
39 | writel(val, pcie->reg_base + reg); | ||
40 | } | ||
41 | |||
42 | static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg) | ||
43 | { | ||
44 | return readl(pcie->reg_base + reg); | ||
45 | } | ||
46 | |||
47 | /* Hip05 PCIe host only supports 32-bit config access */ | ||
48 | static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, | ||
49 | u32 *val) | ||
50 | { | ||
51 | u32 reg; | ||
52 | u32 reg_val; | ||
53 | struct hisi_pcie *pcie = to_hisi_pcie(pp); | ||
54 | void *walker = ®_val; | ||
55 | |||
56 | walker += (where & 0x3); | ||
57 | reg = where & ~0x3; | ||
58 | reg_val = hisi_pcie_apb_readl(pcie, reg); | ||
59 | |||
60 | if (size == 1) | ||
61 | *val = *(u8 __force *) walker; | ||
62 | else if (size == 2) | ||
63 | *val = *(u16 __force *) walker; | ||
64 | else if (size != 4) | ||
65 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
66 | |||
67 | return PCIBIOS_SUCCESSFUL; | ||
68 | } | ||
69 | |||
70 | /* Hip05 PCIe host only supports 32-bit config access */ | ||
71 | static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, | ||
72 | u32 val) | ||
73 | { | ||
74 | u32 reg_val; | ||
75 | u32 reg; | ||
76 | struct hisi_pcie *pcie = to_hisi_pcie(pp); | ||
77 | void *walker = ®_val; | ||
78 | |||
79 | walker += (where & 0x3); | ||
80 | reg = where & ~0x3; | ||
81 | if (size == 4) | ||
82 | hisi_pcie_apb_writel(pcie, val, reg); | ||
83 | else if (size == 2) { | ||
84 | reg_val = hisi_pcie_apb_readl(pcie, reg); | ||
85 | *(u16 __force *) walker = val; | ||
86 | hisi_pcie_apb_writel(pcie, reg_val, reg); | ||
87 | } else if (size == 1) { | ||
88 | reg_val = hisi_pcie_apb_readl(pcie, reg); | ||
89 | *(u8 __force *) walker = val; | ||
90 | hisi_pcie_apb_writel(pcie, reg_val, reg); | ||
91 | } else | ||
92 | return PCIBIOS_BAD_REGISTER_NUMBER; | ||
93 | |||
94 | return PCIBIOS_SUCCESSFUL; | ||
95 | } | ||
96 | |||
97 | static int hisi_pcie_link_up(struct pcie_port *pp) | ||
98 | { | ||
99 | u32 val; | ||
100 | struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); | ||
101 | |||
102 | regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG + | ||
103 | 0x100 * hisi_pcie->port_id, &val); | ||
104 | |||
105 | return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); | ||
106 | } | ||
107 | |||
108 | static struct pcie_host_ops hisi_pcie_host_ops = { | ||
109 | .rd_own_conf = hisi_pcie_cfg_read, | ||
110 | .wr_own_conf = hisi_pcie_cfg_write, | ||
111 | .link_up = hisi_pcie_link_up, | ||
112 | }; | ||
113 | |||
114 | static int __init hisi_add_pcie_port(struct pcie_port *pp, | ||
115 | struct platform_device *pdev) | ||
116 | { | ||
117 | int ret; | ||
118 | u32 port_id; | ||
119 | struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp); | ||
120 | |||
121 | if (of_property_read_u32(pdev->dev.of_node, "port-id", &port_id)) { | ||
122 | dev_err(&pdev->dev, "failed to read port-id\n"); | ||
123 | return -EINVAL; | ||
124 | } | ||
125 | if (port_id > 3) { | ||
126 | dev_err(&pdev->dev, "Invalid port-id: %d\n", port_id); | ||
127 | return -EINVAL; | ||
128 | } | ||
129 | hisi_pcie->port_id = port_id; | ||
130 | |||
131 | pp->ops = &hisi_pcie_host_ops; | ||
132 | |||
133 | ret = dw_pcie_host_init(pp); | ||
134 | if (ret) { | ||
135 | dev_err(&pdev->dev, "failed to initialize host\n"); | ||
136 | return ret; | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int __init hisi_pcie_probe(struct platform_device *pdev) | ||
143 | { | ||
144 | struct hisi_pcie *hisi_pcie; | ||
145 | struct pcie_port *pp; | ||
146 | struct resource *reg; | ||
147 | int ret; | ||
148 | |||
149 | hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL); | ||
150 | if (!hisi_pcie) | ||
151 | return -ENOMEM; | ||
152 | |||
153 | pp = &hisi_pcie->pp; | ||
154 | pp->dev = &pdev->dev; | ||
155 | |||
156 | hisi_pcie->subctrl = | ||
157 | syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); | ||
158 | if (IS_ERR(hisi_pcie->subctrl)) { | ||
159 | dev_err(pp->dev, "cannot get subctrl base\n"); | ||
160 | return PTR_ERR(hisi_pcie->subctrl); | ||
161 | } | ||
162 | |||
163 | reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); | ||
164 | hisi_pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg); | ||
165 | if (IS_ERR(hisi_pcie->reg_base)) { | ||
166 | dev_err(pp->dev, "cannot get rc_dbi base\n"); | ||
167 | return PTR_ERR(hisi_pcie->reg_base); | ||
168 | } | ||
169 | |||
170 | hisi_pcie->pp.dbi_base = hisi_pcie->reg_base; | ||
171 | |||
172 | ret = hisi_add_pcie_port(pp, pdev); | ||
173 | if (ret) | ||
174 | return ret; | ||
175 | |||
176 | platform_set_drvdata(pdev, hisi_pcie); | ||
177 | |||
178 | dev_warn(pp->dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n"); | ||
179 | |||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static const struct of_device_id hisi_pcie_of_match[] = { | ||
184 | {.compatible = "hisilicon,hip05-pcie",}, | ||
185 | {}, | ||
186 | }; | ||
187 | |||
188 | MODULE_DEVICE_TABLE(of, hisi_pcie_of_match); | ||
189 | |||
190 | static struct platform_driver hisi_pcie_driver = { | ||
191 | .probe = hisi_pcie_probe, | ||
192 | .driver = { | ||
193 | .name = "hisi-pcie", | ||
194 | .of_match_table = hisi_pcie_of_match, | ||
195 | }, | ||
196 | }; | ||
197 | |||
198 | module_platform_driver(hisi_pcie_driver); | ||