blob: 4adf9ef9a113010e2b8f51cabd27e07c57898c83 [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
Rafał Miłecki3d9d8af2012-07-05 22:07:32 +020063 bcma_debug(bus, "Using SPROM revision %d provided by platform.\n",
64 bus->sprom.revision);
Arend van Spriel4ac887c2012-03-06 15:50:47 +010065 return 0;
66fail:
Rafał Miłecki3d9d8af2012-07-05 22:07:32 +020067 bcma_warn(bus, "Using fallback SPROM failed (err %d)\n", err);
Arend van Spriel4ac887c2012-03-06 15:50:47 +010068 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
Hauke Mehrtense2da4bd2012-04-29 02:04:13 +0200188#define SPEX_ARRAY8(_field, _offset, _mask, _shift) \
189 do { \
190 SPEX(_field[0], _offset + 0, _mask, _shift); \
191 SPEX(_field[1], _offset + 2, _mask, _shift); \
192 SPEX(_field[2], _offset + 4, _mask, _shift); \
193 SPEX(_field[3], _offset + 6, _mask, _shift); \
194 SPEX(_field[4], _offset + 8, _mask, _shift); \
195 SPEX(_field[5], _offset + 10, _mask, _shift); \
196 SPEX(_field[6], _offset + 12, _mask, _shift); \
197 SPEX(_field[7], _offset + 14, _mask, _shift); \
198 } while (0)
199
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200200static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
201{
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100202 u16 v, o;
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200203 int i;
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100204 u16 pwr_info_offset[] = {
205 SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
206 SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
207 };
208 BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
209 ARRAY_SIZE(bus->sprom.core_pwr_info));
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200210
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100211 bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
212 SSB_SPROM_REVISION_REV;
213
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200214 for (i = 0; i < 3; i++) {
215 v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
216 *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
217 }
Rafał Miłeckid703a5a2011-08-28 18:47:23 +0200218
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100219 SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
Rafał Miłeckid703a5a2011-08-28 18:47:23 +0200220
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100221 SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
222 SSB_SPROM4_TXPID2G0_SHIFT);
223 SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1,
224 SSB_SPROM4_TXPID2G1_SHIFT);
225 SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2,
226 SSB_SPROM4_TXPID2G2_SHIFT);
227 SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3,
228 SSB_SPROM4_TXPID2G3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100229
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100230 SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0,
231 SSB_SPROM4_TXPID5GL0_SHIFT);
232 SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1,
233 SSB_SPROM4_TXPID5GL1_SHIFT);
234 SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2,
235 SSB_SPROM4_TXPID5GL2_SHIFT);
236 SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3,
237 SSB_SPROM4_TXPID5GL3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100238
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100239 SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0,
240 SSB_SPROM4_TXPID5G0_SHIFT);
241 SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1,
242 SSB_SPROM4_TXPID5G1_SHIFT);
243 SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2,
244 SSB_SPROM4_TXPID5G2_SHIFT);
245 SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3,
246 SSB_SPROM4_TXPID5G3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100247
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100248 SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0,
249 SSB_SPROM4_TXPID5GH0_SHIFT);
250 SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1,
251 SSB_SPROM4_TXPID5GH1_SHIFT);
252 SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2,
253 SSB_SPROM4_TXPID5GH2_SHIFT);
254 SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3,
255 SSB_SPROM4_TXPID5GH3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100256
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100257 SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0);
258 SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0);
259 SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
260 SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
Rafał Miłeckid703a5a2011-08-28 18:47:23 +0200261
Hauke Mehrtensbf7d4202012-04-29 02:04:10 +0200262 SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
263 SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
Rafał Miłeckiaee5ed52011-12-08 18:02:22 +0100264
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100265 /* Extract cores power info info */
266 for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
267 o = pwr_info_offset[i];
268 SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
269 SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
270 SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
271 SSB_SPROM8_2G_MAXP, 0);
272
273 SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
274 SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
275 SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
276
277 SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
278 SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
279 SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
280 SSB_SPROM8_5G_MAXP, 0);
281 SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
282 SSB_SPROM8_5GH_MAXP, 0);
283 SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
284 SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
285
286 SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
287 SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
288 SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
289 SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
290 SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
291 SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
292 SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
293 SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
294 SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
295 }
296
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100297 SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS,
298 SSB_SROM8_FEM_TSSIPOS_SHIFT);
299 SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN,
300 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
301 SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE,
302 SSB_SROM8_FEM_PDET_RANGE_SHIFT);
303 SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO,
304 SSB_SROM8_FEM_TR_ISO_SHIFT);
305 SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT,
306 SSB_SROM8_FEM_ANTSWLUT_SHIFT);
Rafał Miłeckiaee5ed52011-12-08 18:02:22 +0100307
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100308 SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS,
309 SSB_SROM8_FEM_TSSIPOS_SHIFT);
310 SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN,
311 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
312 SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE,
313 SSB_SROM8_FEM_PDET_RANGE_SHIFT);
314 SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO,
315 SSB_SROM8_FEM_TR_ISO_SHIFT);
316 SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
317 SSB_SROM8_FEM_ANTSWLUT_SHIFT);
Hauke Mehrtens432c4d12012-04-29 02:04:12 +0200318
319 SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
320 SSB_SPROM8_ANTAVAIL_A_SHIFT);
321 SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
322 SSB_SPROM8_ANTAVAIL_BG_SHIFT);
323 SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
324 SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
325 SSB_SPROM8_ITSSI_BG_SHIFT);
326 SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
327 SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
328 SSB_SPROM8_ITSSI_A_SHIFT);
329 SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
330 SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
331 SSB_SPROM8_MAXP_AL_SHIFT);
332 SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
333 SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
334 SSB_SPROM8_GPIOA_P1_SHIFT);
335 SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
336 SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
337 SSB_SPROM8_GPIOB_P3_SHIFT);
338 SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
339 SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
340 SSB_SPROM8_TRI5G_SHIFT);
341 SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
342 SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
343 SSB_SPROM8_TRI5GH_SHIFT);
344 SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G,
345 SSB_SPROM8_RXPO2G_SHIFT);
346 SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
347 SSB_SPROM8_RXPO5G_SHIFT);
348 SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
349 SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
350 SSB_SPROM8_RSSISMC2G_SHIFT);
351 SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
352 SSB_SPROM8_RSSISAV2G_SHIFT);
353 SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
354 SSB_SPROM8_BXA2G_SHIFT);
355 SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
356 SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
357 SSB_SPROM8_RSSISMC5G_SHIFT);
358 SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
359 SSB_SPROM8_RSSISAV5G_SHIFT);
360 SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
361 SSB_SPROM8_BXA5G_SHIFT);
362
363 SPEX(pa0b0, SSB_SPROM8_PA0B0, ~0, 0);
364 SPEX(pa0b1, SSB_SPROM8_PA0B1, ~0, 0);
365 SPEX(pa0b2, SSB_SPROM8_PA0B2, ~0, 0);
366 SPEX(pa1b0, SSB_SPROM8_PA1B0, ~0, 0);
367 SPEX(pa1b1, SSB_SPROM8_PA1B1, ~0, 0);
368 SPEX(pa1b2, SSB_SPROM8_PA1B2, ~0, 0);
369 SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, ~0, 0);
370 SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, ~0, 0);
371 SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, ~0, 0);
372 SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, ~0, 0);
373 SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, ~0, 0);
374 SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, ~0, 0);
375 SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, ~0, 0);
376 SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, ~0, 0);
377 SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, ~0, 0);
378 SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, ~0, 0);
379 SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0);
380
381 /* Extract the antenna gain values. */
382 SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
383 SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
384 SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
385 SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
386 SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
387 SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
388 SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
389 SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
Hauke Mehrtense2da4bd2012-04-29 02:04:13 +0200390
391 SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
392 SSB_SPROM8_LEDDC_ON_SHIFT);
393 SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF,
394 SSB_SPROM8_LEDDC_OFF_SHIFT);
395
396 SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
397 SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
398 SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
399 SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
400 SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
401 SSB_SPROM8_TXRXC_SWITCH_SHIFT);
402
403 SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
404
405 SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0);
406 SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0);
407 SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0);
408 SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0);
409
410 SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
411 SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
412 SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
413 SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
414 SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX,
415 SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
416 SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
417 SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
418 SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
419 SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX,
420 SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
421 SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
422 SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP,
423 SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR,
424 SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT);
425 SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP,
426 SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP,
427 SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT);
428 SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL,
429 SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT);
430
431 SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
432 SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
433 SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
434 SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0);
435
436 SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH,
437 SSB_SPROM8_THERMAL_TRESH_SHIFT);
438 SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
439 SSB_SPROM8_THERMAL_OFFSET_SHIFT);
440 SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA,
441 SSB_SPROM8_TEMPDELTA_PHYCAL,
442 SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
443 SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
444 SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
445 SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA,
446 SSB_SPROM8_TEMPDELTA_HYSTERESIS,
447 SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200448}
449
Arend van Spriel10d84932012-03-06 15:50:48 +0100450/*
451 * Indicates the presence of external SPROM.
452 */
453static bool bcma_sprom_ext_available(struct bcma_bus *bus)
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100454{
Arend van Spriel10d84932012-03-06 15:50:48 +0100455 u32 chip_status;
456 u32 srom_control;
457 u32 present_mask;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100458
Arend van Spriel10d84932012-03-06 15:50:48 +0100459 if (bus->drv_cc.core->id.rev >= 31) {
460 if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
461 return false;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100462
Arend van Spriel10d84932012-03-06 15:50:48 +0100463 srom_control = bcma_read32(bus->drv_cc.core,
464 BCMA_CC_SROM_CONTROL);
465 return srom_control & BCMA_CC_SROM_CONTROL_PRESENT;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100466 }
Arend van Spriel10d84932012-03-06 15:50:48 +0100467
468 /* older chipcommon revisions use chip status register */
469 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
470 switch (bus->chipinfo.id) {
Hauke Mehrtens4b4f5be2012-06-30 01:44:38 +0200471 case BCMA_CHIP_ID_BCM4313:
Arend van Spriel10d84932012-03-06 15:50:48 +0100472 present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT;
473 break;
474
Hauke Mehrtens4b4f5be2012-06-30 01:44:38 +0200475 case BCMA_CHIP_ID_BCM4331:
Arend van Spriel10d84932012-03-06 15:50:48 +0100476 present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT;
477 break;
478
479 default:
480 return true;
481 }
482
483 return chip_status & present_mask;
484}
485
486/*
487 * Indicates that on-chip OTP memory is present and enabled.
488 */
489static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
490{
491 u32 chip_status;
492 u32 otpsize = 0;
493 bool present;
494
495 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
496 switch (bus->chipinfo.id) {
Hauke Mehrtens4b4f5be2012-06-30 01:44:38 +0200497 case BCMA_CHIP_ID_BCM4313:
Arend van Spriel10d84932012-03-06 15:50:48 +0100498 present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT;
499 break;
500
Hauke Mehrtens4b4f5be2012-06-30 01:44:38 +0200501 case BCMA_CHIP_ID_BCM4331:
Arend van Spriel10d84932012-03-06 15:50:48 +0100502 present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
503 break;
504
Hauke Mehrtens4b4f5be2012-06-30 01:44:38 +0200505 case BCMA_CHIP_ID_BCM43224:
506 case BCMA_CHIP_ID_BCM43225:
Arend van Spriel10d84932012-03-06 15:50:48 +0100507 /* for these chips OTP is always available */
508 present = true;
509 break;
Rafał Miłecki646e0822012-09-21 08:38:38 +0200510 case BCMA_CHIP_ID_BCM43227:
Rafał Miłeckic263c2c2012-07-23 18:20:12 +0200511 case BCMA_CHIP_ID_BCM43228:
Rafał Miłecki646e0822012-09-21 08:38:38 +0200512 case BCMA_CHIP_ID_BCM43428:
Rafał Miłeckic263c2c2012-07-23 18:20:12 +0200513 present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT;
514 break;
Arend van Spriel10d84932012-03-06 15:50:48 +0100515 default:
516 present = false;
517 break;
518 }
519
520 if (present) {
521 otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS;
522 otpsize >>= BCMA_CC_CAP_OTPS_SHIFT;
523 }
524
525 return otpsize != 0;
526}
527
528/*
529 * Verify OTP is filled and determine the byte
530 * offset where SPROM data is located.
531 *
532 * On error, returns 0; byte offset otherwise.
533 */
534static int bcma_sprom_onchip_offset(struct bcma_bus *bus)
535{
536 struct bcma_device *cc = bus->drv_cc.core;
537 u32 offset;
538
539 /* verify OTP status */
540 if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0)
541 return 0;
542
543 /* obtain bit offset from otplayout register */
544 offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET);
545 return BCMA_CC_SPROM + (offset >> 3);
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100546}
547
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200548int bcma_sprom_get(struct bcma_bus *bus)
549{
Arend van Spriel10d84932012-03-06 15:50:48 +0100550 u16 offset = BCMA_CC_SPROM;
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200551 u16 *sprom;
552 int err = 0;
553
554 if (!bus->drv_cc.core)
555 return -EOPNOTSUPP;
556
Arend van Spriel10d84932012-03-06 15:50:48 +0100557 if (!bcma_sprom_ext_available(bus)) {
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200558 bool sprom_onchip;
559
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100560 /*
Arend van Spriel10d84932012-03-06 15:50:48 +0100561 * External SPROM takes precedence so check
562 * on-chip OTP only when no external SPROM
563 * is present.
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100564 */
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200565 sprom_onchip = bcma_sprom_onchip_available(bus);
566 if (sprom_onchip) {
Arend van Spriel10d84932012-03-06 15:50:48 +0100567 /* determine offset */
568 offset = bcma_sprom_onchip_offset(bus);
569 }
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200570 if (!offset || !sprom_onchip) {
Arend van Spriel10d84932012-03-06 15:50:48 +0100571 /*
572 * Maybe there is no SPROM on the device?
573 * Now we ask the arch code if there is some sprom
574 * available for this device in some other storage.
575 */
576 err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
577 return err;
578 }
Hauke Mehrtensd6865dcc2012-01-31 00:03:37 +0100579 }
580
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200581 sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
582 GFP_KERNEL);
583 if (!sprom)
584 return -ENOMEM;
585
Hauke Mehrtens4b4f5be2012-06-30 01:44:38 +0200586 if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
587 bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
Rafał Miłecki984e5be2011-08-11 23:46:44 +0200588 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
589
Rafał Miłecki3d9d8af2012-07-05 22:07:32 +0200590 bcma_debug(bus, "SPROM offset 0x%x\n", offset);
Rafał Miłeckieb1577b2011-07-17 11:00:59 +0200591 bcma_sprom_read(bus, offset, sprom);
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200592
Hauke Mehrtens4b4f5be2012-06-30 01:44:38 +0200593 if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4331 ||
594 bus->chipinfo.id == BCMA_CHIP_ID_BCM43431)
Rafał Miłecki984e5be2011-08-11 23:46:44 +0200595 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
596
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200597 err = bcma_sprom_valid(sprom);
Hauke Mehrtens017c4c32012-09-29 20:38:11 +0200598 if (err) {
599 bcma_warn(bus, "invalid sprom read from the PCIe card, try to use fallback sprom\n");
600 err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200601 goto out;
Hauke Mehrtens017c4c32012-09-29 20:38:11 +0200602 }
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200603
604 bcma_sprom_extract_r8(bus, sprom);
605
606out:
607 kfree(sprom);
608 return err;
609}