xref: /linux/drivers/bcma/sprom.c (revision 25061d285747f20aafa4b50df1b0b5665fef29cd)
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 	int err;
53 
54 	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;
66 fail:
67 	pr_warn("Using fallback SPROM failed (err %d)\n", err);
68 	return err;
69 }
70 
71 /**************************************************
72  * R/W ops.
73  **************************************************/
74 
75 static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom)
76 {
77 	int i;
78 	for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++)
79 		sprom[i] = bcma_read16(bus->drv_cc.core,
80 				       offset + (i * 2));
81 }
82 
83 /**************************************************
84  * Validation.
85  **************************************************/
86 
87 static 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 
127 static 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 
142 static 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 
157 static 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;
167 	if (revision != 8 && revision != 9) {
168 		pr_err("Unsupported SPROM revision: %d\n", revision);
169 		return -ENOENT;
170 	}
171 
172 	return 0;
173 }
174 
175 /**************************************************
176  * SPROM extraction.
177  **************************************************/
178 
179 #define SPOFF(offset)	((offset) / sizeof(u16))
180 
181 #define SPEX(_field, _offset, _mask, _shift)	\
182 	bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift))
183 
184 static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
185 {
186 	u16 v, o;
187 	int i;
188 	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));
194 
195 	bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] &
196 		SSB_SPROM_REVISION_REV;
197 
198 	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 	}
202 
203 	SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0);
204 
205 	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);
213 
214 	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);
222 
223 	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);
231 
232 	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);
240 
241 	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);
245 
246 	SPEX(country_code, SSB_SPROM8_CCODE, ~0, 0);
247 
248 	/* Extract cores power info info */
249 	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
250 		o = pwr_info_offset[i];
251 		SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
252 			SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
253 		SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
254 			SSB_SPROM8_2G_MAXP, 0);
255 
256 		SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
257 		SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
258 		SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
259 
260 		SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
261 			SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
262 		SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
263 			SSB_SPROM8_5G_MAXP, 0);
264 		SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
265 			SSB_SPROM8_5GH_MAXP, 0);
266 		SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
267 			SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
268 
269 		SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
270 		SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
271 		SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
272 		SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
273 		SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
274 		SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
275 		SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
276 		SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
277 		SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
278 	}
279 
280 	SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS,
281 	     SSB_SROM8_FEM_TSSIPOS_SHIFT);
282 	SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN,
283 	     SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
284 	SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE,
285 	     SSB_SROM8_FEM_PDET_RANGE_SHIFT);
286 	SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO,
287 	     SSB_SROM8_FEM_TR_ISO_SHIFT);
288 	SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT,
289 	     SSB_SROM8_FEM_ANTSWLUT_SHIFT);
290 
291 	SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS,
292 	     SSB_SROM8_FEM_TSSIPOS_SHIFT);
293 	SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN,
294 	     SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
295 	SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE,
296 	     SSB_SROM8_FEM_PDET_RANGE_SHIFT);
297 	SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO,
298 	     SSB_SROM8_FEM_TR_ISO_SHIFT);
299 	SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
300 	     SSB_SROM8_FEM_ANTSWLUT_SHIFT);
301 }
302 
303 /*
304  * Indicates the presence of external SPROM.
305  */
306 static bool bcma_sprom_ext_available(struct bcma_bus *bus)
307 {
308 	u32 chip_status;
309 	u32 srom_control;
310 	u32 present_mask;
311 
312 	if (bus->drv_cc.core->id.rev >= 31) {
313 		if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM))
314 			return false;
315 
316 		srom_control = bcma_read32(bus->drv_cc.core,
317 					   BCMA_CC_SROM_CONTROL);
318 		return srom_control & BCMA_CC_SROM_CONTROL_PRESENT;
319 	}
320 
321 	/* older chipcommon revisions use chip status register */
322 	chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
323 	switch (bus->chipinfo.id) {
324 	case 0x4313:
325 		present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT;
326 		break;
327 
328 	case 0x4331:
329 		present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT;
330 		break;
331 
332 	default:
333 		return true;
334 	}
335 
336 	return chip_status & present_mask;
337 }
338 
339 /*
340  * Indicates that on-chip OTP memory is present and enabled.
341  */
342 static bool bcma_sprom_onchip_available(struct bcma_bus *bus)
343 {
344 	u32 chip_status;
345 	u32 otpsize = 0;
346 	bool present;
347 
348 	chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT);
349 	switch (bus->chipinfo.id) {
350 	case 0x4313:
351 		present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT;
352 		break;
353 
354 	case 0x4331:
355 		present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT;
356 		break;
357 
358 	case 43224:
359 	case 43225:
360 		/* for these chips OTP is always available */
361 		present = true;
362 		break;
363 
364 	default:
365 		present = false;
366 		break;
367 	}
368 
369 	if (present) {
370 		otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS;
371 		otpsize >>= BCMA_CC_CAP_OTPS_SHIFT;
372 	}
373 
374 	return otpsize != 0;
375 }
376 
377 /*
378  * Verify OTP is filled and determine the byte
379  * offset where SPROM data is located.
380  *
381  * On error, returns 0; byte offset otherwise.
382  */
383 static int bcma_sprom_onchip_offset(struct bcma_bus *bus)
384 {
385 	struct bcma_device *cc = bus->drv_cc.core;
386 	u32 offset;
387 
388 	/* verify OTP status */
389 	if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0)
390 		return 0;
391 
392 	/* obtain bit offset from otplayout register */
393 	offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET);
394 	return BCMA_CC_SPROM + (offset >> 3);
395 }
396 
397 int bcma_sprom_get(struct bcma_bus *bus)
398 {
399 	u16 offset = BCMA_CC_SPROM;
400 	u16 *sprom;
401 	int err = 0;
402 
403 	if (!bus->drv_cc.core)
404 		return -EOPNOTSUPP;
405 
406 	if (!bcma_sprom_ext_available(bus)) {
407 		bool sprom_onchip;
408 
409 		/*
410 		 * External SPROM takes precedence so check
411 		 * on-chip OTP only when no external SPROM
412 		 * is present.
413 		 */
414 		sprom_onchip = bcma_sprom_onchip_available(bus);
415 		if (sprom_onchip) {
416 			/* determine offset */
417 			offset = bcma_sprom_onchip_offset(bus);
418 		}
419 		if (!offset || !sprom_onchip) {
420 			/*
421 			 * Maybe there is no SPROM on the device?
422 			 * Now we ask the arch code if there is some sprom
423 			 * available for this device in some other storage.
424 			 */
425 			err = bcma_fill_sprom_with_fallback(bus, &bus->sprom);
426 			return err;
427 		}
428 	}
429 
430 	sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
431 			GFP_KERNEL);
432 	if (!sprom)
433 		return -ENOMEM;
434 
435 	if (bus->chipinfo.id == 0x4331)
436 		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false);
437 
438 	pr_debug("SPROM offset 0x%x\n", offset);
439 	bcma_sprom_read(bus, offset, sprom);
440 
441 	if (bus->chipinfo.id == 0x4331)
442 		bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true);
443 
444 	err = bcma_sprom_valid(sprom);
445 	if (err)
446 		goto out;
447 
448 	bcma_sprom_extract_r8(bus, sprom);
449 
450 out:
451 	kfree(sprom);
452 	return err;
453 }
454