aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2015-10-07 16:05:49 -0400
committerArnd Bergmann <arnd@arndb.de>2015-10-08 10:32:48 -0400
commit498a92d42596a7a32c042319eb62a4c3d8081cf1 (patch)
treefa0a3daef1c40033e224400de2a9ef4d9bc54ed9
parent049e6dde7e57f0054fdc49102e7ef4830c698b46 (diff)
ARM: cns3xxx: pci: avoid potential stack overflow
The cns3xxx_pcie_hw_init function uses excessive kernel stack space because of a hack that puts a fake struct pci_sys_data and struct pci_bus on the stack in order to call the generic pci_bus_read_config accessors, which causes a warning in ARM allmodconfig builds: arch/arm/mach-cns3xxx/pcie.c:266:1: warning: the frame size of 1080 bytes is larger than 1024 bytes I've spent a few hours trying to find out what exactly this code is wants to achieve here. The obvious part is setting up the host_regs using config space accessors, and this can simply be changed to use direct MMIO accesses, as I do in this patch. The second part is how the driver sets up the Max_Read_Request_Size value for the first device/function on bus 1, i.e. the device plugged directly into the PCIe root port. For all I can tell, this is in fact incomplete, as it does not perform the same setting on devices attached to a PCIe switch, or multi-function devices. The solution for this part fortunately is even easier: if we just set the global pcie_bus_config variable to PCIE_BUS_PEER2PEER, all PCIe devices in the system are limited to 128 byte MPS, which in turn limits the MRRS to 128 bytes for all devices, and we no longer even need to touch any devices. With those two changes in place, we no longer need the fake pci_sys_data/pci_bus structures for faking config space writes, and the stack usage goes down as well. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Acked-by: Krzysztof Halasa <khalasa@piap.pl>
-rw-r--r--arch/arm/mach-cns3xxx/pcie.c71
1 files changed, 30 insertions, 41 deletions
diff --git a/arch/arm/mach-cns3xxx/pcie.c b/arch/arm/mach-cns3xxx/pcie.c
index c622c306c390..47905a50e075 100644
--- a/arch/arm/mach-cns3xxx/pcie.c
+++ b/arch/arm/mach-cns3xxx/pcie.c
@@ -65,8 +65,9 @@ static void __iomem *cns3xxx_pci_map_bus(struct pci_bus *bus,
65 65
66 /* 66 /*
67 * The CNS PCI bridge doesn't fit into the PCI hierarchy, though 67 * The CNS PCI bridge doesn't fit into the PCI hierarchy, though
68 * we still want to access it. For this to work, we must place 68 * we still want to access it.
69 * the first device on the same bus as the CNS PCI bridge. 69 * We place the host bridge on bus 0, and the directly connected
70 * device on bus 1, slot 0.
70 */ 71 */
71 if (busno == 0) { /* internal PCIe bus, host bridge device */ 72 if (busno == 0) { /* internal PCIe bus, host bridge device */
72 if (devfn == 0) /* device# and function# are ignored by hw */ 73 if (devfn == 0) /* device# and function# are ignored by hw */
@@ -211,58 +212,46 @@ static void __init cns3xxx_pcie_check_link(struct cns3xxx_pcie *cnspci)
211 } 212 }
212} 213}
213 214
215static void cns3xxx_write_config(struct cns3xxx_pcie *cnspci,
216 int where, int size, u32 val)
217{
218 void __iomem *base = cnspci->host_regs + (where & 0xffc);
219 u32 v;
220 u32 mask = (0x1ull << (size * 8)) - 1;
221 int shift = (where % 4) * 8;
222
223 v = readl_relaxed(base + (where & 0xffc));
224
225 v &= ~(mask << shift);
226 v |= (val & mask) << shift;
227
228 writel_relaxed(v, base + (where & 0xffc));
229 readl_relaxed(base + (where & 0xffc));
230}
231
214static void __init cns3xxx_pcie_hw_init(struct cns3xxx_pcie *cnspci) 232static void __init cns3xxx_pcie_hw_init(struct cns3xxx_pcie *cnspci)
215{ 233{
216 int port = cnspci->port;
217 struct pci_sys_data sd = {
218 .private_data = cnspci,
219 };
220 struct pci_bus bus = {
221 .number = 0,
222 .ops = &cns3xxx_pcie_ops,
223 .sysdata = &sd,
224 };
225 u16 mem_base = cnspci->res_mem.start >> 16; 234 u16 mem_base = cnspci->res_mem.start >> 16;
226 u16 mem_limit = cnspci->res_mem.end >> 16; 235 u16 mem_limit = cnspci->res_mem.end >> 16;
227 u16 io_base = cnspci->res_io.start >> 16; 236 u16 io_base = cnspci->res_io.start >> 16;
228 u16 io_limit = cnspci->res_io.end >> 16; 237 u16 io_limit = cnspci->res_io.end >> 16;
229 u32 devfn = 0;
230 u8 tmp8;
231 u16 pos;
232 u16 dc;
233
234 pci_bus_write_config_byte(&bus, devfn, PCI_PRIMARY_BUS, 0);
235 pci_bus_write_config_byte(&bus, devfn, PCI_SECONDARY_BUS, 1);
236 pci_bus_write_config_byte(&bus, devfn, PCI_SUBORDINATE_BUS, 1);
237 238
238 pci_bus_read_config_byte(&bus, devfn, PCI_PRIMARY_BUS, &tmp8); 239 cns3xxx_write_config(cnspci, PCI_PRIMARY_BUS, 1, 0);
239 pci_bus_read_config_byte(&bus, devfn, PCI_SECONDARY_BUS, &tmp8); 240 cns3xxx_write_config(cnspci, PCI_SECONDARY_BUS, 1, 1);
240 pci_bus_read_config_byte(&bus, devfn, PCI_SUBORDINATE_BUS, &tmp8); 241 cns3xxx_write_config(cnspci, PCI_SUBORDINATE_BUS, 1, 1);
241 242 cns3xxx_write_config(cnspci, PCI_MEMORY_BASE, 2, mem_base);
242 pci_bus_write_config_word(&bus, devfn, PCI_MEMORY_BASE, mem_base); 243 cns3xxx_write_config(cnspci, PCI_MEMORY_LIMIT, 2, mem_limit);
243 pci_bus_write_config_word(&bus, devfn, PCI_MEMORY_LIMIT, mem_limit); 244 cns3xxx_write_config(cnspci, PCI_IO_BASE_UPPER16, 2, io_base);
244 pci_bus_write_config_word(&bus, devfn, PCI_IO_BASE_UPPER16, io_base); 245 cns3xxx_write_config(cnspci, PCI_IO_LIMIT_UPPER16, 2, io_limit);
245 pci_bus_write_config_word(&bus, devfn, PCI_IO_LIMIT_UPPER16, io_limit);
246 246
247 if (!cnspci->linked) 247 if (!cnspci->linked)
248 return; 248 return;
249 249
250 /* Set Device Max_Read_Request_Size to 128 byte */ 250 /* Set Device Max_Read_Request_Size to 128 byte */
251 bus.number = 1; /* directly connected PCIe device */ 251 pcie_bus_config = PCIE_BUS_PEER2PEER;
252 devfn = PCI_DEVFN(0, 0); 252
253 pos = pci_bus_find_capability(&bus, devfn, PCI_CAP_ID_EXP);
254 pci_bus_read_config_word(&bus, devfn, pos + PCI_EXP_DEVCTL, &dc);
255 if (dc & PCI_EXP_DEVCTL_READRQ) {
256 dc &= ~PCI_EXP_DEVCTL_READRQ;
257 pci_bus_write_config_word(&bus, devfn, pos + PCI_EXP_DEVCTL, dc);
258 pci_bus_read_config_word(&bus, devfn, pos + PCI_EXP_DEVCTL, &dc);
259 if (dc & PCI_EXP_DEVCTL_READRQ)
260 pr_warn("PCIe: Unable to set device Max_Read_Request_Size\n");
261 else
262 pr_info("PCIe: Max_Read_Request_Size set to 128 bytes\n");
263 }
264 /* Disable PCIe0 Interrupt Mask INTA to INTD */ 253 /* Disable PCIe0 Interrupt Mask INTA to INTD */
265 __raw_writel(~0x3FFF, MISC_PCIE_INT_MASK(port)); 254 __raw_writel(~0x3FFF, MISC_PCIE_INT_MASK(cnspci->port));
266} 255}
267 256
268static int cns3xxx_pcie_abort_handler(unsigned long addr, unsigned int fsr, 257static int cns3xxx_pcie_abort_handler(unsigned long addr, unsigned int fsr,