Commit | Line | Data |
---|---|---|
5f32f7a0 AV |
1 | /* |
2 | * PCI-E support for CNS3xxx | |
3 | * | |
4 | * Copyright 2008 Cavium Networks | |
5 | * Richard Liu <richard.liu@caviumnetworks.com> | |
6 | * Copyright 2010 MontaVista Software, LLC. | |
7 | * Anton Vorontsov <avorontsov@mvista.com> | |
8 | * | |
9 | * This file 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 | ||
14 | #include <linux/init.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/bug.h> | |
17 | #include <linux/pci.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/ioport.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/ptrace.h> | |
22 | #include <asm/mach/map.h> | |
3f9fb2a0 | 23 | #include "cns3xxx.h" |
5f32f7a0 AV |
24 | #include "core.h" |
25 | ||
5f32f7a0 | 26 | struct cns3xxx_pcie { |
64cf9d07 KH |
27 | void __iomem *host_regs; /* PCI config registers for host bridge */ |
28 | void __iomem *cfg0_regs; /* PCI Type 0 config registers */ | |
29 | void __iomem *cfg1_regs; /* PCI Type 1 config registers */ | |
5f32f7a0 AV |
30 | unsigned int irqs[2]; |
31 | struct resource res_io; | |
32 | struct resource res_mem; | |
c88d54ba | 33 | int port; |
5f32f7a0 AV |
34 | bool linked; |
35 | }; | |
36 | ||
5f32f7a0 AV |
37 | static struct cns3xxx_pcie *sysdata_to_cnspci(void *sysdata) |
38 | { | |
39 | struct pci_sys_data *root = sysdata; | |
40 | ||
c88d54ba | 41 | return root->private_data; |
5f32f7a0 AV |
42 | } |
43 | ||
7caaf7ef | 44 | static struct cns3xxx_pcie *pdev_to_cnspci(const struct pci_dev *dev) |
5f32f7a0 AV |
45 | { |
46 | return sysdata_to_cnspci(dev->sysdata); | |
47 | } | |
48 | ||
49 | static struct cns3xxx_pcie *pbus_to_cnspci(struct pci_bus *bus) | |
50 | { | |
51 | return sysdata_to_cnspci(bus->sysdata); | |
52 | } | |
53 | ||
802b7c06 RH |
54 | static void __iomem *cns3xxx_pci_map_bus(struct pci_bus *bus, |
55 | unsigned int devfn, int where) | |
5f32f7a0 AV |
56 | { |
57 | struct cns3xxx_pcie *cnspci = pbus_to_cnspci(bus); | |
58 | int busno = bus->number; | |
59 | int slot = PCI_SLOT(devfn); | |
5f32f7a0 AV |
60 | void __iomem *base; |
61 | ||
62 | /* If there is no link, just show the CNS PCI bridge. */ | |
defaa4d1 | 63 | if (!cnspci->linked && busno > 0) |
5f32f7a0 AV |
64 | return NULL; |
65 | ||
66 | /* | |
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 | |
69 | * the first device on the same bus as the CNS PCI bridge. | |
70 | */ | |
defaa4d1 KH |
71 | if (busno == 0) { /* internal PCIe bus, host bridge device */ |
72 | if (devfn == 0) /* device# and function# are ignored by hw */ | |
64cf9d07 | 73 | base = cnspci->host_regs; |
defaa4d1 KH |
74 | else |
75 | return NULL; /* no such device */ | |
76 | ||
77 | } else if (busno == 1) { /* directly connected PCIe device */ | |
78 | if (slot == 0) /* device# is ignored by hw */ | |
64cf9d07 | 79 | base = cnspci->cfg0_regs; |
defaa4d1 | 80 | else |
64cf9d07 | 81 | return NULL; /* no such device */ |
64cf9d07 | 82 | } else /* remote PCI bus */ |
defaa4d1 | 83 | base = cnspci->cfg1_regs + ((busno & 0xf) << 20); |
5f32f7a0 | 84 | |
defaa4d1 | 85 | return base + (where & 0xffc) + (devfn << 12); |
5f32f7a0 AV |
86 | } |
87 | ||
88 | static int cns3xxx_pci_read_config(struct pci_bus *bus, unsigned int devfn, | |
89 | int where, int size, u32 *val) | |
90 | { | |
802b7c06 | 91 | int ret; |
5f32f7a0 AV |
92 | u32 mask = (0x1ull << (size * 8)) - 1; |
93 | int shift = (where % 4) * 8; | |
94 | ||
802b7c06 | 95 | ret = pci_generic_config_read32(bus, devfn, where, size, val); |
5f32f7a0 | 96 | |
802b7c06 RH |
97 | if (ret == PCIBIOS_SUCCESSFUL && !bus->number && !devfn && |
98 | (where & 0xffc) == PCI_CLASS_REVISION) | |
5f32f7a0 AV |
99 | /* |
100 | * RC's class is 0xb, but Linux PCI driver needs 0x604 | |
101 | * for a PCIe bridge. So we must fixup the class code | |
102 | * to 0x604 here. | |
103 | */ | |
802b7c06 | 104 | *val = ((((*val << shift) & 0xff) | (0x604 << 16)) >> shift) & mask; |
5f32f7a0 | 105 | |
802b7c06 | 106 | return ret; |
5f32f7a0 AV |
107 | } |
108 | ||
109 | static int cns3xxx_pci_setup(int nr, struct pci_sys_data *sys) | |
110 | { | |
111 | struct cns3xxx_pcie *cnspci = sysdata_to_cnspci(sys); | |
112 | struct resource *res_io = &cnspci->res_io; | |
113 | struct resource *res_mem = &cnspci->res_mem; | |
5f32f7a0 AV |
114 | |
115 | BUG_ON(request_resource(&iomem_resource, res_io) || | |
116 | request_resource(&iomem_resource, res_mem)); | |
117 | ||
9f786d03 BH |
118 | pci_add_resource_offset(&sys->resources, res_io, sys->io_offset); |
119 | pci_add_resource_offset(&sys->resources, res_mem, sys->mem_offset); | |
5f32f7a0 AV |
120 | |
121 | return 1; | |
122 | } | |
123 | ||
124 | static struct pci_ops cns3xxx_pcie_ops = { | |
802b7c06 | 125 | .map_bus = cns3xxx_pci_map_bus, |
5f32f7a0 | 126 | .read = cns3xxx_pci_read_config, |
802b7c06 | 127 | .write = pci_generic_config_write, |
5f32f7a0 AV |
128 | }; |
129 | ||
d5341942 | 130 | static int cns3xxx_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) |
5f32f7a0 AV |
131 | { |
132 | struct cns3xxx_pcie *cnspci = pdev_to_cnspci(dev); | |
defaa4d1 | 133 | int irq = cnspci->irqs[!!dev->bus->number]; |
5f32f7a0 AV |
134 | |
135 | pr_info("PCIe map irq: %04d:%02x:%02x.%02x slot %d, pin %d, irq: %d\n", | |
136 | pci_domain_nr(dev->bus), dev->bus->number, PCI_SLOT(dev->devfn), | |
137 | PCI_FUNC(dev->devfn), slot, pin, irq); | |
138 | ||
139 | return irq; | |
140 | } | |
141 | ||
142 | static struct cns3xxx_pcie cns3xxx_pcie[] = { | |
143 | [0] = { | |
64cf9d07 KH |
144 | .host_regs = (void __iomem *)CNS3XXX_PCIE0_HOST_BASE_VIRT, |
145 | .cfg0_regs = (void __iomem *)CNS3XXX_PCIE0_CFG0_BASE_VIRT, | |
146 | .cfg1_regs = (void __iomem *)CNS3XXX_PCIE0_CFG1_BASE_VIRT, | |
5f32f7a0 AV |
147 | .res_io = { |
148 | .name = "PCIe0 I/O space", | |
149 | .start = CNS3XXX_PCIE0_IO_BASE, | |
64cf9d07 | 150 | .end = CNS3XXX_PCIE0_CFG0_BASE - 1, /* 16 MiB */ |
5f32f7a0 AV |
151 | .flags = IORESOURCE_IO, |
152 | }, | |
153 | .res_mem = { | |
154 | .name = "PCIe0 non-prefetchable", | |
155 | .start = CNS3XXX_PCIE0_MEM_BASE, | |
64cf9d07 | 156 | .end = CNS3XXX_PCIE0_HOST_BASE - 1, /* 176 MiB */ |
5f32f7a0 AV |
157 | .flags = IORESOURCE_MEM, |
158 | }, | |
159 | .irqs = { IRQ_CNS3XXX_PCIE0_RC, IRQ_CNS3XXX_PCIE0_DEVICE, }, | |
c88d54ba | 160 | .port = 0, |
5f32f7a0 AV |
161 | }, |
162 | [1] = { | |
64cf9d07 KH |
163 | .host_regs = (void __iomem *)CNS3XXX_PCIE1_HOST_BASE_VIRT, |
164 | .cfg0_regs = (void __iomem *)CNS3XXX_PCIE1_CFG0_BASE_VIRT, | |
165 | .cfg1_regs = (void __iomem *)CNS3XXX_PCIE1_CFG1_BASE_VIRT, | |
5f32f7a0 AV |
166 | .res_io = { |
167 | .name = "PCIe1 I/O space", | |
168 | .start = CNS3XXX_PCIE1_IO_BASE, | |
64cf9d07 | 169 | .end = CNS3XXX_PCIE1_CFG0_BASE - 1, /* 16 MiB */ |
5f32f7a0 AV |
170 | .flags = IORESOURCE_IO, |
171 | }, | |
172 | .res_mem = { | |
173 | .name = "PCIe1 non-prefetchable", | |
174 | .start = CNS3XXX_PCIE1_MEM_BASE, | |
64cf9d07 | 175 | .end = CNS3XXX_PCIE1_HOST_BASE - 1, /* 176 MiB */ |
5f32f7a0 AV |
176 | .flags = IORESOURCE_MEM, |
177 | }, | |
178 | .irqs = { IRQ_CNS3XXX_PCIE1_RC, IRQ_CNS3XXX_PCIE1_DEVICE, }, | |
c88d54ba | 179 | .port = 1, |
5f32f7a0 AV |
180 | }, |
181 | }; | |
182 | ||
183 | static void __init cns3xxx_pcie_check_link(struct cns3xxx_pcie *cnspci) | |
184 | { | |
c88d54ba | 185 | int port = cnspci->port; |
5f32f7a0 AV |
186 | u32 reg; |
187 | unsigned long time; | |
188 | ||
189 | reg = __raw_readl(MISC_PCIE_CTRL(port)); | |
190 | /* | |
191 | * Enable Application Request to 1, it will exit L1 automatically, | |
192 | * but when chip back, it will use another clock, still can use 0x1. | |
193 | */ | |
194 | reg |= 0x3; | |
195 | __raw_writel(reg, MISC_PCIE_CTRL(port)); | |
196 | ||
197 | pr_info("PCIe: Port[%d] Enable PCIe LTSSM\n", port); | |
198 | pr_info("PCIe: Port[%d] Check data link layer...", port); | |
199 | ||
200 | time = jiffies; | |
201 | while (1) { | |
202 | reg = __raw_readl(MISC_PCIE_PM_DEBUG(port)); | |
203 | if (reg & 0x1) { | |
204 | pr_info("Link up.\n"); | |
205 | cnspci->linked = 1; | |
206 | break; | |
207 | } else if (time_after(jiffies, time + 50)) { | |
208 | pr_info("Device not found.\n"); | |
209 | break; | |
210 | } | |
211 | } | |
212 | } | |
213 | ||
214 | static void __init cns3xxx_pcie_hw_init(struct cns3xxx_pcie *cnspci) | |
215 | { | |
c88d54ba | 216 | int port = cnspci->port; |
5f32f7a0 | 217 | struct pci_sys_data sd = { |
c88d54ba | 218 | .private_data = cnspci, |
5f32f7a0 AV |
219 | }; |
220 | struct pci_bus bus = { | |
221 | .number = 0, | |
222 | .ops = &cns3xxx_pcie_ops, | |
223 | .sysdata = &sd, | |
224 | }; | |
64cf9d07 KH |
225 | u16 mem_base = cnspci->res_mem.start >> 16; |
226 | u16 mem_limit = cnspci->res_mem.end >> 16; | |
227 | u16 io_base = cnspci->res_io.start >> 16; | |
228 | u16 io_limit = cnspci->res_io.end >> 16; | |
5f32f7a0 AV |
229 | u32 devfn = 0; |
230 | u8 tmp8; | |
231 | u16 pos; | |
232 | u16 dc; | |
233 | ||
5f32f7a0 AV |
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 | pci_bus_read_config_byte(&bus, devfn, PCI_PRIMARY_BUS, &tmp8); | |
239 | pci_bus_read_config_byte(&bus, devfn, PCI_SECONDARY_BUS, &tmp8); | |
240 | pci_bus_read_config_byte(&bus, devfn, PCI_SUBORDINATE_BUS, &tmp8); | |
241 | ||
242 | pci_bus_write_config_word(&bus, devfn, PCI_MEMORY_BASE, mem_base); | |
64cf9d07 | 243 | pci_bus_write_config_word(&bus, devfn, PCI_MEMORY_LIMIT, mem_limit); |
5f32f7a0 | 244 | pci_bus_write_config_word(&bus, devfn, PCI_IO_BASE_UPPER16, io_base); |
64cf9d07 | 245 | pci_bus_write_config_word(&bus, devfn, PCI_IO_LIMIT_UPPER16, io_limit); |
5f32f7a0 AV |
246 | |
247 | if (!cnspci->linked) | |
248 | return; | |
249 | ||
250 | /* Set Device Max_Read_Request_Size to 128 byte */ | |
defaa4d1 KH |
251 | bus.number = 1; /* directly connected PCIe device */ |
252 | devfn = PCI_DEVFN(0, 0); | |
5f32f7a0 AV |
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); | |
367dc4b7 KH |
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 | } | |
5f32f7a0 AV |
264 | /* Disable PCIe0 Interrupt Mask INTA to INTD */ |
265 | __raw_writel(~0x3FFF, MISC_PCIE_INT_MASK(port)); | |
266 | } | |
267 | ||
268 | static int cns3xxx_pcie_abort_handler(unsigned long addr, unsigned int fsr, | |
269 | struct pt_regs *regs) | |
270 | { | |
271 | if (fsr & (1 << 10)) | |
272 | regs->ARM_pc += 4; | |
273 | return 0; | |
274 | } | |
275 | ||
0a2e912d | 276 | void __init cns3xxx_pcie_init_late(void) |
5f32f7a0 AV |
277 | { |
278 | int i; | |
c88d54ba LP |
279 | void *private_data; |
280 | struct hw_pci hw_pci = { | |
281 | .nr_controllers = 1, | |
282 | .ops = &cns3xxx_pcie_ops, | |
283 | .setup = cns3xxx_pci_setup, | |
284 | .map_irq = cns3xxx_pcie_map_irq, | |
285 | .private_data = &private_data, | |
286 | }; | |
5f32f7a0 | 287 | |
c9d95fbe RH |
288 | pcibios_min_io = 0; |
289 | pcibios_min_mem = 0; | |
290 | ||
44266416 | 291 | hook_fault_code(16 + 6, cns3xxx_pcie_abort_handler, SIGBUS, 0, |
5f32f7a0 AV |
292 | "imprecise external abort"); |
293 | ||
294 | for (i = 0; i < ARRAY_SIZE(cns3xxx_pcie); i++) { | |
5f32f7a0 AV |
295 | cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_PCIE(i)); |
296 | cns3xxx_pwr_soft_rst(0x1 << PM_SOFT_RST_REG_OFFST_PCIE(i)); | |
297 | cns3xxx_pcie_check_link(&cns3xxx_pcie[i]); | |
298 | cns3xxx_pcie_hw_init(&cns3xxx_pcie[i]); | |
c88d54ba LP |
299 | private_data = &cns3xxx_pcie[i]; |
300 | pci_common_init(&hw_pci); | |
5f32f7a0 AV |
301 | } |
302 | ||
303 | pci_assign_unassigned_resources(); | |
5f32f7a0 | 304 | } |