1 | /* |
2 | * Sonics Silicon Backplane |
3 | * PCMCIA-Hostbus related functions |
4 | * |
5 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> |
6 | * Copyright 2007-2008 Michael Buesch <m@bues.ch> |
7 | * |
8 | * Licensed under the GNU/GPL. See COPYING for details. |
9 | */ |
10 | |
11 | #include "ssb_private.h" |
12 | |
13 | #include <linux/ssb/ssb.h> |
14 | #include <linux/delay.h> |
15 | #include <linux/io.h> |
16 | #include <linux/etherdevice.h> |
17 | |
18 | #include <pcmcia/cistpl.h> |
19 | #include <pcmcia/ciscode.h> |
20 | #include <pcmcia/ds.h> |
21 | #include <pcmcia/cisreg.h> |
22 | |
23 | |
24 | /* Define the following to 1 to enable a printk on each coreswitch. */ |
25 | #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0 |
26 | |
27 | |
28 | /* PCMCIA configuration registers */ |
29 | #define SSB_PCMCIA_ADDRESS0 0x2E |
30 | #define SSB_PCMCIA_ADDRESS1 0x30 |
31 | #define SSB_PCMCIA_ADDRESS2 0x32 |
32 | #define SSB_PCMCIA_MEMSEG 0x34 |
33 | #define SSB_PCMCIA_SPROMCTL 0x36 |
34 | #define SSB_PCMCIA_SPROMCTL_IDLE 0 |
35 | #define SSB_PCMCIA_SPROMCTL_WRITE 1 |
36 | #define SSB_PCMCIA_SPROMCTL_READ 2 |
37 | #define SSB_PCMCIA_SPROMCTL_WRITEEN 4 |
38 | #define SSB_PCMCIA_SPROMCTL_WRITEDIS 7 |
39 | #define SSB_PCMCIA_SPROMCTL_DONE 8 |
40 | #define SSB_PCMCIA_SPROM_DATALO 0x38 |
41 | #define SSB_PCMCIA_SPROM_DATAHI 0x3A |
42 | #define SSB_PCMCIA_SPROM_ADDRLO 0x3C |
43 | #define SSB_PCMCIA_SPROM_ADDRHI 0x3E |
44 | |
45 | /* Hardware invariants CIS tuples */ |
46 | #define SSB_PCMCIA_CIS 0x80 |
47 | #define SSB_PCMCIA_CIS_ID 0x01 |
48 | #define SSB_PCMCIA_CIS_BOARDREV 0x02 |
49 | #define SSB_PCMCIA_CIS_PA 0x03 |
50 | #define SSB_PCMCIA_CIS_PA_PA0B0_LO 0 |
51 | #define SSB_PCMCIA_CIS_PA_PA0B0_HI 1 |
52 | #define SSB_PCMCIA_CIS_PA_PA0B1_LO 2 |
53 | #define SSB_PCMCIA_CIS_PA_PA0B1_HI 3 |
54 | #define SSB_PCMCIA_CIS_PA_PA0B2_LO 4 |
55 | #define SSB_PCMCIA_CIS_PA_PA0B2_HI 5 |
56 | #define SSB_PCMCIA_CIS_PA_ITSSI 6 |
57 | #define SSB_PCMCIA_CIS_PA_MAXPOW 7 |
58 | #define SSB_PCMCIA_CIS_OEMNAME 0x04 |
59 | #define SSB_PCMCIA_CIS_CCODE 0x05 |
60 | #define SSB_PCMCIA_CIS_ANTENNA 0x06 |
61 | #define SSB_PCMCIA_CIS_ANTGAIN 0x07 |
62 | #define SSB_PCMCIA_CIS_BFLAGS 0x08 |
63 | #define SSB_PCMCIA_CIS_LEDS 0x09 |
64 | |
65 | /* PCMCIA SPROM size. */ |
66 | #define SSB_PCMCIA_SPROM_SIZE 256 |
67 | #define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16)) |
68 | |
69 | |
70 | /* Write to a PCMCIA configuration register. */ |
71 | static int ssb_pcmcia_cfg_write(struct ssb_bus *bus, u8 offset, u8 value) |
72 | { |
73 | int res; |
74 | |
75 | res = pcmcia_write_config_byte(p_dev: bus->host_pcmcia, where: offset, val: value); |
76 | if (unlikely(res != 0)) |
77 | return -EBUSY; |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | /* Read from a PCMCIA configuration register. */ |
83 | static int ssb_pcmcia_cfg_read(struct ssb_bus *bus, u8 offset, u8 *value) |
84 | { |
85 | int res; |
86 | |
87 | res = pcmcia_read_config_byte(p_dev: bus->host_pcmcia, where: offset, val: value); |
88 | if (unlikely(res != 0)) |
89 | return -EBUSY; |
90 | |
91 | return 0; |
92 | } |
93 | |
94 | int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, |
95 | u8 coreidx) |
96 | { |
97 | int err; |
98 | int attempts = 0; |
99 | u32 cur_core; |
100 | u32 addr; |
101 | u32 read_addr; |
102 | u8 val; |
103 | |
104 | addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; |
105 | while (1) { |
106 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS0, |
107 | value: (addr & 0x0000F000) >> 12); |
108 | if (err) |
109 | goto error; |
110 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS1, |
111 | value: (addr & 0x00FF0000) >> 16); |
112 | if (err) |
113 | goto error; |
114 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS2, |
115 | value: (addr & 0xFF000000) >> 24); |
116 | if (err) |
117 | goto error; |
118 | |
119 | read_addr = 0; |
120 | |
121 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS0, value: &val); |
122 | if (err) |
123 | goto error; |
124 | read_addr |= ((u32)(val & 0x0F)) << 12; |
125 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS1, value: &val); |
126 | if (err) |
127 | goto error; |
128 | read_addr |= ((u32)val) << 16; |
129 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS2, value: &val); |
130 | if (err) |
131 | goto error; |
132 | read_addr |= ((u32)val) << 24; |
133 | |
134 | cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; |
135 | if (cur_core == coreidx) |
136 | break; |
137 | |
138 | err = -ETIMEDOUT; |
139 | if (attempts++ > SSB_BAR0_MAX_RETRIES) |
140 | goto error; |
141 | udelay(10); |
142 | } |
143 | |
144 | return 0; |
145 | error: |
146 | pr_err("Failed to switch to core %u\n" , coreidx); |
147 | return err; |
148 | } |
149 | |
150 | static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev) |
151 | { |
152 | int err; |
153 | |
154 | #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG |
155 | pr_info("Switching to %s core, index %d\n" , |
156 | ssb_core_name(dev->id.coreid), dev->core_index); |
157 | #endif |
158 | |
159 | err = ssb_pcmcia_switch_coreidx(bus, coreidx: dev->core_index); |
160 | if (!err) |
161 | bus->mapped_device = dev; |
162 | |
163 | return err; |
164 | } |
165 | |
166 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) |
167 | { |
168 | int attempts = 0; |
169 | int err; |
170 | u8 val; |
171 | |
172 | WARN_ON((seg != 0) && (seg != 1)); |
173 | while (1) { |
174 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_MEMSEG, value: seg); |
175 | if (err) |
176 | goto error; |
177 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_MEMSEG, value: &val); |
178 | if (err) |
179 | goto error; |
180 | if (val == seg) |
181 | break; |
182 | |
183 | err = -ETIMEDOUT; |
184 | if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) |
185 | goto error; |
186 | udelay(10); |
187 | } |
188 | bus->mapped_pcmcia_seg = seg; |
189 | |
190 | return 0; |
191 | error: |
192 | pr_err("Failed to switch pcmcia segment\n" ); |
193 | return err; |
194 | } |
195 | |
196 | static int select_core_and_segment(struct ssb_device *dev, |
197 | u16 *offset) |
198 | { |
199 | struct ssb_bus *bus = dev->bus; |
200 | int err; |
201 | u8 need_segment; |
202 | |
203 | if (*offset >= 0x800) { |
204 | *offset -= 0x800; |
205 | need_segment = 1; |
206 | } else |
207 | need_segment = 0; |
208 | |
209 | if (unlikely(dev != bus->mapped_device)) { |
210 | err = ssb_pcmcia_switch_core(bus, dev); |
211 | if (unlikely(err)) |
212 | return err; |
213 | } |
214 | if (unlikely(need_segment != bus->mapped_pcmcia_seg)) { |
215 | err = ssb_pcmcia_switch_segment(bus, seg: need_segment); |
216 | if (unlikely(err)) |
217 | return err; |
218 | } |
219 | |
220 | return 0; |
221 | } |
222 | |
223 | static u8 ssb_pcmcia_read8(struct ssb_device *dev, u16 offset) |
224 | { |
225 | struct ssb_bus *bus = dev->bus; |
226 | unsigned long flags; |
227 | int err; |
228 | u8 value = 0xFF; |
229 | |
230 | spin_lock_irqsave(&bus->bar_lock, flags); |
231 | err = select_core_and_segment(dev, offset: &offset); |
232 | if (likely(!err)) |
233 | value = readb(addr: bus->mmio + offset); |
234 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
235 | |
236 | return value; |
237 | } |
238 | |
239 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) |
240 | { |
241 | struct ssb_bus *bus = dev->bus; |
242 | unsigned long flags; |
243 | int err; |
244 | u16 value = 0xFFFF; |
245 | |
246 | spin_lock_irqsave(&bus->bar_lock, flags); |
247 | err = select_core_and_segment(dev, offset: &offset); |
248 | if (likely(!err)) |
249 | value = readw(addr: bus->mmio + offset); |
250 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
251 | |
252 | return value; |
253 | } |
254 | |
255 | static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) |
256 | { |
257 | struct ssb_bus *bus = dev->bus; |
258 | unsigned long flags; |
259 | int err; |
260 | u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF; |
261 | |
262 | spin_lock_irqsave(&bus->bar_lock, flags); |
263 | err = select_core_and_segment(dev, offset: &offset); |
264 | if (likely(!err)) { |
265 | lo = readw(addr: bus->mmio + offset); |
266 | hi = readw(addr: bus->mmio + offset + 2); |
267 | } |
268 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
269 | |
270 | return (lo | (hi << 16)); |
271 | } |
272 | |
273 | #ifdef CONFIG_SSB_BLOCKIO |
274 | static void ssb_pcmcia_block_read(struct ssb_device *dev, void *buffer, |
275 | size_t count, u16 offset, u8 reg_width) |
276 | { |
277 | struct ssb_bus *bus = dev->bus; |
278 | unsigned long flags; |
279 | void __iomem *addr = bus->mmio + offset; |
280 | int err; |
281 | |
282 | spin_lock_irqsave(&bus->bar_lock, flags); |
283 | err = select_core_and_segment(dev, offset: &offset); |
284 | if (unlikely(err)) { |
285 | memset(buffer, 0xFF, count); |
286 | goto unlock; |
287 | } |
288 | switch (reg_width) { |
289 | case sizeof(u8): { |
290 | u8 *buf = buffer; |
291 | |
292 | while (count) { |
293 | *buf = __raw_readb(addr); |
294 | buf++; |
295 | count--; |
296 | } |
297 | break; |
298 | } |
299 | case sizeof(u16): { |
300 | __le16 *buf = buffer; |
301 | |
302 | WARN_ON(count & 1); |
303 | while (count) { |
304 | *buf = (__force __le16)__raw_readw(addr); |
305 | buf++; |
306 | count -= 2; |
307 | } |
308 | break; |
309 | } |
310 | case sizeof(u32): { |
311 | __le16 *buf = buffer; |
312 | |
313 | WARN_ON(count & 3); |
314 | while (count) { |
315 | *buf = (__force __le16)__raw_readw(addr); |
316 | buf++; |
317 | *buf = (__force __le16)__raw_readw(addr: addr + 2); |
318 | buf++; |
319 | count -= 4; |
320 | } |
321 | break; |
322 | } |
323 | default: |
324 | WARN_ON(1); |
325 | } |
326 | unlock: |
327 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
328 | } |
329 | #endif /* CONFIG_SSB_BLOCKIO */ |
330 | |
331 | static void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value) |
332 | { |
333 | struct ssb_bus *bus = dev->bus; |
334 | unsigned long flags; |
335 | int err; |
336 | |
337 | spin_lock_irqsave(&bus->bar_lock, flags); |
338 | err = select_core_and_segment(dev, offset: &offset); |
339 | if (likely(!err)) |
340 | writeb(val: value, addr: bus->mmio + offset); |
341 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
342 | } |
343 | |
344 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) |
345 | { |
346 | struct ssb_bus *bus = dev->bus; |
347 | unsigned long flags; |
348 | int err; |
349 | |
350 | spin_lock_irqsave(&bus->bar_lock, flags); |
351 | err = select_core_and_segment(dev, offset: &offset); |
352 | if (likely(!err)) |
353 | writew(val: value, addr: bus->mmio + offset); |
354 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
355 | } |
356 | |
357 | static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) |
358 | { |
359 | struct ssb_bus *bus = dev->bus; |
360 | unsigned long flags; |
361 | int err; |
362 | |
363 | spin_lock_irqsave(&bus->bar_lock, flags); |
364 | err = select_core_and_segment(dev, offset: &offset); |
365 | if (likely(!err)) { |
366 | writew(val: (value & 0x0000FFFF), addr: bus->mmio + offset); |
367 | writew(val: ((value & 0xFFFF0000) >> 16), addr: bus->mmio + offset + 2); |
368 | } |
369 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
370 | } |
371 | |
372 | #ifdef CONFIG_SSB_BLOCKIO |
373 | static void ssb_pcmcia_block_write(struct ssb_device *dev, const void *buffer, |
374 | size_t count, u16 offset, u8 reg_width) |
375 | { |
376 | struct ssb_bus *bus = dev->bus; |
377 | unsigned long flags; |
378 | void __iomem *addr = bus->mmio + offset; |
379 | int err; |
380 | |
381 | spin_lock_irqsave(&bus->bar_lock, flags); |
382 | err = select_core_and_segment(dev, offset: &offset); |
383 | if (unlikely(err)) |
384 | goto unlock; |
385 | switch (reg_width) { |
386 | case sizeof(u8): { |
387 | const u8 *buf = buffer; |
388 | |
389 | while (count) { |
390 | __raw_writeb(val: *buf, addr); |
391 | buf++; |
392 | count--; |
393 | } |
394 | break; |
395 | } |
396 | case sizeof(u16): { |
397 | const __le16 *buf = buffer; |
398 | |
399 | WARN_ON(count & 1); |
400 | while (count) { |
401 | __raw_writew(val: (__force u16)(*buf), addr); |
402 | buf++; |
403 | count -= 2; |
404 | } |
405 | break; |
406 | } |
407 | case sizeof(u32): { |
408 | const __le16 *buf = buffer; |
409 | |
410 | WARN_ON(count & 3); |
411 | while (count) { |
412 | __raw_writew(val: (__force u16)(*buf), addr); |
413 | buf++; |
414 | __raw_writew(val: (__force u16)(*buf), addr: addr + 2); |
415 | buf++; |
416 | count -= 4; |
417 | } |
418 | break; |
419 | } |
420 | default: |
421 | WARN_ON(1); |
422 | } |
423 | unlock: |
424 | spin_unlock_irqrestore(lock: &bus->bar_lock, flags); |
425 | } |
426 | #endif /* CONFIG_SSB_BLOCKIO */ |
427 | |
428 | /* Not "static", as it's used in main.c */ |
429 | const struct ssb_bus_ops ssb_pcmcia_ops = { |
430 | .read8 = ssb_pcmcia_read8, |
431 | .read16 = ssb_pcmcia_read16, |
432 | .read32 = ssb_pcmcia_read32, |
433 | .write8 = ssb_pcmcia_write8, |
434 | .write16 = ssb_pcmcia_write16, |
435 | .write32 = ssb_pcmcia_write32, |
436 | #ifdef CONFIG_SSB_BLOCKIO |
437 | .block_read = ssb_pcmcia_block_read, |
438 | .block_write = ssb_pcmcia_block_write, |
439 | #endif |
440 | }; |
441 | |
442 | static int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command) |
443 | { |
444 | unsigned int i; |
445 | int err; |
446 | u8 value; |
447 | |
448 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROMCTL, value: command); |
449 | if (err) |
450 | return err; |
451 | for (i = 0; i < 1000; i++) { |
452 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROMCTL, value: &value); |
453 | if (err) |
454 | return err; |
455 | if (value & SSB_PCMCIA_SPROMCTL_DONE) |
456 | return 0; |
457 | udelay(10); |
458 | } |
459 | |
460 | return -ETIMEDOUT; |
461 | } |
462 | |
463 | /* offset is the 16bit word offset */ |
464 | static int ssb_pcmcia_sprom_read(struct ssb_bus *bus, u16 offset, u16 *value) |
465 | { |
466 | int err; |
467 | u8 lo, hi; |
468 | |
469 | offset *= 2; /* Make byte offset */ |
470 | |
471 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, |
472 | value: (offset & 0x00FF)); |
473 | if (err) |
474 | return err; |
475 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, |
476 | value: (offset & 0xFF00) >> 8); |
477 | if (err) |
478 | return err; |
479 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_READ); |
480 | if (err) |
481 | return err; |
482 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATALO, value: &lo); |
483 | if (err) |
484 | return err; |
485 | err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATAHI, value: &hi); |
486 | if (err) |
487 | return err; |
488 | *value = (lo | (((u16)hi) << 8)); |
489 | |
490 | return 0; |
491 | } |
492 | |
493 | /* offset is the 16bit word offset */ |
494 | static int ssb_pcmcia_sprom_write(struct ssb_bus *bus, u16 offset, u16 value) |
495 | { |
496 | int err; |
497 | |
498 | offset *= 2; /* Make byte offset */ |
499 | |
500 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, |
501 | value: (offset & 0x00FF)); |
502 | if (err) |
503 | return err; |
504 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, |
505 | value: (offset & 0xFF00) >> 8); |
506 | if (err) |
507 | return err; |
508 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATALO, |
509 | value: (value & 0x00FF)); |
510 | if (err) |
511 | return err; |
512 | err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATAHI, |
513 | value: (value & 0xFF00) >> 8); |
514 | if (err) |
515 | return err; |
516 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITE); |
517 | if (err) |
518 | return err; |
519 | msleep(msecs: 20); |
520 | |
521 | return 0; |
522 | } |
523 | |
524 | /* Read the SPROM image. bufsize is in 16bit words. */ |
525 | static int ssb_pcmcia_sprom_read_all(struct ssb_bus *bus, u16 *sprom) |
526 | { |
527 | int err, i; |
528 | |
529 | for (i = 0; i < SSB_PCMCIA_SPROM_SIZE; i++) { |
530 | err = ssb_pcmcia_sprom_read(bus, offset: i, value: &sprom[i]); |
531 | if (err) |
532 | return err; |
533 | } |
534 | |
535 | return 0; |
536 | } |
537 | |
538 | /* Write the SPROM image. size is in 16bit words. */ |
539 | static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom) |
540 | { |
541 | int i, err; |
542 | bool failed = 0; |
543 | size_t size = SSB_PCMCIA_SPROM_SIZE; |
544 | |
545 | pr_notice("Writing SPROM. Do NOT turn off the power! Please stand by...\n" ); |
546 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN); |
547 | if (err) { |
548 | pr_notice("Could not enable SPROM write access\n" ); |
549 | return -EBUSY; |
550 | } |
551 | pr_notice("[ 0%%" ); |
552 | msleep(msecs: 500); |
553 | for (i = 0; i < size; i++) { |
554 | if (i == size / 4) |
555 | pr_cont("25%%" ); |
556 | else if (i == size / 2) |
557 | pr_cont("50%%" ); |
558 | else if (i == (size * 3) / 4) |
559 | pr_cont("75%%" ); |
560 | else if (i % 2) |
561 | pr_cont("." ); |
562 | err = ssb_pcmcia_sprom_write(bus, offset: i, value: sprom[i]); |
563 | if (err) { |
564 | pr_notice("Failed to write to SPROM\n" ); |
565 | failed = 1; |
566 | break; |
567 | } |
568 | } |
569 | err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS); |
570 | if (err) { |
571 | pr_notice("Could not disable SPROM write access\n" ); |
572 | failed = 1; |
573 | } |
574 | msleep(msecs: 500); |
575 | if (!failed) { |
576 | pr_cont("100%% ]\n" ); |
577 | pr_notice("SPROM written\n" ); |
578 | } |
579 | |
580 | return failed ? -EBUSY : 0; |
581 | } |
582 | |
583 | static int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size) |
584 | { |
585 | //TODO |
586 | return 0; |
587 | } |
588 | |
589 | #define GOTO_ERROR_ON(condition, description) do { \ |
590 | if (unlikely(condition)) { \ |
591 | error_description = description; \ |
592 | goto error; \ |
593 | } \ |
594 | } while (0) |
595 | |
596 | static int ssb_pcmcia_get_mac(struct pcmcia_device *p_dev, |
597 | tuple_t *tuple, |
598 | void *priv) |
599 | { |
600 | struct ssb_sprom *sprom = priv; |
601 | |
602 | if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) |
603 | return -EINVAL; |
604 | if (tuple->TupleDataLen != ETH_ALEN + 2) |
605 | return -EINVAL; |
606 | if (tuple->TupleData[1] != ETH_ALEN) |
607 | return -EINVAL; |
608 | memcpy(sprom->il0mac, &tuple->TupleData[2], ETH_ALEN); |
609 | return 0; |
610 | }; |
611 | |
612 | static int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev, |
613 | tuple_t *tuple, |
614 | void *priv) |
615 | { |
616 | struct ssb_init_invariants *iv = priv; |
617 | struct ssb_sprom *sprom = &iv->sprom; |
618 | struct ssb_boardinfo *bi = &iv->boardinfo; |
619 | const char *error_description; |
620 | |
621 | GOTO_ERROR_ON(tuple->TupleDataLen < 1, "VEN tpl < 1" ); |
622 | switch (tuple->TupleData[0]) { |
623 | case SSB_PCMCIA_CIS_ID: |
624 | GOTO_ERROR_ON((tuple->TupleDataLen != 5) && |
625 | (tuple->TupleDataLen != 7), |
626 | "id tpl size" ); |
627 | bi->vendor = tuple->TupleData[1] | |
628 | ((u16)tuple->TupleData[2] << 8); |
629 | break; |
630 | case SSB_PCMCIA_CIS_BOARDREV: |
631 | GOTO_ERROR_ON(tuple->TupleDataLen != 2, |
632 | "boardrev tpl size" ); |
633 | sprom->board_rev = tuple->TupleData[1]; |
634 | break; |
635 | case SSB_PCMCIA_CIS_PA: |
636 | GOTO_ERROR_ON((tuple->TupleDataLen != 9) && |
637 | (tuple->TupleDataLen != 10), |
638 | "pa tpl size" ); |
639 | sprom->pa0b0 = tuple->TupleData[1] | |
640 | ((u16)tuple->TupleData[2] << 8); |
641 | sprom->pa0b1 = tuple->TupleData[3] | |
642 | ((u16)tuple->TupleData[4] << 8); |
643 | sprom->pa0b2 = tuple->TupleData[5] | |
644 | ((u16)tuple->TupleData[6] << 8); |
645 | sprom->itssi_a = tuple->TupleData[7]; |
646 | sprom->itssi_bg = tuple->TupleData[7]; |
647 | sprom->maxpwr_a = tuple->TupleData[8]; |
648 | sprom->maxpwr_bg = tuple->TupleData[8]; |
649 | break; |
650 | case SSB_PCMCIA_CIS_OEMNAME: |
651 | /* We ignore this. */ |
652 | break; |
653 | case SSB_PCMCIA_CIS_CCODE: |
654 | GOTO_ERROR_ON(tuple->TupleDataLen != 2, |
655 | "ccode tpl size" ); |
656 | sprom->country_code = tuple->TupleData[1]; |
657 | break; |
658 | case SSB_PCMCIA_CIS_ANTENNA: |
659 | GOTO_ERROR_ON(tuple->TupleDataLen != 2, |
660 | "ant tpl size" ); |
661 | sprom->ant_available_a = tuple->TupleData[1]; |
662 | sprom->ant_available_bg = tuple->TupleData[1]; |
663 | break; |
664 | case SSB_PCMCIA_CIS_ANTGAIN: |
665 | GOTO_ERROR_ON(tuple->TupleDataLen != 2, |
666 | "antg tpl size" ); |
667 | sprom->antenna_gain.a0 = tuple->TupleData[1]; |
668 | sprom->antenna_gain.a1 = tuple->TupleData[1]; |
669 | sprom->antenna_gain.a2 = tuple->TupleData[1]; |
670 | sprom->antenna_gain.a3 = tuple->TupleData[1]; |
671 | break; |
672 | case SSB_PCMCIA_CIS_BFLAGS: |
673 | GOTO_ERROR_ON((tuple->TupleDataLen != 3) && |
674 | (tuple->TupleDataLen != 5), |
675 | "bfl tpl size" ); |
676 | sprom->boardflags_lo = tuple->TupleData[1] | |
677 | ((u16)tuple->TupleData[2] << 8); |
678 | break; |
679 | case SSB_PCMCIA_CIS_LEDS: |
680 | GOTO_ERROR_ON(tuple->TupleDataLen != 5, |
681 | "leds tpl size" ); |
682 | sprom->gpio0 = tuple->TupleData[1]; |
683 | sprom->gpio1 = tuple->TupleData[2]; |
684 | sprom->gpio2 = tuple->TupleData[3]; |
685 | sprom->gpio3 = tuple->TupleData[4]; |
686 | break; |
687 | } |
688 | return -ENOSPC; /* continue with next entry */ |
689 | |
690 | error: |
691 | pr_err("PCMCIA: Failed to fetch device invariants: %s\n" , |
692 | error_description); |
693 | return -ENODEV; |
694 | } |
695 | |
696 | |
697 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, |
698 | struct ssb_init_invariants *iv) |
699 | { |
700 | struct ssb_sprom *sprom = &iv->sprom; |
701 | int res; |
702 | |
703 | memset(sprom, 0xFF, sizeof(*sprom)); |
704 | sprom->revision = 1; |
705 | sprom->boardflags_lo = 0; |
706 | sprom->boardflags_hi = 0; |
707 | |
708 | /* First fetch the MAC address. */ |
709 | res = pcmcia_loop_tuple(p_dev: bus->host_pcmcia, CISTPL_FUNCE, |
710 | loop_tuple: ssb_pcmcia_get_mac, priv_data: sprom); |
711 | if (res != 0) { |
712 | pr_err("PCMCIA: Failed to fetch MAC address\n" ); |
713 | return -ENODEV; |
714 | } |
715 | |
716 | /* Fetch the vendor specific tuples. */ |
717 | res = pcmcia_loop_tuple(p_dev: bus->host_pcmcia, SSB_PCMCIA_CIS, |
718 | loop_tuple: ssb_pcmcia_do_get_invariants, priv_data: iv); |
719 | if ((res == 0) || (res == -ENOSPC)) |
720 | return 0; |
721 | |
722 | pr_err("PCMCIA: Failed to fetch device invariants\n" ); |
723 | return -ENODEV; |
724 | } |
725 | |
726 | static ssize_t ssb_sprom_show(struct device *pcmciadev, |
727 | struct device_attribute *attr, |
728 | char *buf) |
729 | { |
730 | struct pcmcia_device *pdev = |
731 | container_of(pcmciadev, struct pcmcia_device, dev); |
732 | struct ssb_bus *bus; |
733 | |
734 | bus = ssb_pcmcia_dev_to_bus(pdev); |
735 | if (!bus) |
736 | return -ENODEV; |
737 | |
738 | return ssb_attr_sprom_show(bus, buf, |
739 | sprom_read: ssb_pcmcia_sprom_read_all); |
740 | } |
741 | |
742 | static ssize_t ssb_sprom_store(struct device *pcmciadev, |
743 | struct device_attribute *attr, |
744 | const char *buf, size_t count) |
745 | { |
746 | struct pcmcia_device *pdev = |
747 | container_of(pcmciadev, struct pcmcia_device, dev); |
748 | struct ssb_bus *bus; |
749 | |
750 | bus = ssb_pcmcia_dev_to_bus(pdev); |
751 | if (!bus) |
752 | return -ENODEV; |
753 | |
754 | return ssb_attr_sprom_store(bus, buf, count, |
755 | sprom_check_crc: ssb_pcmcia_sprom_check_crc, |
756 | sprom_write: ssb_pcmcia_sprom_write_all); |
757 | } |
758 | |
759 | static DEVICE_ATTR_ADMIN_RW(ssb_sprom); |
760 | |
761 | static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor) |
762 | { |
763 | u8 val; |
764 | int err; |
765 | |
766 | err = ssb_pcmcia_cfg_read(bus, offset: cor, value: &val); |
767 | if (err) |
768 | return err; |
769 | val &= ~COR_SOFT_RESET; |
770 | val |= COR_FUNC_ENA | COR_IREQ_ENA | COR_LEVEL_REQ; |
771 | err = ssb_pcmcia_cfg_write(bus, offset: cor, value: val); |
772 | if (err) |
773 | return err; |
774 | msleep(msecs: 40); |
775 | |
776 | return 0; |
777 | } |
778 | |
779 | /* Initialize the PCMCIA hardware. This is called on Init and Resume. */ |
780 | int ssb_pcmcia_hardware_setup(struct ssb_bus *bus) |
781 | { |
782 | int err; |
783 | |
784 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) |
785 | return 0; |
786 | |
787 | /* Switch segment to a known state and sync |
788 | * bus->mapped_pcmcia_seg with hardware state. */ |
789 | ssb_pcmcia_switch_segment(bus, seg: 0); |
790 | /* Init the COR register. */ |
791 | err = ssb_pcmcia_cor_setup(bus, CISREG_COR); |
792 | if (err) |
793 | return err; |
794 | /* Some cards also need this register to get poked. */ |
795 | err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80); |
796 | if (err) |
797 | return err; |
798 | |
799 | return 0; |
800 | } |
801 | |
802 | void ssb_pcmcia_exit(struct ssb_bus *bus) |
803 | { |
804 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) |
805 | return; |
806 | |
807 | device_remove_file(dev: &bus->host_pcmcia->dev, attr: &dev_attr_ssb_sprom); |
808 | } |
809 | |
810 | int ssb_pcmcia_init(struct ssb_bus *bus) |
811 | { |
812 | int err; |
813 | |
814 | if (bus->bustype != SSB_BUSTYPE_PCMCIA) |
815 | return 0; |
816 | |
817 | err = ssb_pcmcia_hardware_setup(bus); |
818 | if (err) |
819 | goto error; |
820 | |
821 | bus->sprom_size = SSB_PCMCIA_SPROM_SIZE; |
822 | mutex_init(&bus->sprom_mutex); |
823 | err = device_create_file(device: &bus->host_pcmcia->dev, entry: &dev_attr_ssb_sprom); |
824 | if (err) |
825 | goto error; |
826 | |
827 | return 0; |
828 | error: |
829 | pr_err("Failed to initialize PCMCIA host device\n" ); |
830 | return err; |
831 | } |
832 | |