]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/bcma/sprom.c
bcma: add support for sprom not found on the device
[mv-sheeva.git] / drivers / bcma / sprom.c
1 /*
2  * Broadcom specific AMBA
3  * SPROM reading
4  *
5  * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
6  *
7  * Licensed under the GNU/GPL. See COPYING for details.
8  */
9
10 #include "bcma_private.h"
11
12 #include <linux/bcma/bcma.h>
13 #include <linux/bcma/bcma_regs.h>
14 #include <linux/pci.h>
15 #include <linux/io.h>
16 #include <linux/dma-mapping.h>
17 #include <linux/slab.h>
18
19 static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out);
20
21 /**
22  * bcma_arch_register_fallback_sprom - Registers a method providing a
23  * fallback SPROM if no SPROM is found.
24  *
25  * @sprom_callback: The callback function.
26  *
27  * With this function the architecture implementation may register a
28  * callback handler which fills the SPROM data structure. The fallback is
29  * used for PCI based BCMA devices, where no valid SPROM can be found
30  * in the shadow registers and to provide the SPROM for SoCs where BCMA is
31  * to controll the system bus.
32  *
33  * This function is useful for weird architectures that have a half-assed
34  * BCMA device hardwired to their PCI bus.
35  *
36  * This function is available for architecture code, only. So it is not
37  * exported.
38  */
39 int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus,
40                                      struct ssb_sprom *out))
41 {
42         if (get_fallback_sprom)
43                 return -EEXIST;
44         get_fallback_sprom = sprom_callback;
45
46         return 0;
47 }
48
49 static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
50                                          struct ssb_sprom *out)
51 {
52         if (!get_fallback_sprom)
53                 return -ENOENT;
54
55         return get_fallback_sprom(bus, out);
56 }
57
58 /**************************************************
59  * R/W ops.
60  **************************************************/
61
62 static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
63 {
64         int i;
65         for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
66                 sprom[i] = bcma_read16(bus->drv_cc.core,
67                                        offset + (i * 2));
68 }
69
70 /**************************************************
71  * Validation.
72  **************************************************/
73
74 static inline u8 bcma_crc8(u8 crc, u8 data)
75 {
76         /* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
77         static const u8 t[] = {
78                 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
79                 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
80                 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
81                 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
82                 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
83                 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
84                 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
85                 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
86                 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
87                 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
88                 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
89                 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
90                 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
91                 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
92                 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
93                 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
94                 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
95                 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
96                 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
97                 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
98                 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
99                 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
100                 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
101                 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
102                 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
103                 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
104                 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
105                 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
106                 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
107                 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
108                 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
109                 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
110         };
111         return t[crc ^ data];
112 }
113
114 static u8 bcma_sprom_crc(const u16 *sprom)
115 {
116         int word;
117         u8 crc = 0xFF;
118
119         for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
120                 crc = bcma_crc8(crc, sprom[word] & 0x00FF);
121                 crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
122         }
123         crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
124         crc ^= 0xFF;
125
126         return crc;
127 }
128
129 static int bcma_sprom_check_crc(const u16 *sprom)
130 {
131         u8 crc;
132         u8 expected_crc;
133         u16 tmp;
134
135         crc = bcma_sprom_crc(sprom);
136         tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
137         expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
138         if (crc != expected_crc)
139                 return -EPROTO;
140
141         return 0;
142 }
143
144 static int bcma_sprom_valid(const u16 *sprom)
145 {
146         u16 revision;
147         int err;
148
149         err = bcma_sprom_check_crc(sprom);
150         if (err)
151                 return err;
152
153         revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
154         if (revision != 8 && revision != 9) {
155                 pr_err("Unsupported SPROM revision: %d\n", revision);
156                 return -ENOENT;
157         }
158
159         return 0;
160 }
161
162 /**************************************************
163  * SPROM extraction.
164  **************************************************/
165
166 #define SPOFF(offset)   ((offset) / sizeof(u16))
167
168 #define SPEX(_field, _offset, _mask, _shift)    \
169         bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift))
170
171 static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
172 {
173         u16 v, o;
174         int i;
175         u16 pwr_info_offset[] = {
176                 SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
177                 SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
178         };
179         BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
180                         ARRAY_SIZE(bus->sprom.core_pwr_info));
181
182         bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
183                 SSB_SPROM_REVISION_REV;
184
185         for (i = 0; i < 3; i++) {
186                 v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
187                 *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
188         }
189
190         SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
191
192         SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
193              SSB_SPROM4_TXPID2G0_SHIFT);
194         SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1,
195              SSB_SPROM4_TXPID2G1_SHIFT);
196         SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2,
197              SSB_SPROM4_TXPID2G2_SHIFT);
198         SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3,
199              SSB_SPROM4_TXPID2G3_SHIFT);
200
201         SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0,
202              SSB_SPROM4_TXPID5GL0_SHIFT);
203         SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1,
204              SSB_SPROM4_TXPID5GL1_SHIFT);
205         SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2,
206              SSB_SPROM4_TXPID5GL2_SHIFT);
207         SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3,
208              SSB_SPROM4_TXPID5GL3_SHIFT);
209
210         SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0,
211              SSB_SPROM4_TXPID5G0_SHIFT);
212         SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1,
213              SSB_SPROM4_TXPID5G1_SHIFT);
214         SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2,
215              SSB_SPROM4_TXPID5G2_SHIFT);
216         SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3,
217              SSB_SPROM4_TXPID5G3_SHIFT);
218
219         SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0,
220              SSB_SPROM4_TXPID5GH0_SHIFT);
221         SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1,
222              SSB_SPROM4_TXPID5GH1_SHIFT);
223         SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2,
224              SSB_SPROM4_TXPID5GH2_SHIFT);
225         SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3,
226              SSB_SPROM4_TXPID5GH3_SHIFT);
227
228         SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0);
229         SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0);
230         SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
231         SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
232
233         SPEX(country_code, SSB_SPROM8_CCODE, ~0, 0);
234
235         /* Extract cores power info info */
236         for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
237                 o = pwr_info_offset[i];
238                 SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
239                         SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
240                 SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
241                         SSB_SPROM8_2G_MAXP, 0);
242
243                 SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
244                 SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
245                 SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
246
247                 SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
248                         SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
249                 SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
250                         SSB_SPROM8_5G_MAXP, 0);
251                 SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
252                         SSB_SPROM8_5GH_MAXP, 0);
253                 SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
254                         SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
255
256                 SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
257                 SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
258                 SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
259                 SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
260                 SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
261                 SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
262                 SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
263                 SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
264                 SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
265         }
266
267         SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS,
268              SSB_SROM8_FEM_TSSIPOS_SHIFT);
269         SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN,
270              SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
271         SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE,
272              SSB_SROM8_FEM_PDET_RANGE_SHIFT);
273         SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO,
274              SSB_SROM8_FEM_TR_ISO_SHIFT);
275         SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT,
276              SSB_SROM8_FEM_ANTSWLUT_SHIFT);
277
278         SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS,
279              SSB_SROM8_FEM_TSSIPOS_SHIFT);
280         SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN,
281              SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
282         SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE,
283              SSB_SROM8_FEM_PDET_RANGE_SHIFT);
284         SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO,
285              SSB_SROM8_FEM_TR_ISO_SHIFT);
286         SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
287              SSB_SROM8_FEM_ANTSWLUT_SHIFT);
288 }
289
290 static bool bcma_is_sprom_available(struct bcma_bus *bus)
291 {
292         u32 sromctrl;
293
294         if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
295                 return false;
296
297         if (bus->drv_cc.core->id.rev >= 32) {
298                 sromctrl = bcma_read32(bus->drv_cc.core, BCMA_CC_SROM_CONTROL);
299                 return sromctrl & BCMA_CC_SROM_CONTROL_PRESENT;
300         }
301         return true;
302 }
303
304 int bcma_sprom_get(struct bcma_bus *bus)
305 {
306         u16 offset;
307         u16 *sprom;
308         int err = 0;
309
310         if (!bus->drv_cc.core)
311                 return -EOPNOTSUPP;
312
313         if (!bcma_is_sprom_available(bus)) {
314                 /*
315                  * Maybe there is no SPROM on the device?
316                  * Now we ask the arch code if there is some sprom
317                  * available for this device in some other storage.
318                  */
319                 err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
320                 if (err) {
321                         pr_warn("Using fallback SPROM failed (err %d)\n", err);
322                 } else {
323                         pr_debug("Using SPROM revision %d provided by"
324                                  " platform.\n", bus->sprom.revision);
325                         return 0;
326                 }
327         }
328
329         sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
330                         GFP_KERNEL);
331         if (!sprom)
332                 return -ENOMEM;
333
334         if (bus->chipinfo.id == 0x4331)
335                 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
336
337         /* Most cards have SPROM moved by additional offset 0x30 (48 dwords).
338          * According to brcm80211 this applies to cards with PCIe rev >= 6
339          * TODO: understand this condition and use it */
340         offset = (bus->chipinfo.id == 0x4331) ? BCMA_CC_SPROM :
341                 BCMA_CC_SPROM_PCIE6;
342         pr_debug("SPROM offset 0x%x\n", offset);
343         bcma_sprom_read(bus, offset, sprom);
344
345         if (bus->chipinfo.id == 0x4331)
346                 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
347
348         err = bcma_sprom_valid(sprom);
349         if (err)
350                 goto out;
351
352         bcma_sprom_extract_r8(bus, sprom);
353
354 out:
355         kfree(sprom);
356         return err;
357 }