sprom.c revision 32998cc96a76cc3f42f66b55fec301377e439c66
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
19static 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{
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;
66fail:
67	pr_warn("Using fallback SPROM failed (err %d)\n", err);
68	return err;
69}
70
71/**************************************************
72 * R/W ops.
73 **************************************************/
74
75static 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
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;
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
184static 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 */
306static 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 */
342static 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 */
383static 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
397int 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
450out:
451	kfree(sprom);
452	return err;
453}
454