aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRay Jui <rjui@broadcom.com>2015-04-08 14:21:35 -0400
committerBjorn Helgaas <bhelgaas@google.com>2015-04-08 15:19:36 -0400
commit1fb37a8178da007cad75f48e2355b9dcb0711e06 (patch)
tree640deb8a0d9d13626eecc58cdc83329d0c5b466a /drivers/pci
parent1b55d62259187fe77ade87dccc33522d51f113ee (diff)
PCI: iproc: Add Broadcom iProc PCIe support
Add support for the Broadcom iProc PCIe controller. pcie-iproc.c is the common core driver, and a front-end bus interface needs to be added to support different bus interfaces. pcie-iproc-platform.c contains the support for the platform bus interface. Signed-off-by: Ray Jui <rjui@broadcom.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> Acked-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/host/Kconfig19
-rw-r--r--drivers/pci/host/Makefile2
-rw-r--r--drivers/pci/host/pcie-iproc-platform.c108
-rw-r--r--drivers/pci/host/pcie-iproc.c268
-rw-r--r--drivers/pci/host/pcie-iproc.h42
5 files changed, 439 insertions, 0 deletions
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 7b892a9cc4fc..1dfb567b3522 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -106,4 +106,23 @@ config PCI_VERSATILE
106 bool "ARM Versatile PB PCI controller" 106 bool "ARM Versatile PB PCI controller"
107 depends on ARCH_VERSATILE 107 depends on ARCH_VERSATILE
108 108
109config PCIE_IPROC
110 tristate "Broadcom iProc PCIe controller"
111 depends on OF && ARM
112 default n
113 help
114 This enables the iProc PCIe core controller support for Broadcom's
115 iProc family of SoCs. An appropriate bus interface driver also needs
116 to be enabled
117
118config PCIE_IPROC_PLATFORM
119 tristate "Broadcom iProc PCIe platform bus driver"
120 depends on ARCH_BCM_IPROC || (ARM && COMPILE_TEST)
121 depends on OF
122 select PCIE_IPROC
123 default ARCH_BCM_IPROC
124 help
125 Say Y here if you want to use the Broadcom iProc PCIe controller
126 through the generic platform bus interface
127
109endmenu 128endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index e61d91c92bf1..f733b4e27642 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -13,3 +13,5 @@ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o
13obj-$(CONFIG_PCI_XGENE) += pci-xgene.o 13obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
14obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o 14obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
15obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o 15obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
16obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
17obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c
new file mode 100644
index 000000000000..afad6c21fcfa
--- /dev/null
+++ b/drivers/pci/host/pcie-iproc-platform.c
@@ -0,0 +1,108 @@
1/*
2 * Copyright (C) 2015 Broadcom Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
7 *
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/kernel.h>
15#include <linux/pci.h>
16#include <linux/clk.h>
17#include <linux/module.h>
18#include <linux/slab.h>
19#include <linux/interrupt.h>
20#include <linux/platform_device.h>
21#include <linux/of_address.h>
22#include <linux/of_pci.h>
23#include <linux/of_irq.h>
24#include <linux/of_platform.h>
25#include <linux/phy/phy.h>
26
27#include "pcie-iproc.h"
28
29static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
30{
31 struct iproc_pcie *pcie;
32 struct device_node *np = pdev->dev.of_node;
33 struct resource reg;
34 resource_size_t iobase = 0;
35 LIST_HEAD(res);
36 int ret;
37
38 pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL);
39 if (!pcie)
40 return -ENOMEM;
41
42 pcie->dev = &pdev->dev;
43 platform_set_drvdata(pdev, pcie);
44
45 ret = of_address_to_resource(np, 0, &reg);
46 if (ret < 0) {
47 dev_err(pcie->dev, "unable to obtain controller resources\n");
48 return ret;
49 }
50
51 pcie->base = devm_ioremap(pcie->dev, reg.start, resource_size(&reg));
52 if (!pcie->base) {
53 dev_err(pcie->dev, "unable to map controller registers\n");
54 return -ENOMEM;
55 }
56
57 /* PHY use is optional */
58 pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
59 if (IS_ERR(pcie->phy)) {
60 if (PTR_ERR(pcie->phy) == -EPROBE_DEFER)
61 return -EPROBE_DEFER;
62 pcie->phy = NULL;
63 }
64
65 ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase);
66 if (ret) {
67 dev_err(pcie->dev,
68 "unable to get PCI host bridge resources\n");
69 return ret;
70 }
71
72 pcie->resources = &res;
73
74 ret = iproc_pcie_setup(pcie);
75 if (ret) {
76 dev_err(pcie->dev, "PCIe controller setup failed\n");
77 return ret;
78 }
79
80 return 0;
81}
82
83static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
84{
85 struct iproc_pcie *pcie = platform_get_drvdata(pdev);
86
87 return iproc_pcie_remove(pcie);
88}
89
90static const struct of_device_id iproc_pcie_of_match_table[] = {
91 { .compatible = "brcm,iproc-pcie", },
92 { /* sentinel */ }
93};
94MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
95
96static struct platform_driver iproc_pcie_pltfm_driver = {
97 .driver = {
98 .name = "iproc-pcie",
99 .of_match_table = of_match_ptr(iproc_pcie_of_match_table),
100 },
101 .probe = iproc_pcie_pltfm_probe,
102 .remove = iproc_pcie_pltfm_remove,
103};
104module_platform_driver(iproc_pcie_pltfm_driver);
105
106MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
107MODULE_DESCRIPTION("Broadcom iPROC PCIe platform driver");
108MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
new file mode 100644
index 000000000000..329e1b54528b
--- /dev/null
+++ b/drivers/pci/host/pcie-iproc.c
@@ -0,0 +1,268 @@
1/*
2 * Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de>
3 * Copyright (C) 2015 Broadcom Corporatcommon ion
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation version 2.
8 *
9 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
10 * kind, whether express or implied; without even the implied warranty
11 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/kernel.h>
16#include <linux/pci.h>
17#include <linux/msi.h>
18#include <linux/clk.h>
19#include <linux/module.h>
20#include <linux/mbus.h>
21#include <linux/slab.h>
22#include <linux/delay.h>
23#include <linux/interrupt.h>
24#include <linux/platform_device.h>
25#include <linux/of_address.h>
26#include <linux/of_pci.h>
27#include <linux/of_irq.h>
28#include <linux/of_platform.h>
29#include <linux/phy/phy.h>
30
31#include "pcie-iproc.h"
32
33#define CLK_CONTROL_OFFSET 0x000
34#define EP_MODE_SURVIVE_PERST_SHIFT 1
35#define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT)
36#define RC_PCIE_RST_OUTPUT_SHIFT 0
37#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT)
38
39#define CFG_IND_ADDR_OFFSET 0x120
40#define CFG_IND_ADDR_MASK 0x00001ffc
41
42#define CFG_IND_DATA_OFFSET 0x124
43
44#define CFG_ADDR_OFFSET 0x1f8
45#define CFG_ADDR_BUS_NUM_SHIFT 20
46#define CFG_ADDR_BUS_NUM_MASK 0x0ff00000
47#define CFG_ADDR_DEV_NUM_SHIFT 15
48#define CFG_ADDR_DEV_NUM_MASK 0x000f8000
49#define CFG_ADDR_FUNC_NUM_SHIFT 12
50#define CFG_ADDR_FUNC_NUM_MASK 0x00007000
51#define CFG_ADDR_REG_NUM_SHIFT 2
52#define CFG_ADDR_REG_NUM_MASK 0x00000ffc
53#define CFG_ADDR_CFG_TYPE_SHIFT 0
54#define CFG_ADDR_CFG_TYPE_MASK 0x00000003
55
56#define CFG_DATA_OFFSET 0x1fc
57
58#define SYS_RC_INTX_EN 0x330
59#define SYS_RC_INTX_MASK 0xf
60
61static inline struct iproc_pcie *sys_to_pcie(struct pci_sys_data *sys)
62{
63 return sys->private_data;
64}
65
66/**
67 * Note access to the configuration registers are protected at the higher layer
68 * by 'pci_lock' in drivers/pci/access.c
69 */
70static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
71 unsigned int devfn,
72 int where)
73{
74 struct pci_sys_data *sys = bus->sysdata;
75 struct iproc_pcie *pcie = sys_to_pcie(sys);
76 unsigned slot = PCI_SLOT(devfn);
77 unsigned fn = PCI_FUNC(devfn);
78 unsigned busno = bus->number;
79 u32 val;
80
81 /* root complex access */
82 if (busno == 0) {
83 if (slot >= 1)
84 return NULL;
85 writel(where & CFG_IND_ADDR_MASK,
86 pcie->base + CFG_IND_ADDR_OFFSET);
87 return (pcie->base + CFG_IND_DATA_OFFSET);
88 }
89
90 if (fn > 1)
91 return NULL;
92
93 /* EP device access */
94 val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
95 (slot << CFG_ADDR_DEV_NUM_SHIFT) |
96 (fn << CFG_ADDR_FUNC_NUM_SHIFT) |
97 (where & CFG_ADDR_REG_NUM_MASK) |
98 (1 & CFG_ADDR_CFG_TYPE_MASK);
99 writel(val, pcie->base + CFG_ADDR_OFFSET);
100
101 return (pcie->base + CFG_DATA_OFFSET);
102}
103
104static struct pci_ops iproc_pcie_ops = {
105 .map_bus = iproc_pcie_map_cfg_bus,
106 .read = pci_generic_config_read32,
107 .write = pci_generic_config_write32,
108};
109
110static void iproc_pcie_reset(struct iproc_pcie *pcie)
111{
112 u32 val;
113
114 /*
115 * Configure the PCIe controller as root complex and send a downstream
116 * reset
117 */
118 val = EP_MODE_SURVIVE_PERST | RC_PCIE_RST_OUTPUT;
119 writel(val, pcie->base + CLK_CONTROL_OFFSET);
120 udelay(250);
121 val &= ~EP_MODE_SURVIVE_PERST;
122 writel(val, pcie->base + CLK_CONTROL_OFFSET);
123 msleep(250);
124}
125
126static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
127{
128 u8 hdr_type;
129 u32 link_ctrl;
130 u16 pos, link_status;
131 int link_is_active = 0;
132
133 /* make sure we are not in EP mode */
134 pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type);
135 if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) {
136 dev_err(pcie->dev, "in EP mode, hdr=%#02x\n", hdr_type);
137 return -EFAULT;
138 }
139
140 /* force class to PCI_CLASS_BRIDGE_PCI (0x0604) */
141 pci_bus_write_config_word(bus, 0, PCI_CLASS_DEVICE,
142 PCI_CLASS_BRIDGE_PCI);
143
144 /* check link status to see if link is active */
145 pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
146 pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status);
147 if (link_status & PCI_EXP_LNKSTA_NLW)
148 link_is_active = 1;
149
150 if (!link_is_active) {
151 /* try GEN 1 link speed */
152#define PCI_LINK_STATUS_CTRL_2_OFFSET 0x0dc
153#define PCI_TARGET_LINK_SPEED_MASK 0xf
154#define PCI_TARGET_LINK_SPEED_GEN2 0x2
155#define PCI_TARGET_LINK_SPEED_GEN1 0x1
156 pci_bus_read_config_dword(bus, 0,
157 PCI_LINK_STATUS_CTRL_2_OFFSET,
158 &link_ctrl);
159 if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) ==
160 PCI_TARGET_LINK_SPEED_GEN2) {
161 link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK;
162 link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1;
163 pci_bus_write_config_dword(bus, 0,
164 PCI_LINK_STATUS_CTRL_2_OFFSET,
165 link_ctrl);
166 msleep(100);
167
168 pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
169 pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA,
170 &link_status);
171 if (link_status & PCI_EXP_LNKSTA_NLW)
172 link_is_active = 1;
173 }
174 }
175
176 dev_info(pcie->dev, "link: %s\n", link_is_active ? "UP" : "DOWN");
177
178 return link_is_active ? 0 : -ENODEV;
179}
180
181static void iproc_pcie_enable(struct iproc_pcie *pcie)
182{
183 writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
184}
185
186int iproc_pcie_setup(struct iproc_pcie *pcie)
187{
188 int ret;
189 struct pci_bus *bus;
190
191 if (!pcie || !pcie->dev || !pcie->base)
192 return -EINVAL;
193
194 if (pcie->phy) {
195 ret = phy_init(pcie->phy);
196 if (ret) {
197 dev_err(pcie->dev, "unable to initialize PCIe PHY\n");
198 return ret;
199 }
200
201 ret = phy_power_on(pcie->phy);
202 if (ret) {
203 dev_err(pcie->dev, "unable to power on PCIe PHY\n");
204 goto err_exit_phy;
205 }
206
207 }
208
209 iproc_pcie_reset(pcie);
210
211 pcie->sysdata.private_data = pcie;
212
213 bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops,
214 &pcie->sysdata, pcie->resources);
215 if (!bus) {
216 dev_err(pcie->dev, "unable to create PCI root bus\n");
217 ret = -ENOMEM;
218 goto err_power_off_phy;
219 }
220 pcie->root_bus = bus;
221
222 ret = iproc_pcie_check_link(pcie, bus);
223 if (ret) {
224 dev_err(pcie->dev, "no PCIe EP device detected\n");
225 goto err_rm_root_bus;
226 }
227
228 iproc_pcie_enable(pcie);
229
230 pci_scan_child_bus(bus);
231 pci_assign_unassigned_bus_resources(bus);
232 pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
233 pci_bus_add_devices(bus);
234
235 return 0;
236
237err_rm_root_bus:
238 pci_stop_root_bus(bus);
239 pci_remove_root_bus(bus);
240
241err_power_off_phy:
242 if (pcie->phy)
243 phy_power_off(pcie->phy);
244err_exit_phy:
245 if (pcie->phy)
246 phy_exit(pcie->phy);
247
248 return ret;
249}
250EXPORT_SYMBOL(iproc_pcie_setup);
251
252int iproc_pcie_remove(struct iproc_pcie *pcie)
253{
254 pci_stop_root_bus(pcie->root_bus);
255 pci_remove_root_bus(pcie->root_bus);
256
257 if (pcie->phy) {
258 phy_power_off(pcie->phy);
259 phy_exit(pcie->phy);
260 }
261
262 return 0;
263}
264EXPORT_SYMBOL(iproc_pcie_remove);
265
266MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>");
267MODULE_DESCRIPTION("Broadcom iPROC PCIe common driver");
268MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
new file mode 100644
index 000000000000..e28075ed1856
--- /dev/null
+++ b/drivers/pci/host/pcie-iproc.h
@@ -0,0 +1,42 @@
1/*
2 * Copyright (C) 2014-2015 Broadcom Corporation
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
7 *
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#ifndef _PCIE_IPROC_H
15#define _PCIE_IPROC_H
16
17#define IPROC_PCIE_MAX_NUM_IRQS 6
18
19/**
20 * iProc PCIe device
21 * @dev: pointer to device data structure
22 * @base: PCIe host controller I/O register base
23 * @resources: linked list of all PCI resources
24 * @sysdata: Per PCI controller data
25 * @root_bus: pointer to root bus
26 * @phy: optional PHY device that controls the Serdes
27 * @irqs: interrupt IDs
28 */
29struct iproc_pcie {
30 struct device *dev;
31 void __iomem *base;
32 struct list_head *resources;
33 struct pci_sys_data sysdata;
34 struct pci_bus *root_bus;
35 struct phy *phy;
36 int irqs[IPROC_PCIE_MAX_NUM_IRQS];
37};
38
39int iproc_pcie_setup(struct iproc_pcie *pcie);
40int iproc_pcie_remove(struct iproc_pcie *pcie);
41
42#endif /* _PCIE_IPROC_H */