xref: /openbmc/linux/drivers/ssb/pci.c (revision 095f695cbb07281682462da0618fffabb499d0be)
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 
215c272ef44SLarry Finger static u8 ssb_sprom_crc(const u16 *sprom, u16 size)
21661e115a5SMichael Buesch {
21761e115a5SMichael Buesch 	int word;
21861e115a5SMichael Buesch 	u8 crc = 0xFF;
21961e115a5SMichael Buesch 
220c272ef44SLarry 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 	}
224c272ef44SLarry Finger 	crc = ssb_crc8(crc, sprom[size - 1] & 0x00FF);
22561e115a5SMichael Buesch 	crc ^= 0xFF;
22661e115a5SMichael Buesch 
22761e115a5SMichael Buesch 	return crc;
22861e115a5SMichael Buesch }
22961e115a5SMichael Buesch 
230e7ec2e32SMichael Buesch static int sprom_check_crc(const u16 *sprom, size_t size)
23161e115a5SMichael Buesch {
23261e115a5SMichael Buesch 	u8 crc;
23361e115a5SMichael Buesch 	u8 expected_crc;
23461e115a5SMichael Buesch 	u16 tmp;
23561e115a5SMichael Buesch 
236c272ef44SLarry Finger 	crc = ssb_sprom_crc(sprom, size);
237c272ef44SLarry 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 
245e7ec2e32SMichael Buesch static int sprom_do_read(struct ssb_bus *bus, u16 *sprom)
24661e115a5SMichael Buesch {
24761e115a5SMichael Buesch 	int i;
24861e115a5SMichael Buesch 
249c272ef44SLarry Finger 	for (i = 0; i < bus->sprom_size; i++)
250e861b98dSMichael Buesch 		sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2));
251e7ec2e32SMichael Buesch 
252e7ec2e32SMichael Buesch 	return 0;
25361e115a5SMichael Buesch }
25461e115a5SMichael Buesch 
25561e115a5SMichael Buesch static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom)
25661e115a5SMichael Buesch {
25761e115a5SMichael Buesch 	struct pci_dev *pdev = bus->host_pci;
25861e115a5SMichael Buesch 	int i, err;
25961e115a5SMichael Buesch 	u32 spromctl;
260c272ef44SLarry Finger 	u16 size = bus->sprom_size;
26161e115a5SMichael Buesch 
26261e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");
26361e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
26461e115a5SMichael Buesch 	if (err)
26561e115a5SMichael Buesch 		goto err_ctlreg;
26661e115a5SMichael Buesch 	spromctl |= SSB_SPROMCTL_WE;
26761e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
26861e115a5SMichael Buesch 	if (err)
26961e115a5SMichael Buesch 		goto err_ctlreg;
27061e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "[ 0%%");
27161e115a5SMichael Buesch 	msleep(500);
272c272ef44SLarry Finger 	for (i = 0; i < size; i++) {
273c272ef44SLarry Finger 		if (i == size / 4)
27461e115a5SMichael Buesch 			ssb_printk("25%%");
275c272ef44SLarry Finger 		else if (i == size / 2)
27661e115a5SMichael Buesch 			ssb_printk("50%%");
277c272ef44SLarry Finger 		else if (i == (size * 3) / 4)
27861e115a5SMichael Buesch 			ssb_printk("75%%");
27961e115a5SMichael Buesch 		else if (i % 2)
28061e115a5SMichael Buesch 			ssb_printk(".");
28161e115a5SMichael Buesch 		writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2));
28261e115a5SMichael Buesch 		mmiowb();
28361e115a5SMichael Buesch 		msleep(20);
28461e115a5SMichael Buesch 	}
28561e115a5SMichael Buesch 	err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl);
28661e115a5SMichael Buesch 	if (err)
28761e115a5SMichael Buesch 		goto err_ctlreg;
28861e115a5SMichael Buesch 	spromctl &= ~SSB_SPROMCTL_WE;
28961e115a5SMichael Buesch 	err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl);
29061e115a5SMichael Buesch 	if (err)
29161e115a5SMichael Buesch 		goto err_ctlreg;
29261e115a5SMichael Buesch 	msleep(500);
29361e115a5SMichael Buesch 	ssb_printk("100%% ]\n");
29461e115a5SMichael Buesch 	ssb_printk(KERN_NOTICE PFX "SPROM written.\n");
29561e115a5SMichael Buesch 
29661e115a5SMichael Buesch 	return 0;
29761e115a5SMichael Buesch err_ctlreg:
29861e115a5SMichael Buesch 	ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n");
29961e115a5SMichael Buesch 	return err;
30061e115a5SMichael Buesch }
30161e115a5SMichael Buesch 
302e861b98dSMichael Buesch static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in,
303e861b98dSMichael Buesch 			       u16 mask, u16 shift)
304e861b98dSMichael Buesch {
305e861b98dSMichael Buesch 	u16 v;
306e861b98dSMichael Buesch 	u8 gain;
307e861b98dSMichael Buesch 
308e861b98dSMichael Buesch 	v = in[SPOFF(SSB_SPROM1_AGAIN)];
309e861b98dSMichael Buesch 	gain = (v & mask) >> shift;
310e861b98dSMichael Buesch 	if (gain == 0xFF)
311e861b98dSMichael Buesch 		gain = 2; /* If unset use 2dBm */
312e861b98dSMichael Buesch 	if (sprom_revision == 1) {
313e861b98dSMichael Buesch 		/* Convert to Q5.2 */
314e861b98dSMichael Buesch 		gain <<= 2;
315e861b98dSMichael Buesch 	} else {
316e861b98dSMichael Buesch 		/* Q5.2 Fractional part is stored in 0xC0 */
317e861b98dSMichael Buesch 		gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2);
318e861b98dSMichael Buesch 	}
319e861b98dSMichael Buesch 
320e861b98dSMichael Buesch 	return (s8)gain;
321e861b98dSMichael Buesch }
322e861b98dSMichael Buesch 
323c272ef44SLarry Finger static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in)
324c272ef44SLarry Finger {
325c272ef44SLarry Finger 	int i;
326c272ef44SLarry Finger 	u16 v;
327e861b98dSMichael Buesch 	s8 gain;
328c272ef44SLarry Finger 	u16 loc[3];
329c272ef44SLarry Finger 
330c272ef44SLarry Finger 	if (out->revision == 3) {			/* rev 3 moved MAC */
331c272ef44SLarry Finger 		loc[0] = SSB_SPROM3_IL0MAC;
332c272ef44SLarry Finger 		loc[1] = SSB_SPROM3_ET0MAC;
333c272ef44SLarry Finger 		loc[2] = SSB_SPROM3_ET1MAC;
334c272ef44SLarry Finger 	} else {
335c272ef44SLarry Finger 		loc[0] = SSB_SPROM1_IL0MAC;
336c272ef44SLarry Finger 		loc[1] = SSB_SPROM1_ET0MAC;
337c272ef44SLarry Finger 		loc[2] = SSB_SPROM1_ET1MAC;
338c272ef44SLarry Finger 	}
339c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
340c272ef44SLarry Finger 		v = in[SPOFF(loc[0]) + i];
341c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
342c272ef44SLarry Finger 	}
343c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
344c272ef44SLarry Finger 		v = in[SPOFF(loc[1]) + i];
345c272ef44SLarry Finger 		*(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
346c272ef44SLarry Finger 	}
347c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
348c272ef44SLarry Finger 		v = in[SPOFF(loc[2]) + i];
349c272ef44SLarry Finger 		*(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
350c272ef44SLarry Finger 	}
351c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0);
352c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A,
353c272ef44SLarry Finger 	     SSB_SPROM1_ETHPHY_ET1A_SHIFT);
354e861b98dSMichael Buesch 	SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14);
355e861b98dSMichael Buesch 	SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15);
356e861b98dSMichael Buesch 	SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0);
357c272ef44SLarry Finger 	SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE,
358c272ef44SLarry Finger 	     SSB_SPROM1_BINF_CCODE_SHIFT);
359e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA,
360e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTA_SHIFT);
361e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG,
362e861b98dSMichael Buesch 	     SSB_SPROM1_BINF_ANTBG_SHIFT);
363c272ef44SLarry Finger 	SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0);
364c272ef44SLarry Finger 	SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0);
365c272ef44SLarry Finger 	SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0);
366c272ef44SLarry Finger 	SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0);
367c272ef44SLarry Finger 	SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0);
368c272ef44SLarry Finger 	SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0);
369c272ef44SLarry Finger 	SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0);
370c272ef44SLarry Finger 	SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1,
371c272ef44SLarry Finger 	     SSB_SPROM1_GPIOA_P1_SHIFT);
372c272ef44SLarry Finger 	SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0);
373c272ef44SLarry Finger 	SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3,
374c272ef44SLarry Finger 	     SSB_SPROM1_GPIOB_P3_SHIFT);
375c272ef44SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A,
376c272ef44SLarry Finger 	     SSB_SPROM1_MAXPWR_A_SHIFT);
377c272ef44SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0);
378c272ef44SLarry Finger 	SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A,
379c272ef44SLarry Finger 	     SSB_SPROM1_ITSSI_A_SHIFT);
380c272ef44SLarry Finger 	SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0);
381c272ef44SLarry Finger 	SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0);
382af4b7450SMichael Buesch 	if (out->revision >= 2)
383af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0);
384e861b98dSMichael Buesch 
385e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
386e861b98dSMichael Buesch 	gain = r123_extract_antgain(out->revision, in,
387e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_BG,
388c272ef44SLarry Finger 				    SSB_SPROM1_AGAIN_BG_SHIFT);
389e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a0 = gain;
390e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a1 = gain;
391e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a2 = gain;
392e861b98dSMichael Buesch 	out->antenna_gain.ghz24.a3 = gain;
393e861b98dSMichael Buesch 	gain = r123_extract_antgain(out->revision, in,
394e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_A,
395e861b98dSMichael Buesch 				    SSB_SPROM1_AGAIN_A_SHIFT);
396e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a0 = gain;
397e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a1 = gain;
398e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a2 = gain;
399e861b98dSMichael Buesch 	out->antenna_gain.ghz5.a3 = gain;
400c272ef44SLarry Finger }
401c272ef44SLarry Finger 
402*095f695cSLarry Finger static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in)
40361e115a5SMichael Buesch {
40461e115a5SMichael Buesch 	int i;
40561e115a5SMichael Buesch 	u16 v;
406*095f695cSLarry Finger 	u16 il0mac_offset;
40761e115a5SMichael Buesch 
408*095f695cSLarry Finger 	if (out->revision == 4)
409*095f695cSLarry Finger 		il0mac_offset = SSB_SPROM4_IL0MAC;
410*095f695cSLarry Finger 	else
411*095f695cSLarry Finger 		il0mac_offset = SSB_SPROM5_IL0MAC;
412d3c319f9SLarry Finger 	/* extract the equivalent of the r1 variables */
413c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
414*095f695cSLarry Finger 		v = in[SPOFF(il0mac_offset) + i];
415c272ef44SLarry Finger 		*(((__be16 *)out->il0mac) + i) = cpu_to_be16(v);
41661e115a5SMichael Buesch 	}
417c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
418c272ef44SLarry Finger 		v = in[SPOFF(SSB_SPROM4_ET0MAC) + i];
419c272ef44SLarry Finger 		*(((__be16 *)out->et0mac) + i) = cpu_to_be16(v);
420c272ef44SLarry Finger 	}
421c272ef44SLarry Finger 	for (i = 0; i < 3; i++) {
422c272ef44SLarry Finger 		v = in[SPOFF(SSB_SPROM4_ET1MAC) + i];
423c272ef44SLarry Finger 		*(((__be16 *)out->et1mac) + i) = cpu_to_be16(v);
424c272ef44SLarry Finger 	}
425c272ef44SLarry Finger 	SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0);
426c272ef44SLarry Finger 	SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A,
427c272ef44SLarry Finger 	     SSB_SPROM4_ETHPHY_ET1A_SHIFT);
428*095f695cSLarry Finger 	if (out->revision == 4) {
429c272ef44SLarry Finger 		SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0);
430c272ef44SLarry Finger 		SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0);
431af4b7450SMichael Buesch 		SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0);
432*095f695cSLarry Finger 	} else {
433*095f695cSLarry Finger 		SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0);
434*095f695cSLarry Finger 		SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0);
435*095f695cSLarry Finger 		SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0);
436*095f695cSLarry Finger 	}
437e861b98dSMichael Buesch 	SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A,
438e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_A_SHIFT);
439e861b98dSMichael Buesch 	SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG,
440e861b98dSMichael Buesch 	     SSB_SPROM4_ANTAVAIL_BG_SHIFT);
441d3c319f9SLarry Finger 	SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0);
442d3c319f9SLarry Finger 	SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG,
443d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_BG_SHIFT);
444d3c319f9SLarry Finger 	SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0);
445d3c319f9SLarry Finger 	SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A,
446d3c319f9SLarry Finger 	     SSB_SPROM4_ITSSI_A_SHIFT);
447*095f695cSLarry Finger 	if (out->revision == 4) {
448d3c319f9SLarry Finger 		SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0);
449d3c319f9SLarry Finger 		SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1,
450d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOA_P1_SHIFT);
451d3c319f9SLarry Finger 		SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0);
452d3c319f9SLarry Finger 		SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3,
453d3c319f9SLarry Finger 		     SSB_SPROM4_GPIOB_P3_SHIFT);
454*095f695cSLarry Finger 	} else {
455*095f695cSLarry Finger 		SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0);
456*095f695cSLarry Finger 		SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1,
457*095f695cSLarry Finger 		     SSB_SPROM5_GPIOA_P1_SHIFT);
458*095f695cSLarry Finger 		SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0);
459*095f695cSLarry Finger 		SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3,
460*095f695cSLarry Finger 		     SSB_SPROM5_GPIOB_P3_SHIFT);
461*095f695cSLarry Finger 	}
462e861b98dSMichael Buesch 
463e861b98dSMichael Buesch 	/* Extract the antenna gain values. */
464e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01,
465e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT);
466e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01,
467e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT);
468e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23,
469e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT);
470e861b98dSMichael Buesch 	SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23,
471e861b98dSMichael Buesch 	     SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT);
472e861b98dSMichael Buesch 	memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24,
473e861b98dSMichael Buesch 	       sizeof(out->antenna_gain.ghz5));
474e861b98dSMichael Buesch 
475c272ef44SLarry Finger 	/* TODO - get remaining rev 4 stuff needed */
47661e115a5SMichael Buesch }
47761e115a5SMichael Buesch 
478c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out,
479c272ef44SLarry Finger 			 const u16 *in, u16 size)
48061e115a5SMichael Buesch {
48161e115a5SMichael Buesch 	memset(out, 0, sizeof(*out));
48261e115a5SMichael Buesch 
483c272ef44SLarry Finger 	out->revision = in[size - 1] & 0x00FF;
484e861b98dSMichael Buesch 	ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision);
48561e115a5SMichael Buesch 	if ((bus->chip_id & 0xFF00) == 0x4400) {
48661e115a5SMichael Buesch 		/* Workaround: The BCM44XX chip has a stupid revision
48761e115a5SMichael Buesch 		 * number stored in the SPROM.
48861e115a5SMichael Buesch 		 * Always extract r1. */
489c272ef44SLarry Finger 		out->revision = 1;
490c272ef44SLarry Finger 		sprom_extract_r123(out, in);
491c272ef44SLarry Finger 	} else if (bus->chip_id == 0x4321) {
492c272ef44SLarry Finger 		/* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */
493c272ef44SLarry Finger 		out->revision = 4;
494*095f695cSLarry Finger 		sprom_extract_r45(out, in);
49561e115a5SMichael Buesch 	} else {
49661e115a5SMichael Buesch 		if (out->revision == 0)
49761e115a5SMichael Buesch 			goto unsupported;
498c272ef44SLarry Finger 		if (out->revision >= 1 && out->revision <= 3) {
499c272ef44SLarry Finger 			sprom_extract_r123(out, in);
500c272ef44SLarry Finger 		}
501*095f695cSLarry Finger 		if (out->revision == 4 || out->revision == 5)
502*095f695cSLarry Finger 			sprom_extract_r45(out, in);
503*095f695cSLarry Finger 		if (out->revision > 5)
50461e115a5SMichael Buesch 			goto unsupported;
50561e115a5SMichael Buesch 	}
50661e115a5SMichael Buesch 
5074503183aSLarry Finger 	if (out->boardflags_lo == 0xFFFF)
5084503183aSLarry Finger 		out->boardflags_lo = 0;  /* per specs */
5094503183aSLarry Finger 	if (out->boardflags_hi == 0xFFFF)
5104503183aSLarry Finger 		out->boardflags_hi = 0;  /* per specs */
5114503183aSLarry Finger 
51261e115a5SMichael Buesch 	return 0;
51361e115a5SMichael Buesch unsupported:
51461e115a5SMichael Buesch 	ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d "
51561e115a5SMichael Buesch 		   "detected. Will extract v1\n", out->revision);
516d3c319f9SLarry Finger 	sprom_extract_r123(out, in);
51761e115a5SMichael Buesch 	return 0;
51861e115a5SMichael Buesch }
51961e115a5SMichael Buesch 
52061e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus,
52161e115a5SMichael Buesch 			     struct ssb_sprom *sprom)
52261e115a5SMichael Buesch {
52361e115a5SMichael Buesch 	int err = -ENOMEM;
52461e115a5SMichael Buesch 	u16 *buf;
52561e115a5SMichael Buesch 
526c272ef44SLarry Finger 	buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
52761e115a5SMichael Buesch 	if (!buf)
52861e115a5SMichael Buesch 		goto out;
529c272ef44SLarry Finger 	bus->sprom_size = SSB_SPROMSIZE_WORDS_R123;
53061e115a5SMichael Buesch 	sprom_do_read(bus, buf);
531c272ef44SLarry Finger 	err = sprom_check_crc(buf, bus->sprom_size);
53261e115a5SMichael Buesch 	if (err) {
5332afc4901SLarry.Finger@lwfinger.net 		/* try for a 440 byte SPROM - revision 4 and higher */
534c272ef44SLarry Finger 		kfree(buf);
535c272ef44SLarry Finger 		buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16),
536c272ef44SLarry Finger 			      GFP_KERNEL);
537c272ef44SLarry Finger 		if (!buf)
538c272ef44SLarry Finger 			goto out;
539c272ef44SLarry Finger 		bus->sprom_size = SSB_SPROMSIZE_WORDS_R4;
540c272ef44SLarry Finger 		sprom_do_read(bus, buf);
541c272ef44SLarry Finger 		err = sprom_check_crc(buf, bus->sprom_size);
542c272ef44SLarry Finger 		if (err)
543c272ef44SLarry Finger 			ssb_printk(KERN_WARNING PFX "WARNING: Invalid"
544c272ef44SLarry Finger 				   " SPROM CRC (corrupt SPROM)\n");
545c272ef44SLarry Finger 	}
546c272ef44SLarry Finger 	err = sprom_extract(bus, sprom, buf, bus->sprom_size);
54761e115a5SMichael Buesch 
54861e115a5SMichael Buesch 	kfree(buf);
54961e115a5SMichael Buesch out:
55061e115a5SMichael Buesch 	return err;
55161e115a5SMichael Buesch }
55261e115a5SMichael Buesch 
55361e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus,
55461e115a5SMichael Buesch 				  struct ssb_boardinfo *bi)
55561e115a5SMichael Buesch {
55661e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID,
55761e115a5SMichael Buesch 			     &bi->vendor);
55861e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID,
55961e115a5SMichael Buesch 			     &bi->type);
56061e115a5SMichael Buesch 	pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
56161e115a5SMichael Buesch 			     &bi->rev);
56261e115a5SMichael Buesch }
56361e115a5SMichael Buesch 
56461e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus,
56561e115a5SMichael Buesch 			   struct ssb_init_invariants *iv)
56661e115a5SMichael Buesch {
56761e115a5SMichael Buesch 	int err;
56861e115a5SMichael Buesch 
56961e115a5SMichael Buesch 	err = ssb_pci_sprom_get(bus, &iv->sprom);
57061e115a5SMichael Buesch 	if (err)
57161e115a5SMichael Buesch 		goto out;
57261e115a5SMichael Buesch 	ssb_pci_get_boardinfo(bus, &iv->boardinfo);
57361e115a5SMichael Buesch 
57461e115a5SMichael Buesch out:
57561e115a5SMichael Buesch 	return err;
57661e115a5SMichael Buesch }
57761e115a5SMichael Buesch 
57861e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG
57961e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus)
58061e115a5SMichael Buesch {
58161e115a5SMichael Buesch 	if (likely(bus->powered_up))
58261e115a5SMichael Buesch 		return 0;
58361e115a5SMichael Buesch 
58461e115a5SMichael Buesch 	printk(KERN_ERR PFX "FATAL ERROR: Bus powered down "
58561e115a5SMichael Buesch 	       "while accessing PCI MMIO space\n");
58661e115a5SMichael Buesch 	if (bus->power_warn_count <= 10) {
58761e115a5SMichael Buesch 		bus->power_warn_count++;
58861e115a5SMichael Buesch 		dump_stack();
58961e115a5SMichael Buesch 	}
59061e115a5SMichael Buesch 
59161e115a5SMichael Buesch 	return -ENODEV;
59261e115a5SMichael Buesch }
59361e115a5SMichael Buesch #else /* DEBUG */
59461e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus)
59561e115a5SMichael Buesch {
59661e115a5SMichael Buesch 	return 0;
59761e115a5SMichael Buesch }
59861e115a5SMichael Buesch #endif /* DEBUG */
59961e115a5SMichael Buesch 
600ffc7689dSMichael Buesch static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset)
601ffc7689dSMichael Buesch {
602ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
603ffc7689dSMichael Buesch 
604ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
605ffc7689dSMichael Buesch 		return 0xFF;
606ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
607ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
608ffc7689dSMichael Buesch 			return 0xFF;
609ffc7689dSMichael Buesch 	}
610ffc7689dSMichael Buesch 	return ioread8(bus->mmio + offset);
611ffc7689dSMichael Buesch }
612ffc7689dSMichael Buesch 
61361e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset)
61461e115a5SMichael Buesch {
61561e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
61661e115a5SMichael Buesch 
61761e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
61861e115a5SMichael Buesch 		return 0xFFFF;
61961e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
62061e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
62161e115a5SMichael Buesch 			return 0xFFFF;
62261e115a5SMichael Buesch 	}
6234b402c65SMichael Buesch 	return ioread16(bus->mmio + offset);
62461e115a5SMichael Buesch }
62561e115a5SMichael Buesch 
62661e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset)
62761e115a5SMichael Buesch {
62861e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
62961e115a5SMichael Buesch 
63061e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
63161e115a5SMichael Buesch 		return 0xFFFFFFFF;
63261e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
63361e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
63461e115a5SMichael Buesch 			return 0xFFFFFFFF;
63561e115a5SMichael Buesch 	}
6364b402c65SMichael Buesch 	return ioread32(bus->mmio + offset);
63761e115a5SMichael Buesch }
63861e115a5SMichael Buesch 
639d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
640d625a29bSMichael Buesch static void ssb_pci_block_read(struct ssb_device *dev, void *buffer,
641d625a29bSMichael Buesch 			       size_t count, u16 offset, u8 reg_width)
642d625a29bSMichael Buesch {
643d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
644d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
645d625a29bSMichael Buesch 
646d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
647d625a29bSMichael Buesch 		goto error;
648d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
649d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
650d625a29bSMichael Buesch 			goto error;
651d625a29bSMichael Buesch 	}
652d625a29bSMichael Buesch 	switch (reg_width) {
653d625a29bSMichael Buesch 	case sizeof(u8):
654d625a29bSMichael Buesch 		ioread8_rep(addr, buffer, count);
655d625a29bSMichael Buesch 		break;
656d625a29bSMichael Buesch 	case sizeof(u16):
657d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
658d625a29bSMichael Buesch 		ioread16_rep(addr, buffer, count >> 1);
659d625a29bSMichael Buesch 		break;
660d625a29bSMichael Buesch 	case sizeof(u32):
661d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
662d625a29bSMichael Buesch 		ioread32_rep(addr, buffer, count >> 2);
663d625a29bSMichael Buesch 		break;
664d625a29bSMichael Buesch 	default:
665d625a29bSMichael Buesch 		SSB_WARN_ON(1);
666d625a29bSMichael Buesch 	}
667d625a29bSMichael Buesch 
668d625a29bSMichael Buesch 	return;
669d625a29bSMichael Buesch error:
670d625a29bSMichael Buesch 	memset(buffer, 0xFF, count);
671d625a29bSMichael Buesch }
672d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
673d625a29bSMichael Buesch 
674ffc7689dSMichael Buesch static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value)
675ffc7689dSMichael Buesch {
676ffc7689dSMichael Buesch 	struct ssb_bus *bus = dev->bus;
677ffc7689dSMichael Buesch 
678ffc7689dSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
679ffc7689dSMichael Buesch 		return;
680ffc7689dSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
681ffc7689dSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
682ffc7689dSMichael Buesch 			return;
683ffc7689dSMichael Buesch 	}
684ffc7689dSMichael Buesch 	iowrite8(value, bus->mmio + offset);
685ffc7689dSMichael Buesch }
686ffc7689dSMichael Buesch 
68761e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value)
68861e115a5SMichael Buesch {
68961e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
69061e115a5SMichael Buesch 
69161e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
69261e115a5SMichael Buesch 		return;
69361e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
69461e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
69561e115a5SMichael Buesch 			return;
69661e115a5SMichael Buesch 	}
6974b402c65SMichael Buesch 	iowrite16(value, bus->mmio + offset);
69861e115a5SMichael Buesch }
69961e115a5SMichael Buesch 
70061e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value)
70161e115a5SMichael Buesch {
70261e115a5SMichael Buesch 	struct ssb_bus *bus = dev->bus;
70361e115a5SMichael Buesch 
70461e115a5SMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
70561e115a5SMichael Buesch 		return;
70661e115a5SMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
70761e115a5SMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
70861e115a5SMichael Buesch 			return;
70961e115a5SMichael Buesch 	}
7104b402c65SMichael Buesch 	iowrite32(value, bus->mmio + offset);
71161e115a5SMichael Buesch }
71261e115a5SMichael Buesch 
713d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
714d625a29bSMichael Buesch static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer,
715d625a29bSMichael Buesch 				size_t count, u16 offset, u8 reg_width)
716d625a29bSMichael Buesch {
717d625a29bSMichael Buesch 	struct ssb_bus *bus = dev->bus;
718d625a29bSMichael Buesch 	void __iomem *addr = bus->mmio + offset;
719d625a29bSMichael Buesch 
720d625a29bSMichael Buesch 	if (unlikely(ssb_pci_assert_buspower(bus)))
721d625a29bSMichael Buesch 		return;
722d625a29bSMichael Buesch 	if (unlikely(bus->mapped_device != dev)) {
723d625a29bSMichael Buesch 		if (unlikely(ssb_pci_switch_core(bus, dev)))
724d625a29bSMichael Buesch 			return;
725d625a29bSMichael Buesch 	}
726d625a29bSMichael Buesch 	switch (reg_width) {
727d625a29bSMichael Buesch 	case sizeof(u8):
728d625a29bSMichael Buesch 		iowrite8_rep(addr, buffer, count);
729d625a29bSMichael Buesch 		break;
730d625a29bSMichael Buesch 	case sizeof(u16):
731d625a29bSMichael Buesch 		SSB_WARN_ON(count & 1);
732d625a29bSMichael Buesch 		iowrite16_rep(addr, buffer, count >> 1);
733d625a29bSMichael Buesch 		break;
734d625a29bSMichael Buesch 	case sizeof(u32):
735d625a29bSMichael Buesch 		SSB_WARN_ON(count & 3);
736d625a29bSMichael Buesch 		iowrite32_rep(addr, buffer, count >> 2);
737d625a29bSMichael Buesch 		break;
738d625a29bSMichael Buesch 	default:
739d625a29bSMichael Buesch 		SSB_WARN_ON(1);
740d625a29bSMichael Buesch 	}
741d625a29bSMichael Buesch }
742d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */
743d625a29bSMichael Buesch 
74461e115a5SMichael Buesch /* Not "static", as it's used in main.c */
74561e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = {
746ffc7689dSMichael Buesch 	.read8		= ssb_pci_read8,
74761e115a5SMichael Buesch 	.read16		= ssb_pci_read16,
74861e115a5SMichael Buesch 	.read32		= ssb_pci_read32,
749ffc7689dSMichael Buesch 	.write8		= ssb_pci_write8,
75061e115a5SMichael Buesch 	.write16	= ssb_pci_write16,
75161e115a5SMichael Buesch 	.write32	= ssb_pci_write32,
752d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO
753d625a29bSMichael Buesch 	.block_read	= ssb_pci_block_read,
754d625a29bSMichael Buesch 	.block_write	= ssb_pci_block_write,
755d625a29bSMichael Buesch #endif
75661e115a5SMichael Buesch };
75761e115a5SMichael Buesch 
75861e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev,
75961e115a5SMichael Buesch 				       struct device_attribute *attr,
76061e115a5SMichael Buesch 				       char *buf)
76161e115a5SMichael Buesch {
76261e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
76361e115a5SMichael Buesch 	struct ssb_bus *bus;
76461e115a5SMichael Buesch 
76561e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
76661e115a5SMichael Buesch 	if (!bus)
767e7ec2e32SMichael Buesch 		return -ENODEV;
76861e115a5SMichael Buesch 
769e7ec2e32SMichael Buesch 	return ssb_attr_sprom_show(bus, buf, sprom_do_read);
77061e115a5SMichael Buesch }
77161e115a5SMichael Buesch 
77261e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev,
77361e115a5SMichael Buesch 					struct device_attribute *attr,
77461e115a5SMichael Buesch 					const char *buf, size_t count)
77561e115a5SMichael Buesch {
77661e115a5SMichael Buesch 	struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev);
77761e115a5SMichael Buesch 	struct ssb_bus *bus;
77861e115a5SMichael Buesch 
77961e115a5SMichael Buesch 	bus = ssb_pci_dev_to_bus(pdev);
78061e115a5SMichael Buesch 	if (!bus)
781e7ec2e32SMichael Buesch 		return -ENODEV;
78261e115a5SMichael Buesch 
783e7ec2e32SMichael Buesch 	return ssb_attr_sprom_store(bus, buf, count,
784e7ec2e32SMichael Buesch 				    sprom_check_crc, sprom_do_write);
78561e115a5SMichael Buesch }
78661e115a5SMichael Buesch 
78761e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600,
78861e115a5SMichael Buesch 		   ssb_pci_attr_sprom_show,
78961e115a5SMichael Buesch 		   ssb_pci_attr_sprom_store);
79061e115a5SMichael Buesch 
79161e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus)
79261e115a5SMichael Buesch {
79361e115a5SMichael Buesch 	struct pci_dev *pdev;
79461e115a5SMichael Buesch 
79561e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
79661e115a5SMichael Buesch 		return;
79761e115a5SMichael Buesch 
79861e115a5SMichael Buesch 	pdev = bus->host_pci;
79961e115a5SMichael Buesch 	device_remove_file(&pdev->dev, &dev_attr_ssb_sprom);
80061e115a5SMichael Buesch }
80161e115a5SMichael Buesch 
80261e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus)
80361e115a5SMichael Buesch {
80461e115a5SMichael Buesch 	struct pci_dev *pdev;
80561e115a5SMichael Buesch 	int err;
80661e115a5SMichael Buesch 
80761e115a5SMichael Buesch 	if (bus->bustype != SSB_BUSTYPE_PCI)
80861e115a5SMichael Buesch 		return 0;
80961e115a5SMichael Buesch 
81061e115a5SMichael Buesch 	pdev = bus->host_pci;
811e7ec2e32SMichael Buesch 	mutex_init(&bus->sprom_mutex);
81261e115a5SMichael Buesch 	err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom);
81361e115a5SMichael Buesch 	if (err)
81461e115a5SMichael Buesch 		goto out;
81561e115a5SMichael Buesch 
81661e115a5SMichael Buesch out:
81761e115a5SMichael Buesch 	return err;
81861e115a5SMichael Buesch }
819