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 33031ce12fbSLarry Finger if (out->revision == 3) /* rev 3 moved MAC */ 331c272ef44SLarry Finger loc[0] = SSB_SPROM3_IL0MAC; 33231ce12fbSLarry Finger else { 333c272ef44SLarry Finger loc[0] = SSB_SPROM1_IL0MAC; 334c272ef44SLarry Finger loc[1] = SSB_SPROM1_ET0MAC; 335c272ef44SLarry Finger loc[2] = SSB_SPROM1_ET1MAC; 336c272ef44SLarry Finger } 337c272ef44SLarry Finger for (i = 0; i < 3; i++) { 338c272ef44SLarry Finger v = in[SPOFF(loc[0]) + i]; 339c272ef44SLarry Finger *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); 340c272ef44SLarry Finger } 34131ce12fbSLarry Finger if (out->revision < 3) { /* only rev 1-2 have et0, et1 */ 342c272ef44SLarry Finger for (i = 0; i < 3; i++) { 343c272ef44SLarry Finger v = in[SPOFF(loc[1]) + i]; 344c272ef44SLarry Finger *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); 345c272ef44SLarry Finger } 346c272ef44SLarry Finger for (i = 0; i < 3; i++) { 347c272ef44SLarry Finger v = in[SPOFF(loc[2]) + i]; 348c272ef44SLarry Finger *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); 349c272ef44SLarry Finger } 35031ce12fbSLarry 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 402095f695cSLarry Finger static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) 40361e115a5SMichael Buesch { 40461e115a5SMichael Buesch int i; 40561e115a5SMichael Buesch u16 v; 406095f695cSLarry Finger u16 il0mac_offset; 40761e115a5SMichael Buesch 408095f695cSLarry Finger if (out->revision == 4) 409095f695cSLarry Finger il0mac_offset = SSB_SPROM4_IL0MAC; 410095f695cSLarry Finger else 411095f695cSLarry Finger il0mac_offset = SSB_SPROM5_IL0MAC; 41231ce12fbSLarry Finger /* extract the MAC address */ 413c272ef44SLarry Finger for (i = 0; i < 3; i++) { 414095f695cSLarry Finger v = in[SPOFF(il0mac_offset) + i]; 415c272ef44SLarry Finger *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); 41661e115a5SMichael Buesch } 417c272ef44SLarry Finger SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0); 418c272ef44SLarry Finger SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A, 419c272ef44SLarry Finger SSB_SPROM4_ETHPHY_ET1A_SHIFT); 420095f695cSLarry Finger if (out->revision == 4) { 421c272ef44SLarry Finger SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0); 422c272ef44SLarry Finger SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0); 423af4b7450SMichael Buesch SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0); 424095f695cSLarry Finger } else { 425095f695cSLarry Finger SPEX(country_code, SSB_SPROM5_CCODE, 0xFFFF, 0); 426095f695cSLarry Finger SPEX(boardflags_lo, SSB_SPROM5_BFLLO, 0xFFFF, 0); 427095f695cSLarry Finger SPEX(boardflags_hi, SSB_SPROM5_BFLHI, 0xFFFF, 0); 428095f695cSLarry Finger } 429e861b98dSMichael Buesch SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A, 430e861b98dSMichael Buesch SSB_SPROM4_ANTAVAIL_A_SHIFT); 431e861b98dSMichael Buesch SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG, 432e861b98dSMichael Buesch SSB_SPROM4_ANTAVAIL_BG_SHIFT); 433d3c319f9SLarry Finger SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0); 434d3c319f9SLarry Finger SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG, 435d3c319f9SLarry Finger SSB_SPROM4_ITSSI_BG_SHIFT); 436d3c319f9SLarry Finger SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0); 437d3c319f9SLarry Finger SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A, 438d3c319f9SLarry Finger SSB_SPROM4_ITSSI_A_SHIFT); 439095f695cSLarry Finger if (out->revision == 4) { 440d3c319f9SLarry Finger SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0); 441d3c319f9SLarry Finger SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1, 442d3c319f9SLarry Finger SSB_SPROM4_GPIOA_P1_SHIFT); 443d3c319f9SLarry Finger SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0); 444d3c319f9SLarry Finger SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3, 445d3c319f9SLarry Finger SSB_SPROM4_GPIOB_P3_SHIFT); 446095f695cSLarry Finger } else { 447095f695cSLarry Finger SPEX(gpio0, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P0, 0); 448095f695cSLarry Finger SPEX(gpio1, SSB_SPROM5_GPIOA, SSB_SPROM5_GPIOA_P1, 449095f695cSLarry Finger SSB_SPROM5_GPIOA_P1_SHIFT); 450095f695cSLarry Finger SPEX(gpio2, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P2, 0); 451095f695cSLarry Finger SPEX(gpio3, SSB_SPROM5_GPIOB, SSB_SPROM5_GPIOB_P3, 452095f695cSLarry Finger SSB_SPROM5_GPIOB_P3_SHIFT); 453095f695cSLarry Finger } 454e861b98dSMichael Buesch 455e861b98dSMichael Buesch /* Extract the antenna gain values. */ 456e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01, 457e861b98dSMichael Buesch SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT); 458e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01, 459e861b98dSMichael Buesch SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT); 460e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23, 461e861b98dSMichael Buesch SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT); 462e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23, 463e861b98dSMichael Buesch SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); 464e861b98dSMichael Buesch memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, 465e861b98dSMichael Buesch sizeof(out->antenna_gain.ghz5)); 466e861b98dSMichael Buesch 467c272ef44SLarry Finger /* TODO - get remaining rev 4 stuff needed */ 46861e115a5SMichael Buesch } 46961e115a5SMichael Buesch 470*6b1c7c67SMichael Buesch static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in) 471*6b1c7c67SMichael Buesch { 472*6b1c7c67SMichael Buesch int i; 473*6b1c7c67SMichael Buesch u16 v; 474*6b1c7c67SMichael Buesch 475*6b1c7c67SMichael Buesch /* extract the MAC address */ 476*6b1c7c67SMichael Buesch for (i = 0; i < 3; i++) { 477*6b1c7c67SMichael Buesch v = in[SPOFF(SSB_SPROM1_IL0MAC) + i]; 478*6b1c7c67SMichael Buesch *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); 479*6b1c7c67SMichael Buesch } 480*6b1c7c67SMichael Buesch SPEX(country_code, SSB_SPROM8_CCODE, 0xFFFF, 0); 481*6b1c7c67SMichael Buesch SPEX(boardflags_lo, SSB_SPROM8_BFLLO, 0xFFFF, 0); 482*6b1c7c67SMichael Buesch SPEX(boardflags_hi, SSB_SPROM8_BFLHI, 0xFFFF, 0); 483*6b1c7c67SMichael Buesch SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A, 484*6b1c7c67SMichael Buesch SSB_SPROM8_ANTAVAIL_A_SHIFT); 485*6b1c7c67SMichael Buesch SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG, 486*6b1c7c67SMichael Buesch SSB_SPROM8_ANTAVAIL_BG_SHIFT); 487*6b1c7c67SMichael Buesch SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0); 488*6b1c7c67SMichael Buesch SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG, 489*6b1c7c67SMichael Buesch SSB_SPROM8_ITSSI_BG_SHIFT); 490*6b1c7c67SMichael Buesch SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0); 491*6b1c7c67SMichael Buesch SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A, 492*6b1c7c67SMichael Buesch SSB_SPROM8_ITSSI_A_SHIFT); 493*6b1c7c67SMichael Buesch SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0); 494*6b1c7c67SMichael Buesch SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1, 495*6b1c7c67SMichael Buesch SSB_SPROM8_GPIOA_P1_SHIFT); 496*6b1c7c67SMichael Buesch SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0); 497*6b1c7c67SMichael Buesch SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3, 498*6b1c7c67SMichael Buesch SSB_SPROM8_GPIOB_P3_SHIFT); 499*6b1c7c67SMichael Buesch 500*6b1c7c67SMichael Buesch /* Extract the antenna gain values. */ 501*6b1c7c67SMichael Buesch SPEX(antenna_gain.ghz24.a0, SSB_SPROM8_AGAIN01, 502*6b1c7c67SMichael Buesch SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT); 503*6b1c7c67SMichael Buesch SPEX(antenna_gain.ghz24.a1, SSB_SPROM8_AGAIN01, 504*6b1c7c67SMichael Buesch SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT); 505*6b1c7c67SMichael Buesch SPEX(antenna_gain.ghz24.a2, SSB_SPROM8_AGAIN23, 506*6b1c7c67SMichael Buesch SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT); 507*6b1c7c67SMichael Buesch SPEX(antenna_gain.ghz24.a3, SSB_SPROM8_AGAIN23, 508*6b1c7c67SMichael Buesch SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT); 509*6b1c7c67SMichael Buesch memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, 510*6b1c7c67SMichael Buesch sizeof(out->antenna_gain.ghz5)); 511*6b1c7c67SMichael Buesch 512*6b1c7c67SMichael Buesch /* TODO - get remaining rev 8 stuff needed */ 513*6b1c7c67SMichael Buesch } 514*6b1c7c67SMichael Buesch 515c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, 516c272ef44SLarry Finger const u16 *in, u16 size) 51761e115a5SMichael Buesch { 51861e115a5SMichael Buesch memset(out, 0, sizeof(*out)); 51961e115a5SMichael Buesch 520c272ef44SLarry Finger out->revision = in[size - 1] & 0x00FF; 521e861b98dSMichael Buesch ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision); 52231ce12fbSLarry Finger memset(out->et0mac, 0xFF, 6); /* preset et0 and et1 mac */ 52331ce12fbSLarry Finger memset(out->et1mac, 0xFF, 6); 52461e115a5SMichael Buesch if ((bus->chip_id & 0xFF00) == 0x4400) { 52561e115a5SMichael Buesch /* Workaround: The BCM44XX chip has a stupid revision 52661e115a5SMichael Buesch * number stored in the SPROM. 52761e115a5SMichael Buesch * Always extract r1. */ 528c272ef44SLarry Finger out->revision = 1; 529c272ef44SLarry Finger sprom_extract_r123(out, in); 530c272ef44SLarry Finger } else if (bus->chip_id == 0x4321) { 531c272ef44SLarry Finger /* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */ 532c272ef44SLarry Finger out->revision = 4; 533095f695cSLarry Finger sprom_extract_r45(out, in); 53461e115a5SMichael Buesch } else { 535*6b1c7c67SMichael Buesch switch (out->revision) { 536*6b1c7c67SMichael Buesch case 1: 537*6b1c7c67SMichael Buesch case 2: 538*6b1c7c67SMichael Buesch case 3: 539*6b1c7c67SMichael Buesch sprom_extract_r123(out, in); 540*6b1c7c67SMichael Buesch break; 541*6b1c7c67SMichael Buesch case 4: 542*6b1c7c67SMichael Buesch case 5: 543*6b1c7c67SMichael Buesch sprom_extract_r45(out, in); 544*6b1c7c67SMichael Buesch break; 545*6b1c7c67SMichael Buesch case 8: 546*6b1c7c67SMichael Buesch sprom_extract_r8(out, in); 547*6b1c7c67SMichael Buesch break; 548*6b1c7c67SMichael Buesch default: 549*6b1c7c67SMichael Buesch ssb_printk(KERN_WARNING PFX "Unsupported SPROM" 550*6b1c7c67SMichael Buesch " revision %d detected. Will extract" 551*6b1c7c67SMichael Buesch " v1\n", out->revision); 552c272ef44SLarry Finger sprom_extract_r123(out, in); 553c272ef44SLarry Finger } 55461e115a5SMichael Buesch } 55561e115a5SMichael Buesch 5564503183aSLarry Finger if (out->boardflags_lo == 0xFFFF) 5574503183aSLarry Finger out->boardflags_lo = 0; /* per specs */ 5584503183aSLarry Finger if (out->boardflags_hi == 0xFFFF) 5594503183aSLarry Finger out->boardflags_hi = 0; /* per specs */ 5604503183aSLarry Finger 56161e115a5SMichael Buesch return 0; 56261e115a5SMichael Buesch } 56361e115a5SMichael Buesch 56461e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus, 56561e115a5SMichael Buesch struct ssb_sprom *sprom) 56661e115a5SMichael Buesch { 56761e115a5SMichael Buesch int err = -ENOMEM; 56861e115a5SMichael Buesch u16 *buf; 56961e115a5SMichael Buesch 570c272ef44SLarry Finger buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL); 57161e115a5SMichael Buesch if (!buf) 57261e115a5SMichael Buesch goto out; 573c272ef44SLarry Finger bus->sprom_size = SSB_SPROMSIZE_WORDS_R123; 57461e115a5SMichael Buesch sprom_do_read(bus, buf); 575c272ef44SLarry Finger err = sprom_check_crc(buf, bus->sprom_size); 57661e115a5SMichael Buesch if (err) { 5772afc4901SLarry.Finger@lwfinger.net /* try for a 440 byte SPROM - revision 4 and higher */ 578c272ef44SLarry Finger kfree(buf); 579c272ef44SLarry Finger buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), 580c272ef44SLarry Finger GFP_KERNEL); 581c272ef44SLarry Finger if (!buf) 582c272ef44SLarry Finger goto out; 583c272ef44SLarry Finger bus->sprom_size = SSB_SPROMSIZE_WORDS_R4; 584c272ef44SLarry Finger sprom_do_read(bus, buf); 585c272ef44SLarry Finger err = sprom_check_crc(buf, bus->sprom_size); 586c272ef44SLarry Finger if (err) 587c272ef44SLarry Finger ssb_printk(KERN_WARNING PFX "WARNING: Invalid" 588c272ef44SLarry Finger " SPROM CRC (corrupt SPROM)\n"); 589c272ef44SLarry Finger } 590c272ef44SLarry Finger err = sprom_extract(bus, sprom, buf, bus->sprom_size); 59161e115a5SMichael Buesch 59261e115a5SMichael Buesch kfree(buf); 59361e115a5SMichael Buesch out: 59461e115a5SMichael Buesch return err; 59561e115a5SMichael Buesch } 59661e115a5SMichael Buesch 59761e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus, 59861e115a5SMichael Buesch struct ssb_boardinfo *bi) 59961e115a5SMichael Buesch { 60061e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID, 60161e115a5SMichael Buesch &bi->vendor); 60261e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID, 60361e115a5SMichael Buesch &bi->type); 60461e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_REVISION_ID, 60561e115a5SMichael Buesch &bi->rev); 60661e115a5SMichael Buesch } 60761e115a5SMichael Buesch 60861e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus, 60961e115a5SMichael Buesch struct ssb_init_invariants *iv) 61061e115a5SMichael Buesch { 61161e115a5SMichael Buesch int err; 61261e115a5SMichael Buesch 61361e115a5SMichael Buesch err = ssb_pci_sprom_get(bus, &iv->sprom); 61461e115a5SMichael Buesch if (err) 61561e115a5SMichael Buesch goto out; 61661e115a5SMichael Buesch ssb_pci_get_boardinfo(bus, &iv->boardinfo); 61761e115a5SMichael Buesch 61861e115a5SMichael Buesch out: 61961e115a5SMichael Buesch return err; 62061e115a5SMichael Buesch } 62161e115a5SMichael Buesch 62261e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG 62361e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus) 62461e115a5SMichael Buesch { 62561e115a5SMichael Buesch if (likely(bus->powered_up)) 62661e115a5SMichael Buesch return 0; 62761e115a5SMichael Buesch 62861e115a5SMichael Buesch printk(KERN_ERR PFX "FATAL ERROR: Bus powered down " 62961e115a5SMichael Buesch "while accessing PCI MMIO space\n"); 63061e115a5SMichael Buesch if (bus->power_warn_count <= 10) { 63161e115a5SMichael Buesch bus->power_warn_count++; 63261e115a5SMichael Buesch dump_stack(); 63361e115a5SMichael Buesch } 63461e115a5SMichael Buesch 63561e115a5SMichael Buesch return -ENODEV; 63661e115a5SMichael Buesch } 63761e115a5SMichael Buesch #else /* DEBUG */ 63861e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus) 63961e115a5SMichael Buesch { 64061e115a5SMichael Buesch return 0; 64161e115a5SMichael Buesch } 64261e115a5SMichael Buesch #endif /* DEBUG */ 64361e115a5SMichael Buesch 644ffc7689dSMichael Buesch static u8 ssb_pci_read8(struct ssb_device *dev, u16 offset) 645ffc7689dSMichael Buesch { 646ffc7689dSMichael Buesch struct ssb_bus *bus = dev->bus; 647ffc7689dSMichael Buesch 648ffc7689dSMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 649ffc7689dSMichael Buesch return 0xFF; 650ffc7689dSMichael Buesch if (unlikely(bus->mapped_device != dev)) { 651ffc7689dSMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 652ffc7689dSMichael Buesch return 0xFF; 653ffc7689dSMichael Buesch } 654ffc7689dSMichael Buesch return ioread8(bus->mmio + offset); 655ffc7689dSMichael Buesch } 656ffc7689dSMichael Buesch 65761e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) 65861e115a5SMichael Buesch { 65961e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 66061e115a5SMichael Buesch 66161e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 66261e115a5SMichael Buesch return 0xFFFF; 66361e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 66461e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 66561e115a5SMichael Buesch return 0xFFFF; 66661e115a5SMichael Buesch } 6674b402c65SMichael Buesch return ioread16(bus->mmio + offset); 66861e115a5SMichael Buesch } 66961e115a5SMichael Buesch 67061e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset) 67161e115a5SMichael Buesch { 67261e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 67361e115a5SMichael Buesch 67461e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 67561e115a5SMichael Buesch return 0xFFFFFFFF; 67661e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 67761e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 67861e115a5SMichael Buesch return 0xFFFFFFFF; 67961e115a5SMichael Buesch } 6804b402c65SMichael Buesch return ioread32(bus->mmio + offset); 68161e115a5SMichael Buesch } 68261e115a5SMichael Buesch 683d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO 684d625a29bSMichael Buesch static void ssb_pci_block_read(struct ssb_device *dev, void *buffer, 685d625a29bSMichael Buesch size_t count, u16 offset, u8 reg_width) 686d625a29bSMichael Buesch { 687d625a29bSMichael Buesch struct ssb_bus *bus = dev->bus; 688d625a29bSMichael Buesch void __iomem *addr = bus->mmio + offset; 689d625a29bSMichael Buesch 690d625a29bSMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 691d625a29bSMichael Buesch goto error; 692d625a29bSMichael Buesch if (unlikely(bus->mapped_device != dev)) { 693d625a29bSMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 694d625a29bSMichael Buesch goto error; 695d625a29bSMichael Buesch } 696d625a29bSMichael Buesch switch (reg_width) { 697d625a29bSMichael Buesch case sizeof(u8): 698d625a29bSMichael Buesch ioread8_rep(addr, buffer, count); 699d625a29bSMichael Buesch break; 700d625a29bSMichael Buesch case sizeof(u16): 701d625a29bSMichael Buesch SSB_WARN_ON(count & 1); 702d625a29bSMichael Buesch ioread16_rep(addr, buffer, count >> 1); 703d625a29bSMichael Buesch break; 704d625a29bSMichael Buesch case sizeof(u32): 705d625a29bSMichael Buesch SSB_WARN_ON(count & 3); 706d625a29bSMichael Buesch ioread32_rep(addr, buffer, count >> 2); 707d625a29bSMichael Buesch break; 708d625a29bSMichael Buesch default: 709d625a29bSMichael Buesch SSB_WARN_ON(1); 710d625a29bSMichael Buesch } 711d625a29bSMichael Buesch 712d625a29bSMichael Buesch return; 713d625a29bSMichael Buesch error: 714d625a29bSMichael Buesch memset(buffer, 0xFF, count); 715d625a29bSMichael Buesch } 716d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */ 717d625a29bSMichael Buesch 718ffc7689dSMichael Buesch static void ssb_pci_write8(struct ssb_device *dev, u16 offset, u8 value) 719ffc7689dSMichael Buesch { 720ffc7689dSMichael Buesch struct ssb_bus *bus = dev->bus; 721ffc7689dSMichael Buesch 722ffc7689dSMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 723ffc7689dSMichael Buesch return; 724ffc7689dSMichael Buesch if (unlikely(bus->mapped_device != dev)) { 725ffc7689dSMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 726ffc7689dSMichael Buesch return; 727ffc7689dSMichael Buesch } 728ffc7689dSMichael Buesch iowrite8(value, bus->mmio + offset); 729ffc7689dSMichael Buesch } 730ffc7689dSMichael Buesch 73161e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value) 73261e115a5SMichael Buesch { 73361e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 73461e115a5SMichael Buesch 73561e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 73661e115a5SMichael Buesch return; 73761e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 73861e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 73961e115a5SMichael Buesch return; 74061e115a5SMichael Buesch } 7414b402c65SMichael Buesch iowrite16(value, bus->mmio + offset); 74261e115a5SMichael Buesch } 74361e115a5SMichael Buesch 74461e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value) 74561e115a5SMichael Buesch { 74661e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 74761e115a5SMichael Buesch 74861e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 74961e115a5SMichael Buesch return; 75061e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 75161e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 75261e115a5SMichael Buesch return; 75361e115a5SMichael Buesch } 7544b402c65SMichael Buesch iowrite32(value, bus->mmio + offset); 75561e115a5SMichael Buesch } 75661e115a5SMichael Buesch 757d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO 758d625a29bSMichael Buesch static void ssb_pci_block_write(struct ssb_device *dev, const void *buffer, 759d625a29bSMichael Buesch size_t count, u16 offset, u8 reg_width) 760d625a29bSMichael Buesch { 761d625a29bSMichael Buesch struct ssb_bus *bus = dev->bus; 762d625a29bSMichael Buesch void __iomem *addr = bus->mmio + offset; 763d625a29bSMichael Buesch 764d625a29bSMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 765d625a29bSMichael Buesch return; 766d625a29bSMichael Buesch if (unlikely(bus->mapped_device != dev)) { 767d625a29bSMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 768d625a29bSMichael Buesch return; 769d625a29bSMichael Buesch } 770d625a29bSMichael Buesch switch (reg_width) { 771d625a29bSMichael Buesch case sizeof(u8): 772d625a29bSMichael Buesch iowrite8_rep(addr, buffer, count); 773d625a29bSMichael Buesch break; 774d625a29bSMichael Buesch case sizeof(u16): 775d625a29bSMichael Buesch SSB_WARN_ON(count & 1); 776d625a29bSMichael Buesch iowrite16_rep(addr, buffer, count >> 1); 777d625a29bSMichael Buesch break; 778d625a29bSMichael Buesch case sizeof(u32): 779d625a29bSMichael Buesch SSB_WARN_ON(count & 3); 780d625a29bSMichael Buesch iowrite32_rep(addr, buffer, count >> 2); 781d625a29bSMichael Buesch break; 782d625a29bSMichael Buesch default: 783d625a29bSMichael Buesch SSB_WARN_ON(1); 784d625a29bSMichael Buesch } 785d625a29bSMichael Buesch } 786d625a29bSMichael Buesch #endif /* CONFIG_SSB_BLOCKIO */ 787d625a29bSMichael Buesch 78861e115a5SMichael Buesch /* Not "static", as it's used in main.c */ 78961e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = { 790ffc7689dSMichael Buesch .read8 = ssb_pci_read8, 79161e115a5SMichael Buesch .read16 = ssb_pci_read16, 79261e115a5SMichael Buesch .read32 = ssb_pci_read32, 793ffc7689dSMichael Buesch .write8 = ssb_pci_write8, 79461e115a5SMichael Buesch .write16 = ssb_pci_write16, 79561e115a5SMichael Buesch .write32 = ssb_pci_write32, 796d625a29bSMichael Buesch #ifdef CONFIG_SSB_BLOCKIO 797d625a29bSMichael Buesch .block_read = ssb_pci_block_read, 798d625a29bSMichael Buesch .block_write = ssb_pci_block_write, 799d625a29bSMichael Buesch #endif 80061e115a5SMichael Buesch }; 80161e115a5SMichael Buesch 80261e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, 80361e115a5SMichael Buesch struct device_attribute *attr, 80461e115a5SMichael Buesch char *buf) 80561e115a5SMichael Buesch { 80661e115a5SMichael Buesch struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); 80761e115a5SMichael Buesch struct ssb_bus *bus; 80861e115a5SMichael Buesch 80961e115a5SMichael Buesch bus = ssb_pci_dev_to_bus(pdev); 81061e115a5SMichael Buesch if (!bus) 811e7ec2e32SMichael Buesch return -ENODEV; 81261e115a5SMichael Buesch 813e7ec2e32SMichael Buesch return ssb_attr_sprom_show(bus, buf, sprom_do_read); 81461e115a5SMichael Buesch } 81561e115a5SMichael Buesch 81661e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, 81761e115a5SMichael Buesch struct device_attribute *attr, 81861e115a5SMichael Buesch const char *buf, size_t count) 81961e115a5SMichael Buesch { 82061e115a5SMichael Buesch struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); 82161e115a5SMichael Buesch struct ssb_bus *bus; 82261e115a5SMichael Buesch 82361e115a5SMichael Buesch bus = ssb_pci_dev_to_bus(pdev); 82461e115a5SMichael Buesch if (!bus) 825e7ec2e32SMichael Buesch return -ENODEV; 82661e115a5SMichael Buesch 827e7ec2e32SMichael Buesch return ssb_attr_sprom_store(bus, buf, count, 828e7ec2e32SMichael Buesch sprom_check_crc, sprom_do_write); 82961e115a5SMichael Buesch } 83061e115a5SMichael Buesch 83161e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600, 83261e115a5SMichael Buesch ssb_pci_attr_sprom_show, 83361e115a5SMichael Buesch ssb_pci_attr_sprom_store); 83461e115a5SMichael Buesch 83561e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus) 83661e115a5SMichael Buesch { 83761e115a5SMichael Buesch struct pci_dev *pdev; 83861e115a5SMichael Buesch 83961e115a5SMichael Buesch if (bus->bustype != SSB_BUSTYPE_PCI) 84061e115a5SMichael Buesch return; 84161e115a5SMichael Buesch 84261e115a5SMichael Buesch pdev = bus->host_pci; 84361e115a5SMichael Buesch device_remove_file(&pdev->dev, &dev_attr_ssb_sprom); 84461e115a5SMichael Buesch } 84561e115a5SMichael Buesch 84661e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus) 84761e115a5SMichael Buesch { 84861e115a5SMichael Buesch struct pci_dev *pdev; 84961e115a5SMichael Buesch int err; 85061e115a5SMichael Buesch 85161e115a5SMichael Buesch if (bus->bustype != SSB_BUSTYPE_PCI) 85261e115a5SMichael Buesch return 0; 85361e115a5SMichael Buesch 85461e115a5SMichael Buesch pdev = bus->host_pci; 855e7ec2e32SMichael Buesch mutex_init(&bus->sprom_mutex); 85661e115a5SMichael Buesch err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); 85761e115a5SMichael Buesch if (err) 85861e115a5SMichael Buesch goto out; 85961e115a5SMichael Buesch 86061e115a5SMichael Buesch out: 86161e115a5SMichael Buesch return err; 86261e115a5SMichael Buesch } 863