Commit | Line | Data |
---|---|---|
21e0534a HM |
1 | /* |
2 | * Broadcom specific AMBA | |
3 | * Broadcom MIPS32 74K core driver | |
4 | * | |
5 | * Copyright 2009, Broadcom Corporation | |
6 | * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de> | |
7 | * Copyright 2010, Bernhard Loos <bernhardloos@googlemail.com> | |
8 | * Copyright 2011, Hauke Mehrtens <hauke@hauke-m.de> | |
9 | * | |
10 | * Licensed under the GNU/GPL. See COPYING for details. | |
11 | */ | |
12 | ||
13 | #include "bcma_private.h" | |
14 | ||
15 | #include <linux/bcma/bcma.h> | |
16 | ||
17 | #include <linux/serial.h> | |
18 | #include <linux/serial_core.h> | |
19 | #include <linux/serial_reg.h> | |
20 | #include <linux/time.h> | |
7177efc5 | 21 | #ifdef CONFIG_BCM47XX |
138173d4 | 22 | #include <linux/bcm47xx_nvram.h> |
7177efc5 | 23 | #endif |
21e0534a | 24 | |
87fed556 RM |
25 | enum bcma_boot_dev { |
26 | BCMA_BOOT_DEV_UNK = 0, | |
27 | BCMA_BOOT_DEV_ROM, | |
28 | BCMA_BOOT_DEV_PARALLEL, | |
29 | BCMA_BOOT_DEV_SERIAL, | |
30 | BCMA_BOOT_DEV_NAND, | |
31 | }; | |
32 | ||
21e0534a HM |
33 | /* The 47162a0 hangs when reading MIPS DMP registers registers */ |
34 | static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev) | |
35 | { | |
4b4f5be2 HM |
36 | return dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM47162 && |
37 | dev->bus->chipinfo.rev == 0 && dev->id.id == BCMA_CORE_MIPS_74K; | |
21e0534a HM |
38 | } |
39 | ||
40 | /* The 5357b0 hangs when reading USB20H DMP registers */ | |
41 | static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev) | |
42 | { | |
4b4f5be2 HM |
43 | return (dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || |
44 | dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) && | |
21e0534a HM |
45 | dev->bus->chipinfo.pkg == 11 && |
46 | dev->id.id == BCMA_CORE_USB20_HOST; | |
47 | } | |
48 | ||
49 | static inline u32 mips_read32(struct bcma_drv_mips *mcore, | |
50 | u16 offset) | |
51 | { | |
52 | return bcma_read32(mcore->core, offset); | |
53 | } | |
54 | ||
21e0534a HM |
55 | static u32 bcma_core_mips_irqflag(struct bcma_device *dev) |
56 | { | |
57 | u32 flag; | |
58 | ||
59 | if (bcma_core_mips_bcm47162a0_quirk(dev)) | |
60 | return dev->core_index; | |
61 | if (bcma_core_mips_bcm5357b0_quirk(dev)) | |
62 | return dev->core_index; | |
63 | flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30); | |
64 | ||
db5230d1 HM |
65 | if (flag) |
66 | return flag & 0x1F; | |
67 | else | |
68 | return 0x3f; | |
21e0534a HM |
69 | } |
70 | ||
71 | /* Get the MIPS IRQ assignment for a specified device. | |
72 | * If unassigned, 0 is returned. | |
db5230d1 HM |
73 | * If disabled, 5 is returned. |
74 | * If not supported, 6 is returned. | |
21e0534a | 75 | */ |
85eb92e8 | 76 | unsigned int bcma_core_mips_irq(struct bcma_device *dev) |
21e0534a HM |
77 | { |
78 | struct bcma_device *mdev = dev->bus->drv_mips.core; | |
79 | u32 irqflag; | |
80 | unsigned int irq; | |
81 | ||
82 | irqflag = bcma_core_mips_irqflag(dev); | |
db5230d1 HM |
83 | if (irqflag == 0x3f) |
84 | return 6; | |
21e0534a | 85 | |
db5230d1 | 86 | for (irq = 0; irq <= 4; irq++) |
21e0534a HM |
87 | if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) & |
88 | (1 << irqflag)) | |
89 | return irq; | |
90 | ||
db5230d1 | 91 | return 5; |
21e0534a | 92 | } |
e2aa19fa | 93 | |
21e0534a HM |
94 | static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) |
95 | { | |
96 | unsigned int oldirq = bcma_core_mips_irq(dev); | |
97 | struct bcma_bus *bus = dev->bus; | |
98 | struct bcma_device *mdev = bus->drv_mips.core; | |
99 | u32 irqflag; | |
100 | ||
101 | irqflag = bcma_core_mips_irqflag(dev); | |
102 | BUG_ON(oldirq == 6); | |
103 | ||
104 | dev->irq = irq + 2; | |
105 | ||
106 | /* clear the old irq */ | |
107 | if (oldirq == 0) | |
108 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0), | |
109 | bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) & | |
110 | ~(1 << irqflag)); | |
db5230d1 | 111 | else if (oldirq != 5) |
cbbc0138 | 112 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(oldirq), 0); |
21e0534a HM |
113 | |
114 | /* assign the new one */ | |
115 | if (irq == 0) { | |
116 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0), | |
117 | bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) | | |
118 | (1 << irqflag)); | |
119 | } else { | |
6ba1eafe HM |
120 | u32 irqinitmask = bcma_read32(mdev, |
121 | BCMA_MIPS_MIPS74K_INTMASK(irq)); | |
122 | if (irqinitmask) { | |
21e0534a HM |
123 | struct bcma_device *core; |
124 | ||
125 | /* backplane irq line is in use, find out who uses | |
126 | * it and set user to irq 0 | |
127 | */ | |
d8f1bd2f | 128 | list_for_each_entry(core, &bus->cores, list) { |
21e0534a | 129 | if ((1 << bcma_core_mips_irqflag(core)) == |
6ba1eafe | 130 | irqinitmask) { |
21e0534a HM |
131 | bcma_core_mips_set_irq(core, 0); |
132 | break; | |
133 | } | |
134 | } | |
135 | } | |
136 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), | |
137 | 1 << irqflag); | |
138 | } | |
139 | ||
7401cb63 | 140 | bcma_debug(bus, "set_irq: core 0x%04x, irq %d => %d\n", |
db5230d1 | 141 | dev->id.id, oldirq <= 4 ? oldirq + 2 : 0, irq + 2); |
21e0534a HM |
142 | } |
143 | ||
e3f05a42 HM |
144 | static void bcma_core_mips_set_irq_name(struct bcma_bus *bus, unsigned int irq, |
145 | u16 coreid, u8 unit) | |
146 | { | |
147 | struct bcma_device *core; | |
148 | ||
149 | core = bcma_find_core_unit(bus, coreid, unit); | |
150 | if (!core) { | |
151 | bcma_warn(bus, | |
152 | "Can not find core (id: 0x%x, unit %i) for IRQ configuration.\n", | |
153 | coreid, unit); | |
154 | return; | |
155 | } | |
156 | ||
157 | bcma_core_mips_set_irq(core, irq); | |
158 | } | |
159 | ||
21e0534a HM |
160 | static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq) |
161 | { | |
162 | int i; | |
163 | static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"}; | |
361de091 | 164 | char interrupts[25]; |
758f7e06 | 165 | char *ints = interrupts; |
66cc0442 | 166 | |
758f7e06 JP |
167 | for (i = 0; i < ARRAY_SIZE(irq_name); i++) |
168 | ints += sprintf(ints, " %s%c", | |
169 | irq_name[i], i == irq ? '*' : ' '); | |
170 | ||
171 | bcma_debug(dev->bus, "core 0x%04x, irq:%s\n", dev->id.id, interrupts); | |
21e0534a HM |
172 | } |
173 | ||
174 | static void bcma_core_mips_dump_irq(struct bcma_bus *bus) | |
175 | { | |
176 | struct bcma_device *core; | |
177 | ||
d8f1bd2f | 178 | list_for_each_entry(core, &bus->cores, list) { |
21e0534a HM |
179 | bcma_core_mips_print_irq(core, bcma_core_mips_irq(core)); |
180 | } | |
181 | } | |
182 | ||
908debc8 HM |
183 | u32 bcma_cpu_clock(struct bcma_drv_mips *mcore) |
184 | { | |
185 | struct bcma_bus *bus = mcore->core->bus; | |
186 | ||
187 | if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU) | |
5b5ac414 | 188 | return bcma_pmu_get_cpu_clock(&bus->drv_cc); |
908debc8 | 189 | |
3d9d8af3 | 190 | bcma_err(bus, "No PMU available, need this to get the cpu clock\n"); |
908debc8 HM |
191 | return 0; |
192 | } | |
193 | EXPORT_SYMBOL(bcma_cpu_clock); | |
194 | ||
87fed556 RM |
195 | static enum bcma_boot_dev bcma_boot_dev(struct bcma_bus *bus) |
196 | { | |
197 | struct bcma_drv_cc *cc = &bus->drv_cc; | |
198 | u8 cc_rev = cc->core->id.rev; | |
199 | ||
200 | if (cc_rev == 42) { | |
201 | struct bcma_device *core; | |
202 | ||
203 | core = bcma_find_core(bus, BCMA_CORE_NS_ROM); | |
204 | if (core) { | |
205 | switch (bcma_aread32(core, BCMA_IOST) & | |
206 | BCMA_NS_ROM_IOST_BOOT_DEV_MASK) { | |
207 | case BCMA_NS_ROM_IOST_BOOT_DEV_NOR: | |
208 | return BCMA_BOOT_DEV_SERIAL; | |
209 | case BCMA_NS_ROM_IOST_BOOT_DEV_NAND: | |
210 | return BCMA_BOOT_DEV_NAND; | |
211 | case BCMA_NS_ROM_IOST_BOOT_DEV_ROM: | |
212 | default: | |
213 | return BCMA_BOOT_DEV_ROM; | |
214 | } | |
215 | } | |
216 | } else { | |
217 | if (cc_rev == 38) { | |
218 | if (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT) | |
219 | return BCMA_BOOT_DEV_NAND; | |
220 | else if (cc->status & BIT(5)) | |
221 | return BCMA_BOOT_DEV_ROM; | |
222 | } | |
223 | ||
224 | if ((cc->capabilities & BCMA_CC_CAP_FLASHT) == | |
225 | BCMA_CC_FLASHT_PARA) | |
226 | return BCMA_BOOT_DEV_PARALLEL; | |
227 | else | |
228 | return BCMA_BOOT_DEV_SERIAL; | |
229 | } | |
230 | ||
231 | return BCMA_BOOT_DEV_SERIAL; | |
232 | } | |
233 | ||
0ea6f0c5 | 234 | static void bcma_core_mips_nvram_init(struct bcma_drv_mips *mcore) |
21e0534a HM |
235 | { |
236 | struct bcma_bus *bus = mcore->core->bus; | |
87fed556 | 237 | enum bcma_boot_dev boot_dev; |
21e0534a | 238 | |
87fed556 RM |
239 | /* Determine flash type this SoC boots from */ |
240 | boot_dev = bcma_boot_dev(bus); | |
241 | switch (boot_dev) { | |
242 | case BCMA_BOOT_DEV_PARALLEL: | |
243 | case BCMA_BOOT_DEV_SERIAL: | |
7177efc5 RM |
244 | #ifdef CONFIG_BCM47XX |
245 | bcm47xx_nvram_init_from_mem(BCMA_SOC_FLASH2, | |
246 | BCMA_SOC_FLASH2_SZ); | |
247 | #endif | |
87fed556 RM |
248 | break; |
249 | case BCMA_BOOT_DEV_NAND: | |
7177efc5 RM |
250 | #ifdef CONFIG_BCM47XX |
251 | bcm47xx_nvram_init_from_mem(BCMA_SOC_FLASH1, | |
252 | BCMA_SOC_FLASH1_SZ); | |
253 | #endif | |
87fed556 RM |
254 | break; |
255 | default: | |
256 | break; | |
257 | } | |
21e0534a HM |
258 | } |
259 | ||
49655bb8 HM |
260 | void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) |
261 | { | |
7195439d RM |
262 | struct bcma_bus *bus = mcore->core->bus; |
263 | ||
49655bb8 HM |
264 | if (mcore->early_setup_done) |
265 | return; | |
266 | ||
7195439d | 267 | bcma_chipco_serial_init(&bus->drv_cc); |
0ea6f0c5 | 268 | bcma_core_mips_nvram_init(mcore); |
49655bb8 HM |
269 | |
270 | mcore->early_setup_done = true; | |
271 | } | |
272 | ||
6bf2e546 NH |
273 | static void bcma_fix_i2s_irqflag(struct bcma_bus *bus) |
274 | { | |
275 | struct bcma_device *cpu, *pcie, *i2s; | |
276 | ||
277 | /* Fixup the interrupts in 4716/4748 for i2s core (2010 Broadcom SDK) | |
278 | * (IRQ flags > 7 are ignored when setting the interrupt masks) | |
279 | */ | |
280 | if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4716 && | |
281 | bus->chipinfo.id != BCMA_CHIP_ID_BCM4748) | |
282 | return; | |
283 | ||
284 | cpu = bcma_find_core(bus, BCMA_CORE_MIPS_74K); | |
285 | pcie = bcma_find_core(bus, BCMA_CORE_PCIE); | |
286 | i2s = bcma_find_core(bus, BCMA_CORE_I2S); | |
287 | if (cpu && pcie && i2s && | |
288 | bcma_aread32(cpu, BCMA_MIPS_OOBSELINA74) == 0x08060504 && | |
289 | bcma_aread32(pcie, BCMA_MIPS_OOBSELINA74) == 0x08060504 && | |
290 | bcma_aread32(i2s, BCMA_MIPS_OOBSELOUTA30) == 0x88) { | |
291 | bcma_awrite32(cpu, BCMA_MIPS_OOBSELINA74, 0x07060504); | |
292 | bcma_awrite32(pcie, BCMA_MIPS_OOBSELINA74, 0x07060504); | |
293 | bcma_awrite32(i2s, BCMA_MIPS_OOBSELOUTA30, 0x87); | |
294 | bcma_debug(bus, | |
295 | "Moved i2s interrupt to oob line 7 instead of 8\n"); | |
296 | } | |
297 | } | |
298 | ||
21e0534a HM |
299 | void bcma_core_mips_init(struct bcma_drv_mips *mcore) |
300 | { | |
301 | struct bcma_bus *bus; | |
302 | struct bcma_device *core; | |
303 | bus = mcore->core->bus; | |
304 | ||
49655bb8 HM |
305 | if (mcore->setup_done) |
306 | return; | |
307 | ||
7401cb63 | 308 | bcma_debug(bus, "Initializing MIPS core...\n"); |
21e0534a | 309 | |
49655bb8 HM |
310 | bcma_core_mips_early_init(mcore); |
311 | ||
6bf2e546 NH |
312 | bcma_fix_i2s_irqflag(bus); |
313 | ||
e3f05a42 HM |
314 | switch (bus->chipinfo.id) { |
315 | case BCMA_CHIP_ID_BCM4716: | |
316 | case BCMA_CHIP_ID_BCM4748: | |
317 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); | |
318 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); | |
319 | bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0); | |
320 | bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_PCIE, 0); | |
321 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); | |
322 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0); | |
323 | break; | |
324 | case BCMA_CHIP_ID_BCM5356: | |
325 | case BCMA_CHIP_ID_BCM47162: | |
326 | case BCMA_CHIP_ID_BCM53572: | |
327 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); | |
328 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); | |
329 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); | |
330 | break; | |
331 | case BCMA_CHIP_ID_BCM5357: | |
332 | case BCMA_CHIP_ID_BCM4749: | |
333 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); | |
334 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); | |
335 | bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0); | |
336 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); | |
337 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0); | |
338 | break; | |
339 | case BCMA_CHIP_ID_BCM4706: | |
340 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_PCIE, 0); | |
341 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_4706_MAC_GBIT, | |
342 | 0); | |
343 | bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_PCIE, 1); | |
344 | bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_USB20_HOST, 0); | |
345 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_4706_CHIPCOMMON, | |
346 | 0); | |
347 | break; | |
348 | default: | |
349 | list_for_each_entry(core, &bus->cores, list) { | |
85eb92e8 | 350 | core->irq = bcma_core_irq(core, 0); |
21e0534a | 351 | } |
e3f05a42 HM |
352 | bcma_err(bus, |
353 | "Unknown device (0x%x) found, can not configure IRQs\n", | |
354 | bus->chipinfo.id); | |
21e0534a | 355 | } |
7401cb63 | 356 | bcma_debug(bus, "IRQ reconfiguration done\n"); |
21e0534a HM |
357 | bcma_core_mips_dump_irq(bus); |
358 | ||
21e0534a HM |
359 | mcore->setup_done = true; |
360 | } |