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