Root/
1 | /* |
2 | * Sonics Silicon Backplane |
3 | * Broadcom MIPS core driver |
4 | * |
5 | * Copyright 2005, Broadcom Corporation |
6 | * Copyright 2006, 2007, Michael Buesch <m@bues.ch> |
7 | * |
8 | * Licensed under the GNU/GPL. See COPYING for details. |
9 | */ |
10 | |
11 | #include <linux/ssb/ssb.h> |
12 | |
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> |
18 | |
19 | #include "ssb_private.h" |
20 | |
21 | static const char *part_probes[] = { "bcm47xxpart", NULL }; |
22 | |
23 | static struct physmap_flash_data ssb_pflash_data = { |
24 | .part_probe_types = part_probes, |
25 | }; |
26 | |
27 | static struct resource ssb_pflash_resource = { |
28 | .name = "ssb_pflash", |
29 | .flags = IORESOURCE_MEM, |
30 | }; |
31 | |
32 | struct platform_device ssb_pflash_dev = { |
33 | .name = "physmap-flash", |
34 | .dev = { |
35 | .platform_data = &ssb_pflash_data, |
36 | }, |
37 | .resource = &ssb_pflash_resource, |
38 | .num_resources = 1, |
39 | }; |
40 | |
41 | static inline u32 mips_read32(struct ssb_mipscore *mcore, |
42 | u16 offset) |
43 | { |
44 | return ssb_read32(mcore->dev, offset); |
45 | } |
46 | |
47 | static inline void mips_write32(struct ssb_mipscore *mcore, |
48 | u16 offset, |
49 | u32 value) |
50 | { |
51 | ssb_write32(mcore->dev, offset, value); |
52 | } |
53 | |
54 | static const u32 ipsflag_irq_mask[] = { |
55 | 0, |
56 | SSB_IPSFLAG_IRQ1, |
57 | SSB_IPSFLAG_IRQ2, |
58 | SSB_IPSFLAG_IRQ3, |
59 | SSB_IPSFLAG_IRQ4, |
60 | }; |
61 | |
62 | static const u32 ipsflag_irq_shift[] = { |
63 | 0, |
64 | SSB_IPSFLAG_IRQ1_SHIFT, |
65 | SSB_IPSFLAG_IRQ2_SHIFT, |
66 | SSB_IPSFLAG_IRQ3_SHIFT, |
67 | SSB_IPSFLAG_IRQ4_SHIFT, |
68 | }; |
69 | |
70 | static inline u32 ssb_irqflag(struct ssb_device *dev) |
71 | { |
72 | u32 tpsflag = ssb_read32(dev, SSB_TPSFLAG); |
73 | if (tpsflag) |
74 | return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG; |
75 | else |
76 | /* not irq supported */ |
77 | return 0x3f; |
78 | } |
79 | |
80 | static struct ssb_device *find_device(struct ssb_device *rdev, int irqflag) |
81 | { |
82 | struct ssb_bus *bus = rdev->bus; |
83 | int i; |
84 | for (i = 0; i < bus->nr_devices; i++) { |
85 | struct ssb_device *dev; |
86 | dev = &(bus->devices[i]); |
87 | if (ssb_irqflag(dev) == irqflag) |
88 | return dev; |
89 | } |
90 | return NULL; |
91 | } |
92 | |
93 | /* Get the MIPS IRQ assignment for a specified device. |
94 | * If unassigned, 0 is returned. |
95 | * If disabled, 5 is returned. |
96 | * If not supported, 6 is returned. |
97 | */ |
98 | unsigned int ssb_mips_irq(struct ssb_device *dev) |
99 | { |
100 | struct ssb_bus *bus = dev->bus; |
101 | struct ssb_device *mdev = bus->mipscore.dev; |
102 | u32 irqflag; |
103 | u32 ipsflag; |
104 | u32 tmp; |
105 | unsigned int irq; |
106 | |
107 | irqflag = ssb_irqflag(dev); |
108 | if (irqflag == 0x3f) |
109 | return 6; |
110 | ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG); |
111 | for (irq = 1; irq <= 4; irq++) { |
112 | tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]); |
113 | if (tmp == irqflag) |
114 | break; |
115 | } |
116 | if (irq == 5) { |
117 | if ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)) |
118 | irq = 0; |
119 | } |
120 | |
121 | return irq; |
122 | } |
123 | |
124 | static void clear_irq(struct ssb_bus *bus, unsigned int irq) |
125 | { |
126 | struct ssb_device *dev = bus->mipscore.dev; |
127 | |
128 | /* Clear the IRQ in the MIPScore backplane registers */ |
129 | if (irq == 0) { |
130 | ssb_write32(dev, SSB_INTVEC, 0); |
131 | } else { |
132 | ssb_write32(dev, SSB_IPSFLAG, |
133 | ssb_read32(dev, SSB_IPSFLAG) | |
134 | ipsflag_irq_mask[irq]); |
135 | } |
136 | } |
137 | |
138 | static void set_irq(struct ssb_device *dev, unsigned int irq) |
139 | { |
140 | unsigned int oldirq = ssb_mips_irq(dev); |
141 | struct ssb_bus *bus = dev->bus; |
142 | struct ssb_device *mdev = bus->mipscore.dev; |
143 | u32 irqflag = ssb_irqflag(dev); |
144 | |
145 | BUG_ON(oldirq == 6); |
146 | |
147 | dev->irq = irq + 2; |
148 | |
149 | /* clear the old irq */ |
150 | if (oldirq == 0) |
151 | ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))); |
152 | else if (oldirq != 5) |
153 | clear_irq(bus, oldirq); |
154 | |
155 | /* assign the new one */ |
156 | if (irq == 0) { |
157 | ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) | ssb_read32(mdev, SSB_INTVEC))); |
158 | } else { |
159 | u32 ipsflag = ssb_read32(mdev, SSB_IPSFLAG); |
160 | if ((ipsflag & ipsflag_irq_mask[irq]) != ipsflag_irq_mask[irq]) { |
161 | u32 oldipsflag = (ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]; |
162 | struct ssb_device *olddev = find_device(dev, oldipsflag); |
163 | if (olddev) |
164 | set_irq(olddev, 0); |
165 | } |
166 | irqflag <<= ipsflag_irq_shift[irq]; |
167 | irqflag |= (ipsflag & ~ipsflag_irq_mask[irq]); |
168 | ssb_write32(mdev, SSB_IPSFLAG, irqflag); |
169 | } |
170 | ssb_dprintk(KERN_INFO PFX |
171 | "set_irq: core 0x%04x, irq %d => %d\n", |
172 | dev->id.coreid, oldirq+2, irq+2); |
173 | } |
174 | |
175 | static void print_irq(struct ssb_device *dev, unsigned int irq) |
176 | { |
177 | int i; |
178 | static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"}; |
179 | ssb_dprintk(KERN_INFO PFX |
180 | "core 0x%04x, irq :", dev->id.coreid); |
181 | for (i = 0; i <= 6; i++) { |
182 | ssb_dprintk(" %s%s", irq_name[i], i==irq?"*":" "); |
183 | } |
184 | ssb_dprintk("\n"); |
185 | } |
186 | |
187 | static void dump_irq(struct ssb_bus *bus) |
188 | { |
189 | int i; |
190 | for (i = 0; i < bus->nr_devices; i++) { |
191 | struct ssb_device *dev; |
192 | dev = &(bus->devices[i]); |
193 | print_irq(dev, ssb_mips_irq(dev)); |
194 | } |
195 | } |
196 | |
197 | static void ssb_mips_serial_init(struct ssb_mipscore *mcore) |
198 | { |
199 | struct ssb_bus *bus = mcore->dev->bus; |
200 | |
201 | if (ssb_extif_available(&bus->extif)) |
202 | mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports); |
203 | else if (ssb_chipco_available(&bus->chipco)) |
204 | mcore->nr_serial_ports = ssb_chipco_serial_init(&bus->chipco, mcore->serial_ports); |
205 | else |
206 | mcore->nr_serial_ports = 0; |
207 | } |
208 | |
209 | static void ssb_mips_flash_detect(struct ssb_mipscore *mcore) |
210 | { |
211 | struct ssb_bus *bus = mcore->dev->bus; |
212 | struct ssb_pflash *pflash = &mcore->pflash; |
213 | |
214 | /* When there is no chipcommon on the bus there is 4MB flash */ |
215 | if (!ssb_chipco_available(&bus->chipco)) { |
216 | pflash->present = true; |
217 | pflash->buswidth = 2; |
218 | pflash->window = SSB_FLASH1; |
219 | pflash->window_size = SSB_FLASH1_SZ; |
220 | goto ssb_pflash; |
221 | } |
222 | |
223 | /* There is ChipCommon, so use it to read info about flash */ |
224 | switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { |
225 | case SSB_CHIPCO_FLASHT_STSER: |
226 | case SSB_CHIPCO_FLASHT_ATSER: |
227 | pr_debug("Found serial flash\n"); |
228 | ssb_sflash_init(&bus->chipco); |
229 | break; |
230 | case SSB_CHIPCO_FLASHT_PARA: |
231 | pr_debug("Found parallel flash\n"); |
232 | pflash->present = true; |
233 | pflash->window = SSB_FLASH2; |
234 | pflash->window_size = SSB_FLASH2_SZ; |
235 | if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) |
236 | & SSB_CHIPCO_CFG_DS16) == 0) |
237 | pflash->buswidth = 1; |
238 | else |
239 | pflash->buswidth = 2; |
240 | break; |
241 | } |
242 | |
243 | ssb_pflash: |
244 | if (pflash->present) { |
245 | ssb_pflash_data.width = pflash->buswidth; |
246 | ssb_pflash_resource.start = pflash->window; |
247 | ssb_pflash_resource.end = pflash->window + pflash->window_size; |
248 | } |
249 | } |
250 | |
251 | u32 ssb_cpu_clock(struct ssb_mipscore *mcore) |
252 | { |
253 | struct ssb_bus *bus = mcore->dev->bus; |
254 | u32 pll_type, n, m, rate = 0; |
255 | |
256 | if (bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU) |
257 | return ssb_pmu_get_cpu_clock(&bus->chipco); |
258 | |
259 | if (ssb_extif_available(&bus->extif)) { |
260 | ssb_extif_get_clockcontrol(&bus->extif, &pll_type, &n, &m); |
261 | } else if (ssb_chipco_available(&bus->chipco)) { |
262 | ssb_chipco_get_clockcpu(&bus->chipco, &pll_type, &n, &m); |
263 | } else |
264 | return 0; |
265 | |
266 | if ((pll_type == SSB_PLLTYPE_5) || (bus->chip_id == 0x5365)) { |
267 | rate = 200000000; |
268 | } else { |
269 | rate = ssb_calc_clock_rate(pll_type, n, m); |
270 | } |
271 | |
272 | if (pll_type == SSB_PLLTYPE_6) { |
273 | rate *= 2; |
274 | } |
275 | |
276 | return rate; |
277 | } |
278 | |
279 | void ssb_mipscore_init(struct ssb_mipscore *mcore) |
280 | { |
281 | struct ssb_bus *bus; |
282 | struct ssb_device *dev; |
283 | unsigned long hz, ns; |
284 | unsigned int irq, i; |
285 | |
286 | if (!mcore->dev) |
287 | return; /* We don't have a MIPS core */ |
288 | |
289 | ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n"); |
290 | |
291 | bus = mcore->dev->bus; |
292 | hz = ssb_clockspeed(bus); |
293 | if (!hz) |
294 | hz = 100000000; |
295 | ns = 1000000000 / hz; |
296 | |
297 | if (ssb_extif_available(&bus->extif)) |
298 | ssb_extif_timing_init(&bus->extif, ns); |
299 | else if (ssb_chipco_available(&bus->chipco)) |
300 | ssb_chipco_timing_init(&bus->chipco, ns); |
301 | |
302 | /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */ |
303 | for (irq = 2, i = 0; i < bus->nr_devices; i++) { |
304 | int mips_irq; |
305 | dev = &(bus->devices[i]); |
306 | mips_irq = ssb_mips_irq(dev); |
307 | if (mips_irq > 4) |
308 | dev->irq = 0; |
309 | else |
310 | dev->irq = mips_irq + 2; |
311 | if (dev->irq > 5) |
312 | continue; |
313 | switch (dev->id.coreid) { |
314 | case SSB_DEV_USB11_HOST: |
315 | /* shouldn't need a separate irq line for non-4710, most of them have a proper |
316 | * external usb controller on the pci */ |
317 | if ((bus->chip_id == 0x4710) && (irq <= 4)) { |
318 | set_irq(dev, irq++); |
319 | } |
320 | break; |
321 | case SSB_DEV_PCI: |
322 | case SSB_DEV_ETHERNET: |
323 | case SSB_DEV_ETHERNET_GBIT: |
324 | case SSB_DEV_80211: |
325 | case SSB_DEV_USB20_HOST: |
326 | /* These devices get their own IRQ line if available, the rest goes on IRQ0 */ |
327 | if (irq <= 4) { |
328 | set_irq(dev, irq++); |
329 | break; |
330 | } |
331 | /* fallthrough */ |
332 | case SSB_DEV_EXTIF: |
333 | set_irq(dev, 0); |
334 | break; |
335 | } |
336 | } |
337 | ssb_dprintk(KERN_INFO PFX "after irq reconfiguration\n"); |
338 | dump_irq(bus); |
339 | |
340 | ssb_mips_serial_init(mcore); |
341 | ssb_mips_flash_detect(mcore); |
342 | } |
343 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9