xref: /openbmc/linux/drivers/ssb/pci.c (revision da1fdb02d9200ff28b6f3a380d21930335fe5429)
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>
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:
5961e115a5SMichael Buesch 	ssb_printk(KERN_ERR PFX "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
7061e115a5SMichael Buesch 	ssb_printk(KERN_INFO PFX
7161e115a5SMichael Buesch 		   "Switching to %s core, index %d\n",
7261e115a5SMichael Buesch 		   ssb_core_name(dev->id.coreid),
7361e115a5SMichael Buesch 		   dev->core_index);
7461e115a5SMichael Buesch #endif
7561e115a5SMichael Buesch 
7661e115a5SMichael Buesch 	spin_lock_irqsave(&bus->bar_lock, flags);
7761e115a5SMichael Buesch 	err = ssb_pci_switch_coreidx(bus, dev->core_index);
7861e115a5SMichael Buesch 	if (!err)
7961e115a5SMichael Buesch 		bus->mapped_device = dev;
8061e115a5SMichael Buesch 	spin_unlock_irqrestore(&bus->bar_lock, flags);
8161e115a5SMichael Buesch 
8261e115a5SMichael Buesch 	return err;
8361e115a5SMichael Buesch }
8461e115a5SMichael Buesch 
8561e115a5SMichael Buesch /* Enable/disable the on board crystal oscillator and/or PLL. */
8661e115a5SMichael Buesch int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on)
8761e115a5SMichael Buesch {
8861e115a5SMichael Buesch 	int err;
8961e115a5SMichael Buesch 	u32 in, out, outenable;
9061e115a5SMichael Buesch 	u16 pci_status;
9161e115a5SMichael Buesch 
9261e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
9361e115a5SMichael Buesch 		return 0;
9461e115a5SMichael Buesch 
9561e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in);
9661e115a5SMichael Buesch 	if (err)
9761e115a5SMichael Buesch 		goto err_pci;
9861e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out);
9961e115a5SMichael Buesch 	if (err)
10061e115a5SMichael Buesch 		goto err_pci;
10161e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable);
10261e115a5SMichael Buesch 	if (err)
10361e115a5SMichael Buesch 		goto err_pci;
10461e115a5SMichael Buesch 
10561e115a5SMichael Buesch 	outenable |= what;
10661e115a5SMichael Buesch 
10761e115a5SMichael Buesch 	if (turn_on) {
10861e115a5SMichael Buesch 		/* Avoid glitching the clock if GPRS is already using it.
10961e115a5SMichael Buesch 		 * We can't actually read the state of the PLLPD so we infer it
11061e115a5SMichael Buesch 		 * by the value of XTAL_PU which *is* readable via gpioin.
11161e115a5SMichael Buesch 		 */
11261e115a5SMichael Buesch 		if (!(in & SSB_GPIO_XTAL)) {
11361e115a5SMichael Buesch 			if (what & SSB_GPIO_XTAL) {
11461e115a5SMichael Buesch 				/* Turn the crystal on */
11561e115a5SMichael Buesch 				out |= SSB_GPIO_XTAL;
11661e115a5SMichael Buesch 				if (what & SSB_GPIO_PLL)
11761e115a5SMichael Buesch 					out |= SSB_GPIO_PLL;
11861e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
11961e115a5SMichael Buesch 				if (err)
12061e115a5SMichael Buesch 					goto err_pci;
12161e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE,
12261e115a5SMichael Buesch 							     outenable);
12361e115a5SMichael Buesch 				if (err)
12461e115a5SMichael Buesch 					goto err_pci;
12561e115a5SMichael Buesch 				msleep(1);
12661e115a5SMichael Buesch 			}
12761e115a5SMichael Buesch 			if (what & SSB_GPIO_PLL) {
12861e115a5SMichael Buesch 				/* Turn the PLL on */
12961e115a5SMichael Buesch 				out &= ~SSB_GPIO_PLL;
13061e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
13161e115a5SMichael Buesch 				if (err)
13261e115a5SMichael Buesch 					goto err_pci;
13361e115a5SMichael Buesch 				msleep(5);
13461e115a5SMichael Buesch 			}
13561e115a5SMichael Buesch 		}
13661e115a5SMichael Buesch 
13761e115a5SMichael Buesch 		err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status);
13861e115a5SMichael Buesch 		if (err)
13961e115a5SMichael Buesch 			goto err_pci;
14061e115a5SMichael Buesch 		pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT;
14161e115a5SMichael Buesch 		err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status);
14261e115a5SMichael Buesch 		if (err)
14361e115a5SMichael Buesch 			goto err_pci;
14461e115a5SMichael Buesch 	} else {
14561e115a5SMichael Buesch 		if (what & SSB_GPIO_XTAL) {
14661e115a5SMichael Buesch 			/* Turn the crystal off */
14761e115a5SMichael Buesch 			out &= ~SSB_GPIO_XTAL;
14861e115a5SMichael Buesch 		}
14961e115a5SMichael Buesch 		if (what & SSB_GPIO_PLL) {
15061e115a5SMichael Buesch 			/* Turn the PLL off */
15161e115a5SMichael Buesch 			out |= SSB_GPIO_PLL;
15261e115a5SMichael Buesch 		}
15361e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
15461e115a5SMichael Buesch 		if (err)
15561e115a5SMichael Buesch 			goto err_pci;
15661e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable);
15761e115a5SMichael Buesch 		if (err)
15861e115a5SMichael Buesch 			goto err_pci;
15961e115a5SMichael Buesch 	}
16061e115a5SMichael Buesch 
16161e115a5SMichael Buesch out:
16261e115a5SMichael Buesch 	return err;
16361e115a5SMichael Buesch 
16461e115a5SMichael Buesch err_pci:
16561e115a5SMichael Buesch 	printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n");
16661e115a5SMichael Buesch 	err = -EBUSY;
16761e115a5SMichael Buesch 	goto out;
16861e115a5SMichael Buesch }
16961e115a5SMichael Buesch 
17061e115a5SMichael Buesch /* Get the word-offset for a SSB_SPROM_XXX define. */
1710a182fd8SRafał Miłecki #define SPOFF(offset)	((offset) / sizeof(u16))
17261e115a5SMichael Buesch /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
173f679056bSGábor Stefanik #define SPEX16(_outvar, _offset, _mask, _shift)	\
17461e115a5SMichael Buesch 	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
175f679056bSGábor Stefanik #define SPEX32(_outvar, _offset, _mask, _shift)	\
176f679056bSGábor Stefanik 	out->_outvar = ((((u32)in[SPOFF((_offset)+2)] << 16 | \
177f679056bSGábor Stefanik 			   in[SPOFF(_offset)]) & (_mask)) >> (_shift))
178f679056bSGábor Stefanik #define SPEX(_outvar, _offset, _mask, _shift) \
179f679056bSGábor Stefanik 	SPEX16(_outvar, _offset, _mask, _shift)
180f679056bSGábor Stefanik 
18161e115a5SMichael Buesch 
18261e115a5SMichael Buesch static inline u8 ssb_crc8(u8 crc, u8 data)
18361e115a5SMichael Buesch {
18461e115a5SMichael Buesch 	/* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
18561e115a5SMichael Buesch 	static const u8 t[] = {
18661e115a5SMichael Buesch 		0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
18761e115a5SMichael Buesch 		0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
18861e115a5SMichael Buesch 		0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
18961e115a5SMichael Buesch 		0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
19061e115a5SMichael Buesch 		0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
19161e115a5SMichael Buesch 		0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
19261e115a5SMichael Buesch 		0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
19361e115a5SMichael Buesch 		0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
19461e115a5SMichael Buesch 		0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
19561e115a5SMichael Buesch 		0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
19661e115a5SMichael Buesch 		0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
19761e115a5SMichael Buesch 		0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
19861e115a5SMichael Buesch 		0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
19961e115a5SMichael Buesch 		0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
20061e115a5SMichael Buesch 		0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
20161e115a5SMichael Buesch 		0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
20261e115a5SMichael Buesch 		0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
20361e115a5SMichael Buesch 		0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
20461e115a5SMichael Buesch 		0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
20561e115a5SMichael Buesch 		0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
20661e115a5SMichael Buesch 		0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
20761e115a5SMichael Buesch 		0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
20861e115a5SMichael Buesch 		0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
20961e115a5SMichael Buesch 		0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
21061e115a5SMichael Buesch 		0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
21161e115a5SMichael Buesch 		0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
21261e115a5SMichael Buesch 		0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
21361e115a5SMichael Buesch 		0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
21461e115a5SMichael Buesch 		0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
21561e115a5SMichael Buesch 		0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
21661e115a5SMichael Buesch 		0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
21761e115a5SMichael Buesch 		0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
21861e115a5SMichael Buesch 	};
21961e115a5SMichael Buesch 	return t[crc ^ data];
22061e115a5SMichael Buesch }
22161e115a5SMichael Buesch 
222c272ef44SLarry Finger static u8 ssb_sprom_crc(const u16 *sprom, u16 size)
22361e115a5SMichael Buesch {
22461e115a5SMichael Buesch 	int word;
22561e115a5SMichael Buesch 	u8 crc = 0xFF;
22661e115a5SMichael Buesch 
227c272ef44SLarry Finger 	for (word = 0; word < size - 1; word++) {
22861e115a5SMichael Buesch 		crc = ssb_crc8(crc, sprom[word] & 0x00FF);
22961e115a5SMichael Buesch 		crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8);
23061e115a5SMichael Buesch 	}
231c272ef44SLarry Finger 	crc = ssb_crc8(crc, sprom[size - 1] & 0x00FF);
23261e115a5SMichael Buesch 	crc ^= 0xFF;
23361e115a5SMichael Buesch 
23461e115a5SMichael Buesch 	return crc;
23561e115a5SMichael Buesch }
23661e115a5SMichael Buesch 
237e7ec2e32SMichael Buesch static int sprom_check_crc(const u16 *sprom, size_t size)
23861e115a5SMichael Buesch {
23961e115a5SMichael Buesch 	u8 crc;
24061e115a5SMichael Buesch 	u8 expected_crc;
24161e115a5SMichael Buesch 	u16 tmp;
24261e115a5SMichael Buesch 
243c272ef44SLarry Finger 	crc = ssb_sprom_crc(sprom, size);
244c272ef44SLarry Finger 	tmp = sprom[size - 1] & SSB_SPROM_REVISION_CRC;
24561e115a5SMichael Buesch 	expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
24661e115a5SMichael Buesch 	if (crc != expected_crc)
24761e115a5SMichael Buesch 		return -EPROTO;
24861e115a5SMichael Buesch 
24961e115a5SMichael Buesch 	return 0;
25061e115a5SMichael Buesch }
25161e115a5SMichael Buesch 
252e7ec2e32SMichael Buesch static int sprom_do_read(struct ssb_bus *bus, u16 *sprom)
25361e115a5SMichael Buesch {
25461e115a5SMichael Buesch 	int i;
25561e115a5SMichael Buesch 
256c272ef44SLarry Finger 	for (i = 0; i < bus->sprom_size; i++)
257ea2db495SRafał Miłecki 		sprom[i] = ioread16(bus->mmio + bus->sprom_offset + (i * 2));
258e7ec2e32SMichael Buesch 
259e7ec2e32SMichael Buesch 	return 0;
26061e115a5SMichael Buesch }
26161e115a5SMichael Buesch 
26261e115a5SMichael Buesch static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
26361e115a5SMichael Buesch {
26461e115a5SMichael Buesch 	struct pci_dev *pdev = bus->host_pci;
26561e115a5SMichael Buesch 	int i, err;
26661e115a5SMichael Buesch 	u32 spromctl;
267c272ef44SLarry Finger 	u16 size = bus->sprom_size;
26861e115a5SMichael Buesch 
26961e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");
27061e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
27161e115a5SMichael Buesch 	if (err)
27261e115a5SMichael Buesch 		goto err_ctlreg;
27361e115a5SMichael Buesch 	spromctl |= SSB_SPROMCTL_WE;
27461e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
27561e115a5SMichael Buesch 	if (err)
27661e115a5SMichael Buesch 		goto err_ctlreg;
27761e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "[ 0%%");
27861e115a5SMichael Buesch 	msleep(500);
279c272ef44SLarry Finger 	for (i = 0; i < size; i++) {
280c272ef44SLarry Finger 		if (i == size / 4)
28161e115a5SMichael Buesch 			ssb_printk("25%%");
282c272ef44SLarry Finger 		else if (i == size / 2)
28361e115a5SMichael Buesch 			ssb_printk("50%%");
284c272ef44SLarry Finger 		else if (i == (size * 3) / 4)
28561e115a5SMichael Buesch 			ssb_printk("75%%");
28661e115a5SMichael Buesch 		else if (i % 2)
28761e115a5SMichael Buesch 			ssb_printk(".");
288ea2db495SRafał Miłecki 		writew(sprom[i], bus->mmio + bus->sprom_offset + (i * 2));
28961e115a5SMichael Buesch 		mmiowb();
29061e115a5SMichael Buesch 		msleep(20);
29161e115a5SMichael Buesch 	}
29261e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
29361e115a5SMichael Buesch 	if (err)
29461e115a5SMichael Buesch 		goto err_ctlreg;
29561e115a5SMichael Buesch 	spromctl &= ~SSB_SPROMCTL_WE;
29661e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
29761e115a5SMichael Buesch 	if (err)
29861e115a5SMichael Buesch 		goto err_ctlreg;
29961e115a5SMichael Buesch 	msleep(500);
30061e115a5SMichael Buesch 	ssb_printk("100%% ]\n");
30161e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "SPROM written.\n");
30261e115a5SMichael Buesch 
30361e115a5SMichael Buesch 	return 0;
30461e115a5SMichael Buesch err_ctlreg:
30561e115a5SMichael Buesch 	ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n");
30661e115a5SMichael Buesch 	return err;
30761e115a5SMichael Buesch }
30861e115a5SMichael Buesch 
309e861b98dSMichael Buesch static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in,
310e861b98dSMichael Buesch 			       u16 mask, u16 shift)
311e861b98dSMichael Buesch {
312e861b98dSMichael Buesch 	u16 v;
313e861b98dSMichael Buesch 	u8 gain;
314e861b98dSMichael Buesch 
315e861b98dSMichael Buesch 	v = in[SPOFF(SSB_SPROM1_AGAIN)];
316e861b98dSMichael Buesch 	gain = (v & mask) >> shift;
317e861b98dSMichael Buesch 	if (gain == 0xFF)
318e861b98dSMichael Buesch 		gain = 2; /* If unset use 2dBm */
319e861b98dSMichael Buesch 	if (sprom_revision == 1) {
320e861b98dSMichael Buesch 		/* Convert to Q5.2 */
321e861b98dSMichael Buesch 		gain <<= 2;
322e861b98dSMichael Buesch 	} else {
323e861b98dSMichael Buesch 		/* Q5.2 Fractional part is stored in 0xC0 */
324e861b98dSMichael Buesch 		gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
325e861b98dSMichael Buesch 	}
326e861b98dSMichael Buesch 
327e861b98dSMichael Buesch 	return (s8)gain;
328e861b98dSMichael Buesch }
329e861b98dSMichael Buesch 
330c272ef44SLarry Finger static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
331c272ef44SLarry Finger {
332c272ef44SLarry Finger 	int i;
333c272ef44SLarry Finger 	u16 v;
334e861b98dSMichael Buesch 	s8 gain;
335c272ef44SLarry Finger 	u16 loc[3];
336c272ef44SLarry Finger 
33731ce12fbSLarry Finger 	if (out->revision == 3)			/* rev 3 moved MAC */
338c272ef44SLarry Finger 		loc[0] = SSB_SPROM3_IL0MAC;
33931ce12fbSLarry Finger 	else {
340c272ef44SLarry Finger 		loc[0] = SSB_SPROM1_IL0MAC;
341c272ef44SLarry Finger 		loc[1] = SSB_SPROM1_ET0MAC;
342c272ef44SLarry Finger 		loc[2] = SSB_SPROM1_ET1MAC;
343c272ef44SLarry Finger 	}
344c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
345c272ef44SLarry Finger 		v = in[SPOFF(loc[0]) + i];
346c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
347c272ef44SLarry Finger 	}
34831ce12fbSLarry Finger 	if (out->revision < 3) { 	/* only rev 1-2 have et0, et1 */
349c272ef44SLarry Finger 		for (i = 0; i < 3; i++) {
350c272ef44SLarry Finger 			v = in[SPOFF(loc[1]) + i];
351c272ef44SLarry Finger 			*(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
352c272ef44SLarry Finger 		}
353c272ef44SLarry Finger 		for (i = 0; i < 3; i++) {
354c272ef44SLarry Finger 			v = in[SPOFF(loc[2]) + i];
355c272ef44SLarry Finger 			*(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
356c272ef44SLarry Finger 		}
35731ce12fbSLarry Finger 	}
358c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
359c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
360c272ef44SLarry Finger 	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
361e861b98dSMichael Buesch 	SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
362e861b98dSMichael Buesch 	SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
363e861b98dSMichael Buesch 	SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
364c272ef44SLarry Finger 	SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
365c272ef44SLarry Finger 	     SSB_SPROM1_BINF_CCODE_SHIFT);
366e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
367e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTA_SHIFT);
368e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
369e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTBG_SHIFT);
370c272ef44SLarry Finger 	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
371c272ef44SLarry Finger 	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
372c272ef44SLarry Finger 	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
373c272ef44SLarry Finger 	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
374c272ef44SLarry Finger 	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
375c272ef44SLarry Finger 	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
376c272ef44SLarry Finger 	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
377c272ef44SLarry Finger 	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
378c272ef44SLarry Finger 	     SSB_SPROM1_GPIOA_P1_SHIFT);
379c272ef44SLarry Finger 	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
380c272ef44SLarry Finger 	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
381c272ef44SLarry Finger 	     SSB_SPROM1_GPIOB_P3_SHIFT);
382c272ef44SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
383c272ef44SLarry Finger 	     SSB_SPROM1_MAXPWR_A_SHIFT);
384c272ef44SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
385c272ef44SLarry Finger 	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
386c272ef44SLarry Finger 	     SSB_SPROM1_ITSSI_A_SHIFT);
387c272ef44SLarry Finger 	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
388c272ef44SLarry Finger 	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
389af4b7450SMichael Buesch 	if (out->revision >= 2)
390af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
391e861b98dSMichael Buesch 
392e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
393e861b98dSMichael Buesch 	gain = r123_extract_antgain(out->revision, in,
394e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_BG,
395c272ef44SLarry Finger 				    SSB_SPROM1_AGAIN_BG_SHIFT);
396e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a0 = gain;
397e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a1 = gain;
398e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a2 = gain;
399e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a3 = gain;
400e861b98dSMichael Buesch 	gain = r123_extract_antgain(out->revision, in,
401e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_A,
402e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_A_SHIFT);
403e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a0 = gain;
404e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a1 = gain;
405e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a2 = gain;
406e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a3 = gain;
407c272ef44SLarry Finger }
408c272ef44SLarry Finger 
409095f695cSLarry Finger static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
41061e115a5SMichael Buesch {
41161e115a5SMichael Buesch 	int i;
41261e115a5SMichael Buesch 	u16 v;
413095f695cSLarry Finger 	u16 il0mac_offset;
41461e115a5SMichael Buesch 
415095f695cSLarry Finger 	if (out->revision == 4)
416095f695cSLarry Finger 		il0mac_offset = SSB_SPROM4_IL0MAC;
417095f695cSLarry Finger 	else
418095f695cSLarry Finger 		il0mac_offset = SSB_SPROM5_IL0MAC;
41931ce12fbSLarry Finger 	/* extract the MAC address */
420c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
421095f695cSLarry Finger 		v = in[SPOFF(il0mac_offset) + i];
422c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
42361e115a5SMichael Buesch 	}
424c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
425c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
426c272ef44SLarry Finger 	     SSB_SPROM4_ETHPHY_ET1A_SHIFT);
427095f695cSLarry Finger 	if (out->revision == 4) {
428c272ef44SLarry Finger 		SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0);
429c272ef44SLarry Finger 		SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
430af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
431095f695cSLarry Finger 	} else {
432095f695cSLarry Finger 		SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0);
433095f695cSLarry Finger 		SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
434095f695cSLarry Finger 		SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
435095f695cSLarry Finger 	}
436e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
437e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_A_SHIFT);
438e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG,
439e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_BG_SHIFT);
440d3c319f9SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0);
441d3c319f9SLarry Finger 	SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG,
442d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_BG_SHIFT);
443d3c319f9SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0);
444d3c319f9SLarry Finger 	SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A,
445d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_A_SHIFT);
446095f695cSLarry Finger 	if (out->revision == 4) {
447d3c319f9SLarry Finger 		SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0);
448d3c319f9SLarry Finger 		SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1,
449d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOA_P1_SHIFT);
450d3c319f9SLarry Finger 		SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0);
451d3c319f9SLarry Finger 		SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3,
452d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOB_P3_SHIFT);
453095f695cSLarry Finger 	} else {
454095f695cSLarry Finger 		SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0);
455095f695cSLarry Finger 		SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1,
456095f695cSLarry Finger 		     SSB_SPROM5_GPIOA_P1_SHIFT);
457095f695cSLarry Finger 		SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0);
458095f695cSLarry Finger 		SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3,
459095f695cSLarry Finger 		     SSB_SPROM5_GPIOB_P3_SHIFT);
460095f695cSLarry Finger 	}
461e861b98dSMichael Buesch 
462e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
463e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01,
464e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT);
465e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01,
466e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT);
467e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23,
468e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT);
469e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23,
470e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT);
471e861b98dSMichael Buesch 	memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
472e861b98dSMichael Buesch 	       sizeof(out->antenna_gain.ghz5));
473e861b98dSMichael Buesch 
474c272ef44SLarry Finger 	/* TODO - get remaining rev 4 stuff needed */
47561e115a5SMichael Buesch }
47661e115a5SMichael Buesch 
4776b1c7c67SMichael Buesch static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
4786b1c7c67SMichael Buesch {
4796b1c7c67SMichael Buesch 	int i;
4806b1c7c67SMichael Buesch 	u16 v;
4816b1c7c67SMichael Buesch 
4826b1c7c67SMichael Buesch 	/* extract the MAC address */
4836b1c7c67SMichael Buesch 	for (i = 0; i < 3; i++) {
484f0ea6ce1SGábor Stefanik 		v = in[SPOFF(SSB_SPROM8_IL0MAC) + i];
4856b1c7c67SMichael Buesch 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
4866b1c7c67SMichael Buesch 	}
4876b1c7c67SMichael Buesch 	SPEX(country_code, SSB_SPROM8_CCODE, 0xFFFF, 0);
4886b1c7c67SMichael Buesch 	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
4896b1c7c67SMichael Buesch 	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
490f679056bSGábor Stefanik 	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
491f679056bSGábor Stefanik 	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
4926b1c7c67SMichael Buesch 	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
4936b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
4946b1c7c67SMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
4956b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
4966b1c7c67SMichael Buesch 	SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
4976b1c7c67SMichael Buesch 	SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
4986b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_BG_SHIFT);
4996b1c7c67SMichael Buesch 	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
5006b1c7c67SMichael Buesch 	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
5016b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_A_SHIFT);
502f679056bSGábor Stefanik 	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
503f679056bSGábor Stefanik 	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
504f679056bSGábor Stefanik 	     SSB_SPROM8_MAXP_AL_SHIFT);
5056b1c7c67SMichael Buesch 	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
5066b1c7c67SMichael Buesch 	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
5076b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOA_P1_SHIFT);
5086b1c7c67SMichael Buesch 	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
5096b1c7c67SMichael Buesch 	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
5106b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOB_P3_SHIFT);
511f679056bSGábor Stefanik 	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
512f679056bSGábor Stefanik 	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
513f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5G_SHIFT);
514f679056bSGábor Stefanik 	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
515f679056bSGábor Stefanik 	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
516f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5GH_SHIFT);
517f679056bSGábor Stefanik 	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
518f679056bSGábor Stefanik 	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
519f679056bSGábor Stefanik 	     SSB_SPROM8_RXPO5G_SHIFT);
520f679056bSGábor Stefanik 	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
521f679056bSGábor Stefanik 	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
522f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC2G_SHIFT);
523f679056bSGábor Stefanik 	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
524f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV2G_SHIFT);
525f679056bSGábor Stefanik 	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
526f679056bSGábor Stefanik 	     SSB_SPROM8_BXA2G_SHIFT);
527f679056bSGábor Stefanik 	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
528f679056bSGábor Stefanik 	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
529f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC5G_SHIFT);
530f679056bSGábor Stefanik 	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
531f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV5G_SHIFT);
532f679056bSGábor Stefanik 	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
533f679056bSGábor Stefanik 	     SSB_SPROM8_BXA5G_SHIFT);
534f679056bSGábor Stefanik 	SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
535f679056bSGábor Stefanik 	SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
536f679056bSGábor Stefanik 	SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
537f679056bSGábor Stefanik 	SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
538f679056bSGábor Stefanik 	SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
539f679056bSGábor Stefanik 	SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
540f679056bSGábor Stefanik 	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
541f679056bSGábor Stefanik 	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
542f679056bSGábor Stefanik 	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
543f679056bSGábor Stefanik 	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
544f679056bSGábor Stefanik 	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
545f679056bSGábor Stefanik 	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
546f679056bSGábor Stefanik 	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
547f679056bSGábor Stefanik 	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
548f679056bSGábor Stefanik 	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
549f679056bSGábor Stefanik 	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
550f679056bSGábor Stefanik 	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
5516b1c7c67SMichael Buesch 
5526b1c7c67SMichael Buesch 	/* Extract the antenna gain values. */
5536b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01,
5546b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
5556b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a1, SSB_SPROM8_AGAIN01,
5566b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
5576b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a2, SSB_SPROM8_AGAIN23,
5586b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
5596b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a3, SSB_SPROM8_AGAIN23,
5606b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
5616b1c7c67SMichael Buesch 	memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
5626b1c7c67SMichael Buesch 	       sizeof(out->antenna_gain.ghz5));
5636b1c7c67SMichael Buesch 
5646b1c7c67SMichael Buesch 	/* TODO - get remaining rev 8 stuff needed */
5656b1c7c67SMichael Buesch }
5666b1c7c67SMichael Buesch 
567c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
568c272ef44SLarry Finger 			 const u16 *in, u16 size)
56961e115a5SMichael Buesch {
57061e115a5SMichael Buesch 	memset(out, 0, sizeof(*out));
57161e115a5SMichael Buesch 
572c272ef44SLarry Finger 	out->revision = in[size - 1] & 0x00FF;
573e861b98dSMichael Buesch 	ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision);
57431ce12fbSLarry Finger 	memset(out->et0mac, 0xFF, 6);		/* preset et0 and et1 mac */
57531ce12fbSLarry Finger 	memset(out->et1mac, 0xFF, 6);
57661e115a5SMichael Buesch 	if ((bus->chip_id & 0xFF00) == 0x4400) {
57761e115a5SMichael Buesch 		/* Workaround: The BCM44XX chip has a stupid revision
57861e115a5SMichael Buesch 		 * number stored in the SPROM.
57961e115a5SMichael Buesch 		 * Always extract r1. */
580c272ef44SLarry Finger 		out->revision = 1;
581c272ef44SLarry Finger 		sprom_extract_r123(out, in);
582c272ef44SLarry Finger 	} else if (bus->chip_id == 0x4321) {
583c272ef44SLarry Finger 		/* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */
584c272ef44SLarry Finger 		out->revision = 4;
585095f695cSLarry Finger 		sprom_extract_r45(out, in);
58661e115a5SMichael Buesch 	} else {
5876b1c7c67SMichael Buesch 		switch (out->revision) {
5886b1c7c67SMichael Buesch 		case 1:
5896b1c7c67SMichael Buesch 		case 2:
5906b1c7c67SMichael Buesch 		case 3:
5916b1c7c67SMichael Buesch 			sprom_extract_r123(out, in);
5926b1c7c67SMichael Buesch 			break;
5936b1c7c67SMichael Buesch 		case 4:
5946b1c7c67SMichael Buesch 		case 5:
5956b1c7c67SMichael Buesch 			sprom_extract_r45(out, in);
5966b1c7c67SMichael Buesch 			break;
5976b1c7c67SMichael Buesch 		case 8:
5986b1c7c67SMichael Buesch 			sprom_extract_r8(out, in);
5996b1c7c67SMichael Buesch 			break;
6006b1c7c67SMichael Buesch 		default:
6016b1c7c67SMichael Buesch 			ssb_printk(KERN_WARNING PFX "Unsupported SPROM"
6026b1c7c67SMichael Buesch 				   "  revision %d detected. Will extract"
6036b1c7c67SMichael Buesch 				   " v1\n", out->revision);
604cd559b36SLarry Finger 			out->revision = 1;
605c272ef44SLarry Finger 			sprom_extract_r123(out, in);
606c272ef44SLarry Finger 		}
60761e115a5SMichael Buesch 	}
60861e115a5SMichael Buesch 
6094503183aSLarry Finger 	if (out->boardflags_lo == 0xFFFF)
6104503183aSLarry Finger 		out->boardflags_lo = 0;  /* per specs */
6114503183aSLarry Finger 	if (out->boardflags_hi == 0xFFFF)
6124503183aSLarry Finger 		out->boardflags_hi = 0;  /* per specs */
6134503183aSLarry Finger 
61461e115a5SMichael Buesch 	return 0;
61561e115a5SMichael Buesch }
61661e115a5SMichael Buesch 
61761e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus,
61861e115a5SMichael Buesch 			     struct ssb_sprom *sprom)
61961e115a5SMichael Buesch {
620e79c1ba8SMichael Buesch 	const struct ssb_sprom *fallback;
62161e115a5SMichael Buesch 	int err = -ENOMEM;
62261e115a5SMichael Buesch 	u16 *buf;
62361e115a5SMichael Buesch 
624d53cdbb9SJohn W. Linville 	if (!ssb_is_sprom_available(bus)) {
625d53cdbb9SJohn W. Linville 		ssb_printk(KERN_ERR PFX "No SPROM available!\n");
626d53cdbb9SJohn W. Linville 		return -ENODEV;
627d53cdbb9SJohn W. Linville 	}
628*da1fdb02SChristoph Fritz 	if (bus->chipco.dev) {	/* can be unavailible! */
629ea2db495SRafał Miłecki 		bus->sprom_offset = (bus->chipco.dev->id.revision < 31) ?
630ea2db495SRafał Miłecki 			SSB_SPROM_BASE1 : SSB_SPROM_BASE31;
631*da1fdb02SChristoph Fritz 	} else {
632*da1fdb02SChristoph Fritz 		bus->sprom_offset = SSB_SPROM_BASE1;
633*da1fdb02SChristoph Fritz 	}
634ea2db495SRafał Miłecki 
635c272ef44SLarry Finger 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
63661e115a5SMichael Buesch 	if (!buf)
63761e115a5SMichael Buesch 		goto out;
638c272ef44SLarry Finger 	bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
63961e115a5SMichael Buesch 	sprom_do_read(bus, buf);
640c272ef44SLarry Finger 	err = sprom_check_crc(buf, bus->sprom_size);
64161e115a5SMichael Buesch 	if (err) {
6422afc4901SLarry.Finger@lwfinger.net 		/* try for a 440 byte SPROM - revision 4 and higher */
643c272ef44SLarry Finger 		kfree(buf);
644c272ef44SLarry Finger 		buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
645c272ef44SLarry Finger 			      GFP_KERNEL);
646c272ef44SLarry Finger 		if (!buf)
647c272ef44SLarry Finger 			goto out;
648c272ef44SLarry Finger 		bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
649c272ef44SLarry Finger 		sprom_do_read(bus, buf);
650c272ef44SLarry Finger 		err = sprom_check_crc(buf, bus->sprom_size);
651e79c1ba8SMichael Buesch 		if (err) {
652e79c1ba8SMichael Buesch 			/* All CRC attempts failed.
653e79c1ba8SMichael Buesch 			 * Maybe there is no SPROM on the device?
654e79c1ba8SMichael Buesch 			 * If we have a fallback, use that. */
655e79c1ba8SMichael Buesch 			fallback = ssb_get_fallback_sprom();
656e79c1ba8SMichael Buesch 			if (fallback) {
657e79c1ba8SMichael Buesch 				memcpy(sprom, fallback, sizeof(*sprom));
658e79c1ba8SMichael Buesch 				err = 0;
659e79c1ba8SMichael Buesch 				goto out_free;
660e79c1ba8SMichael Buesch 			}
661c272ef44SLarry Finger 			ssb_printk(KERN_WARNING PFX "WARNING: Invalid"
662c272ef44SLarry Finger 				   " SPROM CRC (corrupt SPROM)\n");
663c272ef44SLarry Finger 		}
664e79c1ba8SMichael Buesch 	}
665c272ef44SLarry Finger 	err = sprom_extract(bus, sprom, buf, bus->sprom_size);
66661e115a5SMichael Buesch 
667e79c1ba8SMichael Buesch out_free:
66861e115a5SMichael Buesch 	kfree(buf);
66961e115a5SMichael Buesch out:
67061e115a5SMichael Buesch 	return err;
67161e115a5SMichael Buesch }
67261e115a5SMichael Buesch 
67361e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
67461e115a5SMichael Buesch 				  struct ssb_boardinfo *bi)
67561e115a5SMichael Buesch {
67661e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
67761e115a5SMichael Buesch 			     &bi->vendor);
67861e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
67961e115a5SMichael Buesch 			     &bi->type);
68061e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
68161e115a5SMichael Buesch 			     &bi->rev);
68261e115a5SMichael Buesch }
68361e115a5SMichael Buesch 
68461e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus,
68561e115a5SMichael Buesch 			   struct ssb_init_invariants *iv)
68661e115a5SMichael Buesch {
68761e115a5SMichael Buesch 	int err;
68861e115a5SMichael Buesch 
68961e115a5SMichael Buesch 	err = ssb_pci_sprom_get(bus, &iv->sprom);
69061e115a5SMichael Buesch 	if (err)
69161e115a5SMichael Buesch 		goto out;
69261e115a5SMichael Buesch 	ssb_pci_get_boardinfo(bus, &iv->boardinfo);
69361e115a5SMichael Buesch 
69461e115a5SMichael Buesch out:
69561e115a5SMichael Buesch 	return err;
69661e115a5SMichael Buesch }
69761e115a5SMichael Buesch 
69861e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG
69961e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus)
70061e115a5SMichael Buesch {
70161e115a5SMichael Buesch 	if (likely(bus->powered_up))
70261e115a5SMichael Buesch 		return 0;
70361e115a5SMichael Buesch 
70461e115a5SMichael Buesch 	printk(KERN_ERR PFX "FATAL ERROR: Bus powered down "
70561e115a5SMichael Buesch 	       "while accessing PCI MMIO space\n");
70661e115a5SMichael Buesch 	if (bus->power_warn_count <= 10) {
70761e115a5SMichael Buesch 		bus->power_warn_count++;
70861e115a5SMichael Buesch 		dump_stack();
70961e115a5SMichael Buesch 	}
71061e115a5SMichael Buesch 
71161e115a5SMichael Buesch 	return -ENODEV;
71261e115a5SMichael Buesch }
71361e115a5SMichael Buesch #else /* DEBUG */
71461e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus)
71561e115a5SMichael Buesch {
71661e115a5SMichael Buesch 	return 0;
71761e115a5SMichael Buesch }
71861e115a5SMichael Buesch #endif /* DEBUG */
71961e115a5SMichael Buesch 
720ffc7689dSMichael Buesch static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset)
721ffc7689dSMichael Buesch {
722ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
723ffc7689dSMichael Buesch 
724ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
725ffc7689dSMichael Buesch 		return 0xFF;
726ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
727ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
728ffc7689dSMichael Buesch 			return 0xFF;
729ffc7689dSMichael Buesch 	}
730ffc7689dSMichael Buesch 	return ioread8(bus->mmio + offset);
731ffc7689dSMichael Buesch }
732ffc7689dSMichael Buesch 
73361e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
73461e115a5SMichael Buesch {
73561e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
73661e115a5SMichael Buesch 
73761e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
73861e115a5SMichael Buesch 		return 0xFFFF;
73961e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
74061e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
74161e115a5SMichael Buesch 			return 0xFFFF;
74261e115a5SMichael Buesch 	}
7434b402c65SMichael Buesch 	return ioread16(bus->mmio + offset);
74461e115a5SMichael Buesch }
74561e115a5SMichael Buesch 
74661e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
74761e115a5SMichael Buesch {
74861e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
74961e115a5SMichael Buesch 
75061e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
75161e115a5SMichael Buesch 		return 0xFFFFFFFF;
75261e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
75361e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
75461e115a5SMichael Buesch 			return 0xFFFFFFFF;
75561e115a5SMichael Buesch 	}
7564b402c65SMichael Buesch 	return ioread32(bus->mmio + offset);
75761e115a5SMichael Buesch }
75861e115a5SMichael Buesch 
759d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
760d625a29bSMichael Buesch static void ssb_pci_block_read(struct ssb_device *dev, void *buffer,
761d625a29bSMichael Buesch 			       size_t count, u16 offset, u8 reg_width)
762d625a29bSMichael Buesch {
763d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
764d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
765d625a29bSMichael Buesch 
766d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
767d625a29bSMichael Buesch 		goto error;
768d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
769d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
770d625a29bSMichael Buesch 			goto error;
771d625a29bSMichael Buesch 	}
772d625a29bSMichael Buesch 	switch (reg_width) {
773d625a29bSMichael Buesch 	case sizeof(u8):
774d625a29bSMichael Buesch 		ioread8_rep(addr, buffer, count);
775d625a29bSMichael Buesch 		break;
776d625a29bSMichael Buesch 	case sizeof(u16):
777d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
778d625a29bSMichael Buesch 		ioread16_rep(addr, buffer, count >> 1);
779d625a29bSMichael Buesch 		break;
780d625a29bSMichael Buesch 	case sizeof(u32):
781d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
782d625a29bSMichael Buesch 		ioread32_rep(addr, buffer, count >> 2);
783d625a29bSMichael Buesch 		break;
784d625a29bSMichael Buesch 	default:
785d625a29bSMichael Buesch 		SSB_WARN_ON(1);
786d625a29bSMichael Buesch 	}
787d625a29bSMichael Buesch 
788d625a29bSMichael Buesch 	return;
789d625a29bSMichael Buesch error:
790d625a29bSMichael Buesch 	memset(buffer, 0xFF, count);
791d625a29bSMichael Buesch }
792d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
793d625a29bSMichael Buesch 
794ffc7689dSMichael Buesch static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value)
795ffc7689dSMichael Buesch {
796ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
797ffc7689dSMichael Buesch 
798ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
799ffc7689dSMichael Buesch 		return;
800ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
801ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
802ffc7689dSMichael Buesch 			return;
803ffc7689dSMichael Buesch 	}
804ffc7689dSMichael Buesch 	iowrite8(value, bus->mmio + offset);
805ffc7689dSMichael Buesch }
806ffc7689dSMichael Buesch 
80761e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
80861e115a5SMichael Buesch {
80961e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
81061e115a5SMichael Buesch 
81161e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
81261e115a5SMichael Buesch 		return;
81361e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
81461e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
81561e115a5SMichael Buesch 			return;
81661e115a5SMichael Buesch 	}
8174b402c65SMichael Buesch 	iowrite16(value, bus->mmio + offset);
81861e115a5SMichael Buesch }
81961e115a5SMichael Buesch 
82061e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
82161e115a5SMichael Buesch {
82261e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
82361e115a5SMichael Buesch 
82461e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
82561e115a5SMichael Buesch 		return;
82661e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
82761e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
82861e115a5SMichael Buesch 			return;
82961e115a5SMichael Buesch 	}
8304b402c65SMichael Buesch 	iowrite32(value, bus->mmio + offset);
83161e115a5SMichael Buesch }
83261e115a5SMichael Buesch 
833d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
834d625a29bSMichael Buesch static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer,
835d625a29bSMichael Buesch 				size_t count, u16 offset, u8 reg_width)
836d625a29bSMichael Buesch {
837d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
838d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
839d625a29bSMichael Buesch 
840d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
841d625a29bSMichael Buesch 		return;
842d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
843d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
844d625a29bSMichael Buesch 			return;
845d625a29bSMichael Buesch 	}
846d625a29bSMichael Buesch 	switch (reg_width) {
847d625a29bSMichael Buesch 	case sizeof(u8):
848d625a29bSMichael Buesch 		iowrite8_rep(addr, buffer, count);
849d625a29bSMichael Buesch 		break;
850d625a29bSMichael Buesch 	case sizeof(u16):
851d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
852d625a29bSMichael Buesch 		iowrite16_rep(addr, buffer, count >> 1);
853d625a29bSMichael Buesch 		break;
854d625a29bSMichael Buesch 	case sizeof(u32):
855d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
856d625a29bSMichael Buesch 		iowrite32_rep(addr, buffer, count >> 2);
857d625a29bSMichael Buesch 		break;
858d625a29bSMichael Buesch 	default:
859d625a29bSMichael Buesch 		SSB_WARN_ON(1);
860d625a29bSMichael Buesch 	}
861d625a29bSMichael Buesch }
862d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
863d625a29bSMichael Buesch 
86461e115a5SMichael Buesch /* Not "static", as it's used in main.c */
86561e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = {
866ffc7689dSMichael Buesch 	.read8		= ssb_pci_read8,
86761e115a5SMichael Buesch 	.read16		= ssb_pci_read16,
86861e115a5SMichael Buesch 	.read32		= ssb_pci_read32,
869ffc7689dSMichael Buesch 	.write8		= ssb_pci_write8,
87061e115a5SMichael Buesch 	.write16	= ssb_pci_write16,
87161e115a5SMichael Buesch 	.write32	= ssb_pci_write32,
872d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
873d625a29bSMichael Buesch 	.block_read	= ssb_pci_block_read,
874d625a29bSMichael Buesch 	.block_write	= ssb_pci_block_write,
875d625a29bSMichael Buesch #endif
87661e115a5SMichael Buesch };
87761e115a5SMichael Buesch 
87861e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
87961e115a5SMichael Buesch 				       struct device_attribute *attr,
88061e115a5SMichael Buesch 				       char *buf)
88161e115a5SMichael Buesch {
88261e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
88361e115a5SMichael Buesch 	struct ssb_bus *bus;
88461e115a5SMichael Buesch 
88561e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
88661e115a5SMichael Buesch 	if (!bus)
887e7ec2e32SMichael Buesch 		return -ENODEV;
88861e115a5SMichael Buesch 
889e7ec2e32SMichael Buesch 	return ssb_attr_sprom_show(bus, buf, sprom_do_read);
89061e115a5SMichael Buesch }
89161e115a5SMichael Buesch 
89261e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
89361e115a5SMichael Buesch 					struct device_attribute *attr,
89461e115a5SMichael Buesch 					const char *buf, size_t count)
89561e115a5SMichael Buesch {
89661e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
89761e115a5SMichael Buesch 	struct ssb_bus *bus;
89861e115a5SMichael Buesch 
89961e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
90061e115a5SMichael Buesch 	if (!bus)
901e7ec2e32SMichael Buesch 		return -ENODEV;
90261e115a5SMichael Buesch 
903e7ec2e32SMichael Buesch 	return ssb_attr_sprom_store(bus, buf, count,
904e7ec2e32SMichael Buesch 				    sprom_check_crc, sprom_do_write);
90561e115a5SMichael Buesch }
90661e115a5SMichael Buesch 
90761e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600,
90861e115a5SMichael Buesch 		   ssb_pci_attr_sprom_show,
90961e115a5SMichael Buesch 		   ssb_pci_attr_sprom_store);
91061e115a5SMichael Buesch 
91161e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus)
91261e115a5SMichael Buesch {
91361e115a5SMichael Buesch 	struct pci_dev *pdev;
91461e115a5SMichael Buesch 
91561e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
91661e115a5SMichael Buesch 		return;
91761e115a5SMichael Buesch 
91861e115a5SMichael Buesch 	pdev = bus->host_pci;
91961e115a5SMichael Buesch 	device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
92061e115a5SMichael Buesch }
92161e115a5SMichael Buesch 
92261e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus)
92361e115a5SMichael Buesch {
92461e115a5SMichael Buesch 	struct pci_dev *pdev;
92561e115a5SMichael Buesch 	int err;
92661e115a5SMichael Buesch 
92761e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
92861e115a5SMichael Buesch 		return 0;
92961e115a5SMichael Buesch 
93061e115a5SMichael Buesch 	pdev = bus->host_pci;
931e7ec2e32SMichael Buesch 	mutex_init(&bus->sprom_mutex);
93261e115a5SMichael Buesch 	err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
93361e115a5SMichael Buesch 	if (err)
93461e115a5SMichael Buesch 		goto out;
93561e115a5SMichael Buesch 
93661e115a5SMichael Buesch out:
93761e115a5SMichael Buesch 	return err;
93861e115a5SMichael Buesch }
939