Commit | Line | Data |
---|---|---|
61e115a5 MB |
1 | /* |
2 | * Sonics Silicon Backplane | |
3 | * PCMCIA-Hostbus related functions | |
4 | * | |
5 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | |
6 | * Copyright 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 | #include <linux/delay.h> | |
409f2435 | 13 | #include <linux/io.h> |
61e115a5 MB |
14 | |
15 | #include <pcmcia/cs_types.h> | |
16 | #include <pcmcia/cs.h> | |
17 | #include <pcmcia/cistpl.h> | |
18 | #include <pcmcia/ciscode.h> | |
19 | #include <pcmcia/ds.h> | |
20 | #include <pcmcia/cisreg.h> | |
21 | ||
22 | #include "ssb_private.h" | |
23 | ||
24 | ||
25 | /* Define the following to 1 to enable a printk on each coreswitch. */ | |
26 | #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0 | |
27 | ||
28 | ||
29 | int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, | |
30 | u8 coreidx) | |
31 | { | |
32 | struct pcmcia_device *pdev = bus->host_pcmcia; | |
33 | int err; | |
34 | int attempts = 0; | |
35 | u32 cur_core; | |
36 | conf_reg_t reg; | |
37 | u32 addr; | |
38 | u32 read_addr; | |
39 | ||
40 | addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; | |
41 | while (1) { | |
42 | reg.Action = CS_WRITE; | |
43 | reg.Offset = 0x2E; | |
44 | reg.Value = (addr & 0x0000F000) >> 12; | |
45 | err = pcmcia_access_configuration_register(pdev, ®); | |
46 | if (err != CS_SUCCESS) | |
47 | goto error; | |
48 | reg.Offset = 0x30; | |
49 | reg.Value = (addr & 0x00FF0000) >> 16; | |
50 | err = pcmcia_access_configuration_register(pdev, ®); | |
51 | if (err != CS_SUCCESS) | |
52 | goto error; | |
53 | reg.Offset = 0x32; | |
54 | reg.Value = (addr & 0xFF000000) >> 24; | |
55 | err = pcmcia_access_configuration_register(pdev, ®); | |
56 | if (err != CS_SUCCESS) | |
57 | goto error; | |
58 | ||
59 | read_addr = 0; | |
60 | ||
61 | reg.Action = CS_READ; | |
62 | reg.Offset = 0x2E; | |
63 | err = pcmcia_access_configuration_register(pdev, ®); | |
64 | if (err != CS_SUCCESS) | |
65 | goto error; | |
60d78c44 | 66 | read_addr |= ((u32)(reg.Value & 0x0F)) << 12; |
61e115a5 MB |
67 | reg.Offset = 0x30; |
68 | err = pcmcia_access_configuration_register(pdev, ®); | |
69 | if (err != CS_SUCCESS) | |
70 | goto error; | |
60d78c44 | 71 | read_addr |= ((u32)reg.Value) << 16; |
61e115a5 MB |
72 | reg.Offset = 0x32; |
73 | err = pcmcia_access_configuration_register(pdev, ®); | |
74 | if (err != CS_SUCCESS) | |
75 | goto error; | |
60d78c44 | 76 | read_addr |= ((u32)reg.Value) << 24; |
61e115a5 MB |
77 | |
78 | cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; | |
79 | if (cur_core == coreidx) | |
80 | break; | |
81 | ||
82 | if (attempts++ > SSB_BAR0_MAX_RETRIES) | |
83 | goto error; | |
84 | udelay(10); | |
85 | } | |
86 | ||
87 | return 0; | |
88 | error: | |
89 | ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); | |
90 | return -ENODEV; | |
91 | } | |
92 | ||
93 | int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |
94 | struct ssb_device *dev) | |
95 | { | |
96 | int err; | |
61e115a5 MB |
97 | |
98 | #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG | |
99 | ssb_printk(KERN_INFO PFX | |
100 | "Switching to %s core, index %d\n", | |
101 | ssb_core_name(dev->id.coreid), | |
102 | dev->core_index); | |
103 | #endif | |
104 | ||
61e115a5 MB |
105 | err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); |
106 | if (!err) | |
107 | bus->mapped_device = dev; | |
61e115a5 MB |
108 | |
109 | return err; | |
110 | } | |
111 | ||
112 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | |
113 | { | |
114 | int attempts = 0; | |
61e115a5 | 115 | conf_reg_t reg; |
993e1c78 | 116 | int res; |
61e115a5 MB |
117 | |
118 | SSB_WARN_ON((seg != 0) && (seg != 1)); | |
119 | reg.Offset = 0x34; | |
120 | reg.Function = 0; | |
61e115a5 MB |
121 | while (1) { |
122 | reg.Action = CS_WRITE; | |
123 | reg.Value = seg; | |
124 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | |
125 | if (unlikely(res != CS_SUCCESS)) | |
126 | goto error; | |
127 | reg.Value = 0xFF; | |
128 | reg.Action = CS_READ; | |
129 | res = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | |
130 | if (unlikely(res != CS_SUCCESS)) | |
131 | goto error; | |
132 | ||
133 | if (reg.Value == seg) | |
134 | break; | |
135 | ||
136 | if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) | |
137 | goto error; | |
138 | udelay(10); | |
139 | } | |
140 | bus->mapped_pcmcia_seg = seg; | |
993e1c78 MB |
141 | |
142 | return 0; | |
61e115a5 MB |
143 | error: |
144 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); | |
993e1c78 | 145 | return -ENODEV; |
61e115a5 MB |
146 | } |
147 | ||
60d78c44 MB |
148 | static int select_core_and_segment(struct ssb_device *dev, |
149 | u16 *offset) | |
61e115a5 | 150 | { |
60d78c44 | 151 | struct ssb_bus *bus = dev->bus; |
61e115a5 | 152 | int err; |
60d78c44 MB |
153 | u8 need_segment; |
154 | ||
155 | if (*offset >= 0x800) { | |
156 | *offset -= 0x800; | |
157 | need_segment = 1; | |
158 | } else | |
159 | need_segment = 0; | |
61e115a5 MB |
160 | |
161 | if (unlikely(dev != bus->mapped_device)) { | |
162 | err = ssb_pcmcia_switch_core(bus, dev); | |
163 | if (unlikely(err)) | |
164 | return err; | |
165 | } | |
60d78c44 MB |
166 | if (unlikely(need_segment != bus->mapped_pcmcia_seg)) { |
167 | err = ssb_pcmcia_switch_segment(bus, need_segment); | |
61e115a5 MB |
168 | if (unlikely(err)) |
169 | return err; | |
170 | } | |
61e115a5 MB |
171 | |
172 | return 0; | |
173 | } | |
174 | ||
ffc7689d MB |
175 | static u8 ssb_pcmcia_read8(struct ssb_device *dev, u16 offset) |
176 | { | |
177 | struct ssb_bus *bus = dev->bus; | |
178 | unsigned long flags; | |
179 | int err; | |
180 | u8 value = 0xFF; | |
181 | ||
182 | spin_lock_irqsave(&bus->bar_lock, flags); | |
183 | err = select_core_and_segment(dev, &offset); | |
184 | if (likely(!err)) | |
185 | value = readb(bus->mmio + offset); | |
186 | spin_unlock_irqrestore(&bus->bar_lock, flags); | |
187 | ||
188 | return value; | |
189 | } | |
190 | ||
61e115a5 MB |
191 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) |
192 | { | |
193 | struct ssb_bus *bus = dev->bus; | |
993e1c78 MB |
194 | unsigned long flags; |
195 | int err; | |
196 | u16 value = 0xFFFF; | |
61e115a5 | 197 | |
993e1c78 MB |
198 | spin_lock_irqsave(&bus->bar_lock, flags); |
199 | err = select_core_and_segment(dev, &offset); | |
200 | if (likely(!err)) | |
201 | value = readw(bus->mmio + offset); | |
202 | spin_unlock_irqrestore(&bus->bar_lock, flags); | |
61e115a5 | 203 | |
993e1c78 | 204 | return value; |
61e115a5 MB |
205 | } |
206 | ||
207 | static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) | |
208 | { | |
209 | struct ssb_bus *bus = dev->bus; | |
993e1c78 MB |
210 | unsigned long flags; |
211 | int err; | |
212 | u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF; | |
61e115a5 | 213 | |
993e1c78 MB |
214 | spin_lock_irqsave(&bus->bar_lock, flags); |
215 | err = select_core_and_segment(dev, &offset); | |
216 | if (likely(!err)) { | |
217 | lo = readw(bus->mmio + offset); | |
218 | hi = readw(bus->mmio + offset + 2); | |
219 | } | |
220 | spin_unlock_irqrestore(&bus->bar_lock, flags); | |
61e115a5 | 221 | |
60d78c44 | 222 | return (lo | (hi << 16)); |
61e115a5 MB |
223 | } |
224 | ||
ffc7689d MB |
225 | static void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value) |
226 | { | |
227 | struct ssb_bus *bus = dev->bus; | |
228 | unsigned long flags; | |
229 | int err; | |
230 | ||
231 | spin_lock_irqsave(&bus->bar_lock, flags); | |
232 | err = select_core_and_segment(dev, &offset); | |
233 | if (likely(!err)) | |
234 | writeb(value, bus->mmio + offset); | |
235 | mmiowb(); | |
236 | spin_unlock_irqrestore(&bus->bar_lock, flags); | |
237 | } | |
238 | ||
61e115a5 MB |
239 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) |
240 | { | |
241 | struct ssb_bus *bus = dev->bus; | |
993e1c78 MB |
242 | unsigned long flags; |
243 | int err; | |
61e115a5 | 244 | |
993e1c78 MB |
245 | spin_lock_irqsave(&bus->bar_lock, flags); |
246 | err = select_core_and_segment(dev, &offset); | |
247 | if (likely(!err)) | |
248 | writew(value, bus->mmio + offset); | |
249 | mmiowb(); | |
250 | spin_unlock_irqrestore(&bus->bar_lock, flags); | |
61e115a5 MB |
251 | } |
252 | ||
253 | static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) | |
254 | { | |
255 | struct ssb_bus *bus = dev->bus; | |
993e1c78 MB |
256 | unsigned long flags; |
257 | int err; | |
61e115a5 | 258 | |
993e1c78 MB |
259 | spin_lock_irqsave(&bus->bar_lock, flags); |
260 | err = select_core_and_segment(dev, &offset); | |
261 | if (likely(!err)) { | |
262 | writew((value & 0x0000FFFF), bus->mmio + offset); | |
263 | writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2); | |
264 | } | |
265 | mmiowb(); | |
266 | spin_unlock_irqrestore(&bus->bar_lock, flags); | |
61e115a5 MB |
267 | } |
268 | ||
269 | /* Not "static", as it's used in main.c */ | |
270 | const struct ssb_bus_ops ssb_pcmcia_ops = { | |
ffc7689d | 271 | .read8 = ssb_pcmcia_read8, |
61e115a5 MB |
272 | .read16 = ssb_pcmcia_read16, |
273 | .read32 = ssb_pcmcia_read32, | |
ffc7689d | 274 | .write8 = ssb_pcmcia_write8, |
61e115a5 MB |
275 | .write16 = ssb_pcmcia_write16, |
276 | .write32 = ssb_pcmcia_write32, | |
277 | }; | |
278 | ||
993e1c78 | 279 | #include <linux/etherdevice.h> |
61e115a5 MB |
280 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, |
281 | struct ssb_init_invariants *iv) | |
282 | { | |
283 | //TODO | |
993e1c78 | 284 | random_ether_addr(iv->sprom.il0mac); |
61e115a5 MB |
285 | return 0; |
286 | } | |
287 | ||
288 | int ssb_pcmcia_init(struct ssb_bus *bus) | |
289 | { | |
290 | conf_reg_t reg; | |
291 | int err; | |
292 | ||
293 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) | |
294 | return 0; | |
295 | ||
296 | /* Switch segment to a known state and sync | |
297 | * bus->mapped_pcmcia_seg with hardware state. */ | |
298 | ssb_pcmcia_switch_segment(bus, 0); | |
299 | ||
300 | /* Init IRQ routing */ | |
301 | reg.Action = CS_READ; | |
302 | reg.Function = 0; | |
303 | if (bus->chip_id == 0x4306) | |
304 | reg.Offset = 0x00; | |
305 | else | |
306 | reg.Offset = 0x80; | |
307 | err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | |
308 | if (err != CS_SUCCESS) | |
309 | goto error; | |
310 | reg.Action = CS_WRITE; | |
311 | reg.Value |= 0x04 | 0x01; | |
312 | err = pcmcia_access_configuration_register(bus->host_pcmcia, ®); | |
313 | if (err != CS_SUCCESS) | |
314 | goto error; | |
315 | ||
316 | return 0; | |
317 | error: | |
318 | return -ENODEV; | |
319 | } |