xref: /openbmc/linux/drivers/ssb/pci.c (revision 3623b266c8c5012ef376128026ca2dfc63f1abcb)
161e115a5SMichael Buesch /*
261e115a5SMichael Buesch  * Sonics Silicon Backplane PCI-Hostbus related functions.
361e115a5SMichael Buesch  *
4eb032b98SMichael Büsch  * Copyright (C) 2005-2006 Michael Buesch <m@bues.ch>
561e115a5SMichael Buesch  * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
661e115a5SMichael Buesch  * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
761e115a5SMichael Buesch  * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
861e115a5SMichael Buesch  * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
961e115a5SMichael Buesch  *
1061e115a5SMichael Buesch  * Derived from the Broadcom 4400 device driver.
1161e115a5SMichael Buesch  * Copyright (C) 2002 David S. Miller (davem@redhat.com)
1261e115a5SMichael Buesch  * Fixed by Pekka Pietikainen (pp@ee.oulu.fi)
1361e115a5SMichael Buesch  * Copyright (C) 2006 Broadcom Corporation.
1461e115a5SMichael Buesch  *
1561e115a5SMichael Buesch  * Licensed under the GNU/GPL. See COPYING for details.
1661e115a5SMichael Buesch  */
1761e115a5SMichael Buesch 
1861e115a5SMichael Buesch #include <linux/ssb/ssb.h>
1961e115a5SMichael Buesch #include <linux/ssb/ssb_regs.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
2161e115a5SMichael Buesch #include <linux/pci.h>
2261e115a5SMichael Buesch #include <linux/delay.h>
2361e115a5SMichael Buesch 
2461e115a5SMichael Buesch #include "ssb_private.h"
2561e115a5SMichael Buesch 
2661e115a5SMichael Buesch 
2761e115a5SMichael Buesch /* Define the following to 1 to enable a printk on each coreswitch. */
2861e115a5SMichael Buesch #define SSB_VERBOSE_PCICORESWITCH_DEBUG		0
2961e115a5SMichael Buesch 
3061e115a5SMichael Buesch 
3161e115a5SMichael Buesch /* Lowlevel coreswitching */
3261e115a5SMichael Buesch int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
3361e115a5SMichael Buesch {
3461e115a5SMichael Buesch 	int err;
3561e115a5SMichael Buesch 	int attempts = 0;
3661e115a5SMichael Buesch 	u32 cur_core;
3761e115a5SMichael Buesch 
3861e115a5SMichael Buesch 	while (1) {
3961e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN,
4061e115a5SMichael Buesch 					     (coreidx * SSB_CORE_SIZE)
4161e115a5SMichael Buesch 					     + SSB_ENUM_BASE);
4261e115a5SMichael Buesch 		if (err)
4361e115a5SMichael Buesch 			goto error;
4461e115a5SMichael Buesch 		err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN,
4561e115a5SMichael Buesch 					    &cur_core);
4661e115a5SMichael Buesch 		if (err)
4761e115a5SMichael Buesch 			goto error;
4861e115a5SMichael Buesch 		cur_core = (cur_core - SSB_ENUM_BASE)
4961e115a5SMichael Buesch 			   / SSB_CORE_SIZE;
5061e115a5SMichael Buesch 		if (cur_core == coreidx)
5161e115a5SMichael Buesch 			break;
5261e115a5SMichael Buesch 
5361e115a5SMichael Buesch 		if (attempts++ > SSB_BAR0_MAX_RETRIES)
5461e115a5SMichael Buesch 			goto error;
5561e115a5SMichael Buesch 		udelay(10);
5661e115a5SMichael Buesch 	}
5761e115a5SMichael Buesch 	return 0;
5861e115a5SMichael Buesch error:
5933a606acSJoe Perches 	ssb_err("Failed to switch to core %u\n", coreidx);
6061e115a5SMichael Buesch 	return -ENODEV;
6161e115a5SMichael Buesch }
6261e115a5SMichael Buesch 
6361e115a5SMichael Buesch int ssb_pci_switch_core(struct ssb_bus *bus,
6461e115a5SMichael Buesch 			struct ssb_device *dev)
6561e115a5SMichael Buesch {
6661e115a5SMichael Buesch 	int err;
6761e115a5SMichael Buesch 	unsigned long flags;
6861e115a5SMichael Buesch 
6961e115a5SMichael Buesch #if SSB_VERBOSE_PCICORESWITCH_DEBUG
7033a606acSJoe Perches 	ssb_info("Switching to %s core, index %d\n",
7161e115a5SMichael Buesch 		 ssb_core_name(dev->id.coreid),
7261e115a5SMichael Buesch 		 dev->core_index);
7361e115a5SMichael Buesch #endif
7461e115a5SMichael Buesch 
7561e115a5SMichael Buesch 	spin_lock_irqsave(&bus->bar_lock, flags);
7661e115a5SMichael Buesch 	err = ssb_pci_switch_coreidx(bus, dev->core_index);
7761e115a5SMichael Buesch 	if (!err)
7861e115a5SMichael Buesch 		bus->mapped_device = dev;
7961e115a5SMichael Buesch 	spin_unlock_irqrestore(&bus->bar_lock, flags);
8061e115a5SMichael Buesch 
8161e115a5SMichael Buesch 	return err;
8261e115a5SMichael Buesch }
8361e115a5SMichael Buesch 
8461e115a5SMichael Buesch /* Enable/disable the on board crystal oscillator and/or PLL. */
8561e115a5SMichael Buesch int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on)
8661e115a5SMichael Buesch {
8761e115a5SMichael Buesch 	int err;
8861e115a5SMichael Buesch 	u32 in, out, outenable;
8961e115a5SMichael Buesch 	u16 pci_status;
9061e115a5SMichael Buesch 
9161e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
9261e115a5SMichael Buesch 		return 0;
9361e115a5SMichael Buesch 
9461e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in);
9561e115a5SMichael Buesch 	if (err)
9661e115a5SMichael Buesch 		goto err_pci;
9761e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out);
9861e115a5SMichael Buesch 	if (err)
9961e115a5SMichael Buesch 		goto err_pci;
10061e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable);
10161e115a5SMichael Buesch 	if (err)
10261e115a5SMichael Buesch 		goto err_pci;
10361e115a5SMichael Buesch 
10461e115a5SMichael Buesch 	outenable |= what;
10561e115a5SMichael Buesch 
10661e115a5SMichael Buesch 	if (turn_on) {
10761e115a5SMichael Buesch 		/* Avoid glitching the clock if GPRS is already using it.
10861e115a5SMichael Buesch 		 * We can't actually read the state of the PLLPD so we infer it
10961e115a5SMichael Buesch 		 * by the value of XTAL_PU which *is* readable via gpioin.
11061e115a5SMichael Buesch 		 */
11161e115a5SMichael Buesch 		if (!(in & SSB_GPIO_XTAL)) {
11261e115a5SMichael Buesch 			if (what & SSB_GPIO_XTAL) {
11361e115a5SMichael Buesch 				/* Turn the crystal on */
11461e115a5SMichael Buesch 				out |= SSB_GPIO_XTAL;
11561e115a5SMichael Buesch 				if (what & SSB_GPIO_PLL)
11661e115a5SMichael Buesch 					out |= SSB_GPIO_PLL;
11761e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
11861e115a5SMichael Buesch 				if (err)
11961e115a5SMichael Buesch 					goto err_pci;
12061e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE,
12161e115a5SMichael Buesch 							     outenable);
12261e115a5SMichael Buesch 				if (err)
12361e115a5SMichael Buesch 					goto err_pci;
12461e115a5SMichael Buesch 				msleep(1);
12561e115a5SMichael Buesch 			}
12661e115a5SMichael Buesch 			if (what & SSB_GPIO_PLL) {
12761e115a5SMichael Buesch 				/* Turn the PLL on */
12861e115a5SMichael Buesch 				out &= ~SSB_GPIO_PLL;
12961e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
13061e115a5SMichael Buesch 				if (err)
13161e115a5SMichael Buesch 					goto err_pci;
13261e115a5SMichael Buesch 				msleep(5);
13361e115a5SMichael Buesch 			}
13461e115a5SMichael Buesch 		}
13561e115a5SMichael Buesch 
13661e115a5SMichael Buesch 		err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status);
13761e115a5SMichael Buesch 		if (err)
13861e115a5SMichael Buesch 			goto err_pci;
13961e115a5SMichael Buesch 		pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT;
14061e115a5SMichael Buesch 		err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status);
14161e115a5SMichael Buesch 		if (err)
14261e115a5SMichael Buesch 			goto err_pci;
14361e115a5SMichael Buesch 	} else {
14461e115a5SMichael Buesch 		if (what & SSB_GPIO_XTAL) {
14561e115a5SMichael Buesch 			/* Turn the crystal off */
14661e115a5SMichael Buesch 			out &= ~SSB_GPIO_XTAL;
14761e115a5SMichael Buesch 		}
14861e115a5SMichael Buesch 		if (what & SSB_GPIO_PLL) {
14961e115a5SMichael Buesch 			/* Turn the PLL off */
15061e115a5SMichael Buesch 			out |= SSB_GPIO_PLL;
15161e115a5SMichael Buesch 		}
15261e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
15361e115a5SMichael Buesch 		if (err)
15461e115a5SMichael Buesch 			goto err_pci;
15561e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable);
15661e115a5SMichael Buesch 		if (err)
15761e115a5SMichael Buesch 			goto err_pci;
15861e115a5SMichael Buesch 	}
15961e115a5SMichael Buesch 
16061e115a5SMichael Buesch out:
16161e115a5SMichael Buesch 	return err;
16261e115a5SMichael Buesch 
16361e115a5SMichael Buesch err_pci:
16461e115a5SMichael Buesch 	printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n");
16561e115a5SMichael Buesch 	err = -EBUSY;
16661e115a5SMichael Buesch 	goto out;
16761e115a5SMichael Buesch }
16861e115a5SMichael Buesch 
16961e115a5SMichael Buesch /* Get the word-offset for a SSB_SPROM_XXX define. */
1700a182fd8SRafał Miłecki #define SPOFF(offset)	((offset) / sizeof(u16))
17161e115a5SMichael Buesch /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
172f679056bSGábor Stefanik #define SPEX16(_outvar, _offset, _mask, _shift)	\
17361e115a5SMichael Buesch 	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
174f679056bSGábor Stefanik #define SPEX32(_outvar, _offset, _mask, _shift)	\
175f679056bSGábor Stefanik 	out->_outvar = ((((u32)in[SPOFF((_offset)+2)] << 16 | \
176f679056bSGábor Stefanik 			   in[SPOFF(_offset)]) & (_mask)) >> (_shift))
177f679056bSGábor Stefanik #define SPEX(_outvar, _offset, _mask, _shift) \
178f679056bSGábor Stefanik 	SPEX16(_outvar, _offset, _mask, _shift)
179f679056bSGábor Stefanik 
180e2da4bd3SHauke Mehrtens #define SPEX_ARRAY8(_field, _offset, _mask, _shift)	\
181e2da4bd3SHauke Mehrtens 	do {	\
182e2da4bd3SHauke Mehrtens 		SPEX(_field[0], _offset +  0, _mask, _shift);	\
183e2da4bd3SHauke Mehrtens 		SPEX(_field[1], _offset +  2, _mask, _shift);	\
184e2da4bd3SHauke Mehrtens 		SPEX(_field[2], _offset +  4, _mask, _shift);	\
185e2da4bd3SHauke Mehrtens 		SPEX(_field[3], _offset +  6, _mask, _shift);	\
186e2da4bd3SHauke Mehrtens 		SPEX(_field[4], _offset +  8, _mask, _shift);	\
187e2da4bd3SHauke Mehrtens 		SPEX(_field[5], _offset + 10, _mask, _shift);	\
188e2da4bd3SHauke Mehrtens 		SPEX(_field[6], _offset + 12, _mask, _shift);	\
189e2da4bd3SHauke Mehrtens 		SPEX(_field[7], _offset + 14, _mask, _shift);	\
190e2da4bd3SHauke Mehrtens 	} while (0)
191e2da4bd3SHauke Mehrtens 
19261e115a5SMichael Buesch 
19361e115a5SMichael Buesch static inline u8 ssb_crc8(u8 crc, u8 data)
19461e115a5SMichael Buesch {
19561e115a5SMichael Buesch 	/* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
19661e115a5SMichael Buesch 	static const u8 t[] = {
19761e115a5SMichael Buesch 		0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
19861e115a5SMichael Buesch 		0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
19961e115a5SMichael Buesch 		0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
20061e115a5SMichael Buesch 		0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
20161e115a5SMichael Buesch 		0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
20261e115a5SMichael Buesch 		0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
20361e115a5SMichael Buesch 		0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
20461e115a5SMichael Buesch 		0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
20561e115a5SMichael Buesch 		0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
20661e115a5SMichael Buesch 		0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
20761e115a5SMichael Buesch 		0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
20861e115a5SMichael Buesch 		0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
20961e115a5SMichael Buesch 		0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
21061e115a5SMichael Buesch 		0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
21161e115a5SMichael Buesch 		0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
21261e115a5SMichael Buesch 		0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
21361e115a5SMichael Buesch 		0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
21461e115a5SMichael Buesch 		0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
21561e115a5SMichael Buesch 		0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
21661e115a5SMichael Buesch 		0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
21761e115a5SMichael Buesch 		0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
21861e115a5SMichael Buesch 		0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
21961e115a5SMichael Buesch 		0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
22061e115a5SMichael Buesch 		0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
22161e115a5SMichael Buesch 		0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
22261e115a5SMichael Buesch 		0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
22361e115a5SMichael Buesch 		0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
22461e115a5SMichael Buesch 		0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
22561e115a5SMichael Buesch 		0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
22661e115a5SMichael Buesch 		0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
22761e115a5SMichael Buesch 		0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
22861e115a5SMichael Buesch 		0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
22961e115a5SMichael Buesch 	};
23061e115a5SMichael Buesch 	return t[crc ^ data];
23161e115a5SMichael Buesch }
23261e115a5SMichael Buesch 
233e5652756SJoe Perches static void sprom_get_mac(char *mac, const u16 *in)
234e5652756SJoe Perches {
235e5652756SJoe Perches 	int i;
236e5652756SJoe Perches 	for (i = 0; i < 3; i++) {
237e5652756SJoe Perches 		*mac++ = in[i] >> 8;
238a9fac739SLarry Finger 		*mac++ = in[i];
239e5652756SJoe Perches 	}
240e5652756SJoe Perches }
241e5652756SJoe Perches 
242c272ef44SLarry Finger static u8 ssb_sprom_crc(const u16 *sprom, u16 size)
24361e115a5SMichael Buesch {
24461e115a5SMichael Buesch 	int word;
24561e115a5SMichael Buesch 	u8 crc = 0xFF;
24661e115a5SMichael Buesch 
247c272ef44SLarry Finger 	for (word = 0; word < size - 1; word++) {
24861e115a5SMichael Buesch 		crc = ssb_crc8(crc, sprom[word] & 0x00FF);
24961e115a5SMichael Buesch 		crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8);
25061e115a5SMichael Buesch 	}
251c272ef44SLarry Finger 	crc = ssb_crc8(crc, sprom[size - 1] & 0x00FF);
25261e115a5SMichael Buesch 	crc ^= 0xFF;
25361e115a5SMichael Buesch 
25461e115a5SMichael Buesch 	return crc;
25561e115a5SMichael Buesch }
25661e115a5SMichael Buesch 
257e7ec2e32SMichael Buesch static int sprom_check_crc(const u16 *sprom, size_t size)
25861e115a5SMichael Buesch {
25961e115a5SMichael Buesch 	u8 crc;
26061e115a5SMichael Buesch 	u8 expected_crc;
26161e115a5SMichael Buesch 	u16 tmp;
26261e115a5SMichael Buesch 
263c272ef44SLarry Finger 	crc = ssb_sprom_crc(sprom, size);
264c272ef44SLarry Finger 	tmp = sprom[size - 1] & SSB_SPROM_REVISION_CRC;
26561e115a5SMichael Buesch 	expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
26661e115a5SMichael Buesch 	if (crc != expected_crc)
26761e115a5SMichael Buesch 		return -EPROTO;
26861e115a5SMichael Buesch 
26961e115a5SMichael Buesch 	return 0;
27061e115a5SMichael Buesch }
27161e115a5SMichael Buesch 
272e7ec2e32SMichael Buesch static int sprom_do_read(struct ssb_bus *bus, u16 *sprom)
27361e115a5SMichael Buesch {
27461e115a5SMichael Buesch 	int i;
27561e115a5SMichael Buesch 
276c272ef44SLarry Finger 	for (i = 0; i < bus->sprom_size; i++)
277ea2db495SRafał Miłecki 		sprom[i] = ioread16(bus->mmio + bus->sprom_offset + (i * 2));
278e7ec2e32SMichael Buesch 
279e7ec2e32SMichael Buesch 	return 0;
28061e115a5SMichael Buesch }
28161e115a5SMichael Buesch 
28261e115a5SMichael Buesch static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
28361e115a5SMichael Buesch {
28461e115a5SMichael Buesch 	struct pci_dev *pdev = bus->host_pci;
28561e115a5SMichael Buesch 	int i, err;
28661e115a5SMichael Buesch 	u32 spromctl;
287c272ef44SLarry Finger 	u16 size = bus->sprom_size;
28861e115a5SMichael Buesch 
28933a606acSJoe Perches 	ssb_notice("Writing SPROM. Do NOT turn off the power! Please stand by...\n");
29061e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
29161e115a5SMichael Buesch 	if (err)
29261e115a5SMichael Buesch 		goto err_ctlreg;
29361e115a5SMichael Buesch 	spromctl |= SSB_SPROMCTL_WE;
29461e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
29561e115a5SMichael Buesch 	if (err)
29661e115a5SMichael Buesch 		goto err_ctlreg;
29733a606acSJoe Perches 	ssb_notice("[ 0%%");
29861e115a5SMichael Buesch 	msleep(500);
299c272ef44SLarry Finger 	for (i = 0; i < size; i++) {
300c272ef44SLarry Finger 		if (i == size / 4)
30133a606acSJoe Perches 			ssb_cont("25%%");
302c272ef44SLarry Finger 		else if (i == size / 2)
30333a606acSJoe Perches 			ssb_cont("50%%");
304c272ef44SLarry Finger 		else if (i == (size * 3) / 4)
30533a606acSJoe Perches 			ssb_cont("75%%");
30661e115a5SMichael Buesch 		else if (i % 2)
30733a606acSJoe Perches 			ssb_cont(".");
308ea2db495SRafał Miłecki 		writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2));
30961e115a5SMichael Buesch 		mmiowb();
31061e115a5SMichael Buesch 		msleep(20);
31161e115a5SMichael Buesch 	}
31261e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
31361e115a5SMichael Buesch 	if (err)
31461e115a5SMichael Buesch 		goto err_ctlreg;
31561e115a5SMichael Buesch 	spromctl &= ~SSB_SPROMCTL_WE;
31661e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
31761e115a5SMichael Buesch 	if (err)
31861e115a5SMichael Buesch 		goto err_ctlreg;
31961e115a5SMichael Buesch 	msleep(500);
32033a606acSJoe Perches 	ssb_cont("100%% ]\n");
32133a606acSJoe Perches 	ssb_notice("SPROM written\n");
32261e115a5SMichael Buesch 
32361e115a5SMichael Buesch 	return 0;
32461e115a5SMichael Buesch err_ctlreg:
32533a606acSJoe Perches 	ssb_err("Could not access SPROM control register.\n");
32661e115a5SMichael Buesch 	return err;
32761e115a5SMichael Buesch }
32861e115a5SMichael Buesch 
329e861b98dSMichael Buesch static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in,
330e861b98dSMichael Buesch 			       u16 mask, u16 shift)
331e861b98dSMichael Buesch {
332e861b98dSMichael Buesch 	u16 v;
333e861b98dSMichael Buesch 	u8 gain;
334e861b98dSMichael Buesch 
335e861b98dSMichael Buesch 	v = in[SPOFF(SSB_SPROM1_AGAIN)];
336e861b98dSMichael Buesch 	gain = (v & mask) >> shift;
337e861b98dSMichael Buesch 	if (gain == 0xFF)
338e861b98dSMichael Buesch 		gain = 2; /* If unset use 2dBm */
339e861b98dSMichael Buesch 	if (sprom_revision == 1) {
340e861b98dSMichael Buesch 		/* Convert to Q5.2 */
341e861b98dSMichael Buesch 		gain <<= 2;
342e861b98dSMichael Buesch 	} else {
343e861b98dSMichael Buesch 		/* Q5.2 Fractional part is stored in 0xC0 */
344e861b98dSMichael Buesch 		gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
345e861b98dSMichael Buesch 	}
346e861b98dSMichael Buesch 
347e861b98dSMichael Buesch 	return (s8)gain;
348e861b98dSMichael Buesch }
349e861b98dSMichael Buesch 
350c272ef44SLarry Finger static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
351c272ef44SLarry Finger {
352c272ef44SLarry Finger 	u16 loc[3];
353c272ef44SLarry Finger 
35431ce12fbSLarry Finger 	if (out->revision == 3)			/* rev 3 moved MAC */
355c272ef44SLarry Finger 		loc[0] = SSB_SPROM3_IL0MAC;
35631ce12fbSLarry Finger 	else {
357c272ef44SLarry Finger 		loc[0] = SSB_SPROM1_IL0MAC;
358c272ef44SLarry Finger 		loc[1] = SSB_SPROM1_ET0MAC;
359c272ef44SLarry Finger 		loc[2] = SSB_SPROM1_ET1MAC;
360c272ef44SLarry Finger 	}
361e5652756SJoe Perches 	sprom_get_mac(out->il0mac, &in[SPOFF(loc[0])]);
36231ce12fbSLarry Finger 	if (out->revision < 3) { 	/* only rev 1-2 have et0, et1 */
363e5652756SJoe Perches 		sprom_get_mac(out->et0mac, &in[SPOFF(loc[1])]);
364e5652756SJoe Perches 		sprom_get_mac(out->et1mac, &in[SPOFF(loc[2])]);
36531ce12fbSLarry Finger 	}
366c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
367c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
368c272ef44SLarry Finger 	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
369e861b98dSMichael Buesch 	SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
370e861b98dSMichael Buesch 	SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
371e861b98dSMichael Buesch 	SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
372*3623b266SRafał Miłecki 	SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
373bf7d420bSHauke Mehrtens 	if (out->revision == 1)
374c272ef44SLarry Finger 		SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
375c272ef44SLarry Finger 		     SSB_SPROM1_BINF_CCODE_SHIFT);
376e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
377e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTA_SHIFT);
378e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
379e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTBG_SHIFT);
380c272ef44SLarry Finger 	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
381c272ef44SLarry Finger 	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
382c272ef44SLarry Finger 	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
383c272ef44SLarry Finger 	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
384c272ef44SLarry Finger 	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
385c272ef44SLarry Finger 	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
386c272ef44SLarry Finger 	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
387c272ef44SLarry Finger 	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
388c272ef44SLarry Finger 	     SSB_SPROM1_GPIOA_P1_SHIFT);
389c272ef44SLarry Finger 	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
390c272ef44SLarry Finger 	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
391c272ef44SLarry Finger 	     SSB_SPROM1_GPIOB_P3_SHIFT);
392c272ef44SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
393c272ef44SLarry Finger 	     SSB_SPROM1_MAXPWR_A_SHIFT);
394c272ef44SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
395c272ef44SLarry Finger 	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
396c272ef44SLarry Finger 	     SSB_SPROM1_ITSSI_A_SHIFT);
397c272ef44SLarry Finger 	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
398c272ef44SLarry Finger 	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
399af4b7450SMichael Buesch 	if (out->revision >= 2)
400af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
401bf7d420bSHauke Mehrtens 	SPEX(alpha2[0], SSB_SPROM1_CCODE, 0xff00, 8);
402bf7d420bSHauke Mehrtens 	SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0);
403e861b98dSMichael Buesch 
404e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
405f8f8a660SHauke Mehrtens 	out->antenna_gain.a0 = r123_extract_antgain(out->revision, in,
406e861b98dSMichael Buesch 						    SSB_SPROM1_AGAIN_BG,
407c272ef44SLarry Finger 						    SSB_SPROM1_AGAIN_BG_SHIFT);
408f8f8a660SHauke Mehrtens 	out->antenna_gain.a1 = r123_extract_antgain(out->revision, in,
409e861b98dSMichael Buesch 						    SSB_SPROM1_AGAIN_A,
410e861b98dSMichael Buesch 						    SSB_SPROM1_AGAIN_A_SHIFT);
411c272ef44SLarry Finger }
412c272ef44SLarry Finger 
413172c69a4SRafał Miłecki /* Revs 4 5 and 8 have partially shared layout */
414172c69a4SRafał Miłecki static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
415172c69a4SRafał Miłecki {
416172c69a4SRafał Miłecki 	SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01,
417172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G0, SSB_SPROM4_TXPID2G0_SHIFT);
418172c69a4SRafał Miłecki 	SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01,
419172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G1, SSB_SPROM4_TXPID2G1_SHIFT);
420172c69a4SRafał Miłecki 	SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23,
421172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G2, SSB_SPROM4_TXPID2G2_SHIFT);
422172c69a4SRafał Miłecki 	SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23,
423172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G3, SSB_SPROM4_TXPID2G3_SHIFT);
424172c69a4SRafał Miłecki 
425172c69a4SRafał Miłecki 	SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01,
426172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL0, SSB_SPROM4_TXPID5GL0_SHIFT);
427172c69a4SRafał Miłecki 	SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01,
428172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL1, SSB_SPROM4_TXPID5GL1_SHIFT);
429172c69a4SRafał Miłecki 	SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23,
430172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL2, SSB_SPROM4_TXPID5GL2_SHIFT);
431172c69a4SRafał Miłecki 	SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23,
432172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL3, SSB_SPROM4_TXPID5GL3_SHIFT);
433172c69a4SRafał Miłecki 
434172c69a4SRafał Miłecki 	SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01,
435172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G0, SSB_SPROM4_TXPID5G0_SHIFT);
436172c69a4SRafał Miłecki 	SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01,
437172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G1, SSB_SPROM4_TXPID5G1_SHIFT);
438172c69a4SRafał Miłecki 	SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23,
439172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G2, SSB_SPROM4_TXPID5G2_SHIFT);
440172c69a4SRafał Miłecki 	SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23,
441172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G3, SSB_SPROM4_TXPID5G3_SHIFT);
442172c69a4SRafał Miłecki 
443172c69a4SRafał Miłecki 	SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01,
444172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH0, SSB_SPROM4_TXPID5GH0_SHIFT);
445172c69a4SRafał Miłecki 	SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01,
446172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH1, SSB_SPROM4_TXPID5GH1_SHIFT);
447172c69a4SRafał Miłecki 	SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23,
448172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH2, SSB_SPROM4_TXPID5GH2_SHIFT);
449172c69a4SRafał Miłecki 	SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23,
450172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH3, SSB_SPROM4_TXPID5GH3_SHIFT);
451172c69a4SRafał Miłecki }
452172c69a4SRafał Miłecki 
453095f695cSLarry Finger static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
45461e115a5SMichael Buesch {
455095f695cSLarry Finger 	u16 il0mac_offset;
45661e115a5SMichael Buesch 
457095f695cSLarry Finger 	if (out->revision == 4)
458095f695cSLarry Finger 		il0mac_offset = SSB_SPROM4_IL0MAC;
459095f695cSLarry Finger 	else
460095f695cSLarry Finger 		il0mac_offset = SSB_SPROM5_IL0MAC;
461e5652756SJoe Perches 
462e5652756SJoe Perches 	sprom_get_mac(out->il0mac, &in[SPOFF(il0mac_offset)]);
463e5652756SJoe Perches 
464c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
465c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
466c272ef44SLarry Finger 	     SSB_SPROM4_ETHPHY_ET1A_SHIFT);
467673335c8SHauke Mehrtens 	SPEX(board_rev, SSB_SPROM4_BOARDREV, 0xFFFF, 0);
468*3623b266SRafał Miłecki 	SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
469095f695cSLarry Finger 	if (out->revision == 4) {
470bf7d420bSHauke Mehrtens 		SPEX(alpha2[0], SSB_SPROM4_CCODE, 0xff00, 8);
471bf7d420bSHauke Mehrtens 		SPEX(alpha2[1], SSB_SPROM4_CCODE, 0x00ff, 0);
472c272ef44SLarry Finger 		SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
473af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
4746d1d4ea4SRafał Miłecki 		SPEX(boardflags2_lo, SSB_SPROM4_BFL2LO, 0xFFFF, 0);
4756d1d4ea4SRafał Miłecki 		SPEX(boardflags2_hi, SSB_SPROM4_BFL2HI, 0xFFFF, 0);
476095f695cSLarry Finger 	} else {
477bf7d420bSHauke Mehrtens 		SPEX(alpha2[0], SSB_SPROM5_CCODE, 0xff00, 8);
478bf7d420bSHauke Mehrtens 		SPEX(alpha2[1], SSB_SPROM5_CCODE, 0x00ff, 0);
479095f695cSLarry Finger 		SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
480095f695cSLarry Finger 		SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
4816d1d4ea4SRafał Miłecki 		SPEX(boardflags2_lo, SSB_SPROM5_BFL2LO, 0xFFFF, 0);
4826d1d4ea4SRafał Miłecki 		SPEX(boardflags2_hi, SSB_SPROM5_BFL2HI, 0xFFFF, 0);
483095f695cSLarry Finger 	}
484e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
485e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_A_SHIFT);
486e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG,
487e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_BG_SHIFT);
488d3c319f9SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0);
489d3c319f9SLarry Finger 	SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG,
490d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_BG_SHIFT);
491d3c319f9SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0);
492d3c319f9SLarry Finger 	SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A,
493d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_A_SHIFT);
494095f695cSLarry Finger 	if (out->revision == 4) {
495d3c319f9SLarry Finger 		SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0);
496d3c319f9SLarry Finger 		SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1,
497d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOA_P1_SHIFT);
498d3c319f9SLarry Finger 		SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0);
499d3c319f9SLarry Finger 		SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3,
500d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOB_P3_SHIFT);
501095f695cSLarry Finger 	} else {
502095f695cSLarry Finger 		SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0);
503095f695cSLarry Finger 		SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1,
504095f695cSLarry Finger 		     SSB_SPROM5_GPIOA_P1_SHIFT);
505095f695cSLarry Finger 		SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0);
506095f695cSLarry Finger 		SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3,
507095f695cSLarry Finger 		     SSB_SPROM5_GPIOB_P3_SHIFT);
508095f695cSLarry Finger 	}
509e861b98dSMichael Buesch 
510e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
511f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a0, SSB_SPROM4_AGAIN01,
512e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT);
513f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a1, SSB_SPROM4_AGAIN01,
514e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT);
515f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a2, SSB_SPROM4_AGAIN23,
516e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT);
517f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a3, SSB_SPROM4_AGAIN23,
518e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT);
519e861b98dSMichael Buesch 
520172c69a4SRafał Miłecki 	sprom_extract_r458(out, in);
521172c69a4SRafał Miłecki 
522c272ef44SLarry Finger 	/* TODO - get remaining rev 4 stuff needed */
52361e115a5SMichael Buesch }
52461e115a5SMichael Buesch 
5256b1c7c67SMichael Buesch static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
5266b1c7c67SMichael Buesch {
5276b1c7c67SMichael Buesch 	int i;
528e5652756SJoe Perches 	u16 o;
529b0f70292SRafał Miłecki 	u16 pwr_info_offset[] = {
530b0f70292SRafał Miłecki 		SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
531b0f70292SRafał Miłecki 		SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
532b0f70292SRafał Miłecki 	};
533b0f70292SRafał Miłecki 	BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
534b0f70292SRafał Miłecki 			ARRAY_SIZE(out->core_pwr_info));
5356b1c7c67SMichael Buesch 
5366b1c7c67SMichael Buesch 	/* extract the MAC address */
537e5652756SJoe Perches 	sprom_get_mac(out->il0mac, &in[SPOFF(SSB_SPROM8_IL0MAC)]);
538e5652756SJoe Perches 
539673335c8SHauke Mehrtens 	SPEX(board_rev, SSB_SPROM8_BOARDREV, 0xFFFF, 0);
540*3623b266SRafał Miłecki 	SPEX(board_type, SSB_SPROM1_SPID, 0xFFFF, 0);
541bf7d420bSHauke Mehrtens 	SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
542bf7d420bSHauke Mehrtens 	SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
5436b1c7c67SMichael Buesch 	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
5446b1c7c67SMichael Buesch 	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
545f679056bSGábor Stefanik 	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
546f679056bSGábor Stefanik 	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
5476b1c7c67SMichael Buesch 	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
5486b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
5496b1c7c67SMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
5506b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
5516b1c7c67SMichael Buesch 	SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
5526b1c7c67SMichael Buesch 	SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
5536b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_BG_SHIFT);
5546b1c7c67SMichael Buesch 	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
5556b1c7c67SMichael Buesch 	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
5566b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_A_SHIFT);
557f679056bSGábor Stefanik 	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
558f679056bSGábor Stefanik 	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
559f679056bSGábor Stefanik 	     SSB_SPROM8_MAXP_AL_SHIFT);
5606b1c7c67SMichael Buesch 	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
5616b1c7c67SMichael Buesch 	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
5626b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOA_P1_SHIFT);
5636b1c7c67SMichael Buesch 	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
5646b1c7c67SMichael Buesch 	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
5656b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOB_P3_SHIFT);
566f679056bSGábor Stefanik 	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
567f679056bSGábor Stefanik 	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
568f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5G_SHIFT);
569f679056bSGábor Stefanik 	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
570f679056bSGábor Stefanik 	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
571f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5GH_SHIFT);
572f679056bSGábor Stefanik 	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
573f679056bSGábor Stefanik 	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
574f679056bSGábor Stefanik 	     SSB_SPROM8_RXPO5G_SHIFT);
575f679056bSGábor Stefanik 	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
576f679056bSGábor Stefanik 	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
577f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC2G_SHIFT);
578f679056bSGábor Stefanik 	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
579f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV2G_SHIFT);
580f679056bSGábor Stefanik 	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
581f679056bSGábor Stefanik 	     SSB_SPROM8_BXA2G_SHIFT);
582f679056bSGábor Stefanik 	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
583f679056bSGábor Stefanik 	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
584f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC5G_SHIFT);
585f679056bSGábor Stefanik 	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
586f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV5G_SHIFT);
587f679056bSGábor Stefanik 	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
588f679056bSGábor Stefanik 	     SSB_SPROM8_BXA5G_SHIFT);
589f679056bSGábor Stefanik 	SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
590f679056bSGábor Stefanik 	SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
591f679056bSGábor Stefanik 	SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
592f679056bSGábor Stefanik 	SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
593f679056bSGábor Stefanik 	SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
594f679056bSGábor Stefanik 	SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
595f679056bSGábor Stefanik 	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
596f679056bSGábor Stefanik 	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
597f679056bSGábor Stefanik 	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
598f679056bSGábor Stefanik 	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
599f679056bSGábor Stefanik 	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
600f679056bSGábor Stefanik 	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
601f679056bSGábor Stefanik 	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
602f679056bSGábor Stefanik 	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
603f679056bSGábor Stefanik 	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
604f679056bSGábor Stefanik 	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
605f679056bSGábor Stefanik 	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
6066b1c7c67SMichael Buesch 
6076b1c7c67SMichael Buesch 	/* Extract the antenna gain values. */
608f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
6096b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
610f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
6116b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
612f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
6136b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
614f8f8a660SHauke Mehrtens 	SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
6156b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
6166b1c7c67SMichael Buesch 
617b0f70292SRafał Miłecki 	/* Extract cores power info info */
618b0f70292SRafał Miłecki 	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
619b0f70292SRafał Miłecki 		o = pwr_info_offset[i];
620b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
621b0f70292SRafał Miłecki 			SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
622b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
623b0f70292SRafał Miłecki 			SSB_SPROM8_2G_MAXP, 0);
624b0f70292SRafał Miłecki 
625b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
626b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
627b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
628b0f70292SRafał Miłecki 
629b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
630b0f70292SRafał Miłecki 			SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
631b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
632b0f70292SRafał Miłecki 			SSB_SPROM8_5G_MAXP, 0);
633b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
634b0f70292SRafał Miłecki 			SSB_SPROM8_5GH_MAXP, 0);
635b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
636b0f70292SRafał Miłecki 			SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
637b0f70292SRafał Miłecki 
638b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
639b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
640b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
641b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
642b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
643b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
644b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
645b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
646b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
647b0f70292SRafał Miłecki 	}
648b0f70292SRafał Miłecki 
6498a5ac6ecSRafał Miłecki 	/* Extract FEM info */
6508a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G,
6518a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
6528a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G,
6538a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
6548a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G,
6558a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
6568a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G,
6578a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
6588a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G,
6598a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
6608a5ac6ecSRafał Miłecki 
6618a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G,
6628a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
6638a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G,
6648a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
6658a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G,
6668a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
6678a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G,
6688a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
6698a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G,
6708a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
6718a5ac6ecSRafał Miłecki 
672e2da4bd3SHauke Mehrtens 	SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
673e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_LEDDC_ON_SHIFT);
674e2da4bd3SHauke Mehrtens 	SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF,
675e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_LEDDC_OFF_SHIFT);
676e2da4bd3SHauke Mehrtens 
677e2da4bd3SHauke Mehrtens 	SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
678e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
679e2da4bd3SHauke Mehrtens 	SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
680e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
681e2da4bd3SHauke Mehrtens 	SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
682e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TXRXC_SWITCH_SHIFT);
683e2da4bd3SHauke Mehrtens 
684e2da4bd3SHauke Mehrtens 	SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
685e2da4bd3SHauke Mehrtens 
686e2da4bd3SHauke Mehrtens 	SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0);
687e2da4bd3SHauke Mehrtens 	SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0);
688e2da4bd3SHauke Mehrtens 	SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0);
689e2da4bd3SHauke Mehrtens 	SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0);
690e2da4bd3SHauke Mehrtens 
691e2da4bd3SHauke Mehrtens 	SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
692e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
693e2da4bd3SHauke Mehrtens 	SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
694e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
695e2da4bd3SHauke Mehrtens 	SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX,
696e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
697e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
698e2da4bd3SHauke Mehrtens 	SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
699e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
700e2da4bd3SHauke Mehrtens 	SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX,
701e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
702e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
703e2da4bd3SHauke Mehrtens 	SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP,
704e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR,
705e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT);
706e2da4bd3SHauke Mehrtens 	SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP,
707e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP,
708e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT);
709e2da4bd3SHauke Mehrtens 	SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL,
710e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT);
711e2da4bd3SHauke Mehrtens 
712e2da4bd3SHauke Mehrtens 	SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
713e2da4bd3SHauke Mehrtens 	SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
714e2da4bd3SHauke Mehrtens 	SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
715e2da4bd3SHauke Mehrtens 	SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0);
716e2da4bd3SHauke Mehrtens 
717e2da4bd3SHauke Mehrtens 	SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH,
718e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_THERMAL_TRESH_SHIFT);
719e2da4bd3SHauke Mehrtens 	SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
720e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_THERMAL_OFFSET_SHIFT);
721e2da4bd3SHauke Mehrtens 	SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA,
722e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TEMPDELTA_PHYCAL,
723e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
724e2da4bd3SHauke Mehrtens 	SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
725e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
726e2da4bd3SHauke Mehrtens 	SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA,
727e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TEMPDELTA_HYSTERESIS,
728e2da4bd3SHauke Mehrtens 	     SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
729172c69a4SRafał Miłecki 	sprom_extract_r458(out, in);
730172c69a4SRafał Miłecki 
7316b1c7c67SMichael Buesch 	/* TODO - get remaining rev 8 stuff needed */
7326b1c7c67SMichael Buesch }
7336b1c7c67SMichael Buesch 
734c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
735c272ef44SLarry Finger 			 const u16 *in, u16 size)
73661e115a5SMichael Buesch {
73761e115a5SMichael Buesch 	memset(out, 0, sizeof(*out));
73861e115a5SMichael Buesch 
739c272ef44SLarry Finger 	out->revision = in[size - 1] & 0x00FF;
74033a606acSJoe Perches 	ssb_dbg("SPROM revision %d detected\n", out->revision);
74131ce12fbSLarry Finger 	memset(out->et0mac, 0xFF, 6);		/* preset et0 and et1 mac */
74231ce12fbSLarry Finger 	memset(out->et1mac, 0xFF, 6);
74354435f9eSRafał Miłecki 
74461e115a5SMichael Buesch 	if ((bus->chip_id & 0xFF00) == 0x4400) {
74561e115a5SMichael Buesch 		/* Workaround: The BCM44XX chip has a stupid revision
74661e115a5SMichael Buesch 		 * number stored in the SPROM.
74761e115a5SMichael Buesch 		 * Always extract r1. */
748c272ef44SLarry Finger 		out->revision = 1;
74933a606acSJoe Perches 		ssb_dbg("SPROM treated as revision %d\n", out->revision);
75054435f9eSRafał Miłecki 	}
75154435f9eSRafał Miłecki 
7526b1c7c67SMichael Buesch 	switch (out->revision) {
7536b1c7c67SMichael Buesch 	case 1:
7546b1c7c67SMichael Buesch 	case 2:
7556b1c7c67SMichael Buesch 	case 3:
7566b1c7c67SMichael Buesch 		sprom_extract_r123(out, in);
7576b1c7c67SMichael Buesch 		break;
7586b1c7c67SMichael Buesch 	case 4:
7596b1c7c67SMichael Buesch 	case 5:
7606b1c7c67SMichael Buesch 		sprom_extract_r45(out, in);
7616b1c7c67SMichael Buesch 		break;
7626b1c7c67SMichael Buesch 	case 8:
7636b1c7c67SMichael Buesch 		sprom_extract_r8(out, in);
7646b1c7c67SMichael Buesch 		break;
7656b1c7c67SMichael Buesch 	default:
76633a606acSJoe Perches 		ssb_warn("Unsupported SPROM revision %d detected. Will extract v1\n",
76733a606acSJoe Perches 			 out->revision);
768cd559b36SLarry Finger 		out->revision = 1;
769c272ef44SLarry Finger 		sprom_extract_r123(out, in);
770c272ef44SLarry Finger 	}
77161e115a5SMichael Buesch 
7724503183aSLarry Finger 	if (out->boardflags_lo == 0xFFFF)
7734503183aSLarry Finger 		out->boardflags_lo = 0;  /* per specs */
7744503183aSLarry Finger 	if (out->boardflags_hi == 0xFFFF)
7754503183aSLarry Finger 		out->boardflags_hi = 0;  /* per specs */
7764503183aSLarry Finger 
77761e115a5SMichael Buesch 	return 0;
77861e115a5SMichael Buesch }
77961e115a5SMichael Buesch 
78061e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus,
78161e115a5SMichael Buesch 			     struct ssb_sprom *sprom)
78261e115a5SMichael Buesch {
783ca4a0831SRafał Miłecki 	int err;
78461e115a5SMichael Buesch 	u16 *buf;
78561e115a5SMichael Buesch 
786d53cdbb9SJohn W. Linville 	if (!ssb_is_sprom_available(bus)) {
78733a606acSJoe Perches 		ssb_err("No SPROM available!\n");
788d53cdbb9SJohn W. Linville 		return -ENODEV;
789d53cdbb9SJohn W. Linville 	}
79025985edcSLucas De Marchi 	if (bus->chipco.dev) {	/* can be unavailable! */
7919d1ac34eSLarry Finger 		/*
7929d1ac34eSLarry Finger 		 * get SPROM offset: SSB_SPROM_BASE1 except for
7939d1ac34eSLarry Finger 		 * chipcommon rev >= 31 or chip ID is 0x4312 and
7949d1ac34eSLarry Finger 		 * chipcommon status & 3 == 2
7959d1ac34eSLarry Finger 		 */
7969d1ac34eSLarry Finger 		if (bus->chipco.dev->id.revision >= 31)
7979d1ac34eSLarry Finger 			bus->sprom_offset = SSB_SPROM_BASE31;
7989d1ac34eSLarry Finger 		else if (bus->chip_id == 0x4312 &&
7999d1ac34eSLarry Finger 			 (bus->chipco.status & 0x03) == 2)
8009d1ac34eSLarry Finger 			bus->sprom_offset = SSB_SPROM_BASE31;
8019d1ac34eSLarry Finger 		else
8029d1ac34eSLarry Finger 			bus->sprom_offset = SSB_SPROM_BASE1;
803da1fdb02SChristoph Fritz 	} else {
804da1fdb02SChristoph Fritz 		bus->sprom_offset = SSB_SPROM_BASE1;
805da1fdb02SChristoph Fritz 	}
80633a606acSJoe Perches 	ssb_dbg("SPROM offset is 0x%x\n", bus->sprom_offset);
807ea2db495SRafał Miłecki 
808c272ef44SLarry Finger 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
80961e115a5SMichael Buesch 	if (!buf)
810ca4a0831SRafał Miłecki 		return -ENOMEM;
811c272ef44SLarry Finger 	bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
81261e115a5SMichael Buesch 	sprom_do_read(bus, buf);
813c272ef44SLarry Finger 	err = sprom_check_crc(buf, bus->sprom_size);
81461e115a5SMichael Buesch 	if (err) {
8152afc4901SLarry.Finger@lwfinger.net 		/* try for a 440 byte SPROM - revision 4 and higher */
816c272ef44SLarry Finger 		kfree(buf);
817c272ef44SLarry Finger 		buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
818c272ef44SLarry Finger 			      GFP_KERNEL);
819c272ef44SLarry Finger 		if (!buf)
820ca4a0831SRafał Miłecki 			return -ENOMEM;
821c272ef44SLarry Finger 		bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
822c272ef44SLarry Finger 		sprom_do_read(bus, buf);
823c272ef44SLarry Finger 		err = sprom_check_crc(buf, bus->sprom_size);
824e79c1ba8SMichael Buesch 		if (err) {
825e79c1ba8SMichael Buesch 			/* All CRC attempts failed.
826e79c1ba8SMichael Buesch 			 * Maybe there is no SPROM on the device?
827b3ae52b6SHauke Mehrtens 			 * Now we ask the arch code if there is some sprom
828b3ae52b6SHauke Mehrtens 			 * available for this device in some other storage */
829b3ae52b6SHauke Mehrtens 			err = ssb_fill_sprom_with_fallback(bus, sprom);
830b3ae52b6SHauke Mehrtens 			if (err) {
83133a606acSJoe Perches 				ssb_warn("WARNING: Using fallback SPROM failed (err %d)\n",
832b3ae52b6SHauke Mehrtens 					 err);
833b3ae52b6SHauke Mehrtens 			} else {
83433a606acSJoe Perches 				ssb_dbg("Using SPROM revision %d provided by platform\n",
83533a606acSJoe Perches 					sprom->revision);
836e79c1ba8SMichael Buesch 				err = 0;
837e79c1ba8SMichael Buesch 				goto out_free;
838e79c1ba8SMichael Buesch 			}
83933a606acSJoe Perches 			ssb_warn("WARNING: Invalid SPROM CRC (corrupt SPROM)\n");
840c272ef44SLarry Finger 		}
841e79c1ba8SMichael Buesch 	}
842c272ef44SLarry Finger 	err = sprom_extract(bus, sprom, buf, bus->sprom_size);
84361e115a5SMichael Buesch 
844e79c1ba8SMichael Buesch out_free:
84561e115a5SMichael Buesch 	kfree(buf);
84661e115a5SMichael Buesch 	return err;
84761e115a5SMichael Buesch }
84861e115a5SMichael Buesch 
84961e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
85061e115a5SMichael Buesch 				  struct ssb_boardinfo *bi)
85161e115a5SMichael Buesch {
852115f9450SSergei Shtylyov 	bi->vendor = bus->host_pci->subsystem_vendor;
853115f9450SSergei Shtylyov 	bi->type = bus->host_pci->subsystem_device;
85461e115a5SMichael Buesch }
85561e115a5SMichael Buesch 
85661e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus,
85761e115a5SMichael Buesch 			   struct ssb_init_invariants *iv)
85861e115a5SMichael Buesch {
85961e115a5SMichael Buesch 	int err;
86061e115a5SMichael Buesch 
86161e115a5SMichael Buesch 	err = ssb_pci_sprom_get(bus, &iv->sprom);
86261e115a5SMichael Buesch 	if (err)
86361e115a5SMichael Buesch 		goto out;
86461e115a5SMichael Buesch 	ssb_pci_get_boardinfo(bus, &iv->boardinfo);
86561e115a5SMichael Buesch 
86661e115a5SMichael Buesch out:
86761e115a5SMichael Buesch 	return err;
86861e115a5SMichael Buesch }
86961e115a5SMichael Buesch 
87061e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG
87161e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus)
87261e115a5SMichael Buesch {
87361e115a5SMichael Buesch 	if (likely(bus->powered_up))
87461e115a5SMichael Buesch 		return 0;
87561e115a5SMichael Buesch 
87661e115a5SMichael Buesch 	printk(KERN_ERR PFX "FATAL ERROR: Bus powered down "
87761e115a5SMichael Buesch 	       "while accessing PCI MMIO space\n");
87861e115a5SMichael Buesch 	if (bus->power_warn_count <= 10) {
87961e115a5SMichael Buesch 		bus->power_warn_count++;
88061e115a5SMichael Buesch 		dump_stack();
88161e115a5SMichael Buesch 	}
88261e115a5SMichael Buesch 
88361e115a5SMichael Buesch 	return -ENODEV;
88461e115a5SMichael Buesch }
88561e115a5SMichael Buesch #else /* DEBUG */
88661e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus)
88761e115a5SMichael Buesch {
88861e115a5SMichael Buesch 	return 0;
88961e115a5SMichael Buesch }
89061e115a5SMichael Buesch #endif /* DEBUG */
89161e115a5SMichael Buesch 
892ffc7689dSMichael Buesch static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset)
893ffc7689dSMichael Buesch {
894ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
895ffc7689dSMichael Buesch 
896ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
897ffc7689dSMichael Buesch 		return 0xFF;
898ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
899ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
900ffc7689dSMichael Buesch 			return 0xFF;
901ffc7689dSMichael Buesch 	}
902ffc7689dSMichael Buesch 	return ioread8(bus->mmio + offset);
903ffc7689dSMichael Buesch }
904ffc7689dSMichael Buesch 
90561e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
90661e115a5SMichael Buesch {
90761e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
90861e115a5SMichael Buesch 
90961e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
91061e115a5SMichael Buesch 		return 0xFFFF;
91161e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
91261e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
91361e115a5SMichael Buesch 			return 0xFFFF;
91461e115a5SMichael Buesch 	}
9154b402c65SMichael Buesch 	return ioread16(bus->mmio + offset);
91661e115a5SMichael Buesch }
91761e115a5SMichael Buesch 
91861e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
91961e115a5SMichael Buesch {
92061e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
92161e115a5SMichael Buesch 
92261e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
92361e115a5SMichael Buesch 		return 0xFFFFFFFF;
92461e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
92561e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
92661e115a5SMichael Buesch 			return 0xFFFFFFFF;
92761e115a5SMichael Buesch 	}
9284b402c65SMichael Buesch 	return ioread32(bus->mmio + offset);
92961e115a5SMichael Buesch }
93061e115a5SMichael Buesch 
931d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
932d625a29bSMichael Buesch static void ssb_pci_block_read(struct ssb_device *dev, void *buffer,
933d625a29bSMichael Buesch 			       size_t count, u16 offset, u8 reg_width)
934d625a29bSMichael Buesch {
935d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
936d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
937d625a29bSMichael Buesch 
938d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
939d625a29bSMichael Buesch 		goto error;
940d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
941d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
942d625a29bSMichael Buesch 			goto error;
943d625a29bSMichael Buesch 	}
944d625a29bSMichael Buesch 	switch (reg_width) {
945d625a29bSMichael Buesch 	case sizeof(u8):
946d625a29bSMichael Buesch 		ioread8_rep(addr, buffer, count);
947d625a29bSMichael Buesch 		break;
948d625a29bSMichael Buesch 	case sizeof(u16):
949d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
950d625a29bSMichael Buesch 		ioread16_rep(addr, buffer, count >> 1);
951d625a29bSMichael Buesch 		break;
952d625a29bSMichael Buesch 	case sizeof(u32):
953d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
954d625a29bSMichael Buesch 		ioread32_rep(addr, buffer, count >> 2);
955d625a29bSMichael Buesch 		break;
956d625a29bSMichael Buesch 	default:
957d625a29bSMichael Buesch 		SSB_WARN_ON(1);
958d625a29bSMichael Buesch 	}
959d625a29bSMichael Buesch 
960d625a29bSMichael Buesch 	return;
961d625a29bSMichael Buesch error:
962d625a29bSMichael Buesch 	memset(buffer, 0xFF, count);
963d625a29bSMichael Buesch }
964d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
965d625a29bSMichael Buesch 
966ffc7689dSMichael Buesch static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value)
967ffc7689dSMichael Buesch {
968ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
969ffc7689dSMichael Buesch 
970ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
971ffc7689dSMichael Buesch 		return;
972ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
973ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
974ffc7689dSMichael Buesch 			return;
975ffc7689dSMichael Buesch 	}
976ffc7689dSMichael Buesch 	iowrite8(value, bus->mmio + offset);
977ffc7689dSMichael Buesch }
978ffc7689dSMichael Buesch 
97961e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
98061e115a5SMichael Buesch {
98161e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
98261e115a5SMichael Buesch 
98361e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
98461e115a5SMichael Buesch 		return;
98561e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
98661e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
98761e115a5SMichael Buesch 			return;
98861e115a5SMichael Buesch 	}
9894b402c65SMichael Buesch 	iowrite16(value, bus->mmio + offset);
99061e115a5SMichael Buesch }
99161e115a5SMichael Buesch 
99261e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
99361e115a5SMichael Buesch {
99461e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
99561e115a5SMichael Buesch 
99661e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
99761e115a5SMichael Buesch 		return;
99861e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
99961e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
100061e115a5SMichael Buesch 			return;
100161e115a5SMichael Buesch 	}
10024b402c65SMichael Buesch 	iowrite32(value, bus->mmio + offset);
100361e115a5SMichael Buesch }
100461e115a5SMichael Buesch 
1005d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
1006d625a29bSMichael Buesch static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer,
1007d625a29bSMichael Buesch 				size_t count, u16 offset, u8 reg_width)
1008d625a29bSMichael Buesch {
1009d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
1010d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
1011d625a29bSMichael Buesch 
1012d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
1013d625a29bSMichael Buesch 		return;
1014d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
1015d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
1016d625a29bSMichael Buesch 			return;
1017d625a29bSMichael Buesch 	}
1018d625a29bSMichael Buesch 	switch (reg_width) {
1019d625a29bSMichael Buesch 	case sizeof(u8):
1020d625a29bSMichael Buesch 		iowrite8_rep(addr, buffer, count);
1021d625a29bSMichael Buesch 		break;
1022d625a29bSMichael Buesch 	case sizeof(u16):
1023d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
1024d625a29bSMichael Buesch 		iowrite16_rep(addr, buffer, count >> 1);
1025d625a29bSMichael Buesch 		break;
1026d625a29bSMichael Buesch 	case sizeof(u32):
1027d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
1028d625a29bSMichael Buesch 		iowrite32_rep(addr, buffer, count >> 2);
1029d625a29bSMichael Buesch 		break;
1030d625a29bSMichael Buesch 	default:
1031d625a29bSMichael Buesch 		SSB_WARN_ON(1);
1032d625a29bSMichael Buesch 	}
1033d625a29bSMichael Buesch }
1034d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
1035d625a29bSMichael Buesch 
103661e115a5SMichael Buesch /* Not "static", as it's used in main.c */
103761e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = {
1038ffc7689dSMichael Buesch 	.read8		= ssb_pci_read8,
103961e115a5SMichael Buesch 	.read16		= ssb_pci_read16,
104061e115a5SMichael Buesch 	.read32		= ssb_pci_read32,
1041ffc7689dSMichael Buesch 	.write8		= ssb_pci_write8,
104261e115a5SMichael Buesch 	.write16	= ssb_pci_write16,
104361e115a5SMichael Buesch 	.write32	= ssb_pci_write32,
1044d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
1045d625a29bSMichael Buesch 	.block_read	= ssb_pci_block_read,
1046d625a29bSMichael Buesch 	.block_write	= ssb_pci_block_write,
1047d625a29bSMichael Buesch #endif
104861e115a5SMichael Buesch };
104961e115a5SMichael Buesch 
105061e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
105161e115a5SMichael Buesch 				       struct device_attribute *attr,
105261e115a5SMichael Buesch 				       char *buf)
105361e115a5SMichael Buesch {
105461e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
105561e115a5SMichael Buesch 	struct ssb_bus *bus;
105661e115a5SMichael Buesch 
105761e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
105861e115a5SMichael Buesch 	if (!bus)
1059e7ec2e32SMichael Buesch 		return -ENODEV;
106061e115a5SMichael Buesch 
1061e7ec2e32SMichael Buesch 	return ssb_attr_sprom_show(bus, buf, sprom_do_read);
106261e115a5SMichael Buesch }
106361e115a5SMichael Buesch 
106461e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
106561e115a5SMichael Buesch 					struct device_attribute *attr,
106661e115a5SMichael Buesch 					const char *buf, size_t count)
106761e115a5SMichael Buesch {
106861e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
106961e115a5SMichael Buesch 	struct ssb_bus *bus;
107061e115a5SMichael Buesch 
107161e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
107261e115a5SMichael Buesch 	if (!bus)
1073e7ec2e32SMichael Buesch 		return -ENODEV;
107461e115a5SMichael Buesch 
1075e7ec2e32SMichael Buesch 	return ssb_attr_sprom_store(bus, buf, count,
1076e7ec2e32SMichael Buesch 				    sprom_check_crc, sprom_do_write);
107761e115a5SMichael Buesch }
107861e115a5SMichael Buesch 
107961e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600,
108061e115a5SMichael Buesch 		   ssb_pci_attr_sprom_show,
108161e115a5SMichael Buesch 		   ssb_pci_attr_sprom_store);
108261e115a5SMichael Buesch 
108361e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus)
108461e115a5SMichael Buesch {
108561e115a5SMichael Buesch 	struct pci_dev *pdev;
108661e115a5SMichael Buesch 
108761e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
108861e115a5SMichael Buesch 		return;
108961e115a5SMichael Buesch 
109061e115a5SMichael Buesch 	pdev = bus->host_pci;
109161e115a5SMichael Buesch 	device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
109261e115a5SMichael Buesch }
109361e115a5SMichael Buesch 
109461e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus)
109561e115a5SMichael Buesch {
109661e115a5SMichael Buesch 	struct pci_dev *pdev;
109761e115a5SMichael Buesch 	int err;
109861e115a5SMichael Buesch 
109961e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
110061e115a5SMichael Buesch 		return 0;
110161e115a5SMichael Buesch 
110261e115a5SMichael Buesch 	pdev = bus->host_pci;
1103e7ec2e32SMichael Buesch 	mutex_init(&bus->sprom_mutex);
110461e115a5SMichael Buesch 	err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
110561e115a5SMichael Buesch 	if (err)
110661e115a5SMichael Buesch 		goto out;
110761e115a5SMichael Buesch 
110861e115a5SMichael Buesch out:
110961e115a5SMichael Buesch 	return err;
111061e115a5SMichael Buesch }
1111