xref: /openbmc/linux/drivers/ssb/pci.c (revision cd559b36e77c396425284a58ce4b2c5d2167d40d)
161e115a5SMichael Buesch /*
261e115a5SMichael Buesch  * Sonics Silicon Backplane PCI-Hostbus related functions.
361e115a5SMichael Buesch  *
461e115a5SMichael Buesch  * Copyright (C) 2005-2006 Michael Buesch <mb@bu3sch.de>
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>
2061e115a5SMichael Buesch #include <linux/pci.h>
2161e115a5SMichael Buesch #include <linux/delay.h>
2261e115a5SMichael Buesch 
2361e115a5SMichael Buesch #include "ssb_private.h"
2461e115a5SMichael Buesch 
2561e115a5SMichael Buesch 
2661e115a5SMichael Buesch /* Define the following to 1 to enable a printk on each coreswitch. */
2761e115a5SMichael Buesch #define SSB_VERBOSE_PCICORESWITCH_DEBUG		0
2861e115a5SMichael Buesch 
2961e115a5SMichael Buesch 
3061e115a5SMichael Buesch /* Lowlevel coreswitching */
3161e115a5SMichael Buesch int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
3261e115a5SMichael Buesch {
3361e115a5SMichael Buesch 	int err;
3461e115a5SMichael Buesch 	int attempts = 0;
3561e115a5SMichael Buesch 	u32 cur_core;
3661e115a5SMichael Buesch 
3761e115a5SMichael Buesch 	while (1) {
3861e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN,
3961e115a5SMichael Buesch 					     (coreidx * SSB_CORE_SIZE)
4061e115a5SMichael Buesch 					     + SSB_ENUM_BASE);
4161e115a5SMichael Buesch 		if (err)
4261e115a5SMichael Buesch 			goto error;
4361e115a5SMichael Buesch 		err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN,
4461e115a5SMichael Buesch 					    &cur_core);
4561e115a5SMichael Buesch 		if (err)
4661e115a5SMichael Buesch 			goto error;
4761e115a5SMichael Buesch 		cur_core = (cur_core - SSB_ENUM_BASE)
4861e115a5SMichael Buesch 			   / SSB_CORE_SIZE;
4961e115a5SMichael Buesch 		if (cur_core == coreidx)
5061e115a5SMichael Buesch 			break;
5161e115a5SMichael Buesch 
5261e115a5SMichael Buesch 		if (attempts++ > SSB_BAR0_MAX_RETRIES)
5361e115a5SMichael Buesch 			goto error;
5461e115a5SMichael Buesch 		udelay(10);
5561e115a5SMichael Buesch 	}
5661e115a5SMichael Buesch 	return 0;
5761e115a5SMichael Buesch error:
5861e115a5SMichael Buesch 	ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
5961e115a5SMichael Buesch 	return -ENODEV;
6061e115a5SMichael Buesch }
6161e115a5SMichael Buesch 
6261e115a5SMichael Buesch int ssb_pci_switch_core(struct ssb_bus *bus,
6361e115a5SMichael Buesch 			struct ssb_device *dev)
6461e115a5SMichael Buesch {
6561e115a5SMichael Buesch 	int err;
6661e115a5SMichael Buesch 	unsigned long flags;
6761e115a5SMichael Buesch 
6861e115a5SMichael Buesch #if SSB_VERBOSE_PCICORESWITCH_DEBUG
6961e115a5SMichael Buesch 	ssb_printk(KERN_INFO PFX
7061e115a5SMichael Buesch 		   "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. */
17061e115a5SMichael Buesch #define SPOFF(offset)	(((offset) - SSB_SPROM_BASE) / 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 
18061e115a5SMichael Buesch 
18161e115a5SMichael Buesch static inline u8 ssb_crc8(u8 crc, u8 data)
18261e115a5SMichael Buesch {
18361e115a5SMichael Buesch 	/* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
18461e115a5SMichael Buesch 	static const u8 t[] = {
18561e115a5SMichael Buesch 		0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
18661e115a5SMichael Buesch 		0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
18761e115a5SMichael Buesch 		0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
18861e115a5SMichael Buesch 		0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
18961e115a5SMichael Buesch 		0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
19061e115a5SMichael Buesch 		0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
19161e115a5SMichael Buesch 		0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
19261e115a5SMichael Buesch 		0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
19361e115a5SMichael Buesch 		0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
19461e115a5SMichael Buesch 		0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
19561e115a5SMichael Buesch 		0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
19661e115a5SMichael Buesch 		0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
19761e115a5SMichael Buesch 		0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
19861e115a5SMichael Buesch 		0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
19961e115a5SMichael Buesch 		0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
20061e115a5SMichael Buesch 		0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
20161e115a5SMichael Buesch 		0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
20261e115a5SMichael Buesch 		0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
20361e115a5SMichael Buesch 		0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
20461e115a5SMichael Buesch 		0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
20561e115a5SMichael Buesch 		0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
20661e115a5SMichael Buesch 		0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
20761e115a5SMichael Buesch 		0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
20861e115a5SMichael Buesch 		0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
20961e115a5SMichael Buesch 		0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
21061e115a5SMichael Buesch 		0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
21161e115a5SMichael Buesch 		0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
21261e115a5SMichael Buesch 		0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
21361e115a5SMichael Buesch 		0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
21461e115a5SMichael Buesch 		0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
21561e115a5SMichael Buesch 		0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
21661e115a5SMichael Buesch 		0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
21761e115a5SMichael Buesch 	};
21861e115a5SMichael Buesch 	return t[crc ^ data];
21961e115a5SMichael Buesch }
22061e115a5SMichael Buesch 
221c272ef44SLarry Finger static u8 ssb_sprom_crc(const u16 *sprom, u16 size)
22261e115a5SMichael Buesch {
22361e115a5SMichael Buesch 	int word;
22461e115a5SMichael Buesch 	u8 crc = 0xFF;
22561e115a5SMichael Buesch 
226c272ef44SLarry Finger 	for (word = 0; word < size - 1; word++) {
22761e115a5SMichael Buesch 		crc = ssb_crc8(crc, sprom[word] & 0x00FF);
22861e115a5SMichael Buesch 		crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8);
22961e115a5SMichael Buesch 	}
230c272ef44SLarry Finger 	crc = ssb_crc8(crc, sprom[size - 1] & 0x00FF);
23161e115a5SMichael Buesch 	crc ^= 0xFF;
23261e115a5SMichael Buesch 
23361e115a5SMichael Buesch 	return crc;
23461e115a5SMichael Buesch }
23561e115a5SMichael Buesch 
236e7ec2e32SMichael Buesch static int sprom_check_crc(const u16 *sprom, size_t size)
23761e115a5SMichael Buesch {
23861e115a5SMichael Buesch 	u8 crc;
23961e115a5SMichael Buesch 	u8 expected_crc;
24061e115a5SMichael Buesch 	u16 tmp;
24161e115a5SMichael Buesch 
242c272ef44SLarry Finger 	crc = ssb_sprom_crc(sprom, size);
243c272ef44SLarry Finger 	tmp = sprom[size - 1] & SSB_SPROM_REVISION_CRC;
24461e115a5SMichael Buesch 	expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
24561e115a5SMichael Buesch 	if (crc != expected_crc)
24661e115a5SMichael Buesch 		return -EPROTO;
24761e115a5SMichael Buesch 
24861e115a5SMichael Buesch 	return 0;
24961e115a5SMichael Buesch }
25061e115a5SMichael Buesch 
251e7ec2e32SMichael Buesch static int sprom_do_read(struct ssb_bus *bus, u16 *sprom)
25261e115a5SMichael Buesch {
25361e115a5SMichael Buesch 	int i;
25461e115a5SMichael Buesch 
255c272ef44SLarry Finger 	for (i = 0; i < bus->sprom_size; i++)
256e861b98dSMichael Buesch 		sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2));
257e7ec2e32SMichael Buesch 
258e7ec2e32SMichael Buesch 	return 0;
25961e115a5SMichael Buesch }
26061e115a5SMichael Buesch 
26161e115a5SMichael Buesch static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
26261e115a5SMichael Buesch {
26361e115a5SMichael Buesch 	struct pci_dev *pdev = bus->host_pci;
26461e115a5SMichael Buesch 	int i, err;
26561e115a5SMichael Buesch 	u32 spromctl;
266c272ef44SLarry Finger 	u16 size = bus->sprom_size;
26761e115a5SMichael Buesch 
26861e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");
26961e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
27061e115a5SMichael Buesch 	if (err)
27161e115a5SMichael Buesch 		goto err_ctlreg;
27261e115a5SMichael Buesch 	spromctl |= SSB_SPROMCTL_WE;
27361e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
27461e115a5SMichael Buesch 	if (err)
27561e115a5SMichael Buesch 		goto err_ctlreg;
27661e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "[ 0%%");
27761e115a5SMichael Buesch 	msleep(500);
278c272ef44SLarry Finger 	for (i = 0; i < size; i++) {
279c272ef44SLarry Finger 		if (i == size / 4)
28061e115a5SMichael Buesch 			ssb_printk("25%%");
281c272ef44SLarry Finger 		else if (i == size / 2)
28261e115a5SMichael Buesch 			ssb_printk("50%%");
283c272ef44SLarry Finger 		else if (i == (size * 3) / 4)
28461e115a5SMichael Buesch 			ssb_printk("75%%");
28561e115a5SMichael Buesch 		else if (i % 2)
28661e115a5SMichael Buesch 			ssb_printk(".");
28761e115a5SMichael Buesch 		writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2));
28861e115a5SMichael Buesch 		mmiowb();
28961e115a5SMichael Buesch 		msleep(20);
29061e115a5SMichael Buesch 	}
29161e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
29261e115a5SMichael Buesch 	if (err)
29361e115a5SMichael Buesch 		goto err_ctlreg;
29461e115a5SMichael Buesch 	spromctl &= ~SSB_SPROMCTL_WE;
29561e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
29661e115a5SMichael Buesch 	if (err)
29761e115a5SMichael Buesch 		goto err_ctlreg;
29861e115a5SMichael Buesch 	msleep(500);
29961e115a5SMichael Buesch 	ssb_printk("100%% ]\n");
30061e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "SPROM written.\n");
30161e115a5SMichael Buesch 
30261e115a5SMichael Buesch 	return 0;
30361e115a5SMichael Buesch err_ctlreg:
30461e115a5SMichael Buesch 	ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n");
30561e115a5SMichael Buesch 	return err;
30661e115a5SMichael Buesch }
30761e115a5SMichael Buesch 
308e861b98dSMichael Buesch static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in,
309e861b98dSMichael Buesch 			       u16 mask, u16 shift)
310e861b98dSMichael Buesch {
311e861b98dSMichael Buesch 	u16 v;
312e861b98dSMichael Buesch 	u8 gain;
313e861b98dSMichael Buesch 
314e861b98dSMichael Buesch 	v = in[SPOFF(SSB_SPROM1_AGAIN)];
315e861b98dSMichael Buesch 	gain = (v & mask) >> shift;
316e861b98dSMichael Buesch 	if (gain == 0xFF)
317e861b98dSMichael Buesch 		gain = 2; /* If unset use 2dBm */
318e861b98dSMichael Buesch 	if (sprom_revision == 1) {
319e861b98dSMichael Buesch 		/* Convert to Q5.2 */
320e861b98dSMichael Buesch 		gain <<= 2;
321e861b98dSMichael Buesch 	} else {
322e861b98dSMichael Buesch 		/* Q5.2 Fractional part is stored in 0xC0 */
323e861b98dSMichael Buesch 		gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
324e861b98dSMichael Buesch 	}
325e861b98dSMichael Buesch 
326e861b98dSMichael Buesch 	return (s8)gain;
327e861b98dSMichael Buesch }
328e861b98dSMichael Buesch 
329c272ef44SLarry Finger static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
330c272ef44SLarry Finger {
331c272ef44SLarry Finger 	int i;
332c272ef44SLarry Finger 	u16 v;
333e861b98dSMichael Buesch 	s8 gain;
334c272ef44SLarry Finger 	u16 loc[3];
335c272ef44SLarry Finger 
33631ce12fbSLarry Finger 	if (out->revision == 3)			/* rev 3 moved MAC */
337c272ef44SLarry Finger 		loc[0] = SSB_SPROM3_IL0MAC;
33831ce12fbSLarry Finger 	else {
339c272ef44SLarry Finger 		loc[0] = SSB_SPROM1_IL0MAC;
340c272ef44SLarry Finger 		loc[1] = SSB_SPROM1_ET0MAC;
341c272ef44SLarry Finger 		loc[2] = SSB_SPROM1_ET1MAC;
342c272ef44SLarry Finger 	}
343c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
344c272ef44SLarry Finger 		v = in[SPOFF(loc[0]) + i];
345c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
346c272ef44SLarry Finger 	}
34731ce12fbSLarry Finger 	if (out->revision < 3) { 	/* only rev 1-2 have et0, et1 */
348c272ef44SLarry Finger 		for (i = 0; i < 3; i++) {
349c272ef44SLarry Finger 			v = in[SPOFF(loc[1]) + i];
350c272ef44SLarry Finger 			*(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
351c272ef44SLarry Finger 		}
352c272ef44SLarry Finger 		for (i = 0; i < 3; i++) {
353c272ef44SLarry Finger 			v = in[SPOFF(loc[2]) + i];
354c272ef44SLarry Finger 			*(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
355c272ef44SLarry Finger 		}
35631ce12fbSLarry Finger 	}
357c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
358c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
359c272ef44SLarry Finger 	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
360e861b98dSMichael Buesch 	SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
361e861b98dSMichael Buesch 	SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
362e861b98dSMichael Buesch 	SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
363c272ef44SLarry Finger 	SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
364c272ef44SLarry Finger 	     SSB_SPROM1_BINF_CCODE_SHIFT);
365e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
366e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTA_SHIFT);
367e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
368e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTBG_SHIFT);
369c272ef44SLarry Finger 	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
370c272ef44SLarry Finger 	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
371c272ef44SLarry Finger 	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
372c272ef44SLarry Finger 	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
373c272ef44SLarry Finger 	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
374c272ef44SLarry Finger 	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
375c272ef44SLarry Finger 	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
376c272ef44SLarry Finger 	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
377c272ef44SLarry Finger 	     SSB_SPROM1_GPIOA_P1_SHIFT);
378c272ef44SLarry Finger 	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
379c272ef44SLarry Finger 	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
380c272ef44SLarry Finger 	     SSB_SPROM1_GPIOB_P3_SHIFT);
381c272ef44SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
382c272ef44SLarry Finger 	     SSB_SPROM1_MAXPWR_A_SHIFT);
383c272ef44SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
384c272ef44SLarry Finger 	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
385c272ef44SLarry Finger 	     SSB_SPROM1_ITSSI_A_SHIFT);
386c272ef44SLarry Finger 	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
387c272ef44SLarry Finger 	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
388af4b7450SMichael Buesch 	if (out->revision >= 2)
389af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
390e861b98dSMichael Buesch 
391e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
392e861b98dSMichael Buesch 	gain = r123_extract_antgain(out->revision, in,
393e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_BG,
394c272ef44SLarry Finger 				    SSB_SPROM1_AGAIN_BG_SHIFT);
395e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a0 = gain;
396e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a1 = gain;
397e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a2 = gain;
398e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a3 = gain;
399e861b98dSMichael Buesch 	gain = r123_extract_antgain(out->revision, in,
400e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_A,
401e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_A_SHIFT);
402e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a0 = gain;
403e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a1 = gain;
404e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a2 = gain;
405e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a3 = gain;
406c272ef44SLarry Finger }
407c272ef44SLarry Finger 
408095f695cSLarry Finger static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
40961e115a5SMichael Buesch {
41061e115a5SMichael Buesch 	int i;
41161e115a5SMichael Buesch 	u16 v;
412095f695cSLarry Finger 	u16 il0mac_offset;
41361e115a5SMichael Buesch 
414095f695cSLarry Finger 	if (out->revision == 4)
415095f695cSLarry Finger 		il0mac_offset = SSB_SPROM4_IL0MAC;
416095f695cSLarry Finger 	else
417095f695cSLarry Finger 		il0mac_offset = SSB_SPROM5_IL0MAC;
41831ce12fbSLarry Finger 	/* extract the MAC address */
419c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
420095f695cSLarry Finger 		v = in[SPOFF(il0mac_offset) + i];
421c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
42261e115a5SMichael Buesch 	}
423c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
424c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
425c272ef44SLarry Finger 	     SSB_SPROM4_ETHPHY_ET1A_SHIFT);
426095f695cSLarry Finger 	if (out->revision == 4) {
427c272ef44SLarry Finger 		SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0);
428c272ef44SLarry Finger 		SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
429af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
430095f695cSLarry Finger 	} else {
431095f695cSLarry Finger 		SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0);
432095f695cSLarry Finger 		SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
433095f695cSLarry Finger 		SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
434095f695cSLarry Finger 	}
435e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
436e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_A_SHIFT);
437e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG,
438e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_BG_SHIFT);
439d3c319f9SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0);
440d3c319f9SLarry Finger 	SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG,
441d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_BG_SHIFT);
442d3c319f9SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0);
443d3c319f9SLarry Finger 	SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A,
444d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_A_SHIFT);
445095f695cSLarry Finger 	if (out->revision == 4) {
446d3c319f9SLarry Finger 		SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0);
447d3c319f9SLarry Finger 		SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1,
448d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOA_P1_SHIFT);
449d3c319f9SLarry Finger 		SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0);
450d3c319f9SLarry Finger 		SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3,
451d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOB_P3_SHIFT);
452095f695cSLarry Finger 	} else {
453095f695cSLarry Finger 		SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0);
454095f695cSLarry Finger 		SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1,
455095f695cSLarry Finger 		     SSB_SPROM5_GPIOA_P1_SHIFT);
456095f695cSLarry Finger 		SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0);
457095f695cSLarry Finger 		SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3,
458095f695cSLarry Finger 		     SSB_SPROM5_GPIOB_P3_SHIFT);
459095f695cSLarry Finger 	}
460e861b98dSMichael Buesch 
461e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
462e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01,
463e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT);
464e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01,
465e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT);
466e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23,
467e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT);
468e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23,
469e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT);
470e861b98dSMichael Buesch 	memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
471e861b98dSMichael Buesch 	       sizeof(out->antenna_gain.ghz5));
472e861b98dSMichael Buesch 
473c272ef44SLarry Finger 	/* TODO - get remaining rev 4 stuff needed */
47461e115a5SMichael Buesch }
47561e115a5SMichael Buesch 
4766b1c7c67SMichael Buesch static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
4776b1c7c67SMichael Buesch {
4786b1c7c67SMichael Buesch 	int i;
4796b1c7c67SMichael Buesch 	u16 v;
4806b1c7c67SMichael Buesch 
4816b1c7c67SMichael Buesch 	/* extract the MAC address */
4826b1c7c67SMichael Buesch 	for (i = 0; i < 3; i++) {
483f0ea6ce1SGábor Stefanik 		v = in[SPOFF(SSB_SPROM8_IL0MAC) + i];
4846b1c7c67SMichael Buesch 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
4856b1c7c67SMichael Buesch 	}
4866b1c7c67SMichael Buesch 	SPEX(country_code, SSB_SPROM8_CCODE, 0xFFFF, 0);
4876b1c7c67SMichael Buesch 	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
4886b1c7c67SMichael Buesch 	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
489f679056bSGábor Stefanik 	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
490f679056bSGábor Stefanik 	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
4916b1c7c67SMichael Buesch 	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
4926b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
4936b1c7c67SMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
4946b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
4956b1c7c67SMichael Buesch 	SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
4966b1c7c67SMichael Buesch 	SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
4976b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_BG_SHIFT);
4986b1c7c67SMichael Buesch 	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
4996b1c7c67SMichael Buesch 	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
5006b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_A_SHIFT);
501f679056bSGábor Stefanik 	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
502f679056bSGábor Stefanik 	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
503f679056bSGábor Stefanik 	     SSB_SPROM8_MAXP_AL_SHIFT);
5046b1c7c67SMichael Buesch 	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
5056b1c7c67SMichael Buesch 	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
5066b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOA_P1_SHIFT);
5076b1c7c67SMichael Buesch 	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
5086b1c7c67SMichael Buesch 	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
5096b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOB_P3_SHIFT);
510f679056bSGábor Stefanik 	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
511f679056bSGábor Stefanik 	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
512f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5G_SHIFT);
513f679056bSGábor Stefanik 	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
514f679056bSGábor Stefanik 	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
515f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5GH_SHIFT);
516f679056bSGábor Stefanik 	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
517f679056bSGábor Stefanik 	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
518f679056bSGábor Stefanik 	     SSB_SPROM8_RXPO5G_SHIFT);
519f679056bSGábor Stefanik 	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
520f679056bSGábor Stefanik 	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
521f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC2G_SHIFT);
522f679056bSGábor Stefanik 	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
523f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV2G_SHIFT);
524f679056bSGábor Stefanik 	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
525f679056bSGábor Stefanik 	     SSB_SPROM8_BXA2G_SHIFT);
526f679056bSGábor Stefanik 	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
527f679056bSGábor Stefanik 	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
528f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC5G_SHIFT);
529f679056bSGábor Stefanik 	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
530f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV5G_SHIFT);
531f679056bSGábor Stefanik 	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
532f679056bSGábor Stefanik 	     SSB_SPROM8_BXA5G_SHIFT);
533f679056bSGábor Stefanik 	SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
534f679056bSGábor Stefanik 	SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
535f679056bSGábor Stefanik 	SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
536f679056bSGábor Stefanik 	SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
537f679056bSGábor Stefanik 	SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
538f679056bSGábor Stefanik 	SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
539f679056bSGábor Stefanik 	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
540f679056bSGábor Stefanik 	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
541f679056bSGábor Stefanik 	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
542f679056bSGábor Stefanik 	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
543f679056bSGábor Stefanik 	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
544f679056bSGábor Stefanik 	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
545f679056bSGábor Stefanik 	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
546f679056bSGábor Stefanik 	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
547f679056bSGábor Stefanik 	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
548f679056bSGábor Stefanik 	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
549f679056bSGábor Stefanik 	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
5506b1c7c67SMichael Buesch 
5516b1c7c67SMichael Buesch 	/* Extract the antenna gain values. */
5526b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01,
5536b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
5546b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a1, SSB_SPROM8_AGAIN01,
5556b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
5566b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a2, SSB_SPROM8_AGAIN23,
5576b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
5586b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a3, SSB_SPROM8_AGAIN23,
5596b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
5606b1c7c67SMichael Buesch 	memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
5616b1c7c67SMichael Buesch 	       sizeof(out->antenna_gain.ghz5));
5626b1c7c67SMichael Buesch 
5636b1c7c67SMichael Buesch 	/* TODO - get remaining rev 8 stuff needed */
5646b1c7c67SMichael Buesch }
5656b1c7c67SMichael Buesch 
566c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
567c272ef44SLarry Finger 			 const u16 *in, u16 size)
56861e115a5SMichael Buesch {
56961e115a5SMichael Buesch 	memset(out, 0, sizeof(*out));
57061e115a5SMichael Buesch 
571c272ef44SLarry Finger 	out->revision = in[size - 1] & 0x00FF;
572e861b98dSMichael Buesch 	ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision);
57331ce12fbSLarry Finger 	memset(out->et0mac, 0xFF, 6);		/* preset et0 and et1 mac */
57431ce12fbSLarry Finger 	memset(out->et1mac, 0xFF, 6);
57561e115a5SMichael Buesch 	if ((bus->chip_id & 0xFF00) == 0x4400) {
57661e115a5SMichael Buesch 		/* Workaround: The BCM44XX chip has a stupid revision
57761e115a5SMichael Buesch 		 * number stored in the SPROM.
57861e115a5SMichael Buesch 		 * Always extract r1. */
579c272ef44SLarry Finger 		out->revision = 1;
580c272ef44SLarry Finger 		sprom_extract_r123(out, in);
581c272ef44SLarry Finger 	} else if (bus->chip_id == 0x4321) {
582c272ef44SLarry Finger 		/* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */
583c272ef44SLarry Finger 		out->revision = 4;
584095f695cSLarry Finger 		sprom_extract_r45(out, in);
58561e115a5SMichael Buesch 	} else {
5866b1c7c67SMichael Buesch 		switch (out->revision) {
5876b1c7c67SMichael Buesch 		case 1:
5886b1c7c67SMichael Buesch 		case 2:
5896b1c7c67SMichael Buesch 		case 3:
5906b1c7c67SMichael Buesch 			sprom_extract_r123(out, in);
5916b1c7c67SMichael Buesch 			break;
5926b1c7c67SMichael Buesch 		case 4:
5936b1c7c67SMichael Buesch 		case 5:
5946b1c7c67SMichael Buesch 			sprom_extract_r45(out, in);
5956b1c7c67SMichael Buesch 			break;
5966b1c7c67SMichael Buesch 		case 8:
5976b1c7c67SMichael Buesch 			sprom_extract_r8(out, in);
5986b1c7c67SMichael Buesch 			break;
5996b1c7c67SMichael Buesch 		default:
6006b1c7c67SMichael Buesch 			ssb_printk(KERN_WARNING PFX "Unsupported SPROM"
6016b1c7c67SMichael Buesch 				   "  revision %d detected. Will extract"
6026b1c7c67SMichael Buesch 				   " v1\n", out->revision);
603*cd559b36SLarry Finger 			out->revision = 1;
604c272ef44SLarry Finger 			sprom_extract_r123(out, in);
605c272ef44SLarry Finger 		}
60661e115a5SMichael Buesch 	}
60761e115a5SMichael Buesch 
6084503183aSLarry Finger 	if (out->boardflags_lo == 0xFFFF)
6094503183aSLarry Finger 		out->boardflags_lo = 0;  /* per specs */
6104503183aSLarry Finger 	if (out->boardflags_hi == 0xFFFF)
6114503183aSLarry Finger 		out->boardflags_hi = 0;  /* per specs */
6124503183aSLarry Finger 
61361e115a5SMichael Buesch 	return 0;
61461e115a5SMichael Buesch }
61561e115a5SMichael Buesch 
61661e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus,
61761e115a5SMichael Buesch 			     struct ssb_sprom *sprom)
61861e115a5SMichael Buesch {
619e79c1ba8SMichael Buesch 	const struct ssb_sprom *fallback;
62061e115a5SMichael Buesch 	int err = -ENOMEM;
62161e115a5SMichael Buesch 	u16 *buf;
62261e115a5SMichael Buesch 
623c272ef44SLarry Finger 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
62461e115a5SMichael Buesch 	if (!buf)
62561e115a5SMichael Buesch 		goto out;
626c272ef44SLarry Finger 	bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
62761e115a5SMichael Buesch 	sprom_do_read(bus, buf);
628c272ef44SLarry Finger 	err = sprom_check_crc(buf, bus->sprom_size);
62961e115a5SMichael Buesch 	if (err) {
6302afc4901SLarry.Finger@lwfinger.net 		/* try for a 440 byte SPROM - revision 4 and higher */
631c272ef44SLarry Finger 		kfree(buf);
632c272ef44SLarry Finger 		buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
633c272ef44SLarry Finger 			      GFP_KERNEL);
634c272ef44SLarry Finger 		if (!buf)
635c272ef44SLarry Finger 			goto out;
636c272ef44SLarry Finger 		bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
637c272ef44SLarry Finger 		sprom_do_read(bus, buf);
638c272ef44SLarry Finger 		err = sprom_check_crc(buf, bus->sprom_size);
639e79c1ba8SMichael Buesch 		if (err) {
640e79c1ba8SMichael Buesch 			/* All CRC attempts failed.
641e79c1ba8SMichael Buesch 			 * Maybe there is no SPROM on the device?
642e79c1ba8SMichael Buesch 			 * If we have a fallback, use that. */
643e79c1ba8SMichael Buesch 			fallback = ssb_get_fallback_sprom();
644e79c1ba8SMichael Buesch 			if (fallback) {
645e79c1ba8SMichael Buesch 				memcpy(sprom, fallback, sizeof(*sprom));
646e79c1ba8SMichael Buesch 				err = 0;
647e79c1ba8SMichael Buesch 				goto out_free;
648e79c1ba8SMichael Buesch 			}
649c272ef44SLarry Finger 			ssb_printk(KERN_WARNING PFX "WARNING: Invalid"
650c272ef44SLarry Finger 				   " SPROM CRC (corrupt SPROM)\n");
651c272ef44SLarry Finger 		}
652e79c1ba8SMichael Buesch 	}
653c272ef44SLarry Finger 	err = sprom_extract(bus, sprom, buf, bus->sprom_size);
65461e115a5SMichael Buesch 
655e79c1ba8SMichael Buesch out_free:
65661e115a5SMichael Buesch 	kfree(buf);
65761e115a5SMichael Buesch out:
65861e115a5SMichael Buesch 	return err;
65961e115a5SMichael Buesch }
66061e115a5SMichael Buesch 
66161e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
66261e115a5SMichael Buesch 				  struct ssb_boardinfo *bi)
66361e115a5SMichael Buesch {
66461e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
66561e115a5SMichael Buesch 			     &bi->vendor);
66661e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
66761e115a5SMichael Buesch 			     &bi->type);
66861e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
66961e115a5SMichael Buesch 			     &bi->rev);
67061e115a5SMichael Buesch }
67161e115a5SMichael Buesch 
67261e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus,
67361e115a5SMichael Buesch 			   struct ssb_init_invariants *iv)
67461e115a5SMichael Buesch {
67561e115a5SMichael Buesch 	int err;
67661e115a5SMichael Buesch 
67761e115a5SMichael Buesch 	err = ssb_pci_sprom_get(bus, &iv->sprom);
67861e115a5SMichael Buesch 	if (err)
67961e115a5SMichael Buesch 		goto out;
68061e115a5SMichael Buesch 	ssb_pci_get_boardinfo(bus, &iv->boardinfo);
68161e115a5SMichael Buesch 
68261e115a5SMichael Buesch out:
68361e115a5SMichael Buesch 	return err;
68461e115a5SMichael Buesch }
68561e115a5SMichael Buesch 
68661e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG
68761e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus)
68861e115a5SMichael Buesch {
68961e115a5SMichael Buesch 	if (likely(bus->powered_up))
69061e115a5SMichael Buesch 		return 0;
69161e115a5SMichael Buesch 
69261e115a5SMichael Buesch 	printk(KERN_ERR PFX "FATAL ERROR: Bus powered down "
69361e115a5SMichael Buesch 	       "while accessing PCI MMIO space\n");
69461e115a5SMichael Buesch 	if (bus->power_warn_count <= 10) {
69561e115a5SMichael Buesch 		bus->power_warn_count++;
69661e115a5SMichael Buesch 		dump_stack();
69761e115a5SMichael Buesch 	}
69861e115a5SMichael Buesch 
69961e115a5SMichael Buesch 	return -ENODEV;
70061e115a5SMichael Buesch }
70161e115a5SMichael Buesch #else /* DEBUG */
70261e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus)
70361e115a5SMichael Buesch {
70461e115a5SMichael Buesch 	return 0;
70561e115a5SMichael Buesch }
70661e115a5SMichael Buesch #endif /* DEBUG */
70761e115a5SMichael Buesch 
708ffc7689dSMichael Buesch static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset)
709ffc7689dSMichael Buesch {
710ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
711ffc7689dSMichael Buesch 
712ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
713ffc7689dSMichael Buesch 		return 0xFF;
714ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
715ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
716ffc7689dSMichael Buesch 			return 0xFF;
717ffc7689dSMichael Buesch 	}
718ffc7689dSMichael Buesch 	return ioread8(bus->mmio + offset);
719ffc7689dSMichael Buesch }
720ffc7689dSMichael Buesch 
72161e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
72261e115a5SMichael Buesch {
72361e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
72461e115a5SMichael Buesch 
72561e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
72661e115a5SMichael Buesch 		return 0xFFFF;
72761e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
72861e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
72961e115a5SMichael Buesch 			return 0xFFFF;
73061e115a5SMichael Buesch 	}
7314b402c65SMichael Buesch 	return ioread16(bus->mmio + offset);
73261e115a5SMichael Buesch }
73361e115a5SMichael Buesch 
73461e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
73561e115a5SMichael Buesch {
73661e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
73761e115a5SMichael Buesch 
73861e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
73961e115a5SMichael Buesch 		return 0xFFFFFFFF;
74061e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
74161e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
74261e115a5SMichael Buesch 			return 0xFFFFFFFF;
74361e115a5SMichael Buesch 	}
7444b402c65SMichael Buesch 	return ioread32(bus->mmio + offset);
74561e115a5SMichael Buesch }
74661e115a5SMichael Buesch 
747d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
748d625a29bSMichael Buesch static void ssb_pci_block_read(struct ssb_device *dev, void *buffer,
749d625a29bSMichael Buesch 			       size_t count, u16 offset, u8 reg_width)
750d625a29bSMichael Buesch {
751d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
752d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
753d625a29bSMichael Buesch 
754d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
755d625a29bSMichael Buesch 		goto error;
756d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
757d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
758d625a29bSMichael Buesch 			goto error;
759d625a29bSMichael Buesch 	}
760d625a29bSMichael Buesch 	switch (reg_width) {
761d625a29bSMichael Buesch 	case sizeof(u8):
762d625a29bSMichael Buesch 		ioread8_rep(addr, buffer, count);
763d625a29bSMichael Buesch 		break;
764d625a29bSMichael Buesch 	case sizeof(u16):
765d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
766d625a29bSMichael Buesch 		ioread16_rep(addr, buffer, count >> 1);
767d625a29bSMichael Buesch 		break;
768d625a29bSMichael Buesch 	case sizeof(u32):
769d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
770d625a29bSMichael Buesch 		ioread32_rep(addr, buffer, count >> 2);
771d625a29bSMichael Buesch 		break;
772d625a29bSMichael Buesch 	default:
773d625a29bSMichael Buesch 		SSB_WARN_ON(1);
774d625a29bSMichael Buesch 	}
775d625a29bSMichael Buesch 
776d625a29bSMichael Buesch 	return;
777d625a29bSMichael Buesch error:
778d625a29bSMichael Buesch 	memset(buffer, 0xFF, count);
779d625a29bSMichael Buesch }
780d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
781d625a29bSMichael Buesch 
782ffc7689dSMichael Buesch static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value)
783ffc7689dSMichael Buesch {
784ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
785ffc7689dSMichael Buesch 
786ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
787ffc7689dSMichael Buesch 		return;
788ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
789ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
790ffc7689dSMichael Buesch 			return;
791ffc7689dSMichael Buesch 	}
792ffc7689dSMichael Buesch 	iowrite8(value, bus->mmio + offset);
793ffc7689dSMichael Buesch }
794ffc7689dSMichael Buesch 
79561e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
79661e115a5SMichael Buesch {
79761e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
79861e115a5SMichael Buesch 
79961e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
80061e115a5SMichael Buesch 		return;
80161e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
80261e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
80361e115a5SMichael Buesch 			return;
80461e115a5SMichael Buesch 	}
8054b402c65SMichael Buesch 	iowrite16(value, bus->mmio + offset);
80661e115a5SMichael Buesch }
80761e115a5SMichael Buesch 
80861e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
80961e115a5SMichael Buesch {
81061e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
81161e115a5SMichael Buesch 
81261e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
81361e115a5SMichael Buesch 		return;
81461e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
81561e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
81661e115a5SMichael Buesch 			return;
81761e115a5SMichael Buesch 	}
8184b402c65SMichael Buesch 	iowrite32(value, bus->mmio + offset);
81961e115a5SMichael Buesch }
82061e115a5SMichael Buesch 
821d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
822d625a29bSMichael Buesch static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer,
823d625a29bSMichael Buesch 				size_t count, u16 offset, u8 reg_width)
824d625a29bSMichael Buesch {
825d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
826d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
827d625a29bSMichael Buesch 
828d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
829d625a29bSMichael Buesch 		return;
830d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
831d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
832d625a29bSMichael Buesch 			return;
833d625a29bSMichael Buesch 	}
834d625a29bSMichael Buesch 	switch (reg_width) {
835d625a29bSMichael Buesch 	case sizeof(u8):
836d625a29bSMichael Buesch 		iowrite8_rep(addr, buffer, count);
837d625a29bSMichael Buesch 		break;
838d625a29bSMichael Buesch 	case sizeof(u16):
839d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
840d625a29bSMichael Buesch 		iowrite16_rep(addr, buffer, count >> 1);
841d625a29bSMichael Buesch 		break;
842d625a29bSMichael Buesch 	case sizeof(u32):
843d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
844d625a29bSMichael Buesch 		iowrite32_rep(addr, buffer, count >> 2);
845d625a29bSMichael Buesch 		break;
846d625a29bSMichael Buesch 	default:
847d625a29bSMichael Buesch 		SSB_WARN_ON(1);
848d625a29bSMichael Buesch 	}
849d625a29bSMichael Buesch }
850d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
851d625a29bSMichael Buesch 
85261e115a5SMichael Buesch /* Not "static", as it's used in main.c */
85361e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = {
854ffc7689dSMichael Buesch 	.read8		= ssb_pci_read8,
85561e115a5SMichael Buesch 	.read16		= ssb_pci_read16,
85661e115a5SMichael Buesch 	.read32		= ssb_pci_read32,
857ffc7689dSMichael Buesch 	.write8		= ssb_pci_write8,
85861e115a5SMichael Buesch 	.write16	= ssb_pci_write16,
85961e115a5SMichael Buesch 	.write32	= ssb_pci_write32,
860d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
861d625a29bSMichael Buesch 	.block_read	= ssb_pci_block_read,
862d625a29bSMichael Buesch 	.block_write	= ssb_pci_block_write,
863d625a29bSMichael Buesch #endif
86461e115a5SMichael Buesch };
86561e115a5SMichael Buesch 
86661e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
86761e115a5SMichael Buesch 				       struct device_attribute *attr,
86861e115a5SMichael Buesch 				       char *buf)
86961e115a5SMichael Buesch {
87061e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
87161e115a5SMichael Buesch 	struct ssb_bus *bus;
87261e115a5SMichael Buesch 
87361e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
87461e115a5SMichael Buesch 	if (!bus)
875e7ec2e32SMichael Buesch 		return -ENODEV;
87661e115a5SMichael Buesch 
877e7ec2e32SMichael Buesch 	return ssb_attr_sprom_show(bus, buf, sprom_do_read);
87861e115a5SMichael Buesch }
87961e115a5SMichael Buesch 
88061e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
88161e115a5SMichael Buesch 					struct device_attribute *attr,
88261e115a5SMichael Buesch 					const char *buf, size_t count)
88361e115a5SMichael Buesch {
88461e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
88561e115a5SMichael Buesch 	struct ssb_bus *bus;
88661e115a5SMichael Buesch 
88761e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
88861e115a5SMichael Buesch 	if (!bus)
889e7ec2e32SMichael Buesch 		return -ENODEV;
89061e115a5SMichael Buesch 
891e7ec2e32SMichael Buesch 	return ssb_attr_sprom_store(bus, buf, count,
892e7ec2e32SMichael Buesch 				    sprom_check_crc, sprom_do_write);
89361e115a5SMichael Buesch }
89461e115a5SMichael Buesch 
89561e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600,
89661e115a5SMichael Buesch 		   ssb_pci_attr_sprom_show,
89761e115a5SMichael Buesch 		   ssb_pci_attr_sprom_store);
89861e115a5SMichael Buesch 
89961e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus)
90061e115a5SMichael Buesch {
90161e115a5SMichael Buesch 	struct pci_dev *pdev;
90261e115a5SMichael Buesch 
90361e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
90461e115a5SMichael Buesch 		return;
90561e115a5SMichael Buesch 
90661e115a5SMichael Buesch 	pdev = bus->host_pci;
90761e115a5SMichael Buesch 	device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
90861e115a5SMichael Buesch }
90961e115a5SMichael Buesch 
91061e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus)
91161e115a5SMichael Buesch {
91261e115a5SMichael Buesch 	struct pci_dev *pdev;
91361e115a5SMichael Buesch 	int err;
91461e115a5SMichael Buesch 
91561e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
91661e115a5SMichael Buesch 		return 0;
91761e115a5SMichael Buesch 
91861e115a5SMichael Buesch 	pdev = bus->host_pci;
919e7ec2e32SMichael Buesch 	mutex_init(&bus->sprom_mutex);
92061e115a5SMichael Buesch 	err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
92161e115a5SMichael Buesch 	if (err)
92261e115a5SMichael Buesch 		goto out;
92361e115a5SMichael Buesch 
92461e115a5SMichael Buesch out:
92561e115a5SMichael Buesch 	return err;
92661e115a5SMichael Buesch }
927