aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ssb/driver_mipscore.c
diff options
context:
space:
mode:
authorMichael Buesch <mb@bu3sch.de>2007-09-18 15:12:50 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:51:36 -0400
commit61e115a56d1aafd6e6a8a9fee8ac099a6128ac7b (patch)
treeadd97bf6a1207a4caea3a86cf13495ad3dc477de /drivers/ssb/driver_mipscore.c
parent5ee3afba88f5a79d0bff07ddd87af45919259f91 (diff)
[SSB]: add Sonics Silicon Backplane bus support
SSB is an SoC bus used in a number of embedded devices. The most well-known of these devices is probably the Linksys WRT54G, but there are others as well. The bus is also used internally on the BCM43xx and BCM44xx devices from Broadcom. This patch also includes support for SSB ID tables in modules, so that SSB drivers can be loaded automatically. Signed-off-by: Michael Buesch <mb@bu3sch.de> Signed-off-by: John W. Linville <linville@tuxdriver.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/ssb/driver_mipscore.c')
-rw-r--r--drivers/ssb/driver_mipscore.c223
1 files changed, 223 insertions, 0 deletions
diff --git a/drivers/ssb/driver_mipscore.c b/drivers/ssb/driver_mipscore.c
new file mode 100644
index 000000000000..ab8691a32580
--- /dev/null
+++ b/drivers/ssb/driver_mipscore.c
@@ -0,0 +1,223 @@
1/*
2 * Sonics Silicon Backplane
3 * Broadcom MIPS core driver
4 *
5 * Copyright 2005, Broadcom Corporation
6 * Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
7 *
8 * Licensed under the GNU/GPL. See COPYING for details.
9 */
10
11#include <linux/ssb/ssb.h>
12
13#include <linux/serial.h>
14#include <linux/serial_core.h>
15#include <linux/serial_reg.h>
16#include <linux/time.h>
17
18#include "ssb_private.h"
19
20
21static inline u32 mips_read32(struct ssb_mipscore *mcore,
22 u16 offset)
23{
24 return ssb_read32(mcore->dev, offset);
25}
26
27static inline void mips_write32(struct ssb_mipscore *mcore,
28 u16 offset,
29 u32 value)
30{
31 ssb_write32(mcore->dev, offset, value);
32}
33
34static const u32 ipsflag_irq_mask[] = {
35 0,
36 SSB_IPSFLAG_IRQ1,
37 SSB_IPSFLAG_IRQ2,
38 SSB_IPSFLAG_IRQ3,
39 SSB_IPSFLAG_IRQ4,
40};
41
42static const u32 ipsflag_irq_shift[] = {
43 0,
44 SSB_IPSFLAG_IRQ1_SHIFT,
45 SSB_IPSFLAG_IRQ2_SHIFT,
46 SSB_IPSFLAG_IRQ3_SHIFT,
47 SSB_IPSFLAG_IRQ4_SHIFT,
48};
49
50static inline u32 ssb_irqflag(struct ssb_device *dev)
51{
52 return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
53}
54
55/* Get the MIPS IRQ assignment for a specified device.
56 * If unassigned, 0 is returned.
57 */
58unsigned int ssb_mips_irq(struct ssb_device *dev)
59{
60 struct ssb_bus *bus = dev->bus;
61 u32 irqflag;
62 u32 ipsflag;
63 u32 tmp;
64 unsigned int irq;
65
66 irqflag = ssb_irqflag(dev);
67 ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG);
68 for (irq = 1; irq <= 4; irq++) {
69 tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]);
70 if (tmp == irqflag)
71 break;
72 }
73 if (irq == 5)
74 irq = 0;
75
76 return irq;
77}
78
79static void clear_irq(struct ssb_bus *bus, unsigned int irq)
80{
81 struct ssb_device *dev = bus->mipscore.dev;
82
83 /* Clear the IRQ in the MIPScore backplane registers */
84 if (irq == 0) {
85 ssb_write32(dev, SSB_INTVEC, 0);
86 } else {
87 ssb_write32(dev, SSB_IPSFLAG,
88 ssb_read32(dev, SSB_IPSFLAG) |
89 ipsflag_irq_mask[irq]);
90 }
91}
92
93static void set_irq(struct ssb_device *dev, unsigned int irq)
94{
95 unsigned int oldirq = ssb_mips_irq(dev);
96 struct ssb_bus *bus = dev->bus;
97 struct ssb_device *mdev = bus->mipscore.dev;
98 u32 irqflag = ssb_irqflag(dev);
99
100 dev->irq = irq + 2;
101
102 ssb_dprintk(KERN_INFO PFX
103 "set_irq: core 0x%04x, irq %d => %d\n",
104 dev->id.coreid, oldirq, irq);
105 /* clear the old irq */
106 if (oldirq == 0)
107 ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
108 else
109 clear_irq(bus, oldirq);
110
111 /* assign the new one */
112 if (irq == 0)
113 ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
114
115 irqflag <<= ipsflag_irq_shift[irq];
116 irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]);
117 ssb_write32(mdev, SSB_IPSFLAG, irqflag);
118}
119
120static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
121{
122 struct ssb_bus *bus = mcore->dev->bus;
123
124 if (bus->extif.dev)
125 mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports);
126 else if (bus->chipco.dev)
127 mcore->nr_serial_ports = ssb_chipco_serial_init(&bus->chipco, mcore->serial_ports);
128 else
129 mcore->nr_serial_ports = 0;
130}
131
132static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
133{
134 struct ssb_bus *bus = mcore->dev->bus;
135
136 mcore->flash_buswidth = 2;
137 if (bus->chipco.dev) {
138 mcore->flash_window = 0x1c000000;
139 mcore->flash_window_size = 0x02000000;
140 if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG)
141 & SSB_CHIPCO_CFG_DS16) == 0)
142 mcore->flash_buswidth = 1;
143 } else {
144 mcore->flash_window = 0x1fc00000;
145 mcore->flash_window_size = 0x00400000;
146 }
147}
148
149u32 ssb_cpu_clock(struct ssb_mipscore *mcore)
150{
151 struct ssb_bus *bus = mcore->dev->bus;
152 u32 pll_type, n, m, rate = 0;
153
154 if (bus->extif.dev) {
155 ssb_extif_get_clockcontrol(&bus->extif, &pll_type, &n, &m);
156 } else if (bus->chipco.dev) {
157 ssb_chipco_get_clockcpu(&bus->chipco, &pll_type, &n, &m);
158 } else
159 return 0;
160
161 if ((pll_type == SSB_PLLTYPE_5) || (bus->chip_id == 0x5365)) {
162 rate = 200000000;
163 } else {
164 rate = ssb_calc_clock_rate(pll_type, n, m);
165 }
166
167 if (pll_type == SSB_PLLTYPE_6) {
168 rate *= 2;
169 }
170
171 return rate;
172}
173
174void ssb_mipscore_init(struct ssb_mipscore *mcore)
175{
176 struct ssb_bus *bus = mcore->dev->bus;
177 struct ssb_device *dev;
178 unsigned long hz, ns;
179 unsigned int irq, i;
180
181 if (!mcore->dev)
182 return; /* We don't have a MIPS core */
183
184 ssb_dprintk(KERN_INFO PFX "Initializing MIPS core...\n");
185
186 hz = ssb_clockspeed(bus);
187 if (!hz)
188 hz = 100000000;
189 ns = 1000000000 / hz;
190
191 if (bus->extif.dev)
192 ssb_extif_timing_init(&bus->extif, ns);
193 else if (bus->chipco.dev)
194 ssb_chipco_timing_init(&bus->chipco, ns);
195
196 /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */
197 for (irq = 2, i = 0; i < bus->nr_devices; i++) {
198 dev = &(bus->devices[i]);
199 dev->irq = ssb_mips_irq(dev) + 2;
200 switch (dev->id.coreid) {
201 case SSB_DEV_USB11_HOST:
202 /* shouldn't need a separate irq line for non-4710, most of them have a proper
203 * external usb controller on the pci */
204 if ((bus->chip_id == 0x4710) && (irq <= 4)) {
205 set_irq(dev, irq++);
206 break;
207 }
208 /* fallthrough */
209 case SSB_DEV_PCI:
210 case SSB_DEV_ETHERNET:
211 case SSB_DEV_80211:
212 case SSB_DEV_USB20_HOST:
213 /* These devices get their own IRQ line if available, the rest goes on IRQ0 */
214 if (irq <= 4) {
215 set_irq(dev, irq++);
216 break;
217 }
218 }
219 }
220
221 ssb_mips_serial_init(mcore);
222 ssb_mips_flash_detect(mcore);
223}