sprom.c 28.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*
 *  Copyright (C) 2004 Florian Schirmer <jolt@tuxbox.org>
 *  Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
 *  Copyright (C) 2006 Michael Buesch <m@bues.ch>
 *  Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org>
 *  Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
 *
 *  This program is free software; you can redistribute  it and/or modify it
 *  under  the terms of  the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the  License, or (at your
 *  option) any later version.
 *
 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *  You should have received a copy of the  GNU General Public License along
 *  with this program; if not, write  to the Free Software Foundation, Inc.,
 *  675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <bcm47xx.h>
30 31
#include <linux/if_ether.h>
#include <linux/etherdevice.h>
32 33 34 35 36 37 38 39 40 41 42 43 44 45

static void create_key(const char *prefix, const char *postfix,
		       const char *name, char *buf, int len)
{
	if (prefix && postfix)
		snprintf(buf, len, "%s%s%s", prefix, name, postfix);
	else if (prefix)
		snprintf(buf, len, "%s%s", prefix, name);
	else if (postfix)
		snprintf(buf, len, "%s%s", name, postfix);
	else
		snprintf(buf, len, "%s", name);
}

46 47 48 49 50 51 52 53
static int get_nvram_var(const char *prefix, const char *postfix,
			 const char *name, char *buf, int len, bool fallback)
{
	char key[40];
	int err;

	create_key(prefix, postfix, name, key, sizeof(key));

54
	err = bcm47xx_nvram_getenv(key, buf, len);
55
	if (fallback && err == -ENOENT && prefix) {
56
		create_key(NULL, postfix, name, key, sizeof(key));
57
		err = bcm47xx_nvram_getenv(key, buf, len);
58 59 60 61
	}
	return err;
}

62 63
#define NVRAM_READ_VAL(type)						\
static void nvram_read_ ## type (const char *prefix,			\
R
Ralf Baechle 已提交
64 65
				 const char *postfix, const char *name, \
				 type *val, type allset, bool fallback) \
66 67 68 69 70
{									\
	char buf[100];							\
	int err;							\
	type var;							\
									\
71 72
	err = get_nvram_var(prefix, postfix, name, buf, sizeof(buf),	\
			    fallback);					\
73 74
	if (err < 0)							\
		return;							\
75
	err = kstrto ## type(strim(buf), 0, &var);			\
76
	if (err) {							\
77 78
		pr_warn("can not parse nvram name %s%s%s with value %s got %i\n",	\
			prefix, name, postfix, buf, err);		\
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
		return;							\
	}								\
	if (allset && var == allset)					\
		return;							\
	*val = var;							\
}

NVRAM_READ_VAL(u8)
NVRAM_READ_VAL(s8)
NVRAM_READ_VAL(u16)
NVRAM_READ_VAL(u32)

#undef NVRAM_READ_VAL

static void nvram_read_u32_2(const char *prefix, const char *name,
94
			     u16 *val_lo, u16 *val_hi, bool fallback)
95 96 97 98 99
{
	char buf[100];
	int err;
	u32 val;

100
	err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback);
101 102
	if (err < 0)
		return;
103
	err = kstrtou32(strim(buf), 0, &val);
104
	if (err) {
105 106
		pr_warn("can not parse nvram name %s%s with value %s got %i\n",
			prefix, name, buf, err);
107 108 109 110 111 112 113
		return;
	}
	*val_lo = (val & 0x0000FFFFU);
	*val_hi = (val & 0xFFFF0000U) >> 16;
}

static void nvram_read_leddc(const char *prefix, const char *name,
114 115
			     u8 *leddc_on_time, u8 *leddc_off_time,
			     bool fallback)
116 117 118 119 120
{
	char buf[100];
	int err;
	u32 val;

121
	err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback);
122 123
	if (err < 0)
		return;
124
	err = kstrtou32(strim(buf), 0, &val);
125
	if (err) {
126 127
		pr_warn("can not parse nvram name %s%s with value %s got %i\n",
			prefix, name, buf, err);
128 129 130 131 132 133 134 135 136 137
		return;
	}

	if (val == 0xffff || val == 0xffffffff)
		return;

	*leddc_on_time = val & 0xff;
	*leddc_off_time = (val >> 16) & 0xff;
}

138 139 140 141 142 143 144 145 146 147 148 149 150 151
static void bcm47xx_nvram_parse_macaddr(char *buf, u8 macaddr[6])
{
	if (strchr(buf, ':'))
		sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", &macaddr[0],
			&macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
			&macaddr[5]);
	else if (strchr(buf, '-'))
		sscanf(buf, "%hhx-%hhx-%hhx-%hhx-%hhx-%hhx", &macaddr[0],
			&macaddr[1], &macaddr[2], &macaddr[3], &macaddr[4],
			&macaddr[5]);
	else
		pr_warn("Can not parse mac address: %s\n", buf);
}

152
static void nvram_read_macaddr(const char *prefix, const char *name,
153
			       u8 val[6], bool fallback)
154 155 156 157
{
	char buf[100];
	int err;

158
	err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback);
159 160
	if (err < 0)
		return;
161

162
	bcm47xx_nvram_parse_macaddr(buf, val);
163 164 165
}

static void nvram_read_alpha2(const char *prefix, const char *name,
166
			     char val[2], bool fallback)
167 168 169 170
{
	char buf[10];
	int err;

171
	err = get_nvram_var(prefix, NULL, name, buf, sizeof(buf), fallback);
172 173 174 175 176
	if (err < 0)
		return;
	if (buf[0] == '0')
		return;
	if (strlen(buf) > 2) {
177
		pr_warn("alpha2 is too long %s\n", buf);
178 179
		return;
	}
180
	memcpy(val, buf, 2);
181 182
}

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
/* This is one-function-only macro, it uses local "sprom" variable! */
#define ENTRY(_revmask, _type, _prefix, _name, _val, _allset, _fallback) \
	if (_revmask & BIT(sprom->revision)) \
		nvram_read_ ## _type(_prefix, NULL, _name, &sprom->_val, \
				     _allset, _fallback)
/*
 * Special version of filling function that can be safely called for any SPROM
 * revision. For every NVRAM to SPROM mapping it contains bitmask of revisions
 * for which the mapping is valid.
 * It obviously requires some hexadecimal/bitmasks knowledge, but allows
 * writing cleaner code (easy revisions handling).
 * Note that while SPROM revision 0 was never used, we still keep BIT(0)
 * reserved for it, just to keep numbering sane.
 */
static void bcm47xx_sprom_fill_auto(struct ssb_sprom *sprom,
				    const char *prefix, bool fallback)
{
	const char *pre = prefix;
	bool fb = fallback;

	ENTRY(0xfffffffe, u16, pre, "boardrev", board_rev, 0, true);
204 205 206
	ENTRY(0xfffffffe, u32, pre, "boardflags", boardflags, 0, fb);
	ENTRY(0xfffffff0, u32, pre, "boardflags2", boardflags2, 0, fb);
	ENTRY(0xfffff800, u32, pre, "boardflags3", boardflags3, 0, fb);
207 208
	ENTRY(0x00000002, u16, pre, "boardflags", boardflags_lo, 0, fb);
	ENTRY(0xfffffffc, u16, pre, "boardtype", board_type, 0, true);
209
	ENTRY(0xfffffffe, u16, pre, "boardnum", board_num, 0, fb);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411
	ENTRY(0x00000002, u8, pre, "cc", country_code, 0, fb);
	ENTRY(0xfffffff8, u8, pre, "regrev", regrev, 0, fb);

	ENTRY(0xfffffffe, u8, pre, "ledbh0", gpio0, 0xff, fb);
	ENTRY(0xfffffffe, u8, pre, "ledbh1", gpio1, 0xff, fb);
	ENTRY(0xfffffffe, u8, pre, "ledbh2", gpio2, 0xff, fb);
	ENTRY(0xfffffffe, u8, pre, "ledbh3", gpio3, 0xff, fb);

	ENTRY(0x0000070e, u16, pre, "pa0b0", pa0b0, 0, fb);
	ENTRY(0x0000070e, u16, pre, "pa0b1", pa0b1, 0, fb);
	ENTRY(0x0000070e, u16, pre, "pa0b2", pa0b2, 0, fb);
	ENTRY(0x0000070e, u8, pre, "pa0itssit", itssi_bg, 0, fb);
	ENTRY(0x0000070e, u8, pre, "pa0maxpwr", maxpwr_bg, 0, fb);

	ENTRY(0x0000070c, u8, pre, "opo", opo, 0, fb);
	ENTRY(0xfffffffe, u8, pre, "aa2g", ant_available_bg, 0, fb);
	ENTRY(0xfffffffe, u8, pre, "aa5g", ant_available_a, 0, fb);
	ENTRY(0x000007fe, s8, pre, "ag0", antenna_gain.a0, 0, fb);
	ENTRY(0x000007fe, s8, pre, "ag1", antenna_gain.a1, 0, fb);
	ENTRY(0x000007f0, s8, pre, "ag2", antenna_gain.a2, 0, fb);
	ENTRY(0x000007f0, s8, pre, "ag3", antenna_gain.a3, 0, fb);

	ENTRY(0x0000070e, u16, pre, "pa1b0", pa1b0, 0, fb);
	ENTRY(0x0000070e, u16, pre, "pa1b1", pa1b1, 0, fb);
	ENTRY(0x0000070e, u16, pre, "pa1b2", pa1b2, 0, fb);
	ENTRY(0x0000070c, u16, pre, "pa1lob0", pa1lob0, 0, fb);
	ENTRY(0x0000070c, u16, pre, "pa1lob1", pa1lob1, 0, fb);
	ENTRY(0x0000070c, u16, pre, "pa1lob2", pa1lob2, 0, fb);
	ENTRY(0x0000070c, u16, pre, "pa1hib0", pa1hib0, 0, fb);
	ENTRY(0x0000070c, u16, pre, "pa1hib1", pa1hib1, 0, fb);
	ENTRY(0x0000070c, u16, pre, "pa1hib2", pa1hib2, 0, fb);
	ENTRY(0x0000070e, u8, pre, "pa1itssit", itssi_a, 0, fb);
	ENTRY(0x0000070e, u8, pre, "pa1maxpwr", maxpwr_a, 0, fb);
	ENTRY(0x0000070c, u8, pre, "pa1lomaxpwr", maxpwr_al, 0, fb);
	ENTRY(0x0000070c, u8, pre, "pa1himaxpwr", maxpwr_ah, 0, fb);

	ENTRY(0x00000708, u8, pre, "bxa2g", bxa2g, 0, fb);
	ENTRY(0x00000708, u8, pre, "rssisav2g", rssisav2g, 0, fb);
	ENTRY(0x00000708, u8, pre, "rssismc2g", rssismc2g, 0, fb);
	ENTRY(0x00000708, u8, pre, "rssismf2g", rssismf2g, 0, fb);
	ENTRY(0x00000708, u8, pre, "bxa5g", bxa5g, 0, fb);
	ENTRY(0x00000708, u8, pre, "rssisav5g", rssisav5g, 0, fb);
	ENTRY(0x00000708, u8, pre, "rssismc5g", rssismc5g, 0, fb);
	ENTRY(0x00000708, u8, pre, "rssismf5g", rssismf5g, 0, fb);
	ENTRY(0x00000708, u8, pre, "tri2g", tri2g, 0, fb);
	ENTRY(0x00000708, u8, pre, "tri5g", tri5g, 0, fb);
	ENTRY(0x00000708, u8, pre, "tri5gl", tri5gl, 0, fb);
	ENTRY(0x00000708, u8, pre, "tri5gh", tri5gh, 0, fb);
	ENTRY(0x00000708, s8, pre, "rxpo2g", rxpo2g, 0, fb);
	ENTRY(0x00000708, s8, pre, "rxpo5g", rxpo5g, 0, fb);
	ENTRY(0xfffffff0, u8, pre, "txchain", txchain, 0xf, fb);
	ENTRY(0xfffffff0, u8, pre, "rxchain", rxchain, 0xf, fb);
	ENTRY(0xfffffff0, u8, pre, "antswitch", antswitch, 0xff, fb);
	ENTRY(0x00000700, u8, pre, "tssipos2g", fem.ghz2.tssipos, 0, fb);
	ENTRY(0x00000700, u8, pre, "extpagain2g", fem.ghz2.extpa_gain, 0, fb);
	ENTRY(0x00000700, u8, pre, "pdetrange2g", fem.ghz2.pdet_range, 0, fb);
	ENTRY(0x00000700, u8, pre, "triso2g", fem.ghz2.tr_iso, 0, fb);
	ENTRY(0x00000700, u8, pre, "antswctl2g", fem.ghz2.antswlut, 0, fb);
	ENTRY(0x00000700, u8, pre, "tssipos5g", fem.ghz5.tssipos, 0, fb);
	ENTRY(0x00000700, u8, pre, "extpagain5g", fem.ghz5.extpa_gain, 0, fb);
	ENTRY(0x00000700, u8, pre, "pdetrange5g", fem.ghz5.pdet_range, 0, fb);
	ENTRY(0x00000700, u8, pre, "triso5g", fem.ghz5.tr_iso, 0, fb);
	ENTRY(0x00000700, u8, pre, "antswctl5g", fem.ghz5.antswlut, 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid2ga0", txpid2g[0], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid2ga1", txpid2g[1], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid2ga2", txpid2g[2], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid2ga3", txpid2g[3], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5ga0", txpid5g[0], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5ga1", txpid5g[1], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5ga2", txpid5g[2], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5ga3", txpid5g[3], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gla0", txpid5gl[0], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gla1", txpid5gl[1], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gla2", txpid5gl[2], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gla3", txpid5gl[3], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gha0", txpid5gh[0], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gha1", txpid5gh[1], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gha2", txpid5gh[2], 0, fb);
	ENTRY(0x000000f0, u8, pre, "txpid5gha3", txpid5gh[3], 0, fb);

	ENTRY(0xffffff00, u8, pre, "tempthresh", tempthresh, 0, fb);
	ENTRY(0xffffff00, u8, pre, "tempoffset", tempoffset, 0, fb);
	ENTRY(0xffffff00, u16, pre, "rawtempsense", rawtempsense, 0, fb);
	ENTRY(0xffffff00, u8, pre, "measpower", measpower, 0, fb);
	ENTRY(0xffffff00, u8, pre, "tempsense_slope", tempsense_slope, 0, fb);
	ENTRY(0xffffff00, u8, pre, "tempcorrx", tempcorrx, 0, fb);
	ENTRY(0xffffff00, u8, pre, "tempsense_option", tempsense_option, 0, fb);
	ENTRY(0x00000700, u8, pre, "freqoffset_corr", freqoffset_corr, 0, fb);
	ENTRY(0x00000700, u8, pre, "iqcal_swp_dis", iqcal_swp_dis, 0, fb);
	ENTRY(0x00000700, u8, pre, "hw_iqcal_en", hw_iqcal_en, 0, fb);
	ENTRY(0x00000700, u8, pre, "elna2g", elna2g, 0, fb);
	ENTRY(0x00000700, u8, pre, "elna5g", elna5g, 0, fb);
	ENTRY(0xffffff00, u8, pre, "phycal_tempdelta", phycal_tempdelta, 0, fb);
	ENTRY(0xffffff00, u8, pre, "temps_period", temps_period, 0, fb);
	ENTRY(0xffffff00, u8, pre, "temps_hysteresis", temps_hysteresis, 0, fb);
	ENTRY(0xffffff00, u8, pre, "measpower1", measpower1, 0, fb);
	ENTRY(0xffffff00, u8, pre, "measpower2", measpower2, 0, fb);

	ENTRY(0x000001f0, u16, pre, "cck2gpo", cck2gpo, 0, fb);
	ENTRY(0x000001f0, u32, pre, "ofdm2gpo", ofdm2gpo, 0, fb);
	ENTRY(0x000001f0, u32, pre, "ofdm5gpo", ofdm5gpo, 0, fb);
	ENTRY(0x000001f0, u32, pre, "ofdm5glpo", ofdm5glpo, 0, fb);
	ENTRY(0x000001f0, u32, pre, "ofdm5ghpo", ofdm5ghpo, 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo0", mcs2gpo[0], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo1", mcs2gpo[1], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo2", mcs2gpo[2], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo3", mcs2gpo[3], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo4", mcs2gpo[4], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo5", mcs2gpo[5], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo6", mcs2gpo[6], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs2gpo7", mcs2gpo[7], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo0", mcs5gpo[0], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo1", mcs5gpo[1], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo2", mcs5gpo[2], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo3", mcs5gpo[3], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo4", mcs5gpo[4], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo5", mcs5gpo[5], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo6", mcs5gpo[6], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5gpo7", mcs5gpo[7], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo0", mcs5glpo[0], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo1", mcs5glpo[1], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo2", mcs5glpo[2], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo3", mcs5glpo[3], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo4", mcs5glpo[4], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo5", mcs5glpo[5], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo6", mcs5glpo[6], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5glpo7", mcs5glpo[7], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo0", mcs5ghpo[0], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo1", mcs5ghpo[1], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo2", mcs5ghpo[2], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo3", mcs5ghpo[3], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo4", mcs5ghpo[4], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo5", mcs5ghpo[5], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo6", mcs5ghpo[6], 0, fb);
	ENTRY(0x000001f0, u16, pre, "mcs5ghpo7", mcs5ghpo[7], 0, fb);
	ENTRY(0x000001f0, u16, pre, "cddpo", cddpo, 0, fb);
	ENTRY(0x000001f0, u16, pre, "stbcpo", stbcpo, 0, fb);
	ENTRY(0x000001f0, u16, pre, "bw40po", bw40po, 0, fb);
	ENTRY(0x000001f0, u16, pre, "bwduppo", bwduppo, 0, fb);

	ENTRY(0xfffffe00, u16, pre, "cckbw202gpo", cckbw202gpo, 0, fb);
	ENTRY(0xfffffe00, u16, pre, "cckbw20ul2gpo", cckbw20ul2gpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw202gpo", legofdmbw202gpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw20ul2gpo", legofdmbw20ul2gpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw205glpo", legofdmbw205glpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw20ul5glpo", legofdmbw20ul5glpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw205gmpo", legofdmbw205gmpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw20ul5gmpo", legofdmbw20ul5gmpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw205ghpo", legofdmbw205ghpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "legofdmbw20ul5ghpo", legofdmbw20ul5ghpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw202gpo", mcsbw202gpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "mcsbw20ul2gpo", mcsbw20ul2gpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw402gpo", mcsbw402gpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw205glpo", mcsbw205glpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "mcsbw20ul5glpo", mcsbw20ul5glpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw405glpo", mcsbw405glpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw205gmpo", mcsbw205gmpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "mcsbw20ul5gmpo", mcsbw20ul5gmpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw405gmpo", mcsbw405gmpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw205ghpo", mcsbw205ghpo, 0, fb);
	ENTRY(0x00000600, u32, pre, "mcsbw20ul5ghpo", mcsbw20ul5ghpo, 0, fb);
	ENTRY(0xfffffe00, u32, pre, "mcsbw405ghpo", mcsbw405ghpo, 0, fb);
	ENTRY(0x00000600, u16, pre, "mcs32po", mcs32po, 0, fb);
	ENTRY(0x00000600, u16, pre, "legofdm40duppo", legofdm40duppo, 0, fb);
	ENTRY(0x00000700, u8, pre, "pcieingress_war", pcieingress_war, 0, fb);

	/* TODO: rev 11 support */
	ENTRY(0x00000700, u8, pre, "rxgainerr2ga0", rxgainerr2ga[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr2ga1", rxgainerr2ga[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr2ga2", rxgainerr2ga[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gla0", rxgainerr5gla[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gla1", rxgainerr5gla[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gla2", rxgainerr5gla[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gma0", rxgainerr5gma[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gma1", rxgainerr5gma[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gma2", rxgainerr5gma[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gha0", rxgainerr5gha[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gha1", rxgainerr5gha[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gha2", rxgainerr5gha[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gua0", rxgainerr5gua[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gua1", rxgainerr5gua[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "rxgainerr5gua2", rxgainerr5gua[2], 0, fb);

	ENTRY(0xfffffe00, u8, pre, "sar2g", sar2g, 0, fb);
	ENTRY(0xfffffe00, u8, pre, "sar5g", sar5g, 0, fb);

	/* TODO: rev 11 support */
	ENTRY(0x00000700, u8, pre, "noiselvl2ga0", noiselvl2ga[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl2ga1", noiselvl2ga[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl2ga2", noiselvl2ga[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gla0", noiselvl5gla[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gla1", noiselvl5gla[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gla2", noiselvl5gla[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gma0", noiselvl5gma[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gma1", noiselvl5gma[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gma2", noiselvl5gma[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gha0", noiselvl5gha[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gha1", noiselvl5gha[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gha2", noiselvl5gha[2], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gua0", noiselvl5gua[0], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gua1", noiselvl5gua[1], 0, fb);
	ENTRY(0x00000700, u8, pre, "noiselvl5gua2", noiselvl5gua[2], 0, fb);
412 413 414
}
#undef ENTRY /* It's specififc, uses local variable, don't use it (again). */

415
static void bcm47xx_fill_sprom_r1234589(struct ssb_sprom *sprom,
416
					const char *prefix, bool fallback)
417
{
418
	nvram_read_u16(prefix, NULL, "devid", &sprom->dev_id, 0, fallback);
419
	nvram_read_alpha2(prefix, "ccode", sprom->alpha2, fallback);
420 421
}

422 423
static void bcm47xx_fill_sprom_r3(struct ssb_sprom *sprom, const char *prefix,
				  bool fallback)
424 425
{
	nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time,
426
			 &sprom->leddc_off_time, fallback);
427 428 429
}

static void bcm47xx_fill_sprom_r4589(struct ssb_sprom *sprom,
430
				     const char *prefix, bool fallback)
431 432
{
	nvram_read_leddc(prefix, "leddc", &sprom->leddc_on_time,
433
			 &sprom->leddc_off_time, fallback);
434 435 436
}

static void bcm47xx_fill_sprom_path_r4589(struct ssb_sprom *sprom,
437
					  const char *prefix, bool fallback)
438 439 440 441 442 443 444 445
{
	char postfix[2];
	int i;

	for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) {
		struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i];
		snprintf(postfix, sizeof(postfix), "%i", i);
		nvram_read_u8(prefix, postfix, "maxp2ga",
446
			      &pwr_info->maxpwr_2g, 0, fallback);
447
		nvram_read_u8(prefix, postfix, "itt2ga",
448
			      &pwr_info->itssi_2g, 0, fallback);
449
		nvram_read_u8(prefix, postfix, "itt5ga",
450
			      &pwr_info->itssi_5g, 0, fallback);
451
		nvram_read_u16(prefix, postfix, "pa2gw0a",
452
			       &pwr_info->pa_2g[0], 0, fallback);
453
		nvram_read_u16(prefix, postfix, "pa2gw1a",
454
			       &pwr_info->pa_2g[1], 0, fallback);
455
		nvram_read_u16(prefix, postfix, "pa2gw2a",
456
			       &pwr_info->pa_2g[2], 0, fallback);
457
		nvram_read_u8(prefix, postfix, "maxp5ga",
458
			      &pwr_info->maxpwr_5g, 0, fallback);
459
		nvram_read_u8(prefix, postfix, "maxp5gha",
460
			      &pwr_info->maxpwr_5gh, 0, fallback);
461
		nvram_read_u8(prefix, postfix, "maxp5gla",
462
			      &pwr_info->maxpwr_5gl, 0, fallback);
463
		nvram_read_u16(prefix, postfix, "pa5gw0a",
464
			       &pwr_info->pa_5g[0], 0, fallback);
465
		nvram_read_u16(prefix, postfix, "pa5gw1a",
466
			       &pwr_info->pa_5g[1], 0, fallback);
467
		nvram_read_u16(prefix, postfix, "pa5gw2a",
468
			       &pwr_info->pa_5g[2], 0, fallback);
469
		nvram_read_u16(prefix, postfix, "pa5glw0a",
470
			       &pwr_info->pa_5gl[0], 0, fallback);
471
		nvram_read_u16(prefix, postfix, "pa5glw1a",
472
			       &pwr_info->pa_5gl[1], 0, fallback);
473
		nvram_read_u16(prefix, postfix, "pa5glw2a",
474
			       &pwr_info->pa_5gl[2], 0, fallback);
475
		nvram_read_u16(prefix, postfix, "pa5ghw0a",
476
			       &pwr_info->pa_5gh[0], 0, fallback);
477
		nvram_read_u16(prefix, postfix, "pa5ghw1a",
478
			       &pwr_info->pa_5gh[1], 0, fallback);
479
		nvram_read_u16(prefix, postfix, "pa5ghw2a",
480
			       &pwr_info->pa_5gh[2], 0, fallback);
481 482 483 484
	}
}

static void bcm47xx_fill_sprom_path_r45(struct ssb_sprom *sprom,
485
					const char *prefix, bool fallback)
486 487 488 489 490 491 492 493
{
	char postfix[2];
	int i;

	for (i = 0; i < ARRAY_SIZE(sprom->core_pwr_info); i++) {
		struct ssb_sprom_core_pwr_info *pwr_info = &sprom->core_pwr_info[i];
		snprintf(postfix, sizeof(postfix), "%i", i);
		nvram_read_u16(prefix, postfix, "pa2gw3a",
494
			       &pwr_info->pa_2g[3], 0, fallback);
495
		nvram_read_u16(prefix, postfix, "pa5gw3a",
496
			       &pwr_info->pa_5g[3], 0, fallback);
497
		nvram_read_u16(prefix, postfix, "pa5glw3a",
498
			       &pwr_info->pa_5gl[3], 0, fallback);
499
		nvram_read_u16(prefix, postfix, "pa5ghw3a",
500
			       &pwr_info->pa_5gh[3], 0, fallback);
501 502 503
	}
}

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
static bool bcm47xx_is_valid_mac(u8 *mac)
{
	return mac && !(mac[0] == 0x00 && mac[1] == 0x90 && mac[2] == 0x4c);
}

static int bcm47xx_increase_mac_addr(u8 *mac, u8 num)
{
	u8 *oui = mac + ETH_ALEN/2 - 1;
	u8 *p = mac + ETH_ALEN - 1;

	do {
		(*p) += num;
		if (*p > num)
			break;
		p--;
		num = 1;
	} while (p != oui);

	if (p == oui) {
		pr_err("unable to fetch mac address\n");
		return -ENOENT;
	}
	return 0;
}

static int mac_addr_used = 2;

531 532
static void bcm47xx_fill_sprom_ethernet(struct ssb_sprom *sprom,
					const char *prefix, bool fallback)
533
{
534
	nvram_read_macaddr(prefix, "et0macaddr", sprom->et0mac, fallback);
535 536 537 538 539
	nvram_read_u8(prefix, NULL, "et0mdcport", &sprom->et0mdcport, 0,
		      fallback);
	nvram_read_u8(prefix, NULL, "et0phyaddr", &sprom->et0phyaddr, 0,
		      fallback);

540
	nvram_read_macaddr(prefix, "et1macaddr", sprom->et1mac, fallback);
541 542 543 544 545
	nvram_read_u8(prefix, NULL, "et1mdcport", &sprom->et1mdcport, 0,
		      fallback);
	nvram_read_u8(prefix, NULL, "et1phyaddr", &sprom->et1phyaddr, 0,
		      fallback);

546 547
	nvram_read_macaddr(prefix, "macaddr", sprom->il0mac, fallback);
	nvram_read_macaddr(prefix, "il0macaddr", sprom->il0mac, fallback);
548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566

	/* The address prefix 00:90:4C is used by Broadcom in their initial
	   configuration. When a mac address with the prefix 00:90:4C is used
	   all devices from the same series are sharing the same mac address.
	   To prevent mac address collisions we replace them with a mac address
	   based on the base address. */
	if (!bcm47xx_is_valid_mac(sprom->il0mac)) {
		u8 mac[6];

		nvram_read_macaddr(NULL, "et0macaddr", mac, false);
		if (bcm47xx_is_valid_mac(mac)) {
			int err = bcm47xx_increase_mac_addr(mac, mac_addr_used);

			if (!err) {
				ether_addr_copy(sprom->il0mac, mac);
				mac_addr_used++;
			}
		}
	}
567 568
}

569 570
static void bcm47xx_fill_board_data(struct ssb_sprom *sprom, const char *prefix,
				    bool fallback)
571 572
{
	nvram_read_u32_2(prefix, "boardflags", &sprom->boardflags_lo,
573
			 &sprom->boardflags_hi, fallback);
574
	nvram_read_u32_2(prefix, "boardflags2", &sprom->boardflags2_lo,
575
			 &sprom->boardflags2_hi, fallback);
576 577
}

578 579
void bcm47xx_fill_sprom(struct ssb_sprom *sprom, const char *prefix,
			bool fallback)
580
{
581 582
	bcm47xx_fill_sprom_ethernet(sprom, prefix, fallback);
	bcm47xx_fill_board_data(sprom, prefix, fallback);
583

584
	nvram_read_u8(prefix, NULL, "sromrev", &sprom->revision, 0, fallback);
585 586 587

	switch (sprom->revision) {
	case 1:
588
		bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback);
589 590
		break;
	case 2:
591
		bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback);
592 593
		break;
	case 3:
594 595
		bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_r3(sprom, prefix, fallback);
596 597 598
		break;
	case 4:
	case 5:
599 600 601 602
		bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_r4589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_path_r45(sprom, prefix, fallback);
603 604
		break;
	case 8:
605 606 607
		bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_r4589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback);
608 609
		break;
	case 9:
610 611 612
		bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_r4589(sprom, prefix, fallback);
		bcm47xx_fill_sprom_path_r4589(sprom, prefix, fallback);
613 614
		break;
	default:
615 616
		pr_warn("Unsupported SPROM revision %d detected. Will extract v1\n",
			sprom->revision);
617
		sprom->revision = 1;
618
		bcm47xx_fill_sprom_r1234589(sprom, prefix, fallback);
619
	}
620 621

	bcm47xx_sprom_fill_auto(sprom, prefix, fallback);
622
}
623 624 625 626 627

#ifdef CONFIG_BCM47XX_SSB
void bcm47xx_fill_ssb_boardinfo(struct ssb_boardinfo *boardinfo,
				const char *prefix)
{
628 629
	nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0,
		       true);
630 631 632
	if (!boardinfo->vendor)
		boardinfo->vendor = SSB_BOARDVENDOR_BCM;

633
	nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0, true);
634 635
}
#endif
H
Hauke Mehrtens 已提交
636 637 638 639 640

#ifdef CONFIG_BCM47XX_BCMA
void bcm47xx_fill_bcma_boardinfo(struct bcma_boardinfo *boardinfo,
				 const char *prefix)
{
641 642
	nvram_read_u16(prefix, NULL, "boardvendor", &boardinfo->vendor, 0,
		       true);
H
Hauke Mehrtens 已提交
643 644 645
	if (!boardinfo->vendor)
		boardinfo->vendor = SSB_BOARDVENDOR_BCM;

646
	nvram_read_u16(prefix, NULL, "boardtype", &boardinfo->type, 0, true);
H
Hauke Mehrtens 已提交
647 648
}
#endif
649 650 651 652 653 654 655 656 657 658 659 660 661 662

#if defined(CONFIG_BCM47XX_SSB)
static int bcm47xx_get_sprom_ssb(struct ssb_bus *bus, struct ssb_sprom *out)
{
	char prefix[10];

	if (bus->bustype == SSB_BUSTYPE_PCI) {
		memset(out, 0, sizeof(struct ssb_sprom));
		snprintf(prefix, sizeof(prefix), "pci/%u/%u/",
			 bus->host_pci->bus->number + 1,
			 PCI_SLOT(bus->host_pci->devfn));
		bcm47xx_fill_sprom(out, prefix, false);
		return 0;
	} else {
663
		pr_warn("Unable to fill SPROM for given bustype.\n");
664 665 666 667 668 669
		return -EINVAL;
	}
}
#endif

#if defined(CONFIG_BCM47XX_BCMA)
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
/*
 * Having many NVRAM entries for PCI devices led to repeating prefixes like
 * pci/1/1/ all the time and wasting flash space. So at some point Broadcom
 * decided to introduce prefixes like 0: 1: 2: etc.
 * If we find e.g. devpath0=pci/2/1 or devpath0=pci/2/1/ we should use 0:
 * instead of pci/2/1/.
 */
static void bcm47xx_sprom_apply_prefix_alias(char *prefix, size_t prefix_size)
{
	size_t prefix_len = strlen(prefix);
	size_t short_len = prefix_len - 1;
	char nvram_var[10];
	char buf[20];
	int i;

	/* Passed prefix has to end with a slash */
	if (prefix_len <= 0 || prefix[prefix_len - 1] != '/')
		return;

	for (i = 0; i < 3; i++) {
		if (snprintf(nvram_var, sizeof(nvram_var), "devpath%d", i) <= 0)
			continue;
		if (bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf)) < 0)
			continue;
		if (!strcmp(buf, prefix) ||
		    (short_len && strlen(buf) == short_len && !strncmp(buf, prefix, short_len))) {
			snprintf(prefix, prefix_size, "%d:", i);
			return;
		}
	}
}

702 703 704 705 706 707 708 709 710 711 712
static int bcm47xx_get_sprom_bcma(struct bcma_bus *bus, struct ssb_sprom *out)
{
	char prefix[10];
	struct bcma_device *core;

	switch (bus->hosttype) {
	case BCMA_HOSTTYPE_PCI:
		memset(out, 0, sizeof(struct ssb_sprom));
		snprintf(prefix, sizeof(prefix), "pci/%u/%u/",
			 bus->host_pci->bus->number + 1,
			 PCI_SLOT(bus->host_pci->devfn));
713
		bcm47xx_sprom_apply_prefix_alias(prefix, sizeof(prefix));
714 715 716 717 718 719 720 721 722 723 724 725 726 727
		bcm47xx_fill_sprom(out, prefix, false);
		return 0;
	case BCMA_HOSTTYPE_SOC:
		memset(out, 0, sizeof(struct ssb_sprom));
		core = bcma_find_core(bus, BCMA_CORE_80211);
		if (core) {
			snprintf(prefix, sizeof(prefix), "sb/%u/",
				 core->core_index);
			bcm47xx_fill_sprom(out, prefix, true);
		} else {
			bcm47xx_fill_sprom(out, NULL, false);
		}
		return 0;
	default:
728
		pr_warn("Unable to fill SPROM for given bustype.\n");
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
		return -EINVAL;
	}
}
#endif

/*
 * On bcm47xx we need to register SPROM fallback handler very early, so we can't
 * use anything like platform device / driver for this.
 */
void bcm47xx_sprom_register_fallbacks(void)
{
#if defined(CONFIG_BCM47XX_SSB)
	if (ssb_arch_register_fallback_sprom(&bcm47xx_get_sprom_ssb))
		pr_warn("Failed to registered ssb SPROM handler\n");
#endif

#if defined(CONFIG_BCM47XX_BCMA)
	if (bcma_arch_register_fallback_sprom(&bcm47xx_get_sprom_bcma))
		pr_warn("Failed to registered bcma SPROM handler\n");
#endif
}