2 * Sonics Silicon Backplane
3 * Broadcom MIPS core driver
5 * Copyright 2005, Broadcom Corporation
6 * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
8 * Licensed under the GNU/GPL. See COPYING for details.
11 #include <linux/ssb/ssb.h>
13 #include <linux/mtd/physmap.h>
14 #include <linux/serial.h>
15 #include <linux/serial_core.h>
16 #include <linux/serial_reg.h>
17 #include <linux/time.h>
19 #include <bcm47xx_nvram.h>
22 #include "ssb_private.h"
24 static const char * const part_probes
[] = { "bcm47xxpart", NULL
};
26 static struct physmap_flash_data ssb_pflash_data
= {
27 .part_probe_types
= part_probes
,
30 static struct resource ssb_pflash_resource
= {
32 .flags
= IORESOURCE_MEM
,
35 struct platform_device ssb_pflash_dev
= {
36 .name
= "physmap-flash",
38 .platform_data
= &ssb_pflash_data
,
40 .resource
= &ssb_pflash_resource
,
44 static inline u32
mips_read32(struct ssb_mipscore
*mcore
,
47 return ssb_read32(mcore
->dev
, offset
);
50 static inline void mips_write32(struct ssb_mipscore
*mcore
,
54 ssb_write32(mcore
->dev
, offset
, value
);
57 static const u32 ipsflag_irq_mask
[] = {
65 static const u32 ipsflag_irq_shift
[] = {
67 SSB_IPSFLAG_IRQ1_SHIFT
,
68 SSB_IPSFLAG_IRQ2_SHIFT
,
69 SSB_IPSFLAG_IRQ3_SHIFT
,
70 SSB_IPSFLAG_IRQ4_SHIFT
,
73 static inline u32
ssb_irqflag(struct ssb_device
*dev
)
75 u32 tpsflag
= ssb_read32(dev
, SSB_TPSFLAG
);
77 return ssb_read32(dev
, SSB_TPSFLAG
) & SSB_TPSFLAG_BPFLAG
;
79 /* not irq supported */
83 static struct ssb_device
*find_device(struct ssb_device
*rdev
, int irqflag
)
85 struct ssb_bus
*bus
= rdev
->bus
;
87 for (i
= 0; i
< bus
->nr_devices
; i
++) {
88 struct ssb_device
*dev
;
89 dev
= &(bus
->devices
[i
]);
90 if (ssb_irqflag(dev
) == irqflag
)
96 /* Get the MIPS IRQ assignment for a specified device.
97 * If unassigned, 0 is returned.
98 * If disabled, 5 is returned.
99 * If not supported, 6 is returned.
101 unsigned int ssb_mips_irq(struct ssb_device
*dev
)
103 struct ssb_bus
*bus
= dev
->bus
;
104 struct ssb_device
*mdev
= bus
->mipscore
.dev
;
110 irqflag
= ssb_irqflag(dev
);
113 ipsflag
= ssb_read32(bus
->mipscore
.dev
, SSB_IPSFLAG
);
114 for (irq
= 1; irq
<= 4; irq
++) {
115 tmp
= ((ipsflag
& ipsflag_irq_mask
[irq
]) >> ipsflag_irq_shift
[irq
]);
120 if ((1 << irqflag
) & ssb_read32(mdev
, SSB_INTVEC
))
127 static void clear_irq(struct ssb_bus
*bus
, unsigned int irq
)
129 struct ssb_device
*dev
= bus
->mipscore
.dev
;
131 /* Clear the IRQ in the MIPScore backplane registers */
133 ssb_write32(dev
, SSB_INTVEC
, 0);
135 ssb_write32(dev
, SSB_IPSFLAG
,
136 ssb_read32(dev
, SSB_IPSFLAG
) |
137 ipsflag_irq_mask
[irq
]);
141 static void set_irq(struct ssb_device
*dev
, unsigned int irq
)
143 unsigned int oldirq
= ssb_mips_irq(dev
);
144 struct ssb_bus
*bus
= dev
->bus
;
145 struct ssb_device
*mdev
= bus
->mipscore
.dev
;
146 u32 irqflag
= ssb_irqflag(dev
);
152 /* clear the old irq */
154 ssb_write32(mdev
, SSB_INTVEC
, (~(1 << irqflag
) & ssb_read32(mdev
, SSB_INTVEC
)));
155 else if (oldirq
!= 5)
156 clear_irq(bus
, oldirq
);
158 /* assign the new one */
160 ssb_write32(mdev
, SSB_INTVEC
, ((1 << irqflag
) | ssb_read32(mdev
, SSB_INTVEC
)));
162 u32 ipsflag
= ssb_read32(mdev
, SSB_IPSFLAG
);
163 if ((ipsflag
& ipsflag_irq_mask
[irq
]) != ipsflag_irq_mask
[irq
]) {
164 u32 oldipsflag
= (ipsflag
& ipsflag_irq_mask
[irq
]) >> ipsflag_irq_shift
[irq
];
165 struct ssb_device
*olddev
= find_device(dev
, oldipsflag
);
169 irqflag
<<= ipsflag_irq_shift
[irq
];
170 irqflag
|= (ipsflag
& ~ipsflag_irq_mask
[irq
]);
171 ssb_write32(mdev
, SSB_IPSFLAG
, irqflag
);
173 ssb_dbg("set_irq: core 0x%04x, irq %d => %d\n",
174 dev
->id
.coreid
, oldirq
+2, irq
+2);
177 static void print_irq(struct ssb_device
*dev
, unsigned int irq
)
179 static const char *irq_name
[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
180 ssb_dbg("core 0x%04x, irq : %s%s %s%s %s%s %s%s %s%s %s%s %s%s\n",
182 irq_name
[0], irq
== 0 ? "*" : " ",
183 irq_name
[1], irq
== 1 ? "*" : " ",
184 irq_name
[2], irq
== 2 ? "*" : " ",
185 irq_name
[3], irq
== 3 ? "*" : " ",
186 irq_name
[4], irq
== 4 ? "*" : " ",
187 irq_name
[5], irq
== 5 ? "*" : " ",
188 irq_name
[6], irq
== 6 ? "*" : " ");
191 static void dump_irq(struct ssb_bus
*bus
)
194 for (i
= 0; i
< bus
->nr_devices
; i
++) {
195 struct ssb_device
*dev
;
196 dev
= &(bus
->devices
[i
]);
197 print_irq(dev
, ssb_mips_irq(dev
));
201 static void ssb_mips_serial_init(struct ssb_mipscore
*mcore
)
203 struct ssb_bus
*bus
= mcore
->dev
->bus
;
205 if (ssb_extif_available(&bus
->extif
))
206 mcore
->nr_serial_ports
= ssb_extif_serial_init(&bus
->extif
, mcore
->serial_ports
);
207 else if (ssb_chipco_available(&bus
->chipco
))
208 mcore
->nr_serial_ports
= ssb_chipco_serial_init(&bus
->chipco
, mcore
->serial_ports
);
210 mcore
->nr_serial_ports
= 0;
213 static void ssb_mips_flash_detect(struct ssb_mipscore
*mcore
)
215 struct ssb_bus
*bus
= mcore
->dev
->bus
;
216 struct ssb_sflash
*sflash
= &mcore
->sflash
;
217 struct ssb_pflash
*pflash
= &mcore
->pflash
;
219 /* When there is no chipcommon on the bus there is 4MB flash */
220 if (!ssb_chipco_available(&bus
->chipco
)) {
221 pflash
->present
= true;
222 pflash
->buswidth
= 2;
223 pflash
->window
= SSB_FLASH1
;
224 pflash
->window_size
= SSB_FLASH1_SZ
;
228 /* There is ChipCommon, so use it to read info about flash */
229 switch (bus
->chipco
.capabilities
& SSB_CHIPCO_CAP_FLASHT
) {
230 case SSB_CHIPCO_FLASHT_STSER
:
231 case SSB_CHIPCO_FLASHT_ATSER
:
232 pr_debug("Found serial flash\n");
233 ssb_sflash_init(&bus
->chipco
);
235 case SSB_CHIPCO_FLASHT_PARA
:
236 pr_debug("Found parallel flash\n");
237 pflash
->present
= true;
238 pflash
->window
= SSB_FLASH2
;
239 pflash
->window_size
= SSB_FLASH2_SZ
;
240 if ((ssb_read32(bus
->chipco
.dev
, SSB_CHIPCO_FLASH_CFG
)
241 & SSB_CHIPCO_CFG_DS16
) == 0)
242 pflash
->buswidth
= 1;
244 pflash
->buswidth
= 2;
249 if (sflash
->present
) {
250 #ifdef CONFIG_BCM47XX
251 bcm47xx_nvram_init_from_mem(sflash
->window
, sflash
->size
);
253 } else if (pflash
->present
) {
254 #ifdef CONFIG_BCM47XX
255 bcm47xx_nvram_init_from_mem(pflash
->window
, pflash
->window_size
);
258 ssb_pflash_data
.width
= pflash
->buswidth
;
259 ssb_pflash_resource
.start
= pflash
->window
;
260 ssb_pflash_resource
.end
= pflash
->window
+ pflash
->window_size
;
264 u32
ssb_cpu_clock(struct ssb_mipscore
*mcore
)
266 struct ssb_bus
*bus
= mcore
->dev
->bus
;
267 u32 pll_type
, n
, m
, rate
= 0;
269 if (bus
->chipco
.capabilities
& SSB_CHIPCO_CAP_PMU
)
270 return ssb_pmu_get_cpu_clock(&bus
->chipco
);
272 if (ssb_extif_available(&bus
->extif
)) {
273 ssb_extif_get_clockcontrol(&bus
->extif
, &pll_type
, &n
, &m
);
274 } else if (ssb_chipco_available(&bus
->chipco
)) {
275 ssb_chipco_get_clockcpu(&bus
->chipco
, &pll_type
, &n
, &m
);
279 if ((pll_type
== SSB_PLLTYPE_5
) || (bus
->chip_id
== 0x5365)) {
282 rate
= ssb_calc_clock_rate(pll_type
, n
, m
);
285 if (pll_type
== SSB_PLLTYPE_6
) {
292 void ssb_mipscore_init(struct ssb_mipscore
*mcore
)
295 struct ssb_device
*dev
;
296 unsigned long hz
, ns
;
300 return; /* We don't have a MIPS core */
302 ssb_dbg("Initializing MIPS core...\n");
304 bus
= mcore
->dev
->bus
;
305 hz
= ssb_clockspeed(bus
);
308 ns
= 1000000000 / hz
;
310 if (ssb_extif_available(&bus
->extif
))
311 ssb_extif_timing_init(&bus
->extif
, ns
);
312 else if (ssb_chipco_available(&bus
->chipco
))
313 ssb_chipco_timing_init(&bus
->chipco
, ns
);
315 /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */
316 for (irq
= 2, i
= 0; i
< bus
->nr_devices
; i
++) {
318 dev
= &(bus
->devices
[i
]);
319 mips_irq
= ssb_mips_irq(dev
);
323 dev
->irq
= mips_irq
+ 2;
326 switch (dev
->id
.coreid
) {
327 case SSB_DEV_USB11_HOST
:
328 /* shouldn't need a separate irq line for non-4710, most of them have a proper
329 * external usb controller on the pci */
330 if ((bus
->chip_id
== 0x4710) && (irq
<= 4)) {
335 case SSB_DEV_ETHERNET
:
336 case SSB_DEV_ETHERNET_GBIT
:
338 case SSB_DEV_USB20_HOST
:
339 /* These devices get their own IRQ line if available, the rest goes on IRQ0 */
350 ssb_dbg("after irq reconfiguration\n");
353 ssb_mips_serial_init(mcore
);
354 ssb_mips_flash_detect(mcore
);