xref: /openbmc/linux/drivers/ssb/pci.c (revision b0f70292053a0f68f406564a721a7a3f2d66b44f)
161e115a5SMichael Buesch /*
261e115a5SMichael Buesch  * Sonics Silicon Backplane PCI-Hostbus related functions.
361e115a5SMichael Buesch  *
4eb032b98SMichael Büsch  * Copyright (C) 2005-2006 Michael Buesch <m@bues.ch>
561e115a5SMichael Buesch  * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
661e115a5SMichael Buesch  * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
761e115a5SMichael Buesch  * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
861e115a5SMichael Buesch  * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
961e115a5SMichael Buesch  *
1061e115a5SMichael Buesch  * Derived from the Broadcom 4400 device driver.
1161e115a5SMichael Buesch  * Copyright (C) 2002 David S. Miller (davem@redhat.com)
1261e115a5SMichael Buesch  * Fixed by Pekka Pietikainen (pp@ee.oulu.fi)
1361e115a5SMichael Buesch  * Copyright (C) 2006 Broadcom Corporation.
1461e115a5SMichael Buesch  *
1561e115a5SMichael Buesch  * Licensed under the GNU/GPL. See COPYING for details.
1661e115a5SMichael Buesch  */
1761e115a5SMichael Buesch 
1861e115a5SMichael Buesch #include <linux/ssb/ssb.h>
1961e115a5SMichael Buesch #include <linux/ssb/ssb_regs.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
2161e115a5SMichael Buesch #include <linux/pci.h>
2261e115a5SMichael Buesch #include <linux/delay.h>
2361e115a5SMichael Buesch 
2461e115a5SMichael Buesch #include "ssb_private.h"
2561e115a5SMichael Buesch 
2661e115a5SMichael Buesch 
2761e115a5SMichael Buesch /* Define the following to 1 to enable a printk on each coreswitch. */
2861e115a5SMichael Buesch #define SSB_VERBOSE_PCICORESWITCH_DEBUG		0
2961e115a5SMichael Buesch 
3061e115a5SMichael Buesch 
3161e115a5SMichael Buesch /* Lowlevel coreswitching */
3261e115a5SMichael Buesch int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
3361e115a5SMichael Buesch {
3461e115a5SMichael Buesch 	int err;
3561e115a5SMichael Buesch 	int attempts = 0;
3661e115a5SMichael Buesch 	u32 cur_core;
3761e115a5SMichael Buesch 
3861e115a5SMichael Buesch 	while (1) {
3961e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN,
4061e115a5SMichael Buesch 					     (coreidx * SSB_CORE_SIZE)
4161e115a5SMichael Buesch 					     + SSB_ENUM_BASE);
4261e115a5SMichael Buesch 		if (err)
4361e115a5SMichael Buesch 			goto error;
4461e115a5SMichael Buesch 		err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN,
4561e115a5SMichael Buesch 					    &cur_core);
4661e115a5SMichael Buesch 		if (err)
4761e115a5SMichael Buesch 			goto error;
4861e115a5SMichael Buesch 		cur_core = (cur_core - SSB_ENUM_BASE)
4961e115a5SMichael Buesch 			   / SSB_CORE_SIZE;
5061e115a5SMichael Buesch 		if (cur_core == coreidx)
5161e115a5SMichael Buesch 			break;
5261e115a5SMichael Buesch 
5361e115a5SMichael Buesch 		if (attempts++ > SSB_BAR0_MAX_RETRIES)
5461e115a5SMichael Buesch 			goto error;
5561e115a5SMichael Buesch 		udelay(10);
5661e115a5SMichael Buesch 	}
5761e115a5SMichael Buesch 	return 0;
5861e115a5SMichael Buesch error:
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 
409172c69a4SRafał Miłecki /* Revs 4 5 and 8 have partially shared layout */
410172c69a4SRafał Miłecki static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in)
411172c69a4SRafał Miłecki {
412172c69a4SRafał Miłecki 	SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01,
413172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G0, SSB_SPROM4_TXPID2G0_SHIFT);
414172c69a4SRafał Miłecki 	SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01,
415172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G1, SSB_SPROM4_TXPID2G1_SHIFT);
416172c69a4SRafał Miłecki 	SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23,
417172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G2, SSB_SPROM4_TXPID2G2_SHIFT);
418172c69a4SRafał Miłecki 	SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23,
419172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID2G3, SSB_SPROM4_TXPID2G3_SHIFT);
420172c69a4SRafał Miłecki 
421172c69a4SRafał Miłecki 	SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01,
422172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL0, SSB_SPROM4_TXPID5GL0_SHIFT);
423172c69a4SRafał Miłecki 	SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01,
424172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL1, SSB_SPROM4_TXPID5GL1_SHIFT);
425172c69a4SRafał Miłecki 	SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23,
426172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL2, SSB_SPROM4_TXPID5GL2_SHIFT);
427172c69a4SRafał Miłecki 	SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23,
428172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GL3, SSB_SPROM4_TXPID5GL3_SHIFT);
429172c69a4SRafał Miłecki 
430172c69a4SRafał Miłecki 	SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01,
431172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G0, SSB_SPROM4_TXPID5G0_SHIFT);
432172c69a4SRafał Miłecki 	SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01,
433172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G1, SSB_SPROM4_TXPID5G1_SHIFT);
434172c69a4SRafał Miłecki 	SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23,
435172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G2, SSB_SPROM4_TXPID5G2_SHIFT);
436172c69a4SRafał Miłecki 	SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23,
437172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5G3, SSB_SPROM4_TXPID5G3_SHIFT);
438172c69a4SRafał Miłecki 
439172c69a4SRafał Miłecki 	SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01,
440172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH0, SSB_SPROM4_TXPID5GH0_SHIFT);
441172c69a4SRafał Miłecki 	SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01,
442172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH1, SSB_SPROM4_TXPID5GH1_SHIFT);
443172c69a4SRafał Miłecki 	SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23,
444172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH2, SSB_SPROM4_TXPID5GH2_SHIFT);
445172c69a4SRafał Miłecki 	SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23,
446172c69a4SRafał Miłecki 	     SSB_SPROM4_TXPID5GH3, SSB_SPROM4_TXPID5GH3_SHIFT);
447172c69a4SRafał Miłecki }
448172c69a4SRafał Miłecki 
449095f695cSLarry Finger static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
45061e115a5SMichael Buesch {
45161e115a5SMichael Buesch 	int i;
45261e115a5SMichael Buesch 	u16 v;
453095f695cSLarry Finger 	u16 il0mac_offset;
45461e115a5SMichael Buesch 
455095f695cSLarry Finger 	if (out->revision == 4)
456095f695cSLarry Finger 		il0mac_offset = SSB_SPROM4_IL0MAC;
457095f695cSLarry Finger 	else
458095f695cSLarry Finger 		il0mac_offset = SSB_SPROM5_IL0MAC;
45931ce12fbSLarry Finger 	/* extract the MAC address */
460c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
461095f695cSLarry Finger 		v = in[SPOFF(il0mac_offset) + i];
462c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
46361e115a5SMichael Buesch 	}
464c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
465c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
466c272ef44SLarry Finger 	     SSB_SPROM4_ETHPHY_ET1A_SHIFT);
467095f695cSLarry Finger 	if (out->revision == 4) {
468c272ef44SLarry Finger 		SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0);
469c272ef44SLarry Finger 		SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
470af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
4716d1d4ea4SRafał Miłecki 		SPEX(boardflags2_lo, SSB_SPROM4_BFL2LO, 0xFFFF, 0);
4726d1d4ea4SRafał Miłecki 		SPEX(boardflags2_hi, SSB_SPROM4_BFL2HI, 0xFFFF, 0);
473095f695cSLarry Finger 	} else {
474095f695cSLarry Finger 		SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0);
475095f695cSLarry Finger 		SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
476095f695cSLarry Finger 		SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
4776d1d4ea4SRafał Miłecki 		SPEX(boardflags2_lo, SSB_SPROM5_BFL2LO, 0xFFFF, 0);
4786d1d4ea4SRafał Miłecki 		SPEX(boardflags2_hi, SSB_SPROM5_BFL2HI, 0xFFFF, 0);
479095f695cSLarry Finger 	}
480e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
481e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_A_SHIFT);
482e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG,
483e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_BG_SHIFT);
484d3c319f9SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0);
485d3c319f9SLarry Finger 	SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG,
486d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_BG_SHIFT);
487d3c319f9SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0);
488d3c319f9SLarry Finger 	SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A,
489d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_A_SHIFT);
490095f695cSLarry Finger 	if (out->revision == 4) {
491d3c319f9SLarry Finger 		SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0);
492d3c319f9SLarry Finger 		SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1,
493d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOA_P1_SHIFT);
494d3c319f9SLarry Finger 		SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0);
495d3c319f9SLarry Finger 		SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3,
496d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOB_P3_SHIFT);
497095f695cSLarry Finger 	} else {
498095f695cSLarry Finger 		SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0);
499095f695cSLarry Finger 		SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1,
500095f695cSLarry Finger 		     SSB_SPROM5_GPIOA_P1_SHIFT);
501095f695cSLarry Finger 		SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0);
502095f695cSLarry Finger 		SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3,
503095f695cSLarry Finger 		     SSB_SPROM5_GPIOB_P3_SHIFT);
504095f695cSLarry Finger 	}
505e861b98dSMichael Buesch 
506e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
507e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01,
508e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT);
509e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01,
510e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT);
511e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23,
512e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT);
513e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23,
514e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT);
515e861b98dSMichael Buesch 	memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
516e861b98dSMichael Buesch 	       sizeof(out->antenna_gain.ghz5));
517e861b98dSMichael Buesch 
518172c69a4SRafał Miłecki 	sprom_extract_r458(out, in);
519172c69a4SRafał Miłecki 
520c272ef44SLarry Finger 	/* TODO - get remaining rev 4 stuff needed */
52161e115a5SMichael Buesch }
52261e115a5SMichael Buesch 
5236b1c7c67SMichael Buesch static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in)
5246b1c7c67SMichael Buesch {
5256b1c7c67SMichael Buesch 	int i;
526*b0f70292SRafał Miłecki 	u16 v, o;
527*b0f70292SRafał Miłecki 	u16 pwr_info_offset[] = {
528*b0f70292SRafał Miłecki 		SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1,
529*b0f70292SRafał Miłecki 		SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3
530*b0f70292SRafał Miłecki 	};
531*b0f70292SRafał Miłecki 	BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) !=
532*b0f70292SRafał Miłecki 			ARRAY_SIZE(out->core_pwr_info));
5336b1c7c67SMichael Buesch 
5346b1c7c67SMichael Buesch 	/* extract the MAC address */
5356b1c7c67SMichael Buesch 	for (i = 0; i < 3; i++) {
536f0ea6ce1SGábor Stefanik 		v = in[SPOFF(SSB_SPROM8_IL0MAC) + i];
5376b1c7c67SMichael Buesch 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
5386b1c7c67SMichael Buesch 	}
5396b1c7c67SMichael Buesch 	SPEX(country_code, SSB_SPROM8_CCODE, 0xFFFF, 0);
5406b1c7c67SMichael Buesch 	SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0);
5416b1c7c67SMichael Buesch 	SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0);
542f679056bSGábor Stefanik 	SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, 0xFFFF, 0);
543f679056bSGábor Stefanik 	SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, 0xFFFF, 0);
5446b1c7c67SMichael Buesch 	SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
5456b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_A_SHIFT);
5466b1c7c67SMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
5476b1c7c67SMichael Buesch 	     SSB_SPROM8_ANTAVAIL_BG_SHIFT);
5486b1c7c67SMichael Buesch 	SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
5496b1c7c67SMichael Buesch 	SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
5506b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_BG_SHIFT);
5516b1c7c67SMichael Buesch 	SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
5526b1c7c67SMichael Buesch 	SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
5536b1c7c67SMichael Buesch 	     SSB_SPROM8_ITSSI_A_SHIFT);
554f679056bSGábor Stefanik 	SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
555f679056bSGábor Stefanik 	SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
556f679056bSGábor Stefanik 	     SSB_SPROM8_MAXP_AL_SHIFT);
5576b1c7c67SMichael Buesch 	SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
5586b1c7c67SMichael Buesch 	SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
5596b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOA_P1_SHIFT);
5606b1c7c67SMichael Buesch 	SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
5616b1c7c67SMichael Buesch 	SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
5626b1c7c67SMichael Buesch 	     SSB_SPROM8_GPIOB_P3_SHIFT);
563f679056bSGábor Stefanik 	SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
564f679056bSGábor Stefanik 	SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
565f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5G_SHIFT);
566f679056bSGábor Stefanik 	SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
567f679056bSGábor Stefanik 	SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
568f679056bSGábor Stefanik 	     SSB_SPROM8_TRI5GH_SHIFT);
569f679056bSGábor Stefanik 	SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G, 0);
570f679056bSGábor Stefanik 	SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
571f679056bSGábor Stefanik 	     SSB_SPROM8_RXPO5G_SHIFT);
572f679056bSGábor Stefanik 	SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
573f679056bSGábor Stefanik 	SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
574f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC2G_SHIFT);
575f679056bSGábor Stefanik 	SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
576f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV2G_SHIFT);
577f679056bSGábor Stefanik 	SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
578f679056bSGábor Stefanik 	     SSB_SPROM8_BXA2G_SHIFT);
579f679056bSGábor Stefanik 	SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
580f679056bSGábor Stefanik 	SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
581f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISMC5G_SHIFT);
582f679056bSGábor Stefanik 	SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
583f679056bSGábor Stefanik 	     SSB_SPROM8_RSSISAV5G_SHIFT);
584f679056bSGábor Stefanik 	SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
585f679056bSGábor Stefanik 	     SSB_SPROM8_BXA5G_SHIFT);
586f679056bSGábor Stefanik 	SPEX(pa0b0, SSB_SPROM8_PA0B0, 0xFFFF, 0);
587f679056bSGábor Stefanik 	SPEX(pa0b1, SSB_SPROM8_PA0B1, 0xFFFF, 0);
588f679056bSGábor Stefanik 	SPEX(pa0b2, SSB_SPROM8_PA0B2, 0xFFFF, 0);
589f679056bSGábor Stefanik 	SPEX(pa1b0, SSB_SPROM8_PA1B0, 0xFFFF, 0);
590f679056bSGábor Stefanik 	SPEX(pa1b1, SSB_SPROM8_PA1B1, 0xFFFF, 0);
591f679056bSGábor Stefanik 	SPEX(pa1b2, SSB_SPROM8_PA1B2, 0xFFFF, 0);
592f679056bSGábor Stefanik 	SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, 0xFFFF, 0);
593f679056bSGábor Stefanik 	SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, 0xFFFF, 0);
594f679056bSGábor Stefanik 	SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, 0xFFFF, 0);
595f679056bSGábor Stefanik 	SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, 0xFFFF, 0);
596f679056bSGábor Stefanik 	SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, 0xFFFF, 0);
597f679056bSGábor Stefanik 	SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, 0xFFFF, 0);
598f679056bSGábor Stefanik 	SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, 0xFFFF, 0);
599f679056bSGábor Stefanik 	SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, 0xFFFFFFFF, 0);
600f679056bSGábor Stefanik 	SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, 0xFFFFFFFF, 0);
601f679056bSGábor Stefanik 	SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, 0xFFFFFFFF, 0);
602f679056bSGábor Stefanik 	SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0);
6036b1c7c67SMichael Buesch 
6046b1c7c67SMichael Buesch 	/* Extract the antenna gain values. */
6056b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01,
6066b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
6076b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a1, SSB_SPROM8_AGAIN01,
6086b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
6096b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a2, SSB_SPROM8_AGAIN23,
6106b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
6116b1c7c67SMichael Buesch 	SPEX(antenna_gain.ghz24.a3, SSB_SPROM8_AGAIN23,
6126b1c7c67SMichael Buesch 	     SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
6136b1c7c67SMichael Buesch 	memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
6146b1c7c67SMichael Buesch 	       sizeof(out->antenna_gain.ghz5));
6156b1c7c67SMichael Buesch 
616*b0f70292SRafał Miłecki 	/* Extract cores power info info */
617*b0f70292SRafał Miłecki 	for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
618*b0f70292SRafał Miłecki 		o = pwr_info_offset[i];
619*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
620*b0f70292SRafał Miłecki 			SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT);
621*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI,
622*b0f70292SRafał Miłecki 			SSB_SPROM8_2G_MAXP, 0);
623*b0f70292SRafał Miłecki 
624*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0);
625*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0);
626*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0);
627*b0f70292SRafał Miłecki 
628*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
629*b0f70292SRafał Miłecki 			SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT);
630*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI,
631*b0f70292SRafał Miłecki 			SSB_SPROM8_5G_MAXP, 0);
632*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP,
633*b0f70292SRafał Miłecki 			SSB_SPROM8_5GH_MAXP, 0);
634*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP,
635*b0f70292SRafał Miłecki 			SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT);
636*b0f70292SRafał Miłecki 
637*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0);
638*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0);
639*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0);
640*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0);
641*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0);
642*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0);
643*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0);
644*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0);
645*b0f70292SRafał Miłecki 		SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0);
646*b0f70292SRafał Miłecki 	}
647*b0f70292SRafał Miłecki 
6488a5ac6ecSRafał Miłecki 	/* Extract FEM info */
6498a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G,
6508a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
6518a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G,
6528a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
6538a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G,
6548a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
6558a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G,
6568a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
6578a5ac6ecSRafał Miłecki 	SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G,
6588a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
6598a5ac6ecSRafał Miłecki 
6608a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G,
6618a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TSSIPOS, SSB_SROM8_FEM_TSSIPOS_SHIFT);
6628a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G,
6638a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_EXTPA_GAIN, SSB_SROM8_FEM_EXTPA_GAIN_SHIFT);
6648a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G,
6658a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_PDET_RANGE, SSB_SROM8_FEM_PDET_RANGE_SHIFT);
6668a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G,
6678a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_TR_ISO, SSB_SROM8_FEM_TR_ISO_SHIFT);
6688a5ac6ecSRafał Miłecki 	SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G,
6698a5ac6ecSRafał Miłecki 		SSB_SROM8_FEM_ANTSWLUT, SSB_SROM8_FEM_ANTSWLUT_SHIFT);
6708a5ac6ecSRafał Miłecki 
671172c69a4SRafał Miłecki 	sprom_extract_r458(out, in);
672172c69a4SRafał Miłecki 
6736b1c7c67SMichael Buesch 	/* TODO - get remaining rev 8 stuff needed */
6746b1c7c67SMichael Buesch }
6756b1c7c67SMichael Buesch 
676c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
677c272ef44SLarry Finger 			 const u16 *in, u16 size)
67861e115a5SMichael Buesch {
67961e115a5SMichael Buesch 	memset(out, 0, sizeof(*out));
68061e115a5SMichael Buesch 
681c272ef44SLarry Finger 	out->revision = in[size - 1] & 0x00FF;
682e861b98dSMichael Buesch 	ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision);
68331ce12fbSLarry Finger 	memset(out->et0mac, 0xFF, 6);		/* preset et0 and et1 mac */
68431ce12fbSLarry Finger 	memset(out->et1mac, 0xFF, 6);
68554435f9eSRafał Miłecki 
68661e115a5SMichael Buesch 	if ((bus->chip_id & 0xFF00) == 0x4400) {
68761e115a5SMichael Buesch 		/* Workaround: The BCM44XX chip has a stupid revision
68861e115a5SMichael Buesch 		 * number stored in the SPROM.
68961e115a5SMichael Buesch 		 * Always extract r1. */
690c272ef44SLarry Finger 		out->revision = 1;
69154435f9eSRafał Miłecki 		ssb_dprintk(KERN_DEBUG PFX "SPROM treated as revision %d\n", out->revision);
69254435f9eSRafał Miłecki 	}
69354435f9eSRafał Miłecki 
6946b1c7c67SMichael Buesch 	switch (out->revision) {
6956b1c7c67SMichael Buesch 	case 1:
6966b1c7c67SMichael Buesch 	case 2:
6976b1c7c67SMichael Buesch 	case 3:
6986b1c7c67SMichael Buesch 		sprom_extract_r123(out, in);
6996b1c7c67SMichael Buesch 		break;
7006b1c7c67SMichael Buesch 	case 4:
7016b1c7c67SMichael Buesch 	case 5:
7026b1c7c67SMichael Buesch 		sprom_extract_r45(out, in);
7036b1c7c67SMichael Buesch 		break;
7046b1c7c67SMichael Buesch 	case 8:
7056b1c7c67SMichael Buesch 		sprom_extract_r8(out, in);
7066b1c7c67SMichael Buesch 		break;
7076b1c7c67SMichael Buesch 	default:
7086b1c7c67SMichael Buesch 		ssb_printk(KERN_WARNING PFX "Unsupported SPROM"
7096b1c7c67SMichael Buesch 			   " revision %d detected. Will extract"
7106b1c7c67SMichael Buesch 			   " v1\n", out->revision);
711cd559b36SLarry Finger 		out->revision = 1;
712c272ef44SLarry Finger 		sprom_extract_r123(out, in);
713c272ef44SLarry Finger 	}
71461e115a5SMichael Buesch 
7154503183aSLarry Finger 	if (out->boardflags_lo == 0xFFFF)
7164503183aSLarry Finger 		out->boardflags_lo = 0;  /* per specs */
7174503183aSLarry Finger 	if (out->boardflags_hi == 0xFFFF)
7184503183aSLarry Finger 		out->boardflags_hi = 0;  /* per specs */
7194503183aSLarry Finger 
72061e115a5SMichael Buesch 	return 0;
72161e115a5SMichael Buesch }
72261e115a5SMichael Buesch 
72361e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus,
72461e115a5SMichael Buesch 			     struct ssb_sprom *sprom)
72561e115a5SMichael Buesch {
726ca4a0831SRafał Miłecki 	int err;
72761e115a5SMichael Buesch 	u16 *buf;
72861e115a5SMichael Buesch 
729d53cdbb9SJohn W. Linville 	if (!ssb_is_sprom_available(bus)) {
730d53cdbb9SJohn W. Linville 		ssb_printk(KERN_ERR PFX "No SPROM available!\n");
731d53cdbb9SJohn W. Linville 		return -ENODEV;
732d53cdbb9SJohn W. Linville 	}
73325985edcSLucas De Marchi 	if (bus->chipco.dev) {	/* can be unavailable! */
7349d1ac34eSLarry Finger 		/*
7359d1ac34eSLarry Finger 		 * get SPROM offset: SSB_SPROM_BASE1 except for
7369d1ac34eSLarry Finger 		 * chipcommon rev >= 31 or chip ID is 0x4312 and
7379d1ac34eSLarry Finger 		 * chipcommon status & 3 == 2
7389d1ac34eSLarry Finger 		 */
7399d1ac34eSLarry Finger 		if (bus->chipco.dev->id.revision >= 31)
7409d1ac34eSLarry Finger 			bus->sprom_offset = SSB_SPROM_BASE31;
7419d1ac34eSLarry Finger 		else if (bus->chip_id == 0x4312 &&
7429d1ac34eSLarry Finger 			 (bus->chipco.status & 0x03) == 2)
7439d1ac34eSLarry Finger 			bus->sprom_offset = SSB_SPROM_BASE31;
7449d1ac34eSLarry Finger 		else
7459d1ac34eSLarry Finger 			bus->sprom_offset = SSB_SPROM_BASE1;
746da1fdb02SChristoph Fritz 	} else {
747da1fdb02SChristoph Fritz 		bus->sprom_offset = SSB_SPROM_BASE1;
748da1fdb02SChristoph Fritz 	}
7499d1ac34eSLarry Finger 	ssb_dprintk(KERN_INFO PFX "SPROM offset is 0x%x\n", bus->sprom_offset);
750ea2db495SRafał Miłecki 
751c272ef44SLarry Finger 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
75261e115a5SMichael Buesch 	if (!buf)
753ca4a0831SRafał Miłecki 		return -ENOMEM;
754c272ef44SLarry Finger 	bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
75561e115a5SMichael Buesch 	sprom_do_read(bus, buf);
756c272ef44SLarry Finger 	err = sprom_check_crc(buf, bus->sprom_size);
75761e115a5SMichael Buesch 	if (err) {
7582afc4901SLarry.Finger@lwfinger.net 		/* try for a 440 byte SPROM - revision 4 and higher */
759c272ef44SLarry Finger 		kfree(buf);
760c272ef44SLarry Finger 		buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
761c272ef44SLarry Finger 			      GFP_KERNEL);
762c272ef44SLarry Finger 		if (!buf)
763ca4a0831SRafał Miłecki 			return -ENOMEM;
764c272ef44SLarry Finger 		bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
765c272ef44SLarry Finger 		sprom_do_read(bus, buf);
766c272ef44SLarry Finger 		err = sprom_check_crc(buf, bus->sprom_size);
767e79c1ba8SMichael Buesch 		if (err) {
768e79c1ba8SMichael Buesch 			/* All CRC attempts failed.
769e79c1ba8SMichael Buesch 			 * Maybe there is no SPROM on the device?
770b3ae52b6SHauke Mehrtens 			 * Now we ask the arch code if there is some sprom
771b3ae52b6SHauke Mehrtens 			 * available for this device in some other storage */
772b3ae52b6SHauke Mehrtens 			err = ssb_fill_sprom_with_fallback(bus, sprom);
773b3ae52b6SHauke Mehrtens 			if (err) {
774b3ae52b6SHauke Mehrtens 				ssb_printk(KERN_WARNING PFX "WARNING: Using"
775b3ae52b6SHauke Mehrtens 					   " fallback SPROM failed (err %d)\n",
776b3ae52b6SHauke Mehrtens 					   err);
777b3ae52b6SHauke Mehrtens 			} else {
778b3ae52b6SHauke Mehrtens 				ssb_dprintk(KERN_DEBUG PFX "Using SPROM"
779b3ae52b6SHauke Mehrtens 					    " revision %d provided by"
780b3ae52b6SHauke Mehrtens 					    " platform.\n", sprom->revision);
781e79c1ba8SMichael Buesch 				err = 0;
782e79c1ba8SMichael Buesch 				goto out_free;
783e79c1ba8SMichael Buesch 			}
784c272ef44SLarry Finger 			ssb_printk(KERN_WARNING PFX "WARNING: Invalid"
785c272ef44SLarry Finger 				   " SPROM CRC (corrupt SPROM)\n");
786c272ef44SLarry Finger 		}
787e79c1ba8SMichael Buesch 	}
788c272ef44SLarry Finger 	err = sprom_extract(bus, sprom, buf, bus->sprom_size);
78961e115a5SMichael Buesch 
790e79c1ba8SMichael Buesch out_free:
79161e115a5SMichael Buesch 	kfree(buf);
79261e115a5SMichael Buesch 	return err;
79361e115a5SMichael Buesch }
79461e115a5SMichael Buesch 
79561e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
79661e115a5SMichael Buesch 				  struct ssb_boardinfo *bi)
79761e115a5SMichael Buesch {
798115f9450SSergei Shtylyov 	bi->vendor = bus->host_pci->subsystem_vendor;
799115f9450SSergei Shtylyov 	bi->type = bus->host_pci->subsystem_device;
8002fa23190SSergei Shtylyov 	bi->rev = bus->host_pci->revision;
80161e115a5SMichael Buesch }
80261e115a5SMichael Buesch 
80361e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus,
80461e115a5SMichael Buesch 			   struct ssb_init_invariants *iv)
80561e115a5SMichael Buesch {
80661e115a5SMichael Buesch 	int err;
80761e115a5SMichael Buesch 
80861e115a5SMichael Buesch 	err = ssb_pci_sprom_get(bus, &iv->sprom);
80961e115a5SMichael Buesch 	if (err)
81061e115a5SMichael Buesch 		goto out;
81161e115a5SMichael Buesch 	ssb_pci_get_boardinfo(bus, &iv->boardinfo);
81261e115a5SMichael Buesch 
81361e115a5SMichael Buesch out:
81461e115a5SMichael Buesch 	return err;
81561e115a5SMichael Buesch }
81661e115a5SMichael Buesch 
81761e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG
81861e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus)
81961e115a5SMichael Buesch {
82061e115a5SMichael Buesch 	if (likely(bus->powered_up))
82161e115a5SMichael Buesch 		return 0;
82261e115a5SMichael Buesch 
82361e115a5SMichael Buesch 	printk(KERN_ERR PFX "FATAL ERROR: Bus powered down "
82461e115a5SMichael Buesch 	       "while accessing PCI MMIO space\n");
82561e115a5SMichael Buesch 	if (bus->power_warn_count <= 10) {
82661e115a5SMichael Buesch 		bus->power_warn_count++;
82761e115a5SMichael Buesch 		dump_stack();
82861e115a5SMichael Buesch 	}
82961e115a5SMichael Buesch 
83061e115a5SMichael Buesch 	return -ENODEV;
83161e115a5SMichael Buesch }
83261e115a5SMichael Buesch #else /* DEBUG */
83361e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus)
83461e115a5SMichael Buesch {
83561e115a5SMichael Buesch 	return 0;
83661e115a5SMichael Buesch }
83761e115a5SMichael Buesch #endif /* DEBUG */
83861e115a5SMichael Buesch 
839ffc7689dSMichael Buesch static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset)
840ffc7689dSMichael Buesch {
841ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
842ffc7689dSMichael Buesch 
843ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
844ffc7689dSMichael Buesch 		return 0xFF;
845ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
846ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
847ffc7689dSMichael Buesch 			return 0xFF;
848ffc7689dSMichael Buesch 	}
849ffc7689dSMichael Buesch 	return ioread8(bus->mmio + offset);
850ffc7689dSMichael Buesch }
851ffc7689dSMichael Buesch 
85261e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
85361e115a5SMichael Buesch {
85461e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
85561e115a5SMichael Buesch 
85661e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
85761e115a5SMichael Buesch 		return 0xFFFF;
85861e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
85961e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
86061e115a5SMichael Buesch 			return 0xFFFF;
86161e115a5SMichael Buesch 	}
8624b402c65SMichael Buesch 	return ioread16(bus->mmio + offset);
86361e115a5SMichael Buesch }
86461e115a5SMichael Buesch 
86561e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
86661e115a5SMichael Buesch {
86761e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
86861e115a5SMichael Buesch 
86961e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
87061e115a5SMichael Buesch 		return 0xFFFFFFFF;
87161e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
87261e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
87361e115a5SMichael Buesch 			return 0xFFFFFFFF;
87461e115a5SMichael Buesch 	}
8754b402c65SMichael Buesch 	return ioread32(bus->mmio + offset);
87661e115a5SMichael Buesch }
87761e115a5SMichael Buesch 
878d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
879d625a29bSMichael Buesch static void ssb_pci_block_read(struct ssb_device *dev, void *buffer,
880d625a29bSMichael Buesch 			       size_t count, u16 offset, u8 reg_width)
881d625a29bSMichael Buesch {
882d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
883d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
884d625a29bSMichael Buesch 
885d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
886d625a29bSMichael Buesch 		goto error;
887d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
888d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
889d625a29bSMichael Buesch 			goto error;
890d625a29bSMichael Buesch 	}
891d625a29bSMichael Buesch 	switch (reg_width) {
892d625a29bSMichael Buesch 	case sizeof(u8):
893d625a29bSMichael Buesch 		ioread8_rep(addr, buffer, count);
894d625a29bSMichael Buesch 		break;
895d625a29bSMichael Buesch 	case sizeof(u16):
896d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
897d625a29bSMichael Buesch 		ioread16_rep(addr, buffer, count >> 1);
898d625a29bSMichael Buesch 		break;
899d625a29bSMichael Buesch 	case sizeof(u32):
900d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
901d625a29bSMichael Buesch 		ioread32_rep(addr, buffer, count >> 2);
902d625a29bSMichael Buesch 		break;
903d625a29bSMichael Buesch 	default:
904d625a29bSMichael Buesch 		SSB_WARN_ON(1);
905d625a29bSMichael Buesch 	}
906d625a29bSMichael Buesch 
907d625a29bSMichael Buesch 	return;
908d625a29bSMichael Buesch error:
909d625a29bSMichael Buesch 	memset(buffer, 0xFF, count);
910d625a29bSMichael Buesch }
911d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
912d625a29bSMichael Buesch 
913ffc7689dSMichael Buesch static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value)
914ffc7689dSMichael Buesch {
915ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
916ffc7689dSMichael Buesch 
917ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
918ffc7689dSMichael Buesch 		return;
919ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
920ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
921ffc7689dSMichael Buesch 			return;
922ffc7689dSMichael Buesch 	}
923ffc7689dSMichael Buesch 	iowrite8(value, bus->mmio + offset);
924ffc7689dSMichael Buesch }
925ffc7689dSMichael Buesch 
92661e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
92761e115a5SMichael Buesch {
92861e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
92961e115a5SMichael Buesch 
93061e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
93161e115a5SMichael Buesch 		return;
93261e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
93361e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
93461e115a5SMichael Buesch 			return;
93561e115a5SMichael Buesch 	}
9364b402c65SMichael Buesch 	iowrite16(value, bus->mmio + offset);
93761e115a5SMichael Buesch }
93861e115a5SMichael Buesch 
93961e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
94061e115a5SMichael Buesch {
94161e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
94261e115a5SMichael Buesch 
94361e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
94461e115a5SMichael Buesch 		return;
94561e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
94661e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
94761e115a5SMichael Buesch 			return;
94861e115a5SMichael Buesch 	}
9494b402c65SMichael Buesch 	iowrite32(value, bus->mmio + offset);
95061e115a5SMichael Buesch }
95161e115a5SMichael Buesch 
952d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
953d625a29bSMichael Buesch static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer,
954d625a29bSMichael Buesch 				size_t count, u16 offset, u8 reg_width)
955d625a29bSMichael Buesch {
956d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
957d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
958d625a29bSMichael Buesch 
959d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
960d625a29bSMichael Buesch 		return;
961d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
962d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
963d625a29bSMichael Buesch 			return;
964d625a29bSMichael Buesch 	}
965d625a29bSMichael Buesch 	switch (reg_width) {
966d625a29bSMichael Buesch 	case sizeof(u8):
967d625a29bSMichael Buesch 		iowrite8_rep(addr, buffer, count);
968d625a29bSMichael Buesch 		break;
969d625a29bSMichael Buesch 	case sizeof(u16):
970d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
971d625a29bSMichael Buesch 		iowrite16_rep(addr, buffer, count >> 1);
972d625a29bSMichael Buesch 		break;
973d625a29bSMichael Buesch 	case sizeof(u32):
974d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
975d625a29bSMichael Buesch 		iowrite32_rep(addr, buffer, count >> 2);
976d625a29bSMichael Buesch 		break;
977d625a29bSMichael Buesch 	default:
978d625a29bSMichael Buesch 		SSB_WARN_ON(1);
979d625a29bSMichael Buesch 	}
980d625a29bSMichael Buesch }
981d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
982d625a29bSMichael Buesch 
98361e115a5SMichael Buesch /* Not "static", as it's used in main.c */
98461e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = {
985ffc7689dSMichael Buesch 	.read8		= ssb_pci_read8,
98661e115a5SMichael Buesch 	.read16		= ssb_pci_read16,
98761e115a5SMichael Buesch 	.read32		= ssb_pci_read32,
988ffc7689dSMichael Buesch 	.write8		= ssb_pci_write8,
98961e115a5SMichael Buesch 	.write16	= ssb_pci_write16,
99061e115a5SMichael Buesch 	.write32	= ssb_pci_write32,
991d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
992d625a29bSMichael Buesch 	.block_read	= ssb_pci_block_read,
993d625a29bSMichael Buesch 	.block_write	= ssb_pci_block_write,
994d625a29bSMichael Buesch #endif
99561e115a5SMichael Buesch };
99661e115a5SMichael Buesch 
99761e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
99861e115a5SMichael Buesch 				       struct device_attribute *attr,
99961e115a5SMichael Buesch 				       char *buf)
100061e115a5SMichael Buesch {
100161e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
100261e115a5SMichael Buesch 	struct ssb_bus *bus;
100361e115a5SMichael Buesch 
100461e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
100561e115a5SMichael Buesch 	if (!bus)
1006e7ec2e32SMichael Buesch 		return -ENODEV;
100761e115a5SMichael Buesch 
1008e7ec2e32SMichael Buesch 	return ssb_attr_sprom_show(bus, buf, sprom_do_read);
100961e115a5SMichael Buesch }
101061e115a5SMichael Buesch 
101161e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
101261e115a5SMichael Buesch 					struct device_attribute *attr,
101361e115a5SMichael Buesch 					const char *buf, size_t count)
101461e115a5SMichael Buesch {
101561e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
101661e115a5SMichael Buesch 	struct ssb_bus *bus;
101761e115a5SMichael Buesch 
101861e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
101961e115a5SMichael Buesch 	if (!bus)
1020e7ec2e32SMichael Buesch 		return -ENODEV;
102161e115a5SMichael Buesch 
1022e7ec2e32SMichael Buesch 	return ssb_attr_sprom_store(bus, buf, count,
1023e7ec2e32SMichael Buesch 				    sprom_check_crc, sprom_do_write);
102461e115a5SMichael Buesch }
102561e115a5SMichael Buesch 
102661e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600,
102761e115a5SMichael Buesch 		   ssb_pci_attr_sprom_show,
102861e115a5SMichael Buesch 		   ssb_pci_attr_sprom_store);
102961e115a5SMichael Buesch 
103061e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus)
103161e115a5SMichael Buesch {
103261e115a5SMichael Buesch 	struct pci_dev *pdev;
103361e115a5SMichael Buesch 
103461e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
103561e115a5SMichael Buesch 		return;
103661e115a5SMichael Buesch 
103761e115a5SMichael Buesch 	pdev = bus->host_pci;
103861e115a5SMichael Buesch 	device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
103961e115a5SMichael Buesch }
104061e115a5SMichael Buesch 
104161e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus)
104261e115a5SMichael Buesch {
104361e115a5SMichael Buesch 	struct pci_dev *pdev;
104461e115a5SMichael Buesch 	int err;
104561e115a5SMichael Buesch 
104661e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
104761e115a5SMichael Buesch 		return 0;
104861e115a5SMichael Buesch 
104961e115a5SMichael Buesch 	pdev = bus->host_pci;
1050e7ec2e32SMichael Buesch 	mutex_init(&bus->sprom_mutex);
105161e115a5SMichael Buesch 	err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
105261e115a5SMichael Buesch 	if (err)
105361e115a5SMichael Buesch 		goto out;
105461e115a5SMichael Buesch 
105561e115a5SMichael Buesch out:
105661e115a5SMichael Buesch 	return err;
105761e115a5SMichael Buesch }
1058