]>
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 | ||
73e4dbe4 RM |
17 | #include <linux/mtd/physmap.h> |
18 | #include <linux/platform_device.h> | |
21e0534a HM |
19 | #include <linux/serial.h> |
20 | #include <linux/serial_core.h> | |
21 | #include <linux/serial_reg.h> | |
22 | #include <linux/time.h> | |
23 | ||
87fed556 RM |
24 | enum bcma_boot_dev { |
25 | BCMA_BOOT_DEV_UNK = 0, | |
26 | BCMA_BOOT_DEV_ROM, | |
27 | BCMA_BOOT_DEV_PARALLEL, | |
28 | BCMA_BOOT_DEV_SERIAL, | |
29 | BCMA_BOOT_DEV_NAND, | |
30 | }; | |
31 | ||
f1faa69d | 32 | static const char * const part_probes[] = { "bcm47xxpart", NULL }; |
73e4dbe4 RM |
33 | |
34 | static struct physmap_flash_data bcma_pflash_data = { | |
35 | .part_probe_types = part_probes, | |
36 | }; | |
37 | ||
38 | static struct resource bcma_pflash_resource = { | |
39 | .name = "bcma_pflash", | |
40 | .flags = IORESOURCE_MEM, | |
41 | }; | |
42 | ||
43 | struct platform_device bcma_pflash_dev = { | |
44 | .name = "physmap-flash", | |
45 | .dev = { | |
46 | .platform_data = &bcma_pflash_data, | |
47 | }, | |
48 | .resource = &bcma_pflash_resource, | |
49 | .num_resources = 1, | |
50 | }; | |
51 | ||
21e0534a HM |
52 | /* The 47162a0 hangs when reading MIPS DMP registers registers */ |
53 | static inline bool bcma_core_mips_bcm47162a0_quirk(struct bcma_device *dev) | |
54 | { | |
4b4f5be2 HM |
55 | return dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM47162 && |
56 | dev->bus->chipinfo.rev == 0 && dev->id.id == BCMA_CORE_MIPS_74K; | |
21e0534a HM |
57 | } |
58 | ||
59 | /* The 5357b0 hangs when reading USB20H DMP registers */ | |
60 | static inline bool bcma_core_mips_bcm5357b0_quirk(struct bcma_device *dev) | |
61 | { | |
4b4f5be2 HM |
62 | return (dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM5357 || |
63 | dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM4749) && | |
21e0534a HM |
64 | dev->bus->chipinfo.pkg == 11 && |
65 | dev->id.id == BCMA_CORE_USB20_HOST; | |
66 | } | |
67 | ||
68 | static inline u32 mips_read32(struct bcma_drv_mips *mcore, | |
69 | u16 offset) | |
70 | { | |
71 | return bcma_read32(mcore->core, offset); | |
72 | } | |
73 | ||
74 | static inline void mips_write32(struct bcma_drv_mips *mcore, | |
75 | u16 offset, | |
76 | u32 value) | |
77 | { | |
78 | bcma_write32(mcore->core, offset, value); | |
79 | } | |
80 | ||
81 | static const u32 ipsflag_irq_mask[] = { | |
82 | 0, | |
83 | BCMA_MIPS_IPSFLAG_IRQ1, | |
84 | BCMA_MIPS_IPSFLAG_IRQ2, | |
85 | BCMA_MIPS_IPSFLAG_IRQ3, | |
86 | BCMA_MIPS_IPSFLAG_IRQ4, | |
87 | }; | |
88 | ||
89 | static const u32 ipsflag_irq_shift[] = { | |
90 | 0, | |
91 | BCMA_MIPS_IPSFLAG_IRQ1_SHIFT, | |
92 | BCMA_MIPS_IPSFLAG_IRQ2_SHIFT, | |
93 | BCMA_MIPS_IPSFLAG_IRQ3_SHIFT, | |
94 | BCMA_MIPS_IPSFLAG_IRQ4_SHIFT, | |
95 | }; | |
96 | ||
97 | static u32 bcma_core_mips_irqflag(struct bcma_device *dev) | |
98 | { | |
99 | u32 flag; | |
100 | ||
101 | if (bcma_core_mips_bcm47162a0_quirk(dev)) | |
102 | return dev->core_index; | |
103 | if (bcma_core_mips_bcm5357b0_quirk(dev)) | |
104 | return dev->core_index; | |
105 | flag = bcma_aread32(dev, BCMA_MIPS_OOBSELOUTA30); | |
106 | ||
db5230d1 HM |
107 | if (flag) |
108 | return flag & 0x1F; | |
109 | else | |
110 | return 0x3f; | |
21e0534a HM |
111 | } |
112 | ||
113 | /* Get the MIPS IRQ assignment for a specified device. | |
114 | * If unassigned, 0 is returned. | |
db5230d1 HM |
115 | * If disabled, 5 is returned. |
116 | * If not supported, 6 is returned. | |
21e0534a | 117 | */ |
e2aa19fa | 118 | static unsigned int bcma_core_mips_irq(struct bcma_device *dev) |
21e0534a HM |
119 | { |
120 | struct bcma_device *mdev = dev->bus->drv_mips.core; | |
121 | u32 irqflag; | |
122 | unsigned int irq; | |
123 | ||
124 | irqflag = bcma_core_mips_irqflag(dev); | |
db5230d1 HM |
125 | if (irqflag == 0x3f) |
126 | return 6; | |
21e0534a | 127 | |
db5230d1 | 128 | for (irq = 0; irq <= 4; irq++) |
21e0534a HM |
129 | if (bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq)) & |
130 | (1 << irqflag)) | |
131 | return irq; | |
132 | ||
db5230d1 | 133 | return 5; |
21e0534a | 134 | } |
e2aa19fa NH |
135 | |
136 | unsigned int bcma_core_irq(struct bcma_device *dev) | |
137 | { | |
138 | unsigned int mips_irq = bcma_core_mips_irq(dev); | |
139 | return mips_irq <= 4 ? mips_irq + 2 : 0; | |
140 | } | |
141 | EXPORT_SYMBOL(bcma_core_irq); | |
21e0534a HM |
142 | |
143 | static void bcma_core_mips_set_irq(struct bcma_device *dev, unsigned int irq) | |
144 | { | |
145 | unsigned int oldirq = bcma_core_mips_irq(dev); | |
146 | struct bcma_bus *bus = dev->bus; | |
147 | struct bcma_device *mdev = bus->drv_mips.core; | |
148 | u32 irqflag; | |
149 | ||
150 | irqflag = bcma_core_mips_irqflag(dev); | |
151 | BUG_ON(oldirq == 6); | |
152 | ||
153 | dev->irq = irq + 2; | |
154 | ||
155 | /* clear the old irq */ | |
156 | if (oldirq == 0) | |
157 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0), | |
158 | bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) & | |
159 | ~(1 << irqflag)); | |
db5230d1 | 160 | else if (oldirq != 5) |
cbbc0138 | 161 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(oldirq), 0); |
21e0534a HM |
162 | |
163 | /* assign the new one */ | |
164 | if (irq == 0) { | |
165 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0), | |
166 | bcma_read32(mdev, BCMA_MIPS_MIPS74K_INTMASK(0)) | | |
167 | (1 << irqflag)); | |
168 | } else { | |
6ba1eafe HM |
169 | u32 irqinitmask = bcma_read32(mdev, |
170 | BCMA_MIPS_MIPS74K_INTMASK(irq)); | |
171 | if (irqinitmask) { | |
21e0534a HM |
172 | struct bcma_device *core; |
173 | ||
174 | /* backplane irq line is in use, find out who uses | |
175 | * it and set user to irq 0 | |
176 | */ | |
d8f1bd2f | 177 | list_for_each_entry(core, &bus->cores, list) { |
21e0534a | 178 | if ((1 << bcma_core_mips_irqflag(core)) == |
6ba1eafe | 179 | irqinitmask) { |
21e0534a HM |
180 | bcma_core_mips_set_irq(core, 0); |
181 | break; | |
182 | } | |
183 | } | |
184 | } | |
185 | bcma_write32(mdev, BCMA_MIPS_MIPS74K_INTMASK(irq), | |
186 | 1 << irqflag); | |
187 | } | |
188 | ||
7401cb63 | 189 | bcma_debug(bus, "set_irq: core 0x%04x, irq %d => %d\n", |
db5230d1 | 190 | dev->id.id, oldirq <= 4 ? oldirq + 2 : 0, irq + 2); |
21e0534a HM |
191 | } |
192 | ||
e3f05a42 HM |
193 | static void bcma_core_mips_set_irq_name(struct bcma_bus *bus, unsigned int irq, |
194 | u16 coreid, u8 unit) | |
195 | { | |
196 | struct bcma_device *core; | |
197 | ||
198 | core = bcma_find_core_unit(bus, coreid, unit); | |
199 | if (!core) { | |
200 | bcma_warn(bus, | |
201 | "Can not find core (id: 0x%x, unit %i) for IRQ configuration.\n", | |
202 | coreid, unit); | |
203 | return; | |
204 | } | |
205 | ||
206 | bcma_core_mips_set_irq(core, irq); | |
207 | } | |
208 | ||
21e0534a HM |
209 | static void bcma_core_mips_print_irq(struct bcma_device *dev, unsigned int irq) |
210 | { | |
211 | int i; | |
212 | static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"}; | |
7401cb63 | 213 | printk(KERN_DEBUG KBUILD_MODNAME ": core 0x%04x, irq :", dev->id.id); |
21e0534a HM |
214 | for (i = 0; i <= 6; i++) |
215 | printk(" %s%s", irq_name[i], i == irq ? "*" : " "); | |
216 | printk("\n"); | |
217 | } | |
218 | ||
219 | static void bcma_core_mips_dump_irq(struct bcma_bus *bus) | |
220 | { | |
221 | struct bcma_device *core; | |
222 | ||
d8f1bd2f | 223 | list_for_each_entry(core, &bus->cores, list) { |
21e0534a HM |
224 | bcma_core_mips_print_irq(core, bcma_core_mips_irq(core)); |
225 | } | |
226 | } | |
227 | ||
908debc8 HM |
228 | u32 bcma_cpu_clock(struct bcma_drv_mips *mcore) |
229 | { | |
230 | struct bcma_bus *bus = mcore->core->bus; | |
231 | ||
232 | if (bus->drv_cc.capabilities & BCMA_CC_CAP_PMU) | |
5b5ac414 | 233 | return bcma_pmu_get_cpu_clock(&bus->drv_cc); |
908debc8 | 234 | |
3d9d8af3 | 235 | bcma_err(bus, "No PMU available, need this to get the cpu clock\n"); |
908debc8 HM |
236 | return 0; |
237 | } | |
238 | EXPORT_SYMBOL(bcma_cpu_clock); | |
239 | ||
87fed556 RM |
240 | static enum bcma_boot_dev bcma_boot_dev(struct bcma_bus *bus) |
241 | { | |
242 | struct bcma_drv_cc *cc = &bus->drv_cc; | |
243 | u8 cc_rev = cc->core->id.rev; | |
244 | ||
245 | if (cc_rev == 42) { | |
246 | struct bcma_device *core; | |
247 | ||
248 | core = bcma_find_core(bus, BCMA_CORE_NS_ROM); | |
249 | if (core) { | |
250 | switch (bcma_aread32(core, BCMA_IOST) & | |
251 | BCMA_NS_ROM_IOST_BOOT_DEV_MASK) { | |
252 | case BCMA_NS_ROM_IOST_BOOT_DEV_NOR: | |
253 | return BCMA_BOOT_DEV_SERIAL; | |
254 | case BCMA_NS_ROM_IOST_BOOT_DEV_NAND: | |
255 | return BCMA_BOOT_DEV_NAND; | |
256 | case BCMA_NS_ROM_IOST_BOOT_DEV_ROM: | |
257 | default: | |
258 | return BCMA_BOOT_DEV_ROM; | |
259 | } | |
260 | } | |
261 | } else { | |
262 | if (cc_rev == 38) { | |
263 | if (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT) | |
264 | return BCMA_BOOT_DEV_NAND; | |
265 | else if (cc->status & BIT(5)) | |
266 | return BCMA_BOOT_DEV_ROM; | |
267 | } | |
268 | ||
269 | if ((cc->capabilities & BCMA_CC_CAP_FLASHT) == | |
270 | BCMA_CC_FLASHT_PARA) | |
271 | return BCMA_BOOT_DEV_PARALLEL; | |
272 | else | |
273 | return BCMA_BOOT_DEV_SERIAL; | |
274 | } | |
275 | ||
276 | return BCMA_BOOT_DEV_SERIAL; | |
277 | } | |
278 | ||
21e0534a HM |
279 | static void bcma_core_mips_flash_detect(struct bcma_drv_mips *mcore) |
280 | { | |
281 | struct bcma_bus *bus = mcore->core->bus; | |
3c25ddd9 | 282 | struct bcma_drv_cc *cc = &bus->drv_cc; |
73e4dbe4 | 283 | struct bcma_pflash *pflash = &cc->pflash; |
87fed556 | 284 | enum bcma_boot_dev boot_dev; |
21e0534a | 285 | |
3c25ddd9 | 286 | switch (cc->capabilities & BCMA_CC_CAP_FLASHT) { |
21e0534a HM |
287 | case BCMA_CC_FLASHT_STSER: |
288 | case BCMA_CC_FLASHT_ATSER: | |
23cb3b21 | 289 | bcma_debug(bus, "Found serial flash\n"); |
3c25ddd9 | 290 | bcma_sflash_init(cc); |
21e0534a HM |
291 | break; |
292 | case BCMA_CC_FLASHT_PARA: | |
23cb3b21 | 293 | bcma_debug(bus, "Found parallel flash\n"); |
73e4dbe4 RM |
294 | pflash->present = true; |
295 | pflash->window = BCMA_SOC_FLASH2; | |
296 | pflash->window_size = BCMA_SOC_FLASH2_SZ; | |
21e0534a | 297 | |
3c25ddd9 | 298 | if ((bcma_read32(cc->core, BCMA_CC_FLASH_CFG) & |
21e0534a | 299 | BCMA_CC_FLASH_CFG_DS) == 0) |
73e4dbe4 | 300 | pflash->buswidth = 1; |
21e0534a | 301 | else |
73e4dbe4 RM |
302 | pflash->buswidth = 2; |
303 | ||
304 | bcma_pflash_data.width = pflash->buswidth; | |
305 | bcma_pflash_resource.start = pflash->window; | |
306 | bcma_pflash_resource.end = pflash->window + pflash->window_size; | |
307 | ||
21e0534a HM |
308 | break; |
309 | default: | |
23cb3b21 RM |
310 | bcma_err(bus, "Flash type not supported\n"); |
311 | } | |
312 | ||
3c25ddd9 | 313 | if (cc->core->id.rev == 38 || |
23cb3b21 | 314 | bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) { |
3c25ddd9 | 315 | if (cc->capabilities & BCMA_CC_CAP_NFLASH) { |
23cb3b21 | 316 | bcma_debug(bus, "Found NAND flash\n"); |
3c25ddd9 | 317 | bcma_nflash_init(cc); |
23cb3b21 | 318 | } |
21e0534a | 319 | } |
87fed556 RM |
320 | |
321 | /* Determine flash type this SoC boots from */ | |
322 | boot_dev = bcma_boot_dev(bus); | |
323 | switch (boot_dev) { | |
324 | case BCMA_BOOT_DEV_PARALLEL: | |
325 | case BCMA_BOOT_DEV_SERIAL: | |
326 | /* TODO: Init NVRAM using BCMA_SOC_FLASH2 window */ | |
327 | break; | |
328 | case BCMA_BOOT_DEV_NAND: | |
329 | /* TODO: Init NVRAM using BCMA_SOC_FLASH1 window */ | |
330 | break; | |
331 | default: | |
332 | break; | |
333 | } | |
21e0534a HM |
334 | } |
335 | ||
49655bb8 HM |
336 | void bcma_core_mips_early_init(struct bcma_drv_mips *mcore) |
337 | { | |
338 | struct bcma_bus *bus = mcore->core->bus; | |
339 | ||
340 | if (mcore->early_setup_done) | |
341 | return; | |
342 | ||
343 | bcma_chipco_serial_init(&bus->drv_cc); | |
344 | bcma_core_mips_flash_detect(mcore); | |
345 | ||
346 | mcore->early_setup_done = true; | |
347 | } | |
348 | ||
6bf2e546 NH |
349 | static void bcma_fix_i2s_irqflag(struct bcma_bus *bus) |
350 | { | |
351 | struct bcma_device *cpu, *pcie, *i2s; | |
352 | ||
353 | /* Fixup the interrupts in 4716/4748 for i2s core (2010 Broadcom SDK) | |
354 | * (IRQ flags > 7 are ignored when setting the interrupt masks) | |
355 | */ | |
356 | if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4716 && | |
357 | bus->chipinfo.id != BCMA_CHIP_ID_BCM4748) | |
358 | return; | |
359 | ||
360 | cpu = bcma_find_core(bus, BCMA_CORE_MIPS_74K); | |
361 | pcie = bcma_find_core(bus, BCMA_CORE_PCIE); | |
362 | i2s = bcma_find_core(bus, BCMA_CORE_I2S); | |
363 | if (cpu && pcie && i2s && | |
364 | bcma_aread32(cpu, BCMA_MIPS_OOBSELINA74) == 0x08060504 && | |
365 | bcma_aread32(pcie, BCMA_MIPS_OOBSELINA74) == 0x08060504 && | |
366 | bcma_aread32(i2s, BCMA_MIPS_OOBSELOUTA30) == 0x88) { | |
367 | bcma_awrite32(cpu, BCMA_MIPS_OOBSELINA74, 0x07060504); | |
368 | bcma_awrite32(pcie, BCMA_MIPS_OOBSELINA74, 0x07060504); | |
369 | bcma_awrite32(i2s, BCMA_MIPS_OOBSELOUTA30, 0x87); | |
370 | bcma_debug(bus, | |
371 | "Moved i2s interrupt to oob line 7 instead of 8\n"); | |
372 | } | |
373 | } | |
374 | ||
21e0534a HM |
375 | void bcma_core_mips_init(struct bcma_drv_mips *mcore) |
376 | { | |
377 | struct bcma_bus *bus; | |
378 | struct bcma_device *core; | |
379 | bus = mcore->core->bus; | |
380 | ||
49655bb8 HM |
381 | if (mcore->setup_done) |
382 | return; | |
383 | ||
7401cb63 | 384 | bcma_debug(bus, "Initializing MIPS core...\n"); |
21e0534a | 385 | |
49655bb8 HM |
386 | bcma_core_mips_early_init(mcore); |
387 | ||
6bf2e546 NH |
388 | bcma_fix_i2s_irqflag(bus); |
389 | ||
e3f05a42 HM |
390 | switch (bus->chipinfo.id) { |
391 | case BCMA_CHIP_ID_BCM4716: | |
392 | case BCMA_CHIP_ID_BCM4748: | |
393 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); | |
394 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); | |
395 | bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0); | |
396 | bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_PCIE, 0); | |
397 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); | |
398 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0); | |
399 | break; | |
400 | case BCMA_CHIP_ID_BCM5356: | |
401 | case BCMA_CHIP_ID_BCM47162: | |
402 | case BCMA_CHIP_ID_BCM53572: | |
403 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); | |
404 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); | |
405 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); | |
406 | break; | |
407 | case BCMA_CHIP_ID_BCM5357: | |
408 | case BCMA_CHIP_ID_BCM4749: | |
409 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_80211, 0); | |
410 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_MAC_GBIT, 0); | |
411 | bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_USB20_HOST, 0); | |
412 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_CHIPCOMMON, 0); | |
413 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_I2S, 0); | |
414 | break; | |
415 | case BCMA_CHIP_ID_BCM4706: | |
416 | bcma_core_mips_set_irq_name(bus, 1, BCMA_CORE_PCIE, 0); | |
417 | bcma_core_mips_set_irq_name(bus, 2, BCMA_CORE_4706_MAC_GBIT, | |
418 | 0); | |
419 | bcma_core_mips_set_irq_name(bus, 3, BCMA_CORE_PCIE, 1); | |
420 | bcma_core_mips_set_irq_name(bus, 4, BCMA_CORE_USB20_HOST, 0); | |
421 | bcma_core_mips_set_irq_name(bus, 0, BCMA_CORE_4706_CHIPCOMMON, | |
422 | 0); | |
423 | break; | |
424 | default: | |
425 | list_for_each_entry(core, &bus->cores, list) { | |
e2aa19fa | 426 | core->irq = bcma_core_irq(core); |
21e0534a | 427 | } |
e3f05a42 HM |
428 | bcma_err(bus, |
429 | "Unknown device (0x%x) found, can not configure IRQs\n", | |
430 | bus->chipinfo.id); | |
21e0534a | 431 | } |
7401cb63 | 432 | bcma_debug(bus, "IRQ reconfiguration done\n"); |
21e0534a HM |
433 | bcma_core_mips_dump_irq(bus); |
434 | ||
21e0534a HM |
435 | mcore->setup_done = true; |
436 | } |