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 230c272ef44SLarry 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 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 24561e115a5SMichael Buesch static void 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)); 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; 258c272ef44SLarry 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); 270c272ef44SLarry Finger for (i = 0; i < size; i++) { 271c272ef44SLarry Finger if (i == size / 4) 27261e115a5SMichael Buesch ssb_printk("25%%"); 273c272ef44SLarry Finger else if (i == size / 2) 27461e115a5SMichael Buesch ssb_printk("50%%"); 275c272ef44SLarry 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 300e861b98dSMichael Buesch static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in, 301e861b98dSMichael Buesch u16 mask, u16 shift) 302e861b98dSMichael Buesch { 303e861b98dSMichael Buesch u16 v; 304e861b98dSMichael Buesch u8 gain; 305e861b98dSMichael Buesch 306e861b98dSMichael Buesch v = in[SPOFF(SSB_SPROM1_AGAIN)]; 307e861b98dSMichael Buesch gain = (v & mask) >> shift; 308e861b98dSMichael Buesch if (gain == 0xFF) 309e861b98dSMichael Buesch gain = 2; /* If unset use 2dBm */ 310e861b98dSMichael Buesch if (sprom_revision == 1) { 311e861b98dSMichael Buesch /* Convert to Q5.2 */ 312e861b98dSMichael Buesch gain <<= 2; 313e861b98dSMichael Buesch } else { 314e861b98dSMichael Buesch /* Q5.2 Fractional part is stored in 0xC0 */ 315e861b98dSMichael Buesch gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2); 316e861b98dSMichael Buesch } 317e861b98dSMichael Buesch 318e861b98dSMichael Buesch return (s8)gain; 319e861b98dSMichael Buesch } 320e861b98dSMichael Buesch 321c272ef44SLarry Finger static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) 322c272ef44SLarry Finger { 323c272ef44SLarry Finger int i; 324c272ef44SLarry Finger u16 v; 325e861b98dSMichael Buesch s8 gain; 326c272ef44SLarry Finger u16 loc[3]; 327c272ef44SLarry Finger 328c272ef44SLarry Finger if (out->revision == 3) { /* rev 3 moved MAC */ 329c272ef44SLarry Finger loc[0] = SSB_SPROM3_IL0MAC; 330c272ef44SLarry Finger loc[1] = SSB_SPROM3_ET0MAC; 331c272ef44SLarry Finger loc[2] = SSB_SPROM3_ET1MAC; 332c272ef44SLarry 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 } 341c272ef44SLarry Finger for (i = 0; i < 3; i++) { 342c272ef44SLarry Finger v = in[SPOFF(loc[1]) + i]; 343c272ef44SLarry Finger *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); 344c272ef44SLarry Finger } 345c272ef44SLarry Finger for (i = 0; i < 3; i++) { 346c272ef44SLarry Finger v = in[SPOFF(loc[2]) + i]; 347c272ef44SLarry Finger *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); 348c272ef44SLarry Finger } 349c272ef44SLarry Finger SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0); 350c272ef44SLarry Finger SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A, 351c272ef44SLarry Finger SSB_SPROM1_ETHPHY_ET1A_SHIFT); 352e861b98dSMichael Buesch SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14); 353e861b98dSMichael Buesch SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15); 354e861b98dSMichael Buesch SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0); 355c272ef44SLarry Finger SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE, 356c272ef44SLarry Finger SSB_SPROM1_BINF_CCODE_SHIFT); 357e861b98dSMichael Buesch SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA, 358e861b98dSMichael Buesch SSB_SPROM1_BINF_ANTA_SHIFT); 359e861b98dSMichael Buesch SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG, 360e861b98dSMichael Buesch SSB_SPROM1_BINF_ANTBG_SHIFT); 361c272ef44SLarry Finger SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0); 362c272ef44SLarry Finger SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0); 363c272ef44SLarry Finger SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0); 364c272ef44SLarry Finger SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0); 365c272ef44SLarry Finger SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0); 366c272ef44SLarry Finger SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0); 367c272ef44SLarry Finger SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0); 368c272ef44SLarry Finger SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1, 369c272ef44SLarry Finger SSB_SPROM1_GPIOA_P1_SHIFT); 370c272ef44SLarry Finger SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0); 371c272ef44SLarry Finger SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3, 372c272ef44SLarry Finger SSB_SPROM1_GPIOB_P3_SHIFT); 373c272ef44SLarry Finger SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A, 374c272ef44SLarry Finger SSB_SPROM1_MAXPWR_A_SHIFT); 375c272ef44SLarry Finger SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0); 376c272ef44SLarry Finger SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A, 377c272ef44SLarry Finger SSB_SPROM1_ITSSI_A_SHIFT); 378c272ef44SLarry Finger SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0); 379c272ef44SLarry Finger SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0); 380*af4b7450SMichael Buesch if (out->revision >= 2) 381*af4b7450SMichael Buesch SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); 382e861b98dSMichael Buesch 383e861b98dSMichael Buesch /* Extract the antenna gain values. */ 384e861b98dSMichael Buesch gain = r123_extract_antgain(out->revision, in, 385e861b98dSMichael Buesch SSB_SPROM1_AGAIN_BG, 386c272ef44SLarry Finger SSB_SPROM1_AGAIN_BG_SHIFT); 387e861b98dSMichael Buesch out->antenna_gain.ghz24.a0 = gain; 388e861b98dSMichael Buesch out->antenna_gain.ghz24.a1 = gain; 389e861b98dSMichael Buesch out->antenna_gain.ghz24.a2 = gain; 390e861b98dSMichael Buesch out->antenna_gain.ghz24.a3 = gain; 391e861b98dSMichael Buesch gain = r123_extract_antgain(out->revision, in, 392e861b98dSMichael Buesch SSB_SPROM1_AGAIN_A, 393e861b98dSMichael Buesch SSB_SPROM1_AGAIN_A_SHIFT); 394e861b98dSMichael Buesch out->antenna_gain.ghz5.a0 = gain; 395e861b98dSMichael Buesch out->antenna_gain.ghz5.a1 = gain; 396e861b98dSMichael Buesch out->antenna_gain.ghz5.a2 = gain; 397e861b98dSMichael Buesch out->antenna_gain.ghz5.a3 = gain; 398c272ef44SLarry Finger } 399c272ef44SLarry Finger 400c272ef44SLarry Finger static void sprom_extract_r4(struct ssb_sprom *out, const u16 *in) 40161e115a5SMichael Buesch { 40261e115a5SMichael Buesch int i; 40361e115a5SMichael Buesch u16 v; 40461e115a5SMichael Buesch 405d3c319f9SLarry Finger /* extract the equivalent of the r1 variables */ 406c272ef44SLarry Finger for (i = 0; i < 3; i++) { 407c272ef44SLarry Finger v = in[SPOFF(SSB_SPROM4_IL0MAC) + i]; 408c272ef44SLarry Finger *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); 40961e115a5SMichael Buesch } 410c272ef44SLarry Finger for (i = 0; i < 3; i++) { 411c272ef44SLarry Finger v = in[SPOFF(SSB_SPROM4_ET0MAC) + i]; 412c272ef44SLarry Finger *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); 413c272ef44SLarry Finger } 414c272ef44SLarry Finger for (i = 0; i < 3; i++) { 415c272ef44SLarry Finger v = in[SPOFF(SSB_SPROM4_ET1MAC) + i]; 416c272ef44SLarry Finger *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); 417c272ef44SLarry Finger } 418c272ef44SLarry Finger SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0); 419c272ef44SLarry Finger SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A, 420c272ef44SLarry Finger SSB_SPROM4_ETHPHY_ET1A_SHIFT); 421c272ef44SLarry Finger SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0); 422c272ef44SLarry Finger SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0); 423*af4b7450SMichael Buesch SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0); 424e861b98dSMichael Buesch SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A, 425e861b98dSMichael Buesch SSB_SPROM4_ANTAVAIL_A_SHIFT); 426e861b98dSMichael Buesch SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG, 427e861b98dSMichael Buesch SSB_SPROM4_ANTAVAIL_BG_SHIFT); 428d3c319f9SLarry Finger SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0); 429d3c319f9SLarry Finger SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG, 430d3c319f9SLarry Finger SSB_SPROM4_ITSSI_BG_SHIFT); 431d3c319f9SLarry Finger SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0); 432d3c319f9SLarry Finger SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A, 433d3c319f9SLarry Finger SSB_SPROM4_ITSSI_A_SHIFT); 434d3c319f9SLarry Finger SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0); 435d3c319f9SLarry Finger SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1, 436d3c319f9SLarry Finger SSB_SPROM4_GPIOA_P1_SHIFT); 437d3c319f9SLarry Finger SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0); 438d3c319f9SLarry Finger SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3, 439d3c319f9SLarry Finger SSB_SPROM4_GPIOB_P3_SHIFT); 440e861b98dSMichael Buesch 441e861b98dSMichael Buesch /* Extract the antenna gain values. */ 442e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01, 443e861b98dSMichael Buesch SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT); 444e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01, 445e861b98dSMichael Buesch SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT); 446e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23, 447e861b98dSMichael Buesch SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT); 448e861b98dSMichael Buesch SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23, 449e861b98dSMichael Buesch SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); 450e861b98dSMichael Buesch memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, 451e861b98dSMichael Buesch sizeof(out->antenna_gain.ghz5)); 452e861b98dSMichael Buesch 453c272ef44SLarry Finger /* TODO - get remaining rev 4 stuff needed */ 45461e115a5SMichael Buesch } 45561e115a5SMichael Buesch 456c272ef44SLarry Finger static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, 457c272ef44SLarry Finger const u16 *in, u16 size) 45861e115a5SMichael Buesch { 45961e115a5SMichael Buesch memset(out, 0, sizeof(*out)); 46061e115a5SMichael Buesch 461c272ef44SLarry Finger out->revision = in[size - 1] & 0x00FF; 462e861b98dSMichael Buesch ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision); 46361e115a5SMichael Buesch if ((bus->chip_id & 0xFF00) == 0x4400) { 46461e115a5SMichael Buesch /* Workaround: The BCM44XX chip has a stupid revision 46561e115a5SMichael Buesch * number stored in the SPROM. 46661e115a5SMichael Buesch * Always extract r1. */ 467c272ef44SLarry Finger out->revision = 1; 468c272ef44SLarry Finger sprom_extract_r123(out, in); 469c272ef44SLarry Finger } else if (bus->chip_id == 0x4321) { 470c272ef44SLarry Finger /* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */ 471c272ef44SLarry Finger out->revision = 4; 472c272ef44SLarry Finger sprom_extract_r4(out, in); 47361e115a5SMichael Buesch } else { 47461e115a5SMichael Buesch if (out->revision == 0) 47561e115a5SMichael Buesch goto unsupported; 476c272ef44SLarry Finger if (out->revision >= 1 && out->revision <= 3) { 477c272ef44SLarry Finger sprom_extract_r123(out, in); 478c272ef44SLarry Finger } 479c272ef44SLarry Finger if (out->revision == 4) 480c272ef44SLarry Finger sprom_extract_r4(out, in); 481c272ef44SLarry Finger if (out->revision >= 5) 48261e115a5SMichael Buesch goto unsupported; 48361e115a5SMichael Buesch } 48461e115a5SMichael Buesch 48561e115a5SMichael Buesch return 0; 48661e115a5SMichael Buesch unsupported: 48761e115a5SMichael Buesch ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d " 48861e115a5SMichael Buesch "detected. Will extract v1\n", out->revision); 489d3c319f9SLarry Finger sprom_extract_r123(out, in); 49061e115a5SMichael Buesch return 0; 49161e115a5SMichael Buesch } 49261e115a5SMichael Buesch 49361e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus, 49461e115a5SMichael Buesch struct ssb_sprom *sprom) 49561e115a5SMichael Buesch { 49661e115a5SMichael Buesch int err = -ENOMEM; 49761e115a5SMichael Buesch u16 *buf; 49861e115a5SMichael Buesch 499c272ef44SLarry Finger buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL); 50061e115a5SMichael Buesch if (!buf) 50161e115a5SMichael Buesch goto out; 502c272ef44SLarry Finger bus->sprom_size = SSB_SPROMSIZE_WORDS_R123; 50361e115a5SMichael Buesch sprom_do_read(bus, buf); 504c272ef44SLarry Finger err = sprom_check_crc(buf, bus->sprom_size); 50561e115a5SMichael Buesch if (err) { 506c272ef44SLarry Finger /* check for rev 4 sprom - has special signature */ 507c272ef44SLarry Finger if (buf[32] == 0x5372) { 508c272ef44SLarry Finger kfree(buf); 509c272ef44SLarry Finger buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), 510c272ef44SLarry Finger GFP_KERNEL); 511c272ef44SLarry Finger if (!buf) 512c272ef44SLarry Finger goto out; 513c272ef44SLarry Finger bus->sprom_size = SSB_SPROMSIZE_WORDS_R4; 514c272ef44SLarry Finger sprom_do_read(bus, buf); 515c272ef44SLarry Finger err = sprom_check_crc(buf, bus->sprom_size); 51661e115a5SMichael Buesch } 517c272ef44SLarry Finger if (err) 518c272ef44SLarry Finger ssb_printk(KERN_WARNING PFX "WARNING: Invalid" 519c272ef44SLarry Finger " SPROM CRC (corrupt SPROM)\n"); 520c272ef44SLarry Finger } 521c272ef44SLarry Finger err = sprom_extract(bus, sprom, buf, bus->sprom_size); 52261e115a5SMichael Buesch 52361e115a5SMichael Buesch kfree(buf); 52461e115a5SMichael Buesch out: 52561e115a5SMichael Buesch return err; 52661e115a5SMichael Buesch } 52761e115a5SMichael Buesch 52861e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus, 52961e115a5SMichael Buesch struct ssb_boardinfo *bi) 53061e115a5SMichael Buesch { 53161e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID, 53261e115a5SMichael Buesch &bi->vendor); 53361e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID, 53461e115a5SMichael Buesch &bi->type); 53561e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_REVISION_ID, 53661e115a5SMichael Buesch &bi->rev); 53761e115a5SMichael Buesch } 53861e115a5SMichael Buesch 53961e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus, 54061e115a5SMichael Buesch struct ssb_init_invariants *iv) 54161e115a5SMichael Buesch { 54261e115a5SMichael Buesch int err; 54361e115a5SMichael Buesch 54461e115a5SMichael Buesch err = ssb_pci_sprom_get(bus, &iv->sprom); 54561e115a5SMichael Buesch if (err) 54661e115a5SMichael Buesch goto out; 54761e115a5SMichael Buesch ssb_pci_get_boardinfo(bus, &iv->boardinfo); 54861e115a5SMichael Buesch 54961e115a5SMichael Buesch out: 55061e115a5SMichael Buesch return err; 55161e115a5SMichael Buesch } 55261e115a5SMichael Buesch 55361e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG 55461e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus) 55561e115a5SMichael Buesch { 55661e115a5SMichael Buesch if (likely(bus->powered_up)) 55761e115a5SMichael Buesch return 0; 55861e115a5SMichael Buesch 55961e115a5SMichael Buesch printk(KERN_ERR PFX "FATAL ERROR: Bus powered down " 56061e115a5SMichael Buesch "while accessing PCI MMIO space\n"); 56161e115a5SMichael Buesch if (bus->power_warn_count <= 10) { 56261e115a5SMichael Buesch bus->power_warn_count++; 56361e115a5SMichael Buesch dump_stack(); 56461e115a5SMichael Buesch } 56561e115a5SMichael Buesch 56661e115a5SMichael Buesch return -ENODEV; 56761e115a5SMichael Buesch } 56861e115a5SMichael Buesch #else /* DEBUG */ 56961e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus) 57061e115a5SMichael Buesch { 57161e115a5SMichael Buesch return 0; 57261e115a5SMichael Buesch } 57361e115a5SMichael Buesch #endif /* DEBUG */ 57461e115a5SMichael Buesch 57561e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) 57661e115a5SMichael Buesch { 57761e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 57861e115a5SMichael Buesch 57961e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 58061e115a5SMichael Buesch return 0xFFFF; 58161e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 58261e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 58361e115a5SMichael Buesch return 0xFFFF; 58461e115a5SMichael Buesch } 5854b402c65SMichael Buesch return ioread16(bus->mmio + offset); 58661e115a5SMichael Buesch } 58761e115a5SMichael Buesch 58861e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset) 58961e115a5SMichael Buesch { 59061e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 59161e115a5SMichael Buesch 59261e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 59361e115a5SMichael Buesch return 0xFFFFFFFF; 59461e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 59561e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 59661e115a5SMichael Buesch return 0xFFFFFFFF; 59761e115a5SMichael Buesch } 5984b402c65SMichael Buesch return ioread32(bus->mmio + offset); 59961e115a5SMichael Buesch } 60061e115a5SMichael Buesch 60161e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value) 60261e115a5SMichael Buesch { 60361e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 60461e115a5SMichael Buesch 60561e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 60661e115a5SMichael Buesch return; 60761e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 60861e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 60961e115a5SMichael Buesch return; 61061e115a5SMichael Buesch } 6114b402c65SMichael Buesch iowrite16(value, bus->mmio + offset); 61261e115a5SMichael Buesch } 61361e115a5SMichael Buesch 61461e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value) 61561e115a5SMichael Buesch { 61661e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 61761e115a5SMichael Buesch 61861e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 61961e115a5SMichael Buesch return; 62061e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 62161e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 62261e115a5SMichael Buesch return; 62361e115a5SMichael Buesch } 6244b402c65SMichael Buesch iowrite32(value, bus->mmio + offset); 62561e115a5SMichael Buesch } 62661e115a5SMichael Buesch 62761e115a5SMichael Buesch /* Not "static", as it's used in main.c */ 62861e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = { 62961e115a5SMichael Buesch .read16 = ssb_pci_read16, 63061e115a5SMichael Buesch .read32 = ssb_pci_read32, 63161e115a5SMichael Buesch .write16 = ssb_pci_write16, 63261e115a5SMichael Buesch .write32 = ssb_pci_write32, 63361e115a5SMichael Buesch }; 63461e115a5SMichael Buesch 635c272ef44SLarry Finger static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, u16 size) 63661e115a5SMichael Buesch { 63761e115a5SMichael Buesch int i, pos = 0; 63861e115a5SMichael Buesch 639c272ef44SLarry Finger for (i = 0; i < size; i++) 64061e115a5SMichael Buesch pos += snprintf(buf + pos, buf_len - pos - 1, 64161e115a5SMichael Buesch "%04X", swab16(sprom[i]) & 0xFFFF); 64261e115a5SMichael Buesch pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); 64361e115a5SMichael Buesch 64461e115a5SMichael Buesch return pos + 1; 64561e115a5SMichael Buesch } 64661e115a5SMichael Buesch 647c272ef44SLarry Finger static int hex2sprom(u16 *sprom, const char *dump, size_t len, u16 size) 64861e115a5SMichael Buesch { 64961e115a5SMichael Buesch char tmp[5] = { 0 }; 65061e115a5SMichael Buesch int cnt = 0; 65161e115a5SMichael Buesch unsigned long parsed; 65261e115a5SMichael Buesch 653c272ef44SLarry Finger if (len < size * 2) 65461e115a5SMichael Buesch return -EINVAL; 65561e115a5SMichael Buesch 656c272ef44SLarry Finger while (cnt < size) { 65761e115a5SMichael Buesch memcpy(tmp, dump, 4); 65861e115a5SMichael Buesch dump += 4; 65961e115a5SMichael Buesch parsed = simple_strtoul(tmp, NULL, 16); 66061e115a5SMichael Buesch sprom[cnt++] = swab16((u16)parsed); 66161e115a5SMichael Buesch } 66261e115a5SMichael Buesch 66361e115a5SMichael Buesch return 0; 66461e115a5SMichael Buesch } 66561e115a5SMichael Buesch 66661e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, 66761e115a5SMichael Buesch struct device_attribute *attr, 66861e115a5SMichael Buesch char *buf) 66961e115a5SMichael Buesch { 67061e115a5SMichael Buesch struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); 67161e115a5SMichael Buesch struct ssb_bus *bus; 67261e115a5SMichael Buesch u16 *sprom; 67361e115a5SMichael Buesch int err = -ENODEV; 67461e115a5SMichael Buesch ssize_t count = 0; 67561e115a5SMichael Buesch 67661e115a5SMichael Buesch bus = ssb_pci_dev_to_bus(pdev); 67761e115a5SMichael Buesch if (!bus) 67861e115a5SMichael Buesch goto out; 67961e115a5SMichael Buesch err = -ENOMEM; 680c272ef44SLarry Finger sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); 68161e115a5SMichael Buesch if (!sprom) 68261e115a5SMichael Buesch goto out; 68361e115a5SMichael Buesch 68461e115a5SMichael Buesch /* Use interruptible locking, as the SPROM write might 68561e115a5SMichael Buesch * be holding the lock for several seconds. So allow userspace 68661e115a5SMichael Buesch * to cancel operation. */ 68761e115a5SMichael Buesch err = -ERESTARTSYS; 68861e115a5SMichael Buesch if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) 68961e115a5SMichael Buesch goto out_kfree; 69061e115a5SMichael Buesch sprom_do_read(bus, sprom); 69161e115a5SMichael Buesch mutex_unlock(&bus->pci_sprom_mutex); 69261e115a5SMichael Buesch 693c272ef44SLarry Finger count = sprom2hex(sprom, buf, PAGE_SIZE, bus->sprom_size); 69461e115a5SMichael Buesch err = 0; 69561e115a5SMichael Buesch 69661e115a5SMichael Buesch out_kfree: 69761e115a5SMichael Buesch kfree(sprom); 69861e115a5SMichael Buesch out: 69961e115a5SMichael Buesch return err ? err : count; 70061e115a5SMichael Buesch } 70161e115a5SMichael Buesch 70261e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, 70361e115a5SMichael Buesch struct device_attribute *attr, 70461e115a5SMichael Buesch const char *buf, size_t count) 70561e115a5SMichael Buesch { 70661e115a5SMichael Buesch struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); 70761e115a5SMichael Buesch struct ssb_bus *bus; 70861e115a5SMichael Buesch u16 *sprom; 70961e115a5SMichael Buesch int res = 0, err = -ENODEV; 71061e115a5SMichael Buesch 71161e115a5SMichael Buesch bus = ssb_pci_dev_to_bus(pdev); 71261e115a5SMichael Buesch if (!bus) 71361e115a5SMichael Buesch goto out; 71461e115a5SMichael Buesch err = -ENOMEM; 715c272ef44SLarry Finger sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); 71661e115a5SMichael Buesch if (!sprom) 71761e115a5SMichael Buesch goto out; 718c272ef44SLarry Finger err = hex2sprom(sprom, buf, count, bus->sprom_size); 71961e115a5SMichael Buesch if (err) { 72061e115a5SMichael Buesch err = -EINVAL; 72161e115a5SMichael Buesch goto out_kfree; 72261e115a5SMichael Buesch } 723c272ef44SLarry Finger err = sprom_check_crc(sprom, bus->sprom_size); 72461e115a5SMichael Buesch if (err) { 72561e115a5SMichael Buesch err = -EINVAL; 72661e115a5SMichael Buesch goto out_kfree; 72761e115a5SMichael Buesch } 72861e115a5SMichael Buesch 72961e115a5SMichael Buesch /* Use interruptible locking, as the SPROM write might 73061e115a5SMichael Buesch * be holding the lock for several seconds. So allow userspace 73161e115a5SMichael Buesch * to cancel operation. */ 73261e115a5SMichael Buesch err = -ERESTARTSYS; 73361e115a5SMichael Buesch if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) 73461e115a5SMichael Buesch goto out_kfree; 73561e115a5SMichael Buesch err = ssb_devices_freeze(bus); 73661e115a5SMichael Buesch if (err == -EOPNOTSUPP) { 73761e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " 73861e115a5SMichael Buesch "No suspend support. Is CONFIG_PM enabled?\n"); 73961e115a5SMichael Buesch goto out_unlock; 74061e115a5SMichael Buesch } 74161e115a5SMichael Buesch if (err) { 74261e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); 74361e115a5SMichael Buesch goto out_unlock; 74461e115a5SMichael Buesch } 74561e115a5SMichael Buesch res = sprom_do_write(bus, sprom); 74661e115a5SMichael Buesch err = ssb_devices_thaw(bus); 74761e115a5SMichael Buesch if (err) 74861e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); 74961e115a5SMichael Buesch out_unlock: 75061e115a5SMichael Buesch mutex_unlock(&bus->pci_sprom_mutex); 75161e115a5SMichael Buesch out_kfree: 75261e115a5SMichael Buesch kfree(sprom); 75361e115a5SMichael Buesch out: 75461e115a5SMichael Buesch if (res) 75561e115a5SMichael Buesch return res; 75661e115a5SMichael Buesch return err ? err : count; 75761e115a5SMichael Buesch } 75861e115a5SMichael Buesch 75961e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600, 76061e115a5SMichael Buesch ssb_pci_attr_sprom_show, 76161e115a5SMichael Buesch ssb_pci_attr_sprom_store); 76261e115a5SMichael Buesch 76361e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus) 76461e115a5SMichael Buesch { 76561e115a5SMichael Buesch struct pci_dev *pdev; 76661e115a5SMichael Buesch 76761e115a5SMichael Buesch if (bus->bustype != SSB_BUSTYPE_PCI) 76861e115a5SMichael Buesch return; 76961e115a5SMichael Buesch 77061e115a5SMichael Buesch pdev = bus->host_pci; 77161e115a5SMichael Buesch device_remove_file(&pdev->dev, &dev_attr_ssb_sprom); 77261e115a5SMichael Buesch } 77361e115a5SMichael Buesch 77461e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus) 77561e115a5SMichael Buesch { 77661e115a5SMichael Buesch struct pci_dev *pdev; 77761e115a5SMichael Buesch int err; 77861e115a5SMichael Buesch 77961e115a5SMichael Buesch if (bus->bustype != SSB_BUSTYPE_PCI) 78061e115a5SMichael Buesch return 0; 78161e115a5SMichael Buesch 78261e115a5SMichael Buesch pdev = bus->host_pci; 78361e115a5SMichael Buesch mutex_init(&bus->pci_sprom_mutex); 78461e115a5SMichael Buesch err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); 78561e115a5SMichael Buesch if (err) 78661e115a5SMichael Buesch goto out; 78761e115a5SMichael Buesch 78861e115a5SMichael Buesch out: 78961e115a5SMichael Buesch return err; 79061e115a5SMichael Buesch } 791