blob: 22c99683a1809b6b04c267d49aaa39ab5ec07369 [file] [log] [blame]
Rafał Miłecki27f18dc2011-06-02 02:08:51 +02001/*
2 * Broadcom specific AMBA
3 * SPROM reading
4 *
Hauke Mehrtensa0272372012-02-28 00:56:10 +01005 * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de>
6 *
Rafał Miłecki27f18dc2011-06-02 02:08:51 +02007 * 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
Hauke Mehrtensa0272372012-02-28 00:56:10 +010019static 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 */
39int 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
49static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus,
50 struct ssb_sprom *out)
51{
Arend van Spriel4ac887c2012-03-06 15:50:47 +010052 int err;
Hauke Mehrtensa0272372012-02-28 00:56:10 +010053
Arend van Spriel4ac887c2012-03-06 15:50:47 +010054 if (!get_fallback_sprom) {
55 err = -ENOENT;
56 goto fail;
57 }
58
59 err = get_fallback_sprom(bus, out);
60 if (err)
61 goto fail;
62
63 pr_debug("Using SPROM revision %d provided by"
64 " platform.\n", bus->sprom.revision);
65 return 0;
66fail:
67 pr_warn("Using fallback SPROM failed (err %d)\n", err);
68 return err;
Hauke Mehrtensa0272372012-02-28 00:56:10 +010069}
70
Rafał Miłecki27f18dc2011-06-02 02:08:51 +020071/**************************************************
72 * R/W ops.
73 **************************************************/
74
Rafał Miłeckieb1577b2011-07-17 11:00:59 +020075static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
Rafał Miłecki27f18dc2011-06-02 02:08:51 +020076{
77 int i;
78 for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
79 sprom[i] = bcma_read16(bus->drv_cc.core,
Rafał Miłeckieb1577b2011-07-17 11:00:59 +020080 offset + (i * 2));
Rafał Miłecki27f18dc2011-06-02 02:08:51 +020081}
82
83/**************************************************
84 * Validation.
85 **************************************************/
86
87static inline u8 bcma_crc8(u8 crc, u8 data)
88{
89 /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */
90 static const u8 t[] = {
91 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
92 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
93 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
94 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
95 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
96 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
97 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
98 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
99 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
100 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
101 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
102 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
103 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
104 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
105 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
106 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
107 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
108 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
109 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
110 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
111 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
112 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
113 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
114 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
115 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
116 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
117 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
118 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
119 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
120 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
121 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
122 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
123 };
124 return t[crc ^ data];
125}
126
127static u8 bcma_sprom_crc(const u16 *sprom)
128{
129 int word;
130 u8 crc = 0xFF;
131
132 for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) {
133 crc = bcma_crc8(crc, sprom[word] & 0x00FF);
134 crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8);
135 }
136 crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF);
137 crc ^= 0xFF;
138
139 return crc;
140}
141
142static int bcma_sprom_check_crc(const u16 *sprom)
143{
144 u8 crc;
145 u8 expected_crc;
146 u16 tmp;
147
148 crc = bcma_sprom_crc(sprom);
149 tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC;
150 expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
151 if (crc != expected_crc)
152 return -EPROTO;
153
154 return 0;
155}
156
157static int bcma_sprom_valid(const u16 *sprom)
158{
159 u16 revision;
160 int err;
161
162 err = bcma_sprom_check_crc(sprom);
163 if (err)
164 return err;
165
166 revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV;
Rafał Miłeckic54dcd12011-07-14 21:49:21 +0200167 if (revision != 8 && revision != 9) {
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200168 pr_err("Unsupported SPROM revision: %d\n", revision);
169 return -ENOENT;
170 }
171
172 return 0;
173}
174
175/**************************************************
176 * SPROM extraction.
177 **************************************************/
178
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100179#define SPOFF(offset) ((offset) / sizeof(u16))
180
181#define SPEX(_field, _offset, _mask, _shift) \
182 bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift))
183
Hauke Mehrtens432c4d12012-04-29 02:04:12 +0200184#define SPEX32(_field, _offset, _mask, _shift) \
185 bus->sprom._field = ((((u32)sprom[SPOFF((_offset)+2)] << 16 | \
186 sprom[SPOFF(_offset)]) & (_mask)) >> (_shift))
187
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200188static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
189{
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100190 u16 v, o;
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200191 int i;
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100192 u16 pwr_info_offset[] = {
193 SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
194 SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
195 };
196 BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
197 ARRAY_SIZE(bus->sprom.core_pwr_info));
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200198
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100199 bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
200 SSB_SPROM_REVISION_REV;
201
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200202 for (i = 0; i < 3; i++) {
203 v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
204 *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
205 }
Rafał Miłeckid703a5ae2011-08-28 18:47:23 +0200206
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100207 SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
Rafał Miłeckid703a5ae2011-08-28 18:47:23 +0200208
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100209 SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
210 SSB_SPROM4_TXPID2G0_SHIFT);
211 SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1,
212 SSB_SPROM4_TXPID2G1_SHIFT);
213 SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2,
214 SSB_SPROM4_TXPID2G2_SHIFT);
215 SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3,
216 SSB_SPROM4_TXPID2G3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100217
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100218 SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0,
219 SSB_SPROM4_TXPID5GL0_SHIFT);
220 SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1,
221 SSB_SPROM4_TXPID5GL1_SHIFT);
222 SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2,
223 SSB_SPROM4_TXPID5GL2_SHIFT);
224 SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3,
225 SSB_SPROM4_TXPID5GL3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100226
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100227 SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0,
228 SSB_SPROM4_TXPID5G0_SHIFT);
229 SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1,
230 SSB_SPROM4_TXPID5G1_SHIFT);
231 SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2,
232 SSB_SPROM4_TXPID5G2_SHIFT);
233 SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3,
234 SSB_SPROM4_TXPID5G3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100235
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100236 SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0,
237 SSB_SPROM4_TXPID5GH0_SHIFT);
238 SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1,
239 SSB_SPROM4_TXPID5GH1_SHIFT);
240 SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2,
241 SSB_SPROM4_TXPID5GH2_SHIFT);
242 SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3,
243 SSB_SPROM4_TXPID5GH3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100244
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100245 SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0);
246 SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0);
247 SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
248 SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
Rafał Miłeckid703a5ae2011-08-28 18:47:23 +0200249
Hauke Mehrtensbf7d4202012-04-29 02:04:10 +0200250 SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
251 SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
Rafał Miłeckiaee5ed52011-12-08 18:02:22 +0100252
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100253 /* Extract cores power info info */
254 for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
255 o = pwr_info_offset[i];
256 SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
257 SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
258 SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
259 SSB_SPROM8_2G_MAXP, 0);
260
261 SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
262 SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
263 SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
264
265 SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
266 SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
267 SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
268 SSB_SPROM8_5G_MAXP, 0);
269 SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
270 SSB_SPROM8_5GH_MAXP, 0);
271 SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
272 SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
273
274 SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
275 SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
276 SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
277 SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
278 SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
279 SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
280 SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
281 SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
282 SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
283 }
284
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100285 SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS,
286 SSB_SROM8_FEM_TSSIPOS_SHIFT);
287 SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN,
288 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
289 SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE,
290 SSB_SROM8_FEM_PDET_RANGE_SHIFT);
291 SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO,
292 SSB_SROM8_FEM_TR_ISO_SHIFT);
293 SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT,
294 SSB_SROM8_FEM_ANTSWLUT_SHIFT);
Rafał Miłeckiaee5ed52011-12-08 18:02:22 +0100295
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100296 SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS,
297 SSB_SROM8_FEM_TSSIPOS_SHIFT);
298 SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN,
299 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
300 SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE,
301 SSB_SROM8_FEM_PDET_RANGE_SHIFT);
302 SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO,
303 SSB_SROM8_FEM_TR_ISO_SHIFT);
304 SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
305 SSB_SROM8_FEM_ANTSWLUT_SHIFT);
Hauke Mehrtens432c4d12012-04-29 02:04:12 +0200306
307 SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
308 SSB_SPROM8_ANTAVAIL_A_SHIFT);
309 SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
310 SSB_SPROM8_ANTAVAIL_BG_SHIFT);
311 SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
312 SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
313 SSB_SPROM8_ITSSI_BG_SHIFT);
314 SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
315 SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
316 SSB_SPROM8_ITSSI_A_SHIFT);
317 SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
318 SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
319 SSB_SPROM8_MAXP_AL_SHIFT);
320 SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
321 SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
322 SSB_SPROM8_GPIOA_P1_SHIFT);
323 SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
324 SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
325 SSB_SPROM8_GPIOB_P3_SHIFT);
326 SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
327 SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
328 SSB_SPROM8_TRI5G_SHIFT);
329 SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
330 SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
331 SSB_SPROM8_TRI5GH_SHIFT);
332 SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G,
333 SSB_SPROM8_RXPO2G_SHIFT);
334 SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
335 SSB_SPROM8_RXPO5G_SHIFT);
336 SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
337 SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
338 SSB_SPROM8_RSSISMC2G_SHIFT);
339 SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
340 SSB_SPROM8_RSSISAV2G_SHIFT);
341 SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
342 SSB_SPROM8_BXA2G_SHIFT);
343 SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
344 SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
345 SSB_SPROM8_RSSISMC5G_SHIFT);
346 SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
347 SSB_SPROM8_RSSISAV5G_SHIFT);
348 SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
349 SSB_SPROM8_BXA5G_SHIFT);
350
351 SPEX(pa0b0, SSB_SPROM8_PA0B0, ~0, 0);
352 SPEX(pa0b1, SSB_SPROM8_PA0B1, ~0, 0);
353 SPEX(pa0b2, SSB_SPROM8_PA0B2, ~0, 0);
354 SPEX(pa1b0, SSB_SPROM8_PA1B0, ~0, 0);
355 SPEX(pa1b1, SSB_SPROM8_PA1B1, ~0, 0);
356 SPEX(pa1b2, SSB_SPROM8_PA1B2, ~0, 0);
357 SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, ~0, 0);
358 SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, ~0, 0);
359 SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, ~0, 0);
360 SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, ~0, 0);
361 SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, ~0, 0);
362 SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, ~0, 0);
363 SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, ~0, 0);
364 SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, ~0, 0);
365 SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, ~0, 0);
366 SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, ~0, 0);
367 SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0);
368
369 /* Extract the antenna gain values. */
370 SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
371 SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
372 SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
373 SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
374 SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
375 SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
376 SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
377 SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200378}
379
Arend van Spriel10d84932012-03-06 15:50:48 +0100380/*
381 * Indicates the presence of external SPROM.
382 */
383static bool bcma_sprom_ext_available(struct bcma_bus *bus)
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100384{
Arend van Spriel10d84932012-03-06 15:50:48 +0100385 u32 chip_status;
386 u32 srom_control;
387 u32 present_mask;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100388
Arend van Spriel10d84932012-03-06 15:50:48 +0100389 if (bus->drv_cc.core->id.rev >= 31) {
390 if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
391 return false;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100392
Arend van Spriel10d84932012-03-06 15:50:48 +0100393 srom_control = bcma_read32(bus->drv_cc.core,
394 BCMA_CC_SROM_CONTROL);
395 return srom_control & BCMA_CC_SROM_CONTROL_PRESENT;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100396 }
Arend van Spriel10d84932012-03-06 15:50:48 +0100397
398 /* older chipcommon revisions use chip status register */
399 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
400 switch (bus->chipinfo.id) {
401 case 0x4313:
402 present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT;
403 break;
404
405 case 0x4331:
406 present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT;
407 break;
408
409 default:
410 return true;
411 }
412
413 return chip_status & present_mask;
414}
415
416/*
417 * Indicates that on-chip OTP memory is present and enabled.
418 */
419static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
420{
421 u32 chip_status;
422 u32 otpsize = 0;
423 bool present;
424
425 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
426 switch (bus->chipinfo.id) {
427 case 0x4313:
428 present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT;
429 break;
430
431 case 0x4331:
432 present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
433 break;
434
435 case 43224:
436 case 43225:
437 /* for these chips OTP is always available */
438 present = true;
439 break;
440
441 default:
442 present = false;
443 break;
444 }
445
446 if (present) {
447 otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS;
448 otpsize >>= BCMA_CC_CAP_OTPS_SHIFT;
449 }
450
451 return otpsize != 0;
452}
453
454/*
455 * Verify OTP is filled and determine the byte
456 * offset where SPROM data is located.
457 *
458 * On error, returns 0; byte offset otherwise.
459 */
460static int bcma_sprom_onchip_offset(struct bcma_bus *bus)
461{
462 struct bcma_device *cc = bus->drv_cc.core;
463 u32 offset;
464
465 /* verify OTP status */
466 if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0)
467 return 0;
468
469 /* obtain bit offset from otplayout register */
470 offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET);
471 return BCMA_CC_SPROM + (offset >> 3);
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100472}
473
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200474int bcma_sprom_get(struct bcma_bus *bus)
475{
Arend van Spriel10d84932012-03-06 15:50:48 +0100476 u16 offset = BCMA_CC_SPROM;
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200477 u16 *sprom;
478 int err = 0;
479
480 if (!bus->drv_cc.core)
481 return -EOPNOTSUPP;
482
Arend van Spriel10d84932012-03-06 15:50:48 +0100483 if (!bcma_sprom_ext_available(bus)) {
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200484 bool sprom_onchip;
485
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100486 /*
Arend van Spriel10d84932012-03-06 15:50:48 +0100487 * External SPROM takes precedence so check
488 * on-chip OTP only when no external SPROM
489 * is present.
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100490 */
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200491 sprom_onchip = bcma_sprom_onchip_available(bus);
492 if (sprom_onchip) {
Arend van Spriel10d84932012-03-06 15:50:48 +0100493 /* determine offset */
494 offset = bcma_sprom_onchip_offset(bus);
495 }
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200496 if (!offset || !sprom_onchip) {
Arend van Spriel10d84932012-03-06 15:50:48 +0100497 /*
498 * Maybe there is no SPROM on the device?
499 * Now we ask the arch code if there is some sprom
500 * available for this device in some other storage.
501 */
502 err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
503 return err;
504 }
Hauke Mehrtensd6865dcc2012-01-31 00:03:37 +0100505 }
506
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200507 sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
508 GFP_KERNEL);
509 if (!sprom)
510 return -ENOMEM;
511
Rafał Miłecki984e5be2011-08-11 23:46:44 +0200512 if (bus->chipinfo.id == 0x4331)
513 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
514
Larry Fingercebcab92012-01-11 14:39:32 -0600515 pr_debug("SPROM offset 0x%x\n", offset);
Rafał Miłeckieb1577b2011-07-17 11:00:59 +0200516 bcma_sprom_read(bus, offset, sprom);
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200517
Rafał Miłecki984e5be2011-08-11 23:46:44 +0200518 if (bus->chipinfo.id == 0x4331)
519 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
520
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200521 err = bcma_sprom_valid(sprom);
522 if (err)
523 goto out;
524
525 bcma_sprom_extract_r8(bus, sprom);
526
527out:
528 kfree(sprom);
529 return err;
530}