blob: 1799372131d2a031a12f68d14ccf44c07ffba7b3 [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
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200184static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
185{
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100186 u16 v, o;
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200187 int i;
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100188 u16 pwr_info_offset[] = {
189 SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
190 SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
191 };
192 BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
193 ARRAY_SIZE(bus->sprom.core_pwr_info));
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200194
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100195 bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
196 SSB_SPROM_REVISION_REV;
197
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200198 for (i = 0; i < 3; i++) {
199 v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i];
200 *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v);
201 }
Rafał Miłeckid703a5a2011-08-28 18:47:23 +0200202
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100203 SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
Rafał Miłeckid703a5a2011-08-28 18:47:23 +0200204
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100205 SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0,
206 SSB_SPROM4_TXPID2G0_SHIFT);
207 SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1,
208 SSB_SPROM4_TXPID2G1_SHIFT);
209 SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2,
210 SSB_SPROM4_TXPID2G2_SHIFT);
211 SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3,
212 SSB_SPROM4_TXPID2G3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100213
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100214 SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0,
215 SSB_SPROM4_TXPID5GL0_SHIFT);
216 SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1,
217 SSB_SPROM4_TXPID5GL1_SHIFT);
218 SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2,
219 SSB_SPROM4_TXPID5GL2_SHIFT);
220 SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3,
221 SSB_SPROM4_TXPID5GL3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100222
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100223 SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0,
224 SSB_SPROM4_TXPID5G0_SHIFT);
225 SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1,
226 SSB_SPROM4_TXPID5G1_SHIFT);
227 SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2,
228 SSB_SPROM4_TXPID5G2_SHIFT);
229 SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3,
230 SSB_SPROM4_TXPID5G3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100231
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100232 SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0,
233 SSB_SPROM4_TXPID5GH0_SHIFT);
234 SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1,
235 SSB_SPROM4_TXPID5GH1_SHIFT);
236 SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2,
237 SSB_SPROM4_TXPID5GH2_SHIFT);
238 SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3,
239 SSB_SPROM4_TXPID5GH3_SHIFT);
Rafał Miłeckidaadc6b2011-12-12 21:33:12 +0100240
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100241 SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0);
242 SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0);
243 SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
244 SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
Rafał Miłeckid703a5a2011-08-28 18:47:23 +0200245
Hauke Mehrtensbf7d4202012-04-29 02:04:10 +0200246 SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
247 SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
Rafał Miłeckiaee5ed52011-12-08 18:02:22 +0100248
Rafał Miłecki507f9a72012-01-02 08:41:25 +0100249 /* Extract cores power info info */
250 for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
251 o = pwr_info_offset[i];
252 SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
253 SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
254 SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
255 SSB_SPROM8_2G_MAXP, 0);
256
257 SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
258 SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
259 SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
260
261 SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
262 SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
263 SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
264 SSB_SPROM8_5G_MAXP, 0);
265 SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
266 SSB_SPROM8_5GH_MAXP, 0);
267 SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
268 SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
269
270 SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
271 SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
272 SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
273 SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
274 SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
275 SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
276 SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
277 SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
278 SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
279 }
280
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100281 SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS,
282 SSB_SROM8_FEM_TSSIPOS_SHIFT);
283 SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN,
284 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
285 SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE,
286 SSB_SROM8_FEM_PDET_RANGE_SHIFT);
287 SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO,
288 SSB_SROM8_FEM_TR_ISO_SHIFT);
289 SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT,
290 SSB_SROM8_FEM_ANTSWLUT_SHIFT);
Rafał Miłeckiaee5ed52011-12-08 18:02:22 +0100291
Rafał Miłeckib35a9ac2012-01-02 08:41:24 +0100292 SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS,
293 SSB_SROM8_FEM_TSSIPOS_SHIFT);
294 SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN,
295 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
296 SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE,
297 SSB_SROM8_FEM_PDET_RANGE_SHIFT);
298 SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO,
299 SSB_SROM8_FEM_TR_ISO_SHIFT);
300 SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
301 SSB_SROM8_FEM_ANTSWLUT_SHIFT);
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200302}
303
Arend van Spriel10d84932012-03-06 15:50:48 +0100304/*
305 * Indicates the presence of external SPROM.
306 */
307static bool bcma_sprom_ext_available(struct bcma_bus *bus)
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100308{
Arend van Spriel10d84932012-03-06 15:50:48 +0100309 u32 chip_status;
310 u32 srom_control;
311 u32 present_mask;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100312
Arend van Spriel10d84932012-03-06 15:50:48 +0100313 if (bus->drv_cc.core->id.rev >= 31) {
314 if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
315 return false;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100316
Arend van Spriel10d84932012-03-06 15:50:48 +0100317 srom_control = bcma_read32(bus->drv_cc.core,
318 BCMA_CC_SROM_CONTROL);
319 return srom_control & BCMA_CC_SROM_CONTROL_PRESENT;
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100320 }
Arend van Spriel10d84932012-03-06 15:50:48 +0100321
322 /* older chipcommon revisions use chip status register */
323 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
324 switch (bus->chipinfo.id) {
325 case 0x4313:
326 present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT;
327 break;
328
329 case 0x4331:
330 present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT;
331 break;
332
333 default:
334 return true;
335 }
336
337 return chip_status & present_mask;
338}
339
340/*
341 * Indicates that on-chip OTP memory is present and enabled.
342 */
343static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
344{
345 u32 chip_status;
346 u32 otpsize = 0;
347 bool present;
348
349 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
350 switch (bus->chipinfo.id) {
351 case 0x4313:
352 present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT;
353 break;
354
355 case 0x4331:
356 present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
357 break;
358
359 case 43224:
360 case 43225:
361 /* for these chips OTP is always available */
362 present = true;
363 break;
364
365 default:
366 present = false;
367 break;
368 }
369
370 if (present) {
371 otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS;
372 otpsize >>= BCMA_CC_CAP_OTPS_SHIFT;
373 }
374
375 return otpsize != 0;
376}
377
378/*
379 * Verify OTP is filled and determine the byte
380 * offset where SPROM data is located.
381 *
382 * On error, returns 0; byte offset otherwise.
383 */
384static int bcma_sprom_onchip_offset(struct bcma_bus *bus)
385{
386 struct bcma_device *cc = bus->drv_cc.core;
387 u32 offset;
388
389 /* verify OTP status */
390 if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0)
391 return 0;
392
393 /* obtain bit offset from otplayout register */
394 offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET);
395 return BCMA_CC_SPROM + (offset >> 3);
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100396}
397
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200398int bcma_sprom_get(struct bcma_bus *bus)
399{
Arend van Spriel10d84932012-03-06 15:50:48 +0100400 u16 offset = BCMA_CC_SPROM;
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200401 u16 *sprom;
402 int err = 0;
403
404 if (!bus->drv_cc.core)
405 return -EOPNOTSUPP;
406
Arend van Spriel10d84932012-03-06 15:50:48 +0100407 if (!bcma_sprom_ext_available(bus)) {
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200408 bool sprom_onchip;
409
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100410 /*
Arend van Spriel10d84932012-03-06 15:50:48 +0100411 * External SPROM takes precedence so check
412 * on-chip OTP only when no external SPROM
413 * is present.
Hauke Mehrtensa0272372012-02-28 00:56:10 +0100414 */
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200415 sprom_onchip = bcma_sprom_onchip_available(bus);
416 if (sprom_onchip) {
Arend van Spriel10d84932012-03-06 15:50:48 +0100417 /* determine offset */
418 offset = bcma_sprom_onchip_offset(bus);
419 }
Hauke Mehrtens32998cc2012-04-14 14:38:54 +0200420 if (!offset || !sprom_onchip) {
Arend van Spriel10d84932012-03-06 15:50:48 +0100421 /*
422 * Maybe there is no SPROM on the device?
423 * Now we ask the arch code if there is some sprom
424 * available for this device in some other storage.
425 */
426 err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
427 return err;
428 }
Hauke Mehrtensd6865dcc2012-01-31 00:03:37 +0100429 }
430
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200431 sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
432 GFP_KERNEL);
433 if (!sprom)
434 return -ENOMEM;
435
Rafał Miłecki984e5be2011-08-11 23:46:44 +0200436 if (bus->chipinfo.id == 0x4331)
437 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
438
Larry Fingercebcab92012-01-11 14:39:32 -0600439 pr_debug("SPROM offset 0x%x\n", offset);
Rafał Miłeckieb1577b2011-07-17 11:00:59 +0200440 bcma_sprom_read(bus, offset, sprom);
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200441
Rafał Miłecki984e5be2011-08-11 23:46:44 +0200442 if (bus->chipinfo.id == 0x4331)
443 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
444
Rafał Miłecki27f18dc2011-06-02 02:08:51 +0200445 err = bcma_sprom_valid(sprom);
446 if (err)
447 goto out;
448
449 bcma_sprom_extract_r8(bus, sprom);
450
451out:
452 kfree(sprom);
453 return err;
454}