2 * Broadcom specific AMBA
5 * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
7 * Licensed under the GNU/GPL. See COPYING for details.
10 #include "bcma_private.h"
12 #include <linux/bcma/bcma.h>
13 #include <linux/bcma/bcma_regs.h>
14 #include <linux/pci.h>
16 #include <linux/dma-mapping.h>
17 #include <linux/slab.h>
19 static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out);
22 * bcma_arch_register_fallback_sprom - Registers a method providing a
23 * fallback SPROM if no SPROM is found.
25 * @sprom_callback: The callback function.
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.
33 * This function is useful for weird architectures that have a half-assed
34 * BCMA device hardwired to their PCI bus.
36 * This function is available for architecture code, only. So it is not
39 int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus,
40 struct ssb_sprom *out))
42 if (get_fallback_sprom)
44 get_fallback_sprom = sprom_callback;
49 static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
50 struct ssb_sprom *out)
52 if (!get_fallback_sprom)
55 return get_fallback_sprom(bus, out);
58 /**************************************************
60 **************************************************/
62 static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
65 for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
66 sprom[i] = bcma_read16(bus->drv_cc.core,
70 /**************************************************
72 **************************************************/
74 static inline u8 bcma_crc8(u8 crc, u8 data)
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,
111 return t[crc ^ data];
114 static u8 bcma_sprom_crc(const u16 *sprom)
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);
123 crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
129 static int bcma_sprom_check_crc(const u16 *sprom)
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)
144 static int bcma_sprom_valid(const u16 *sprom)
149 err = bcma_sprom_check_crc(sprom);
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);
162 /**************************************************
164 **************************************************/
166 #define SPOFF(offset) ((offset) / sizeof(u16))
168 #define SPEX(_field, _offset, _mask, _shift) \
169 bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift))
171 static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
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
179 BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
180 ARRAY_SIZE(bus->sprom.core_pwr_info));
182 bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
183 SSB_SPROM_REVISION_REV;
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);
190 SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
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);
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);
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);
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);
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);
233 SPEX(country_code, SSB_SPROM8_CCODE, ~0, 0);
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);
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);
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);
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);
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);
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);
290 static bool bcma_is_sprom_available(struct bcma_bus *bus)
294 if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
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;
304 int bcma_sprom_get(struct bcma_bus *bus)
310 if (!bus->drv_cc.core)
313 if (!bcma_is_sprom_available(bus)) {
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.
319 err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
321 pr_warn("Using fallback SPROM failed (err %d)\n", err);
323 pr_debug("Using SPROM revision %d provided by"
324 " platform.\n", bus->sprom.revision);
329 sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
334 if (bus->chipinfo.id == 0x4331)
335 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
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 :
342 pr_debug("SPROM offset 0x%x\n", offset);
343 bcma_sprom_read(bus, offset, sprom);
345 if (bus->chipinfo.id == 0x4331)
346 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
348 err = bcma_sprom_valid(sprom);
352 bcma_sprom_extract_r8(bus, sprom);