xref: /openbmc/linux/drivers/ssb/pci.c (revision c272ef4403c271799a7f09a4ab7a236c86643843)
161e115a5SMichael Buesch /*
261e115a5SMichael Buesch  * Sonics Silicon Backplane PCI-Hostbus related functions.
361e115a5SMichael Buesch  *
461e115a5SMichael Buesch  * Copyright (C) 2005-2006 Michael Buesch <mb@bu3sch.de>
561e115a5SMichael Buesch  * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
661e115a5SMichael Buesch  * Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
761e115a5SMichael Buesch  * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
861e115a5SMichael Buesch  * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
961e115a5SMichael Buesch  *
1061e115a5SMichael Buesch  * Derived from the Broadcom 4400 device driver.
1161e115a5SMichael Buesch  * Copyright (C) 2002 David S. Miller (davem@redhat.com)
1261e115a5SMichael Buesch  * Fixed by Pekka Pietikainen (pp@ee.oulu.fi)
1361e115a5SMichael Buesch  * Copyright (C) 2006 Broadcom Corporation.
1461e115a5SMichael Buesch  *
1561e115a5SMichael Buesch  * Licensed under the GNU/GPL. See COPYING for details.
1661e115a5SMichael Buesch  */
1761e115a5SMichael Buesch 
1861e115a5SMichael Buesch #include <linux/ssb/ssb.h>
1961e115a5SMichael Buesch #include <linux/ssb/ssb_regs.h>
2061e115a5SMichael Buesch #include <linux/pci.h>
2161e115a5SMichael Buesch #include <linux/delay.h>
2261e115a5SMichael Buesch 
2361e115a5SMichael Buesch #include "ssb_private.h"
2461e115a5SMichael Buesch 
2561e115a5SMichael Buesch 
2661e115a5SMichael Buesch /* Define the following to 1 to enable a printk on each coreswitch. */
2761e115a5SMichael Buesch #define SSB_VERBOSE_PCICORESWITCH_DEBUG		0
2861e115a5SMichael Buesch 
2961e115a5SMichael Buesch 
3061e115a5SMichael Buesch /* Lowlevel coreswitching */
3161e115a5SMichael Buesch int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
3261e115a5SMichael Buesch {
3361e115a5SMichael Buesch 	int err;
3461e115a5SMichael Buesch 	int attempts = 0;
3561e115a5SMichael Buesch 	u32 cur_core;
3661e115a5SMichael Buesch 
3761e115a5SMichael Buesch 	while (1) {
3861e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN,
3961e115a5SMichael Buesch 					     (coreidx * SSB_CORE_SIZE)
4061e115a5SMichael Buesch 					     + SSB_ENUM_BASE);
4161e115a5SMichael Buesch 		if (err)
4261e115a5SMichael Buesch 			goto error;
4361e115a5SMichael Buesch 		err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN,
4461e115a5SMichael Buesch 					    &cur_core);
4561e115a5SMichael Buesch 		if (err)
4661e115a5SMichael Buesch 			goto error;
4761e115a5SMichael Buesch 		cur_core = (cur_core - SSB_ENUM_BASE)
4861e115a5SMichael Buesch 			   / SSB_CORE_SIZE;
4961e115a5SMichael Buesch 		if (cur_core == coreidx)
5061e115a5SMichael Buesch 			break;
5161e115a5SMichael Buesch 
5261e115a5SMichael Buesch 		if (attempts++ > SSB_BAR0_MAX_RETRIES)
5361e115a5SMichael Buesch 			goto error;
5461e115a5SMichael Buesch 		udelay(10);
5561e115a5SMichael Buesch 	}
5661e115a5SMichael Buesch 	return 0;
5761e115a5SMichael Buesch error:
5861e115a5SMichael Buesch 	ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
5961e115a5SMichael Buesch 	return -ENODEV;
6061e115a5SMichael Buesch }
6161e115a5SMichael Buesch 
6261e115a5SMichael Buesch int ssb_pci_switch_core(struct ssb_bus *bus,
6361e115a5SMichael Buesch 			struct ssb_device *dev)
6461e115a5SMichael Buesch {
6561e115a5SMichael Buesch 	int err;
6661e115a5SMichael Buesch 	unsigned long flags;
6761e115a5SMichael Buesch 
6861e115a5SMichael Buesch #if SSB_VERBOSE_PCICORESWITCH_DEBUG
6961e115a5SMichael Buesch 	ssb_printk(KERN_INFO PFX
7061e115a5SMichael Buesch 		   "Switching to %s core, index %d\n",
7161e115a5SMichael Buesch 		   ssb_core_name(dev->id.coreid),
7261e115a5SMichael Buesch 		   dev->core_index);
7361e115a5SMichael Buesch #endif
7461e115a5SMichael Buesch 
7561e115a5SMichael Buesch 	spin_lock_irqsave(&bus->bar_lock, flags);
7661e115a5SMichael Buesch 	err = ssb_pci_switch_coreidx(bus, dev->core_index);
7761e115a5SMichael Buesch 	if (!err)
7861e115a5SMichael Buesch 		bus->mapped_device = dev;
7961e115a5SMichael Buesch 	spin_unlock_irqrestore(&bus->bar_lock, flags);
8061e115a5SMichael Buesch 
8161e115a5SMichael Buesch 	return err;
8261e115a5SMichael Buesch }
8361e115a5SMichael Buesch 
8461e115a5SMichael Buesch /* Enable/disable the on board crystal oscillator and/or PLL. */
8561e115a5SMichael Buesch int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on)
8661e115a5SMichael Buesch {
8761e115a5SMichael Buesch 	int err;
8861e115a5SMichael Buesch 	u32 in, out, outenable;
8961e115a5SMichael Buesch 	u16 pci_status;
9061e115a5SMichael Buesch 
9161e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
9261e115a5SMichael Buesch 		return 0;
9361e115a5SMichael Buesch 
9461e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in);
9561e115a5SMichael Buesch 	if (err)
9661e115a5SMichael Buesch 		goto err_pci;
9761e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out);
9861e115a5SMichael Buesch 	if (err)
9961e115a5SMichael Buesch 		goto err_pci;
10061e115a5SMichael Buesch 	err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable);
10161e115a5SMichael Buesch 	if (err)
10261e115a5SMichael Buesch 		goto err_pci;
10361e115a5SMichael Buesch 
10461e115a5SMichael Buesch 	outenable |= what;
10561e115a5SMichael Buesch 
10661e115a5SMichael Buesch 	if (turn_on) {
10761e115a5SMichael Buesch 		/* Avoid glitching the clock if GPRS is already using it.
10861e115a5SMichael Buesch 		 * We can't actually read the state of the PLLPD so we infer it
10961e115a5SMichael Buesch 		 * by the value of XTAL_PU which *is* readable via gpioin.
11061e115a5SMichael Buesch 		 */
11161e115a5SMichael Buesch 		if (!(in & SSB_GPIO_XTAL)) {
11261e115a5SMichael Buesch 			if (what & SSB_GPIO_XTAL) {
11361e115a5SMichael Buesch 				/* Turn the crystal on */
11461e115a5SMichael Buesch 				out |= SSB_GPIO_XTAL;
11561e115a5SMichael Buesch 				if (what & SSB_GPIO_PLL)
11661e115a5SMichael Buesch 					out |= SSB_GPIO_PLL;
11761e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
11861e115a5SMichael Buesch 				if (err)
11961e115a5SMichael Buesch 					goto err_pci;
12061e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE,
12161e115a5SMichael Buesch 							     outenable);
12261e115a5SMichael Buesch 				if (err)
12361e115a5SMichael Buesch 					goto err_pci;
12461e115a5SMichael Buesch 				msleep(1);
12561e115a5SMichael Buesch 			}
12661e115a5SMichael Buesch 			if (what & SSB_GPIO_PLL) {
12761e115a5SMichael Buesch 				/* Turn the PLL on */
12861e115a5SMichael Buesch 				out &= ~SSB_GPIO_PLL;
12961e115a5SMichael Buesch 				err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
13061e115a5SMichael Buesch 				if (err)
13161e115a5SMichael Buesch 					goto err_pci;
13261e115a5SMichael Buesch 				msleep(5);
13361e115a5SMichael Buesch 			}
13461e115a5SMichael Buesch 		}
13561e115a5SMichael Buesch 
13661e115a5SMichael Buesch 		err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status);
13761e115a5SMichael Buesch 		if (err)
13861e115a5SMichael Buesch 			goto err_pci;
13961e115a5SMichael Buesch 		pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT;
14061e115a5SMichael Buesch 		err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status);
14161e115a5SMichael Buesch 		if (err)
14261e115a5SMichael Buesch 			goto err_pci;
14361e115a5SMichael Buesch 	} else {
14461e115a5SMichael Buesch 		if (what & SSB_GPIO_XTAL) {
14561e115a5SMichael Buesch 			/* Turn the crystal off */
14661e115a5SMichael Buesch 			out &= ~SSB_GPIO_XTAL;
14761e115a5SMichael Buesch 		}
14861e115a5SMichael Buesch 		if (what & SSB_GPIO_PLL) {
14961e115a5SMichael Buesch 			/* Turn the PLL off */
15061e115a5SMichael Buesch 			out |= SSB_GPIO_PLL;
15161e115a5SMichael Buesch 		}
15261e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out);
15361e115a5SMichael Buesch 		if (err)
15461e115a5SMichael Buesch 			goto err_pci;
15561e115a5SMichael Buesch 		err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable);
15661e115a5SMichael Buesch 		if (err)
15761e115a5SMichael Buesch 			goto err_pci;
15861e115a5SMichael Buesch 	}
15961e115a5SMichael Buesch 
16061e115a5SMichael Buesch out:
16161e115a5SMichael Buesch 	return err;
16261e115a5SMichael Buesch 
16361e115a5SMichael Buesch err_pci:
16461e115a5SMichael Buesch 	printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n");
16561e115a5SMichael Buesch 	err = -EBUSY;
16661e115a5SMichael Buesch 	goto out;
16761e115a5SMichael Buesch }
16861e115a5SMichael Buesch 
16961e115a5SMichael Buesch /* Get the word-offset for a SSB_SPROM_XXX define. */
17061e115a5SMichael Buesch #define SPOFF(offset)	(((offset) - SSB_SPROM_BASE) / sizeof(u16))
17161e115a5SMichael Buesch /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */
17261e115a5SMichael Buesch #define SPEX(_outvar, _offset, _mask, _shift)	\
17361e115a5SMichael Buesch 	out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift))
17461e115a5SMichael Buesch 
17561e115a5SMichael Buesch static inline u8 ssb_crc8(u8 crc, u8 data)
17661e115a5SMichael Buesch {
17761e115a5SMichael Buesch 	/* Polynomial:   x^8 + x^7 + x^6 + x^4 + x^2 + 1   */
17861e115a5SMichael Buesch 	static const u8 t[] = {
17961e115a5SMichael Buesch 		0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
18061e115a5SMichael Buesch 		0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
18161e115a5SMichael Buesch 		0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
18261e115a5SMichael Buesch 		0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
18361e115a5SMichael Buesch 		0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
18461e115a5SMichael Buesch 		0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
18561e115a5SMichael Buesch 		0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
18661e115a5SMichael Buesch 		0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
18761e115a5SMichael Buesch 		0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
18861e115a5SMichael Buesch 		0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
18961e115a5SMichael Buesch 		0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
19061e115a5SMichael Buesch 		0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
19161e115a5SMichael Buesch 		0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
19261e115a5SMichael Buesch 		0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
19361e115a5SMichael Buesch 		0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
19461e115a5SMichael Buesch 		0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
19561e115a5SMichael Buesch 		0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
19661e115a5SMichael Buesch 		0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
19761e115a5SMichael Buesch 		0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
19861e115a5SMichael Buesch 		0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
19961e115a5SMichael Buesch 		0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
20061e115a5SMichael Buesch 		0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
20161e115a5SMichael Buesch 		0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
20261e115a5SMichael Buesch 		0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
20361e115a5SMichael Buesch 		0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
20461e115a5SMichael Buesch 		0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
20561e115a5SMichael Buesch 		0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
20661e115a5SMichael Buesch 		0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
20761e115a5SMichael Buesch 		0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
20861e115a5SMichael Buesch 		0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
20961e115a5SMichael Buesch 		0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
21061e115a5SMichael Buesch 		0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F,
21161e115a5SMichael Buesch 	};
21261e115a5SMichael Buesch 	return t[crc ^ data];
21361e115a5SMichael Buesch }
21461e115a5SMichael Buesch 
215*c272ef44SLarry Finger static u8 ssb_sprom_crc(const u16 *sprom, u16 size)
21661e115a5SMichael Buesch {
21761e115a5SMichael Buesch 	int word;
21861e115a5SMichael Buesch 	u8 crc = 0xFF;
21961e115a5SMichael Buesch 
220*c272ef44SLarry Finger 	for (word = 0; word < size - 1; word++) {
22161e115a5SMichael Buesch 		crc = ssb_crc8(crc, sprom[word] & 0x00FF);
22261e115a5SMichael Buesch 		crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8);
22361e115a5SMichael Buesch 	}
224*c272ef44SLarry Finger 	crc = ssb_crc8(crc, sprom[size - 1] & 0x00FF);
22561e115a5SMichael Buesch 	crc ^= 0xFF;
22661e115a5SMichael Buesch 
22761e115a5SMichael Buesch 	return crc;
22861e115a5SMichael Buesch }
22961e115a5SMichael Buesch 
230*c272ef44SLarry Finger static int sprom_check_crc(const u16 *sprom, u16 size)
23161e115a5SMichael Buesch {
23261e115a5SMichael Buesch 	u8 crc;
23361e115a5SMichael Buesch 	u8 expected_crc;
23461e115a5SMichael Buesch 	u16 tmp;
23561e115a5SMichael Buesch 
236*c272ef44SLarry Finger 	crc = ssb_sprom_crc(sprom, size);
237*c272ef44SLarry Finger 	tmp = sprom[size - 1] & SSB_SPROM_REVISION_CRC;
23861e115a5SMichael Buesch 	expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT;
23961e115a5SMichael Buesch 	if (crc != expected_crc)
24061e115a5SMichael Buesch 		return -EPROTO;
24161e115a5SMichael Buesch 
24261e115a5SMichael Buesch 	return 0;
24361e115a5SMichael Buesch }
24461e115a5SMichael Buesch 
24561e115a5SMichael Buesch static void sprom_do_read(struct ssb_bus *bus, u16 *sprom)
24661e115a5SMichael Buesch {
24761e115a5SMichael Buesch 	int i;
24861e115a5SMichael Buesch 
249*c272ef44SLarry Finger 	for (i = 0; i < bus->sprom_size; i++)
25061e115a5SMichael Buesch 		sprom[i] = readw(bus->mmio + SSB_SPROM_BASE + (i * 2));
25161e115a5SMichael Buesch }
25261e115a5SMichael Buesch 
25361e115a5SMichael Buesch static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
25461e115a5SMichael Buesch {
25561e115a5SMichael Buesch 	struct pci_dev *pdev = bus->host_pci;
25661e115a5SMichael Buesch 	int i, err;
25761e115a5SMichael Buesch 	u32 spromctl;
258*c272ef44SLarry Finger 	u16 size = bus->sprom_size;
25961e115a5SMichael Buesch 
26061e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");
26161e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
26261e115a5SMichael Buesch 	if (err)
26361e115a5SMichael Buesch 		goto err_ctlreg;
26461e115a5SMichael Buesch 	spromctl |= SSB_SPROMCTL_WE;
26561e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
26661e115a5SMichael Buesch 	if (err)
26761e115a5SMichael Buesch 		goto err_ctlreg;
26861e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "[ 0%%");
26961e115a5SMichael Buesch 	msleep(500);
270*c272ef44SLarry Finger 	for (i = 0; i < size; i++) {
271*c272ef44SLarry Finger 		if (i == size / 4)
27261e115a5SMichael Buesch 			ssb_printk("25%%");
273*c272ef44SLarry Finger 		else if (i == size / 2)
27461e115a5SMichael Buesch 			ssb_printk("50%%");
275*c272ef44SLarry Finger 		else if (i == (size * 3) / 4)
27661e115a5SMichael Buesch 			ssb_printk("75%%");
27761e115a5SMichael Buesch 		else if (i % 2)
27861e115a5SMichael Buesch 			ssb_printk(".");
27961e115a5SMichael Buesch 		writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2));
28061e115a5SMichael Buesch 		mmiowb();
28161e115a5SMichael Buesch 		msleep(20);
28261e115a5SMichael Buesch 	}
28361e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
28461e115a5SMichael Buesch 	if (err)
28561e115a5SMichael Buesch 		goto err_ctlreg;
28661e115a5SMichael Buesch 	spromctl &= ~SSB_SPROMCTL_WE;
28761e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
28861e115a5SMichael Buesch 	if (err)
28961e115a5SMichael Buesch 		goto err_ctlreg;
29061e115a5SMichael Buesch 	msleep(500);
29161e115a5SMichael Buesch 	ssb_printk("100%% ]\n");
29261e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "SPROM written.\n");
29361e115a5SMichael Buesch 
29461e115a5SMichael Buesch 	return 0;
29561e115a5SMichael Buesch err_ctlreg:
29661e115a5SMichael Buesch 	ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n");
29761e115a5SMichael Buesch 	return err;
29861e115a5SMichael Buesch }
29961e115a5SMichael Buesch 
30061e115a5SMichael Buesch static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in)
30161e115a5SMichael Buesch {
30261e115a5SMichael Buesch 	int i;
30361e115a5SMichael Buesch 	u16 v;
30461e115a5SMichael Buesch 
30561e115a5SMichael Buesch 	SPEX(pci_spid, SSB_SPROM1_SPID, 0xFFFF, 0);
30661e115a5SMichael Buesch 	SPEX(pci_svid, SSB_SPROM1_SVID, 0xFFFF, 0);
30761e115a5SMichael Buesch 	SPEX(pci_pid, SSB_SPROM1_PID, 0xFFFF, 0);
30861e115a5SMichael Buesch 	for (i = 0; i < 3; i++) {
30961e115a5SMichael Buesch 		v = in[SPOFF(SSB_SPROM1_IL0MAC) + i];
3106b9bafecSMichael Buesch 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
31161e115a5SMichael Buesch 	}
31261e115a5SMichael Buesch 	for (i = 0; i < 3; i++) {
31361e115a5SMichael Buesch 		v = in[SPOFF(SSB_SPROM1_ET0MAC) + i];
3146b9bafecSMichael Buesch 		*(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
31561e115a5SMichael Buesch 	}
31661e115a5SMichael Buesch 	for (i = 0; i < 3; i++) {
31761e115a5SMichael Buesch 		v = in[SPOFF(SSB_SPROM1_ET1MAC) + i];
3186b9bafecSMichael Buesch 		*(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
31961e115a5SMichael Buesch 	}
32061e115a5SMichael Buesch 	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
32161e115a5SMichael Buesch 	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
32261e115a5SMichael Buesch 	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
32361e115a5SMichael Buesch 	SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
32461e115a5SMichael Buesch 	SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
32561e115a5SMichael Buesch 	SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
32661e115a5SMichael Buesch 	SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
32761e115a5SMichael Buesch 	     SSB_SPROM1_BINF_CCODE_SHIFT);
32861e115a5SMichael Buesch 	SPEX(antenna_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
32961e115a5SMichael Buesch 	     SSB_SPROM1_BINF_ANTA_SHIFT);
33061e115a5SMichael Buesch 	SPEX(antenna_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
33161e115a5SMichael Buesch 	     SSB_SPROM1_BINF_ANTBG_SHIFT);
33261e115a5SMichael Buesch 	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
33361e115a5SMichael Buesch 	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
33461e115a5SMichael Buesch 	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
33561e115a5SMichael Buesch 	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
33661e115a5SMichael Buesch 	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
33761e115a5SMichael Buesch 	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
33861e115a5SMichael Buesch 	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
33961e115a5SMichael Buesch 	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
34061e115a5SMichael Buesch 	     SSB_SPROM1_GPIOA_P1_SHIFT);
34161e115a5SMichael Buesch 	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
34261e115a5SMichael Buesch 	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
34361e115a5SMichael Buesch 	     SSB_SPROM1_GPIOB_P3_SHIFT);
34461e115a5SMichael Buesch 	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
34561e115a5SMichael Buesch 	     SSB_SPROM1_MAXPWR_A_SHIFT);
34661e115a5SMichael Buesch 	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
34761e115a5SMichael Buesch 	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
34861e115a5SMichael Buesch 	     SSB_SPROM1_ITSSI_A_SHIFT);
34961e115a5SMichael Buesch 	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
35061e115a5SMichael Buesch 	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
35161e115a5SMichael Buesch 	SPEX(antenna_gain_a, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_A, 0);
35261e115a5SMichael Buesch 	SPEX(antenna_gain_bg, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_BG,
35361e115a5SMichael Buesch 	     SSB_SPROM1_AGAIN_BG_SHIFT);
35461e115a5SMichael Buesch }
35561e115a5SMichael Buesch 
356*c272ef44SLarry Finger static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
357*c272ef44SLarry Finger {
358*c272ef44SLarry Finger 	int i;
359*c272ef44SLarry Finger 	u16 v;
360*c272ef44SLarry Finger 	u16 loc[3];
361*c272ef44SLarry Finger 
362*c272ef44SLarry Finger 	if (out->revision == 3) {			/* rev 3 moved MAC */
363*c272ef44SLarry Finger 		loc[0] = SSB_SPROM3_IL0MAC;
364*c272ef44SLarry Finger 		loc[1] = SSB_SPROM3_ET0MAC;
365*c272ef44SLarry Finger 		loc[2] = SSB_SPROM3_ET1MAC;
366*c272ef44SLarry Finger 	} else {
367*c272ef44SLarry Finger 		loc[0] = SSB_SPROM1_IL0MAC;
368*c272ef44SLarry Finger 		loc[1] = SSB_SPROM1_ET0MAC;
369*c272ef44SLarry Finger 		loc[2] = SSB_SPROM1_ET1MAC;
370*c272ef44SLarry Finger 	}
371*c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
372*c272ef44SLarry Finger 		v = in[SPOFF(loc[0]) + i];
373*c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
374*c272ef44SLarry Finger 	}
375*c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
376*c272ef44SLarry Finger 		v = in[SPOFF(loc[1]) + i];
377*c272ef44SLarry Finger 		*(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
378*c272ef44SLarry Finger 	}
379*c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
380*c272ef44SLarry Finger 		v = in[SPOFF(loc[2]) + i];
381*c272ef44SLarry Finger 		*(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
382*c272ef44SLarry Finger 	}
383*c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
384*c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
385*c272ef44SLarry Finger 	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
386*c272ef44SLarry Finger 	SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
387*c272ef44SLarry Finger 	     SSB_SPROM1_BINF_CCODE_SHIFT);
388*c272ef44SLarry Finger 	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
389*c272ef44SLarry Finger 	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
390*c272ef44SLarry Finger 	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
391*c272ef44SLarry Finger 	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
392*c272ef44SLarry Finger 	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
393*c272ef44SLarry Finger 	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
394*c272ef44SLarry Finger 	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
395*c272ef44SLarry Finger 	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
396*c272ef44SLarry Finger 	     SSB_SPROM1_GPIOA_P1_SHIFT);
397*c272ef44SLarry Finger 	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
398*c272ef44SLarry Finger 	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
399*c272ef44SLarry Finger 	     SSB_SPROM1_GPIOB_P3_SHIFT);
400*c272ef44SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
401*c272ef44SLarry Finger 	     SSB_SPROM1_MAXPWR_A_SHIFT);
402*c272ef44SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
403*c272ef44SLarry Finger 	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
404*c272ef44SLarry Finger 	     SSB_SPROM1_ITSSI_A_SHIFT);
405*c272ef44SLarry Finger 	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
406*c272ef44SLarry Finger 	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
407*c272ef44SLarry Finger 	SPEX(antenna_gain_a, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_A, 0);
408*c272ef44SLarry Finger 	SPEX(antenna_gain_bg, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_BG,
409*c272ef44SLarry Finger 	     SSB_SPROM1_AGAIN_BG_SHIFT);
410*c272ef44SLarry Finger }
411*c272ef44SLarry Finger 
412*c272ef44SLarry Finger static void sprom_extract_r4(struct ssb_sprom *out, const u16 *in)
41361e115a5SMichael Buesch {
41461e115a5SMichael Buesch 	int i;
41561e115a5SMichael Buesch 	u16 v;
41661e115a5SMichael Buesch 
417*c272ef44SLarry Finger 	/* extract the r1 variables */
418*c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
419*c272ef44SLarry Finger 		v = in[SPOFF(SSB_SPROM4_IL0MAC) + i];
420*c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
42161e115a5SMichael Buesch 	}
422*c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
423*c272ef44SLarry Finger 		v = in[SPOFF(SSB_SPROM4_ET0MAC) + i];
424*c272ef44SLarry Finger 		*(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
425*c272ef44SLarry Finger 	}
426*c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
427*c272ef44SLarry Finger 		v = in[SPOFF(SSB_SPROM4_ET1MAC) + i];
428*c272ef44SLarry Finger 		*(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
429*c272ef44SLarry Finger 	}
430*c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
431*c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
432*c272ef44SLarry Finger 	     SSB_SPROM4_ETHPHY_ET1A_SHIFT);
433*c272ef44SLarry Finger 	SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0);
434*c272ef44SLarry Finger 	SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
435*c272ef44SLarry Finger 	SPEX(antenna_gain_a, SSB_SPROM4_AGAIN, SSB_SPROM4_AGAIN_0, 0);
436*c272ef44SLarry Finger 	SPEX(antenna_gain_bg, SSB_SPROM4_AGAIN, SSB_SPROM4_AGAIN_1,
437*c272ef44SLarry Finger 	     SSB_SPROM4_AGAIN_1_SHIFT);
438*c272ef44SLarry Finger 	/* TODO - get remaining rev 4 stuff needed */
43961e115a5SMichael Buesch }
44061e115a5SMichael Buesch 
441*c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
442*c272ef44SLarry Finger 			 const u16 *in, u16 size)
44361e115a5SMichael Buesch {
44461e115a5SMichael Buesch 	memset(out, 0, sizeof(*out));
44561e115a5SMichael Buesch 
446*c272ef44SLarry Finger 	out->revision = in[size - 1] & 0x00FF;
44761e115a5SMichael Buesch 	if ((bus->chip_id & 0xFF00) == 0x4400) {
44861e115a5SMichael Buesch 		/* Workaround: The BCM44XX chip has a stupid revision
44961e115a5SMichael Buesch 		 * number stored in the SPROM.
45061e115a5SMichael Buesch 		 * Always extract r1. */
451*c272ef44SLarry Finger 		out->revision = 1;
452*c272ef44SLarry Finger 		sprom_extract_r123(out, in);
45361e115a5SMichael Buesch 		sprom_extract_r1(&out->r1, in);
454*c272ef44SLarry Finger 	} else if (bus->chip_id == 0x4321) {
455*c272ef44SLarry Finger 		/* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */
456*c272ef44SLarry Finger 		out->revision = 4;
457*c272ef44SLarry Finger 		sprom_extract_r4(out, in);
45861e115a5SMichael Buesch 	} else {
45961e115a5SMichael Buesch 		if (out->revision == 0)
46061e115a5SMichael Buesch 			goto unsupported;
461*c272ef44SLarry Finger 		if (out->revision >= 1 && out->revision <= 3) {
462*c272ef44SLarry Finger 			sprom_extract_r123(out, in);
46361e115a5SMichael Buesch 			sprom_extract_r1(&out->r1, in);
464*c272ef44SLarry Finger 		}
465*c272ef44SLarry Finger 		if (out->revision == 4)
466*c272ef44SLarry Finger 			sprom_extract_r4(out, in);
467*c272ef44SLarry Finger 		if (out->revision >= 5)
46861e115a5SMichael Buesch 			goto unsupported;
46961e115a5SMichael Buesch 	}
47061e115a5SMichael Buesch 
47161e115a5SMichael Buesch 	return 0;
47261e115a5SMichael Buesch unsupported:
47361e115a5SMichael Buesch 	ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d "
47461e115a5SMichael Buesch 		   "detected. Will extract v1\n", out->revision);
47561e115a5SMichael Buesch 	sprom_extract_r1(&out->r1, in);
47661e115a5SMichael Buesch 	return 0;
47761e115a5SMichael Buesch }
47861e115a5SMichael Buesch 
47961e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus,
48061e115a5SMichael Buesch 			     struct ssb_sprom *sprom)
48161e115a5SMichael Buesch {
48261e115a5SMichael Buesch 	int err = -ENOMEM;
48361e115a5SMichael Buesch 	u16 *buf;
48461e115a5SMichael Buesch 
485*c272ef44SLarry Finger 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
48661e115a5SMichael Buesch 	if (!buf)
48761e115a5SMichael Buesch 		goto out;
488*c272ef44SLarry Finger 	bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
48961e115a5SMichael Buesch 	sprom_do_read(bus, buf);
490*c272ef44SLarry Finger 	err = sprom_check_crc(buf, bus->sprom_size);
49161e115a5SMichael Buesch 	if (err) {
492*c272ef44SLarry Finger 		/* check for rev 4 sprom - has special signature */
493*c272ef44SLarry Finger 		if (buf [32] == 0x5372) {
494*c272ef44SLarry Finger 			ssb_printk(KERN_WARNING PFX "Extracting a rev 4"
495*c272ef44SLarry Finger 				   " SPROM\n");
496*c272ef44SLarry Finger 			kfree(buf);
497*c272ef44SLarry Finger 			buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
498*c272ef44SLarry Finger 				      GFP_KERNEL);
499*c272ef44SLarry Finger 			if (!buf)
500*c272ef44SLarry Finger 				goto out;
501*c272ef44SLarry Finger 			bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
502*c272ef44SLarry Finger 			sprom_do_read(bus, buf);
503*c272ef44SLarry Finger 			err = sprom_check_crc(buf, bus->sprom_size);
50461e115a5SMichael Buesch 		}
505*c272ef44SLarry Finger 		if (err)
506*c272ef44SLarry Finger 			ssb_printk(KERN_WARNING PFX "WARNING: Invalid"
507*c272ef44SLarry Finger 				   " SPROM CRC (corrupt SPROM)\n");
508*c272ef44SLarry Finger 	}
509*c272ef44SLarry Finger 	err = sprom_extract(bus, sprom, buf, bus->sprom_size);
51061e115a5SMichael Buesch 
51161e115a5SMichael Buesch 	kfree(buf);
51261e115a5SMichael Buesch out:
51361e115a5SMichael Buesch 	return err;
51461e115a5SMichael Buesch }
51561e115a5SMichael Buesch 
51661e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
51761e115a5SMichael Buesch 				  struct ssb_boardinfo *bi)
51861e115a5SMichael Buesch {
51961e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
52061e115a5SMichael Buesch 			     &bi->vendor);
52161e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
52261e115a5SMichael Buesch 			     &bi->type);
52361e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
52461e115a5SMichael Buesch 			     &bi->rev);
52561e115a5SMichael Buesch }
52661e115a5SMichael Buesch 
52761e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus,
52861e115a5SMichael Buesch 			   struct ssb_init_invariants *iv)
52961e115a5SMichael Buesch {
53061e115a5SMichael Buesch 	int err;
53161e115a5SMichael Buesch 
53261e115a5SMichael Buesch 	err = ssb_pci_sprom_get(bus, &iv->sprom);
53361e115a5SMichael Buesch 	if (err)
53461e115a5SMichael Buesch 		goto out;
53561e115a5SMichael Buesch 	ssb_pci_get_boardinfo(bus, &iv->boardinfo);
53661e115a5SMichael Buesch 
53761e115a5SMichael Buesch out:
53861e115a5SMichael Buesch 	return err;
53961e115a5SMichael Buesch }
54061e115a5SMichael Buesch 
54161e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG
54261e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus)
54361e115a5SMichael Buesch {
54461e115a5SMichael Buesch 	if (likely(bus->powered_up))
54561e115a5SMichael Buesch 		return 0;
54661e115a5SMichael Buesch 
54761e115a5SMichael Buesch 	printk(KERN_ERR PFX "FATAL ERROR: Bus powered down "
54861e115a5SMichael Buesch 	       "while accessing PCI MMIO space\n");
54961e115a5SMichael Buesch 	if (bus->power_warn_count <= 10) {
55061e115a5SMichael Buesch 		bus->power_warn_count++;
55161e115a5SMichael Buesch 		dump_stack();
55261e115a5SMichael Buesch 	}
55361e115a5SMichael Buesch 
55461e115a5SMichael Buesch 	return -ENODEV;
55561e115a5SMichael Buesch }
55661e115a5SMichael Buesch #else /* DEBUG */
55761e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus)
55861e115a5SMichael Buesch {
55961e115a5SMichael Buesch 	return 0;
56061e115a5SMichael Buesch }
56161e115a5SMichael Buesch #endif /* DEBUG */
56261e115a5SMichael Buesch 
56361e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
56461e115a5SMichael Buesch {
56561e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
56661e115a5SMichael Buesch 
56761e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
56861e115a5SMichael Buesch 		return 0xFFFF;
56961e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
57061e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
57161e115a5SMichael Buesch 			return 0xFFFF;
57261e115a5SMichael Buesch 	}
5734b402c65SMichael Buesch 	return ioread16(bus->mmio + offset);
57461e115a5SMichael Buesch }
57561e115a5SMichael Buesch 
57661e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
57761e115a5SMichael Buesch {
57861e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
57961e115a5SMichael Buesch 
58061e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
58161e115a5SMichael Buesch 		return 0xFFFFFFFF;
58261e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
58361e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
58461e115a5SMichael Buesch 			return 0xFFFFFFFF;
58561e115a5SMichael Buesch 	}
5864b402c65SMichael Buesch 	return ioread32(bus->mmio + offset);
58761e115a5SMichael Buesch }
58861e115a5SMichael Buesch 
58961e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
59061e115a5SMichael Buesch {
59161e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
59261e115a5SMichael Buesch 
59361e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
59461e115a5SMichael Buesch 		return;
59561e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
59661e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
59761e115a5SMichael Buesch 			return;
59861e115a5SMichael Buesch 	}
5994b402c65SMichael Buesch 	iowrite16(value, bus->mmio + offset);
60061e115a5SMichael Buesch }
60161e115a5SMichael Buesch 
60261e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
60361e115a5SMichael Buesch {
60461e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
60561e115a5SMichael Buesch 
60661e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
60761e115a5SMichael Buesch 		return;
60861e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
60961e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
61061e115a5SMichael Buesch 			return;
61161e115a5SMichael Buesch 	}
6124b402c65SMichael Buesch 	iowrite32(value, bus->mmio + offset);
61361e115a5SMichael Buesch }
61461e115a5SMichael Buesch 
61561e115a5SMichael Buesch /* Not "static", as it's used in main.c */
61661e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = {
61761e115a5SMichael Buesch 	.read16		= ssb_pci_read16,
61861e115a5SMichael Buesch 	.read32		= ssb_pci_read32,
61961e115a5SMichael Buesch 	.write16	= ssb_pci_write16,
62061e115a5SMichael Buesch 	.write32	= ssb_pci_write32,
62161e115a5SMichael Buesch };
62261e115a5SMichael Buesch 
623*c272ef44SLarry Finger static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, u16 size)
62461e115a5SMichael Buesch {
62561e115a5SMichael Buesch 	int i, pos = 0;
62661e115a5SMichael Buesch 
627*c272ef44SLarry Finger 	for (i = 0; i < size; i++)
62861e115a5SMichael Buesch 		pos += snprintf(buf + pos, buf_len - pos - 1,
62961e115a5SMichael Buesch 				"%04X", swab16(sprom[i]) & 0xFFFF);
63061e115a5SMichael Buesch 	pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
63161e115a5SMichael Buesch 
63261e115a5SMichael Buesch 	return pos + 1;
63361e115a5SMichael Buesch }
63461e115a5SMichael Buesch 
635*c272ef44SLarry Finger static int hex2sprom(u16 *sprom, const char *dump, size_t len, u16 size)
63661e115a5SMichael Buesch {
63761e115a5SMichael Buesch 	char tmp[5] = { 0 };
63861e115a5SMichael Buesch 	int cnt = 0;
63961e115a5SMichael Buesch 	unsigned long parsed;
64061e115a5SMichael Buesch 
641*c272ef44SLarry Finger 	if (len < size * 2)
64261e115a5SMichael Buesch 		return -EINVAL;
64361e115a5SMichael Buesch 
644*c272ef44SLarry Finger 	while (cnt < size) {
64561e115a5SMichael Buesch 		memcpy(tmp, dump, 4);
64661e115a5SMichael Buesch 		dump += 4;
64761e115a5SMichael Buesch 		parsed = simple_strtoul(tmp, NULL, 16);
64861e115a5SMichael Buesch 		sprom[cnt++] = swab16((u16)parsed);
64961e115a5SMichael Buesch 	}
65061e115a5SMichael Buesch 
65161e115a5SMichael Buesch 	return 0;
65261e115a5SMichael Buesch }
65361e115a5SMichael Buesch 
65461e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
65561e115a5SMichael Buesch 				       struct device_attribute *attr,
65661e115a5SMichael Buesch 				       char *buf)
65761e115a5SMichael Buesch {
65861e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
65961e115a5SMichael Buesch 	struct ssb_bus *bus;
66061e115a5SMichael Buesch 	u16 *sprom;
66161e115a5SMichael Buesch 	int err = -ENODEV;
66261e115a5SMichael Buesch 	ssize_t count = 0;
66361e115a5SMichael Buesch 
66461e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
66561e115a5SMichael Buesch 	if (!bus)
66661e115a5SMichael Buesch 		goto out;
66761e115a5SMichael Buesch 	err = -ENOMEM;
668*c272ef44SLarry Finger 	sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
66961e115a5SMichael Buesch 	if (!sprom)
67061e115a5SMichael Buesch 		goto out;
67161e115a5SMichael Buesch 
67261e115a5SMichael Buesch 	/* Use interruptible locking, as the SPROM write might
67361e115a5SMichael Buesch 	 * be holding the lock for several seconds. So allow userspace
67461e115a5SMichael Buesch 	 * to cancel operation. */
67561e115a5SMichael Buesch 	err = -ERESTARTSYS;
67661e115a5SMichael Buesch 	if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
67761e115a5SMichael Buesch 		goto out_kfree;
67861e115a5SMichael Buesch 	sprom_do_read(bus, sprom);
67961e115a5SMichael Buesch 	mutex_unlock(&bus->pci_sprom_mutex);
68061e115a5SMichael Buesch 
681*c272ef44SLarry Finger 	count = sprom2hex(sprom, buf, PAGE_SIZE, bus->sprom_size);
68261e115a5SMichael Buesch 	err = 0;
68361e115a5SMichael Buesch 
68461e115a5SMichael Buesch out_kfree:
68561e115a5SMichael Buesch 	kfree(sprom);
68661e115a5SMichael Buesch out:
68761e115a5SMichael Buesch 	return err ? err : count;
68861e115a5SMichael Buesch }
68961e115a5SMichael Buesch 
69061e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
69161e115a5SMichael Buesch 					struct device_attribute *attr,
69261e115a5SMichael Buesch 					const char *buf, size_t count)
69361e115a5SMichael Buesch {
69461e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
69561e115a5SMichael Buesch 	struct ssb_bus *bus;
69661e115a5SMichael Buesch 	u16 *sprom;
69761e115a5SMichael Buesch 	int res = 0, err = -ENODEV;
69861e115a5SMichael Buesch 
69961e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
70061e115a5SMichael Buesch 	if (!bus)
70161e115a5SMichael Buesch 		goto out;
70261e115a5SMichael Buesch 	err = -ENOMEM;
703*c272ef44SLarry Finger 	sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
70461e115a5SMichael Buesch 	if (!sprom)
70561e115a5SMichael Buesch 		goto out;
706*c272ef44SLarry Finger 	err = hex2sprom(sprom, buf, count, bus->sprom_size);
70761e115a5SMichael Buesch 	if (err) {
70861e115a5SMichael Buesch 		err = -EINVAL;
70961e115a5SMichael Buesch 		goto out_kfree;
71061e115a5SMichael Buesch 	}
711*c272ef44SLarry Finger 	err = sprom_check_crc(sprom, bus->sprom_size);
71261e115a5SMichael Buesch 	if (err) {
71361e115a5SMichael Buesch 		err = -EINVAL;
71461e115a5SMichael Buesch 		goto out_kfree;
71561e115a5SMichael Buesch 	}
71661e115a5SMichael Buesch 
71761e115a5SMichael Buesch 	/* Use interruptible locking, as the SPROM write might
71861e115a5SMichael Buesch 	 * be holding the lock for several seconds. So allow userspace
71961e115a5SMichael Buesch 	 * to cancel operation. */
72061e115a5SMichael Buesch 	err = -ERESTARTSYS;
72161e115a5SMichael Buesch 	if (mutex_lock_interruptible(&bus->pci_sprom_mutex))
72261e115a5SMichael Buesch 		goto out_kfree;
72361e115a5SMichael Buesch 	err = ssb_devices_freeze(bus);
72461e115a5SMichael Buesch 	if (err == -EOPNOTSUPP) {
72561e115a5SMichael Buesch 		ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. "
72661e115a5SMichael Buesch 			   "No suspend support. Is CONFIG_PM enabled?\n");
72761e115a5SMichael Buesch 		goto out_unlock;
72861e115a5SMichael Buesch 	}
72961e115a5SMichael Buesch 	if (err) {
73061e115a5SMichael Buesch 		ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n");
73161e115a5SMichael Buesch 		goto out_unlock;
73261e115a5SMichael Buesch 	}
73361e115a5SMichael Buesch 	res = sprom_do_write(bus, sprom);
73461e115a5SMichael Buesch 	err = ssb_devices_thaw(bus);
73561e115a5SMichael Buesch 	if (err)
73661e115a5SMichael Buesch 		ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n");
73761e115a5SMichael Buesch out_unlock:
73861e115a5SMichael Buesch 	mutex_unlock(&bus->pci_sprom_mutex);
73961e115a5SMichael Buesch out_kfree:
74061e115a5SMichael Buesch 	kfree(sprom);
74161e115a5SMichael Buesch out:
74261e115a5SMichael Buesch 	if (res)
74361e115a5SMichael Buesch 		return res;
74461e115a5SMichael Buesch 	return err ? err : count;
74561e115a5SMichael Buesch }
74661e115a5SMichael Buesch 
74761e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600,
74861e115a5SMichael Buesch 		   ssb_pci_attr_sprom_show,
74961e115a5SMichael Buesch 		   ssb_pci_attr_sprom_store);
75061e115a5SMichael Buesch 
75161e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus)
75261e115a5SMichael Buesch {
75361e115a5SMichael Buesch 	struct pci_dev *pdev;
75461e115a5SMichael Buesch 
75561e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
75661e115a5SMichael Buesch 		return;
75761e115a5SMichael Buesch 
75861e115a5SMichael Buesch 	pdev = bus->host_pci;
75961e115a5SMichael Buesch 	device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
76061e115a5SMichael Buesch }
76161e115a5SMichael Buesch 
76261e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus)
76361e115a5SMichael Buesch {
76461e115a5SMichael Buesch 	struct pci_dev *pdev;
76561e115a5SMichael Buesch 	int err;
76661e115a5SMichael Buesch 
76761e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
76861e115a5SMichael Buesch 		return 0;
76961e115a5SMichael Buesch 
77061e115a5SMichael Buesch 	pdev = bus->host_pci;
77161e115a5SMichael Buesch 	mutex_init(&bus->pci_sprom_mutex);
77261e115a5SMichael Buesch 	err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
77361e115a5SMichael Buesch 	if (err)
77461e115a5SMichael Buesch 		goto out;
77561e115a5SMichael Buesch 
77661e115a5SMichael Buesch out:
77761e115a5SMichael Buesch 	return err;
77861e115a5SMichael Buesch }
779