1*61e115a5SMichael Buesch /* 2*61e115a5SMichael Buesch * Sonics Silicon Backplane PCI-Hostbus related functions. 3*61e115a5SMichael Buesch * 4*61e115a5SMichael Buesch * Copyright (C) 2005-2006 Michael Buesch <mb@bu3sch.de> 5*61e115a5SMichael Buesch * Copyright (C) 2005 Martin Langer <martin-langer@gmx.de> 6*61e115a5SMichael Buesch * Copyright (C) 2005 Stefano Brivio <st3@riseup.net> 7*61e115a5SMichael Buesch * Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org> 8*61e115a5SMichael Buesch * Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> 9*61e115a5SMichael Buesch * 10*61e115a5SMichael Buesch * Derived from the Broadcom 4400 device driver. 11*61e115a5SMichael Buesch * Copyright (C) 2002 David S. Miller (davem@redhat.com) 12*61e115a5SMichael Buesch * Fixed by Pekka Pietikainen (pp@ee.oulu.fi) 13*61e115a5SMichael Buesch * Copyright (C) 2006 Broadcom Corporation. 14*61e115a5SMichael Buesch * 15*61e115a5SMichael Buesch * Licensed under the GNU/GPL. See COPYING for details. 16*61e115a5SMichael Buesch */ 17*61e115a5SMichael Buesch 18*61e115a5SMichael Buesch #include <linux/ssb/ssb.h> 19*61e115a5SMichael Buesch #include <linux/ssb/ssb_regs.h> 20*61e115a5SMichael Buesch #include <linux/pci.h> 21*61e115a5SMichael Buesch #include <linux/delay.h> 22*61e115a5SMichael Buesch 23*61e115a5SMichael Buesch #include "ssb_private.h" 24*61e115a5SMichael Buesch 25*61e115a5SMichael Buesch 26*61e115a5SMichael Buesch /* Define the following to 1 to enable a printk on each coreswitch. */ 27*61e115a5SMichael Buesch #define SSB_VERBOSE_PCICORESWITCH_DEBUG 0 28*61e115a5SMichael Buesch 29*61e115a5SMichael Buesch 30*61e115a5SMichael Buesch /* Lowlevel coreswitching */ 31*61e115a5SMichael Buesch int ssb_pci_switch_coreidx(struct ssb_bus *bus, u8 coreidx) 32*61e115a5SMichael Buesch { 33*61e115a5SMichael Buesch int err; 34*61e115a5SMichael Buesch int attempts = 0; 35*61e115a5SMichael Buesch u32 cur_core; 36*61e115a5SMichael Buesch 37*61e115a5SMichael Buesch while (1) { 38*61e115a5SMichael Buesch err = pci_write_config_dword(bus->host_pci, SSB_BAR0_WIN, 39*61e115a5SMichael Buesch (coreidx * SSB_CORE_SIZE) 40*61e115a5SMichael Buesch + SSB_ENUM_BASE); 41*61e115a5SMichael Buesch if (err) 42*61e115a5SMichael Buesch goto error; 43*61e115a5SMichael Buesch err = pci_read_config_dword(bus->host_pci, SSB_BAR0_WIN, 44*61e115a5SMichael Buesch &cur_core); 45*61e115a5SMichael Buesch if (err) 46*61e115a5SMichael Buesch goto error; 47*61e115a5SMichael Buesch cur_core = (cur_core - SSB_ENUM_BASE) 48*61e115a5SMichael Buesch / SSB_CORE_SIZE; 49*61e115a5SMichael Buesch if (cur_core == coreidx) 50*61e115a5SMichael Buesch break; 51*61e115a5SMichael Buesch 52*61e115a5SMichael Buesch if (attempts++ > SSB_BAR0_MAX_RETRIES) 53*61e115a5SMichael Buesch goto error; 54*61e115a5SMichael Buesch udelay(10); 55*61e115a5SMichael Buesch } 56*61e115a5SMichael Buesch return 0; 57*61e115a5SMichael Buesch error: 58*61e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx); 59*61e115a5SMichael Buesch return -ENODEV; 60*61e115a5SMichael Buesch } 61*61e115a5SMichael Buesch 62*61e115a5SMichael Buesch int ssb_pci_switch_core(struct ssb_bus *bus, 63*61e115a5SMichael Buesch struct ssb_device *dev) 64*61e115a5SMichael Buesch { 65*61e115a5SMichael Buesch int err; 66*61e115a5SMichael Buesch unsigned long flags; 67*61e115a5SMichael Buesch 68*61e115a5SMichael Buesch #if SSB_VERBOSE_PCICORESWITCH_DEBUG 69*61e115a5SMichael Buesch ssb_printk(KERN_INFO PFX 70*61e115a5SMichael Buesch "Switching to %s core, index %d\n", 71*61e115a5SMichael Buesch ssb_core_name(dev->id.coreid), 72*61e115a5SMichael Buesch dev->core_index); 73*61e115a5SMichael Buesch #endif 74*61e115a5SMichael Buesch 75*61e115a5SMichael Buesch spin_lock_irqsave(&bus->bar_lock, flags); 76*61e115a5SMichael Buesch err = ssb_pci_switch_coreidx(bus, dev->core_index); 77*61e115a5SMichael Buesch if (!err) 78*61e115a5SMichael Buesch bus->mapped_device = dev; 79*61e115a5SMichael Buesch spin_unlock_irqrestore(&bus->bar_lock, flags); 80*61e115a5SMichael Buesch 81*61e115a5SMichael Buesch return err; 82*61e115a5SMichael Buesch } 83*61e115a5SMichael Buesch 84*61e115a5SMichael Buesch /* Enable/disable the on board crystal oscillator and/or PLL. */ 85*61e115a5SMichael Buesch int ssb_pci_xtal(struct ssb_bus *bus, u32 what, int turn_on) 86*61e115a5SMichael Buesch { 87*61e115a5SMichael Buesch int err; 88*61e115a5SMichael Buesch u32 in, out, outenable; 89*61e115a5SMichael Buesch u16 pci_status; 90*61e115a5SMichael Buesch 91*61e115a5SMichael Buesch if (bus->bustype != SSB_BUSTYPE_PCI) 92*61e115a5SMichael Buesch return 0; 93*61e115a5SMichael Buesch 94*61e115a5SMichael Buesch err = pci_read_config_dword(bus->host_pci, SSB_GPIO_IN, &in); 95*61e115a5SMichael Buesch if (err) 96*61e115a5SMichael Buesch goto err_pci; 97*61e115a5SMichael Buesch err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &out); 98*61e115a5SMichael Buesch if (err) 99*61e115a5SMichael Buesch goto err_pci; 100*61e115a5SMichael Buesch err = pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, &outenable); 101*61e115a5SMichael Buesch if (err) 102*61e115a5SMichael Buesch goto err_pci; 103*61e115a5SMichael Buesch 104*61e115a5SMichael Buesch outenable |= what; 105*61e115a5SMichael Buesch 106*61e115a5SMichael Buesch if (turn_on) { 107*61e115a5SMichael Buesch /* Avoid glitching the clock if GPRS is already using it. 108*61e115a5SMichael Buesch * We can't actually read the state of the PLLPD so we infer it 109*61e115a5SMichael Buesch * by the value of XTAL_PU which *is* readable via gpioin. 110*61e115a5SMichael Buesch */ 111*61e115a5SMichael Buesch if (!(in & SSB_GPIO_XTAL)) { 112*61e115a5SMichael Buesch if (what & SSB_GPIO_XTAL) { 113*61e115a5SMichael Buesch /* Turn the crystal on */ 114*61e115a5SMichael Buesch out |= SSB_GPIO_XTAL; 115*61e115a5SMichael Buesch if (what & SSB_GPIO_PLL) 116*61e115a5SMichael Buesch out |= SSB_GPIO_PLL; 117*61e115a5SMichael Buesch err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); 118*61e115a5SMichael Buesch if (err) 119*61e115a5SMichael Buesch goto err_pci; 120*61e115a5SMichael Buesch err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, 121*61e115a5SMichael Buesch outenable); 122*61e115a5SMichael Buesch if (err) 123*61e115a5SMichael Buesch goto err_pci; 124*61e115a5SMichael Buesch msleep(1); 125*61e115a5SMichael Buesch } 126*61e115a5SMichael Buesch if (what & SSB_GPIO_PLL) { 127*61e115a5SMichael Buesch /* Turn the PLL on */ 128*61e115a5SMichael Buesch out &= ~SSB_GPIO_PLL; 129*61e115a5SMichael Buesch err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); 130*61e115a5SMichael Buesch if (err) 131*61e115a5SMichael Buesch goto err_pci; 132*61e115a5SMichael Buesch msleep(5); 133*61e115a5SMichael Buesch } 134*61e115a5SMichael Buesch } 135*61e115a5SMichael Buesch 136*61e115a5SMichael Buesch err = pci_read_config_word(bus->host_pci, PCI_STATUS, &pci_status); 137*61e115a5SMichael Buesch if (err) 138*61e115a5SMichael Buesch goto err_pci; 139*61e115a5SMichael Buesch pci_status &= ~PCI_STATUS_SIG_TARGET_ABORT; 140*61e115a5SMichael Buesch err = pci_write_config_word(bus->host_pci, PCI_STATUS, pci_status); 141*61e115a5SMichael Buesch if (err) 142*61e115a5SMichael Buesch goto err_pci; 143*61e115a5SMichael Buesch } else { 144*61e115a5SMichael Buesch if (what & SSB_GPIO_XTAL) { 145*61e115a5SMichael Buesch /* Turn the crystal off */ 146*61e115a5SMichael Buesch out &= ~SSB_GPIO_XTAL; 147*61e115a5SMichael Buesch } 148*61e115a5SMichael Buesch if (what & SSB_GPIO_PLL) { 149*61e115a5SMichael Buesch /* Turn the PLL off */ 150*61e115a5SMichael Buesch out |= SSB_GPIO_PLL; 151*61e115a5SMichael Buesch } 152*61e115a5SMichael Buesch err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT, out); 153*61e115a5SMichael Buesch if (err) 154*61e115a5SMichael Buesch goto err_pci; 155*61e115a5SMichael Buesch err = pci_write_config_dword(bus->host_pci, SSB_GPIO_OUT_ENABLE, outenable); 156*61e115a5SMichael Buesch if (err) 157*61e115a5SMichael Buesch goto err_pci; 158*61e115a5SMichael Buesch } 159*61e115a5SMichael Buesch 160*61e115a5SMichael Buesch out: 161*61e115a5SMichael Buesch return err; 162*61e115a5SMichael Buesch 163*61e115a5SMichael Buesch err_pci: 164*61e115a5SMichael Buesch printk(KERN_ERR PFX "Error: ssb_pci_xtal() could not access PCI config space!\n"); 165*61e115a5SMichael Buesch err = -EBUSY; 166*61e115a5SMichael Buesch goto out; 167*61e115a5SMichael Buesch } 168*61e115a5SMichael Buesch 169*61e115a5SMichael Buesch /* Get the word-offset for a SSB_SPROM_XXX define. */ 170*61e115a5SMichael Buesch #define SPOFF(offset) (((offset) - SSB_SPROM_BASE) / sizeof(u16)) 171*61e115a5SMichael Buesch /* Helper to extract some _offset, which is one of the SSB_SPROM_XXX defines. */ 172*61e115a5SMichael Buesch #define SPEX(_outvar, _offset, _mask, _shift) \ 173*61e115a5SMichael Buesch out->_outvar = ((in[SPOFF(_offset)] & (_mask)) >> (_shift)) 174*61e115a5SMichael Buesch 175*61e115a5SMichael Buesch static inline u8 ssb_crc8(u8 crc, u8 data) 176*61e115a5SMichael Buesch { 177*61e115a5SMichael Buesch /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */ 178*61e115a5SMichael Buesch static const u8 t[] = { 179*61e115a5SMichael Buesch 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, 180*61e115a5SMichael Buesch 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, 181*61e115a5SMichael Buesch 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, 182*61e115a5SMichael Buesch 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, 183*61e115a5SMichael Buesch 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, 184*61e115a5SMichael Buesch 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, 185*61e115a5SMichael Buesch 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, 186*61e115a5SMichael Buesch 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, 187*61e115a5SMichael Buesch 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, 188*61e115a5SMichael Buesch 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, 189*61e115a5SMichael Buesch 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, 190*61e115a5SMichael Buesch 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, 191*61e115a5SMichael Buesch 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, 192*61e115a5SMichael Buesch 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, 193*61e115a5SMichael Buesch 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, 194*61e115a5SMichael Buesch 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, 195*61e115a5SMichael Buesch 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, 196*61e115a5SMichael Buesch 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, 197*61e115a5SMichael Buesch 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, 198*61e115a5SMichael Buesch 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, 199*61e115a5SMichael Buesch 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, 200*61e115a5SMichael Buesch 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, 201*61e115a5SMichael Buesch 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, 202*61e115a5SMichael Buesch 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, 203*61e115a5SMichael Buesch 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, 204*61e115a5SMichael Buesch 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, 205*61e115a5SMichael Buesch 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, 206*61e115a5SMichael Buesch 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, 207*61e115a5SMichael Buesch 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, 208*61e115a5SMichael Buesch 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, 209*61e115a5SMichael Buesch 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, 210*61e115a5SMichael Buesch 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, 211*61e115a5SMichael Buesch }; 212*61e115a5SMichael Buesch return t[crc ^ data]; 213*61e115a5SMichael Buesch } 214*61e115a5SMichael Buesch 215*61e115a5SMichael Buesch static u8 ssb_sprom_crc(const u16 *sprom) 216*61e115a5SMichael Buesch { 217*61e115a5SMichael Buesch int word; 218*61e115a5SMichael Buesch u8 crc = 0xFF; 219*61e115a5SMichael Buesch 220*61e115a5SMichael Buesch for (word = 0; word < SSB_SPROMSIZE_WORDS - 1; word++) { 221*61e115a5SMichael Buesch crc = ssb_crc8(crc, sprom[word] & 0x00FF); 222*61e115a5SMichael Buesch crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8); 223*61e115a5SMichael Buesch } 224*61e115a5SMichael Buesch crc = ssb_crc8(crc, sprom[SPOFF(SSB_SPROM_REVISION)] & 0x00FF); 225*61e115a5SMichael Buesch crc ^= 0xFF; 226*61e115a5SMichael Buesch 227*61e115a5SMichael Buesch return crc; 228*61e115a5SMichael Buesch } 229*61e115a5SMichael Buesch 230*61e115a5SMichael Buesch static int sprom_check_crc(const u16 *sprom) 231*61e115a5SMichael Buesch { 232*61e115a5SMichael Buesch u8 crc; 233*61e115a5SMichael Buesch u8 expected_crc; 234*61e115a5SMichael Buesch u16 tmp; 235*61e115a5SMichael Buesch 236*61e115a5SMichael Buesch crc = ssb_sprom_crc(sprom); 237*61e115a5SMichael Buesch tmp = sprom[SPOFF(SSB_SPROM_REVISION)] & SSB_SPROM_REVISION_CRC; 238*61e115a5SMichael Buesch expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; 239*61e115a5SMichael Buesch if (crc != expected_crc) 240*61e115a5SMichael Buesch return -EPROTO; 241*61e115a5SMichael Buesch 242*61e115a5SMichael Buesch return 0; 243*61e115a5SMichael Buesch } 244*61e115a5SMichael Buesch 245*61e115a5SMichael Buesch static void sprom_do_read(struct ssb_bus *bus, u16 *sprom) 246*61e115a5SMichael Buesch { 247*61e115a5SMichael Buesch int i; 248*61e115a5SMichael Buesch 249*61e115a5SMichael Buesch for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) 250*61e115a5SMichael Buesch sprom[i] = readw(bus->mmio + SSB_SPROM_BASE + (i * 2)); 251*61e115a5SMichael Buesch } 252*61e115a5SMichael Buesch 253*61e115a5SMichael Buesch static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) 254*61e115a5SMichael Buesch { 255*61e115a5SMichael Buesch struct pci_dev *pdev = bus->host_pci; 256*61e115a5SMichael Buesch int i, err; 257*61e115a5SMichael Buesch u32 spromctl; 258*61e115a5SMichael Buesch 259*61e115a5SMichael Buesch ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); 260*61e115a5SMichael Buesch err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); 261*61e115a5SMichael Buesch if (err) 262*61e115a5SMichael Buesch goto err_ctlreg; 263*61e115a5SMichael Buesch spromctl |= SSB_SPROMCTL_WE; 264*61e115a5SMichael Buesch err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); 265*61e115a5SMichael Buesch if (err) 266*61e115a5SMichael Buesch goto err_ctlreg; 267*61e115a5SMichael Buesch ssb_printk(KERN_NOTICE PFX "[ 0%%"); 268*61e115a5SMichael Buesch msleep(500); 269*61e115a5SMichael Buesch for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) { 270*61e115a5SMichael Buesch if (i == SSB_SPROMSIZE_WORDS / 4) 271*61e115a5SMichael Buesch ssb_printk("25%%"); 272*61e115a5SMichael Buesch else if (i == SSB_SPROMSIZE_WORDS / 2) 273*61e115a5SMichael Buesch ssb_printk("50%%"); 274*61e115a5SMichael Buesch else if (i == (SSB_SPROMSIZE_WORDS / 4) * 3) 275*61e115a5SMichael Buesch ssb_printk("75%%"); 276*61e115a5SMichael Buesch else if (i % 2) 277*61e115a5SMichael Buesch ssb_printk("."); 278*61e115a5SMichael Buesch writew(sprom[i], bus->mmio + SSB_SPROM_BASE + (i * 2)); 279*61e115a5SMichael Buesch mmiowb(); 280*61e115a5SMichael Buesch msleep(20); 281*61e115a5SMichael Buesch } 282*61e115a5SMichael Buesch err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); 283*61e115a5SMichael Buesch if (err) 284*61e115a5SMichael Buesch goto err_ctlreg; 285*61e115a5SMichael Buesch spromctl &= ~SSB_SPROMCTL_WE; 286*61e115a5SMichael Buesch err = pci_write_config_dword(pdev, SSB_SPROMCTL, spromctl); 287*61e115a5SMichael Buesch if (err) 288*61e115a5SMichael Buesch goto err_ctlreg; 289*61e115a5SMichael Buesch msleep(500); 290*61e115a5SMichael Buesch ssb_printk("100%% ]\n"); 291*61e115a5SMichael Buesch ssb_printk(KERN_NOTICE PFX "SPROM written.\n"); 292*61e115a5SMichael Buesch 293*61e115a5SMichael Buesch return 0; 294*61e115a5SMichael Buesch err_ctlreg: 295*61e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "Could not access SPROM control register.\n"); 296*61e115a5SMichael Buesch return err; 297*61e115a5SMichael Buesch } 298*61e115a5SMichael Buesch 299*61e115a5SMichael Buesch static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in) 300*61e115a5SMichael Buesch { 301*61e115a5SMichael Buesch int i; 302*61e115a5SMichael Buesch u16 v; 303*61e115a5SMichael Buesch 304*61e115a5SMichael Buesch SPEX(pci_spid, SSB_SPROM1_SPID, 0xFFFF, 0); 305*61e115a5SMichael Buesch SPEX(pci_svid, SSB_SPROM1_SVID, 0xFFFF, 0); 306*61e115a5SMichael Buesch SPEX(pci_pid, SSB_SPROM1_PID, 0xFFFF, 0); 307*61e115a5SMichael Buesch for (i = 0; i < 3; i++) { 308*61e115a5SMichael Buesch v = in[SPOFF(SSB_SPROM1_IL0MAC) + i]; 309*61e115a5SMichael Buesch *(((u16 *)out->il0mac) + i) = cpu_to_be16(v); 310*61e115a5SMichael Buesch } 311*61e115a5SMichael Buesch for (i = 0; i < 3; i++) { 312*61e115a5SMichael Buesch v = in[SPOFF(SSB_SPROM1_ET0MAC) + i]; 313*61e115a5SMichael Buesch *(((u16 *)out->et0mac) + i) = cpu_to_be16(v); 314*61e115a5SMichael Buesch } 315*61e115a5SMichael Buesch for (i = 0; i < 3; i++) { 316*61e115a5SMichael Buesch v = in[SPOFF(SSB_SPROM1_ET1MAC) + i]; 317*61e115a5SMichael Buesch *(((u16 *)out->et1mac) + i) = cpu_to_be16(v); 318*61e115a5SMichael Buesch } 319*61e115a5SMichael Buesch SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0); 320*61e115a5SMichael Buesch SPEX(et1phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1A, 321*61e115a5SMichael Buesch SSB_SPROM1_ETHPHY_ET1A_SHIFT); 322*61e115a5SMichael Buesch SPEX(et0mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0M, 14); 323*61e115a5SMichael Buesch SPEX(et1mdcport, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET1M, 15); 324*61e115a5SMichael Buesch SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0); 325*61e115a5SMichael Buesch SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE, 326*61e115a5SMichael Buesch SSB_SPROM1_BINF_CCODE_SHIFT); 327*61e115a5SMichael Buesch SPEX(antenna_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA, 328*61e115a5SMichael Buesch SSB_SPROM1_BINF_ANTA_SHIFT); 329*61e115a5SMichael Buesch SPEX(antenna_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG, 330*61e115a5SMichael Buesch SSB_SPROM1_BINF_ANTBG_SHIFT); 331*61e115a5SMichael Buesch SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0); 332*61e115a5SMichael Buesch SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0); 333*61e115a5SMichael Buesch SPEX(pa0b2, SSB_SPROM1_PA0B2, 0xFFFF, 0); 334*61e115a5SMichael Buesch SPEX(pa1b0, SSB_SPROM1_PA1B0, 0xFFFF, 0); 335*61e115a5SMichael Buesch SPEX(pa1b1, SSB_SPROM1_PA1B1, 0xFFFF, 0); 336*61e115a5SMichael Buesch SPEX(pa1b2, SSB_SPROM1_PA1B2, 0xFFFF, 0); 337*61e115a5SMichael Buesch SPEX(gpio0, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P0, 0); 338*61e115a5SMichael Buesch SPEX(gpio1, SSB_SPROM1_GPIOA, SSB_SPROM1_GPIOA_P1, 339*61e115a5SMichael Buesch SSB_SPROM1_GPIOA_P1_SHIFT); 340*61e115a5SMichael Buesch SPEX(gpio2, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P2, 0); 341*61e115a5SMichael Buesch SPEX(gpio3, SSB_SPROM1_GPIOB, SSB_SPROM1_GPIOB_P3, 342*61e115a5SMichael Buesch SSB_SPROM1_GPIOB_P3_SHIFT); 343*61e115a5SMichael Buesch SPEX(maxpwr_a, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_A, 344*61e115a5SMichael Buesch SSB_SPROM1_MAXPWR_A_SHIFT); 345*61e115a5SMichael Buesch SPEX(maxpwr_bg, SSB_SPROM1_MAXPWR, SSB_SPROM1_MAXPWR_BG, 0); 346*61e115a5SMichael Buesch SPEX(itssi_a, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_A, 347*61e115a5SMichael Buesch SSB_SPROM1_ITSSI_A_SHIFT); 348*61e115a5SMichael Buesch SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0); 349*61e115a5SMichael Buesch SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0); 350*61e115a5SMichael Buesch SPEX(antenna_gain_a, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_A, 0); 351*61e115a5SMichael Buesch SPEX(antenna_gain_bg, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_BG, 352*61e115a5SMichael Buesch SSB_SPROM1_AGAIN_BG_SHIFT); 353*61e115a5SMichael Buesch for (i = 0; i < 4; i++) { 354*61e115a5SMichael Buesch v = in[SPOFF(SSB_SPROM1_OEM) + i]; 355*61e115a5SMichael Buesch *(((u16 *)out->oem) + i) = cpu_to_le16(v); 356*61e115a5SMichael Buesch } 357*61e115a5SMichael Buesch } 358*61e115a5SMichael Buesch 359*61e115a5SMichael Buesch static void sprom_extract_r2(struct ssb_sprom_r2 *out, const u16 *in) 360*61e115a5SMichael Buesch { 361*61e115a5SMichael Buesch int i; 362*61e115a5SMichael Buesch u16 v; 363*61e115a5SMichael Buesch 364*61e115a5SMichael Buesch SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); 365*61e115a5SMichael Buesch SPEX(maxpwr_a_hi, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0); 366*61e115a5SMichael Buesch SPEX(maxpwr_a_lo, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO, 367*61e115a5SMichael Buesch SSB_SPROM2_MAXP_A_LO_SHIFT); 368*61e115a5SMichael Buesch SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0); 369*61e115a5SMichael Buesch SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0); 370*61e115a5SMichael Buesch SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0); 371*61e115a5SMichael Buesch SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0); 372*61e115a5SMichael Buesch SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0); 373*61e115a5SMichael Buesch SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0); 374*61e115a5SMichael Buesch SPEX(ofdm_pwr_off, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0); 375*61e115a5SMichael Buesch for (i = 0; i < 4; i++) { 376*61e115a5SMichael Buesch v = in[SPOFF(SSB_SPROM2_CCODE) + i]; 377*61e115a5SMichael Buesch *(((u16 *)out->country_str) + i) = cpu_to_le16(v); 378*61e115a5SMichael Buesch } 379*61e115a5SMichael Buesch } 380*61e115a5SMichael Buesch 381*61e115a5SMichael Buesch static void sprom_extract_r3(struct ssb_sprom_r3 *out, const u16 *in) 382*61e115a5SMichael Buesch { 383*61e115a5SMichael Buesch out->ofdmapo = (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0xFF00) >> 8; 384*61e115a5SMichael Buesch out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0x00FF) << 8; 385*61e115a5SMichael Buesch out->ofdmapo <<= 16; 386*61e115a5SMichael Buesch out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0xFF00) >> 8; 387*61e115a5SMichael Buesch out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0x00FF) << 8; 388*61e115a5SMichael Buesch 389*61e115a5SMichael Buesch out->ofdmalpo = (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0xFF00) >> 8; 390*61e115a5SMichael Buesch out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0x00FF) << 8; 391*61e115a5SMichael Buesch out->ofdmalpo <<= 16; 392*61e115a5SMichael Buesch out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0xFF00) >> 8; 393*61e115a5SMichael Buesch out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0x00FF) << 8; 394*61e115a5SMichael Buesch 395*61e115a5SMichael Buesch out->ofdmahpo = (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0xFF00) >> 8; 396*61e115a5SMichael Buesch out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0x00FF) << 8; 397*61e115a5SMichael Buesch out->ofdmahpo <<= 16; 398*61e115a5SMichael Buesch out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0xFF00) >> 8; 399*61e115a5SMichael Buesch out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0x00FF) << 8; 400*61e115a5SMichael Buesch 401*61e115a5SMichael Buesch SPEX(gpioldc_on_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_ON, 402*61e115a5SMichael Buesch SSB_SPROM3_GPIOLDC_ON_SHIFT); 403*61e115a5SMichael Buesch SPEX(gpioldc_off_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_OFF, 404*61e115a5SMichael Buesch SSB_SPROM3_GPIOLDC_OFF_SHIFT); 405*61e115a5SMichael Buesch SPEX(cckpo_1M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_1M, 0); 406*61e115a5SMichael Buesch SPEX(cckpo_2M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_2M, 407*61e115a5SMichael Buesch SSB_SPROM3_CCKPO_2M_SHIFT); 408*61e115a5SMichael Buesch SPEX(cckpo_55M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_55M, 409*61e115a5SMichael Buesch SSB_SPROM3_CCKPO_55M_SHIFT); 410*61e115a5SMichael Buesch SPEX(cckpo_11M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_11M, 411*61e115a5SMichael Buesch SSB_SPROM3_CCKPO_11M_SHIFT); 412*61e115a5SMichael Buesch 413*61e115a5SMichael Buesch out->ofdmgpo = (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0xFF00) >> 8; 414*61e115a5SMichael Buesch out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0x00FF) << 8; 415*61e115a5SMichael Buesch out->ofdmgpo <<= 16; 416*61e115a5SMichael Buesch out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0xFF00) >> 8; 417*61e115a5SMichael Buesch out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0x00FF) << 8; 418*61e115a5SMichael Buesch } 419*61e115a5SMichael Buesch 420*61e115a5SMichael Buesch static int sprom_extract(struct ssb_bus *bus, 421*61e115a5SMichael Buesch struct ssb_sprom *out, const u16 *in) 422*61e115a5SMichael Buesch { 423*61e115a5SMichael Buesch memset(out, 0, sizeof(*out)); 424*61e115a5SMichael Buesch 425*61e115a5SMichael Buesch SPEX(revision, SSB_SPROM_REVISION, SSB_SPROM_REVISION_REV, 0); 426*61e115a5SMichael Buesch SPEX(crc, SSB_SPROM_REVISION, SSB_SPROM_REVISION_CRC, 427*61e115a5SMichael Buesch SSB_SPROM_REVISION_CRC_SHIFT); 428*61e115a5SMichael Buesch 429*61e115a5SMichael Buesch if ((bus->chip_id & 0xFF00) == 0x4400) { 430*61e115a5SMichael Buesch /* Workaround: The BCM44XX chip has a stupid revision 431*61e115a5SMichael Buesch * number stored in the SPROM. 432*61e115a5SMichael Buesch * Always extract r1. */ 433*61e115a5SMichael Buesch sprom_extract_r1(&out->r1, in); 434*61e115a5SMichael Buesch } else { 435*61e115a5SMichael Buesch if (out->revision == 0) 436*61e115a5SMichael Buesch goto unsupported; 437*61e115a5SMichael Buesch if (out->revision >= 1 && out->revision <= 3) 438*61e115a5SMichael Buesch sprom_extract_r1(&out->r1, in); 439*61e115a5SMichael Buesch if (out->revision >= 2 && out->revision <= 3) 440*61e115a5SMichael Buesch sprom_extract_r2(&out->r2, in); 441*61e115a5SMichael Buesch if (out->revision == 3) 442*61e115a5SMichael Buesch sprom_extract_r3(&out->r3, in); 443*61e115a5SMichael Buesch if (out->revision >= 4) 444*61e115a5SMichael Buesch goto unsupported; 445*61e115a5SMichael Buesch } 446*61e115a5SMichael Buesch 447*61e115a5SMichael Buesch return 0; 448*61e115a5SMichael Buesch unsupported: 449*61e115a5SMichael Buesch ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d " 450*61e115a5SMichael Buesch "detected. Will extract v1\n", out->revision); 451*61e115a5SMichael Buesch sprom_extract_r1(&out->r1, in); 452*61e115a5SMichael Buesch return 0; 453*61e115a5SMichael Buesch } 454*61e115a5SMichael Buesch 455*61e115a5SMichael Buesch static int ssb_pci_sprom_get(struct ssb_bus *bus, 456*61e115a5SMichael Buesch struct ssb_sprom *sprom) 457*61e115a5SMichael Buesch { 458*61e115a5SMichael Buesch int err = -ENOMEM; 459*61e115a5SMichael Buesch u16 *buf; 460*61e115a5SMichael Buesch 461*61e115a5SMichael Buesch buf = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); 462*61e115a5SMichael Buesch if (!buf) 463*61e115a5SMichael Buesch goto out; 464*61e115a5SMichael Buesch sprom_do_read(bus, buf); 465*61e115a5SMichael Buesch err = sprom_check_crc(buf); 466*61e115a5SMichael Buesch if (err) { 467*61e115a5SMichael Buesch ssb_printk(KERN_WARNING PFX 468*61e115a5SMichael Buesch "WARNING: Invalid SPROM CRC (corrupt SPROM)\n"); 469*61e115a5SMichael Buesch } 470*61e115a5SMichael Buesch err = sprom_extract(bus, sprom, buf); 471*61e115a5SMichael Buesch 472*61e115a5SMichael Buesch kfree(buf); 473*61e115a5SMichael Buesch out: 474*61e115a5SMichael Buesch return err; 475*61e115a5SMichael Buesch } 476*61e115a5SMichael Buesch 477*61e115a5SMichael Buesch static void ssb_pci_get_boardinfo(struct ssb_bus *bus, 478*61e115a5SMichael Buesch struct ssb_boardinfo *bi) 479*61e115a5SMichael Buesch { 480*61e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_VENDOR_ID, 481*61e115a5SMichael Buesch &bi->vendor); 482*61e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_SUBSYSTEM_ID, 483*61e115a5SMichael Buesch &bi->type); 484*61e115a5SMichael Buesch pci_read_config_word(bus->host_pci, PCI_REVISION_ID, 485*61e115a5SMichael Buesch &bi->rev); 486*61e115a5SMichael Buesch } 487*61e115a5SMichael Buesch 488*61e115a5SMichael Buesch int ssb_pci_get_invariants(struct ssb_bus *bus, 489*61e115a5SMichael Buesch struct ssb_init_invariants *iv) 490*61e115a5SMichael Buesch { 491*61e115a5SMichael Buesch int err; 492*61e115a5SMichael Buesch 493*61e115a5SMichael Buesch err = ssb_pci_sprom_get(bus, &iv->sprom); 494*61e115a5SMichael Buesch if (err) 495*61e115a5SMichael Buesch goto out; 496*61e115a5SMichael Buesch ssb_pci_get_boardinfo(bus, &iv->boardinfo); 497*61e115a5SMichael Buesch 498*61e115a5SMichael Buesch out: 499*61e115a5SMichael Buesch return err; 500*61e115a5SMichael Buesch } 501*61e115a5SMichael Buesch 502*61e115a5SMichael Buesch #ifdef CONFIG_SSB_DEBUG 503*61e115a5SMichael Buesch static int ssb_pci_assert_buspower(struct ssb_bus *bus) 504*61e115a5SMichael Buesch { 505*61e115a5SMichael Buesch if (likely(bus->powered_up)) 506*61e115a5SMichael Buesch return 0; 507*61e115a5SMichael Buesch 508*61e115a5SMichael Buesch printk(KERN_ERR PFX "FATAL ERROR: Bus powered down " 509*61e115a5SMichael Buesch "while accessing PCI MMIO space\n"); 510*61e115a5SMichael Buesch if (bus->power_warn_count <= 10) { 511*61e115a5SMichael Buesch bus->power_warn_count++; 512*61e115a5SMichael Buesch dump_stack(); 513*61e115a5SMichael Buesch } 514*61e115a5SMichael Buesch 515*61e115a5SMichael Buesch return -ENODEV; 516*61e115a5SMichael Buesch } 517*61e115a5SMichael Buesch #else /* DEBUG */ 518*61e115a5SMichael Buesch static inline int ssb_pci_assert_buspower(struct ssb_bus *bus) 519*61e115a5SMichael Buesch { 520*61e115a5SMichael Buesch return 0; 521*61e115a5SMichael Buesch } 522*61e115a5SMichael Buesch #endif /* DEBUG */ 523*61e115a5SMichael Buesch 524*61e115a5SMichael Buesch static u16 ssb_pci_read16(struct ssb_device *dev, u16 offset) 525*61e115a5SMichael Buesch { 526*61e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 527*61e115a5SMichael Buesch 528*61e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 529*61e115a5SMichael Buesch return 0xFFFF; 530*61e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 531*61e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 532*61e115a5SMichael Buesch return 0xFFFF; 533*61e115a5SMichael Buesch } 534*61e115a5SMichael Buesch return readw(bus->mmio + offset); 535*61e115a5SMichael Buesch } 536*61e115a5SMichael Buesch 537*61e115a5SMichael Buesch static u32 ssb_pci_read32(struct ssb_device *dev, u16 offset) 538*61e115a5SMichael Buesch { 539*61e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 540*61e115a5SMichael Buesch 541*61e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 542*61e115a5SMichael Buesch return 0xFFFFFFFF; 543*61e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 544*61e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 545*61e115a5SMichael Buesch return 0xFFFFFFFF; 546*61e115a5SMichael Buesch } 547*61e115a5SMichael Buesch return readl(bus->mmio + offset); 548*61e115a5SMichael Buesch } 549*61e115a5SMichael Buesch 550*61e115a5SMichael Buesch static void ssb_pci_write16(struct ssb_device *dev, u16 offset, u16 value) 551*61e115a5SMichael Buesch { 552*61e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 553*61e115a5SMichael Buesch 554*61e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 555*61e115a5SMichael Buesch return; 556*61e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 557*61e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 558*61e115a5SMichael Buesch return; 559*61e115a5SMichael Buesch } 560*61e115a5SMichael Buesch writew(value, bus->mmio + offset); 561*61e115a5SMichael Buesch } 562*61e115a5SMichael Buesch 563*61e115a5SMichael Buesch static void ssb_pci_write32(struct ssb_device *dev, u16 offset, u32 value) 564*61e115a5SMichael Buesch { 565*61e115a5SMichael Buesch struct ssb_bus *bus = dev->bus; 566*61e115a5SMichael Buesch 567*61e115a5SMichael Buesch if (unlikely(ssb_pci_assert_buspower(bus))) 568*61e115a5SMichael Buesch return; 569*61e115a5SMichael Buesch if (unlikely(bus->mapped_device != dev)) { 570*61e115a5SMichael Buesch if (unlikely(ssb_pci_switch_core(bus, dev))) 571*61e115a5SMichael Buesch return; 572*61e115a5SMichael Buesch } 573*61e115a5SMichael Buesch writel(value, bus->mmio + offset); 574*61e115a5SMichael Buesch } 575*61e115a5SMichael Buesch 576*61e115a5SMichael Buesch /* Not "static", as it's used in main.c */ 577*61e115a5SMichael Buesch const struct ssb_bus_ops ssb_pci_ops = { 578*61e115a5SMichael Buesch .read16 = ssb_pci_read16, 579*61e115a5SMichael Buesch .read32 = ssb_pci_read32, 580*61e115a5SMichael Buesch .write16 = ssb_pci_write16, 581*61e115a5SMichael Buesch .write32 = ssb_pci_write32, 582*61e115a5SMichael Buesch }; 583*61e115a5SMichael Buesch 584*61e115a5SMichael Buesch static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len) 585*61e115a5SMichael Buesch { 586*61e115a5SMichael Buesch int i, pos = 0; 587*61e115a5SMichael Buesch 588*61e115a5SMichael Buesch for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) { 589*61e115a5SMichael Buesch pos += snprintf(buf + pos, buf_len - pos - 1, 590*61e115a5SMichael Buesch "%04X", swab16(sprom[i]) & 0xFFFF); 591*61e115a5SMichael Buesch } 592*61e115a5SMichael Buesch pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); 593*61e115a5SMichael Buesch 594*61e115a5SMichael Buesch return pos + 1; 595*61e115a5SMichael Buesch } 596*61e115a5SMichael Buesch 597*61e115a5SMichael Buesch static int hex2sprom(u16 *sprom, const char *dump, size_t len) 598*61e115a5SMichael Buesch { 599*61e115a5SMichael Buesch char tmp[5] = { 0 }; 600*61e115a5SMichael Buesch int cnt = 0; 601*61e115a5SMichael Buesch unsigned long parsed; 602*61e115a5SMichael Buesch 603*61e115a5SMichael Buesch if (len < SSB_SPROMSIZE_BYTES * 2) 604*61e115a5SMichael Buesch return -EINVAL; 605*61e115a5SMichael Buesch 606*61e115a5SMichael Buesch while (cnt < SSB_SPROMSIZE_WORDS) { 607*61e115a5SMichael Buesch memcpy(tmp, dump, 4); 608*61e115a5SMichael Buesch dump += 4; 609*61e115a5SMichael Buesch parsed = simple_strtoul(tmp, NULL, 16); 610*61e115a5SMichael Buesch sprom[cnt++] = swab16((u16)parsed); 611*61e115a5SMichael Buesch } 612*61e115a5SMichael Buesch 613*61e115a5SMichael Buesch return 0; 614*61e115a5SMichael Buesch } 615*61e115a5SMichael Buesch 616*61e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, 617*61e115a5SMichael Buesch struct device_attribute *attr, 618*61e115a5SMichael Buesch char *buf) 619*61e115a5SMichael Buesch { 620*61e115a5SMichael Buesch struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); 621*61e115a5SMichael Buesch struct ssb_bus *bus; 622*61e115a5SMichael Buesch u16 *sprom; 623*61e115a5SMichael Buesch int err = -ENODEV; 624*61e115a5SMichael Buesch ssize_t count = 0; 625*61e115a5SMichael Buesch 626*61e115a5SMichael Buesch bus = ssb_pci_dev_to_bus(pdev); 627*61e115a5SMichael Buesch if (!bus) 628*61e115a5SMichael Buesch goto out; 629*61e115a5SMichael Buesch err = -ENOMEM; 630*61e115a5SMichael Buesch sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); 631*61e115a5SMichael Buesch if (!sprom) 632*61e115a5SMichael Buesch goto out; 633*61e115a5SMichael Buesch 634*61e115a5SMichael Buesch /* Use interruptible locking, as the SPROM write might 635*61e115a5SMichael Buesch * be holding the lock for several seconds. So allow userspace 636*61e115a5SMichael Buesch * to cancel operation. */ 637*61e115a5SMichael Buesch err = -ERESTARTSYS; 638*61e115a5SMichael Buesch if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) 639*61e115a5SMichael Buesch goto out_kfree; 640*61e115a5SMichael Buesch sprom_do_read(bus, sprom); 641*61e115a5SMichael Buesch mutex_unlock(&bus->pci_sprom_mutex); 642*61e115a5SMichael Buesch 643*61e115a5SMichael Buesch count = sprom2hex(sprom, buf, PAGE_SIZE); 644*61e115a5SMichael Buesch err = 0; 645*61e115a5SMichael Buesch 646*61e115a5SMichael Buesch out_kfree: 647*61e115a5SMichael Buesch kfree(sprom); 648*61e115a5SMichael Buesch out: 649*61e115a5SMichael Buesch return err ? err : count; 650*61e115a5SMichael Buesch } 651*61e115a5SMichael Buesch 652*61e115a5SMichael Buesch static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, 653*61e115a5SMichael Buesch struct device_attribute *attr, 654*61e115a5SMichael Buesch const char *buf, size_t count) 655*61e115a5SMichael Buesch { 656*61e115a5SMichael Buesch struct pci_dev *pdev = container_of(pcidev, struct pci_dev, dev); 657*61e115a5SMichael Buesch struct ssb_bus *bus; 658*61e115a5SMichael Buesch u16 *sprom; 659*61e115a5SMichael Buesch int res = 0, err = -ENODEV; 660*61e115a5SMichael Buesch 661*61e115a5SMichael Buesch bus = ssb_pci_dev_to_bus(pdev); 662*61e115a5SMichael Buesch if (!bus) 663*61e115a5SMichael Buesch goto out; 664*61e115a5SMichael Buesch err = -ENOMEM; 665*61e115a5SMichael Buesch sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); 666*61e115a5SMichael Buesch if (!sprom) 667*61e115a5SMichael Buesch goto out; 668*61e115a5SMichael Buesch err = hex2sprom(sprom, buf, count); 669*61e115a5SMichael Buesch if (err) { 670*61e115a5SMichael Buesch err = -EINVAL; 671*61e115a5SMichael Buesch goto out_kfree; 672*61e115a5SMichael Buesch } 673*61e115a5SMichael Buesch err = sprom_check_crc(sprom); 674*61e115a5SMichael Buesch if (err) { 675*61e115a5SMichael Buesch err = -EINVAL; 676*61e115a5SMichael Buesch goto out_kfree; 677*61e115a5SMichael Buesch } 678*61e115a5SMichael Buesch 679*61e115a5SMichael Buesch /* Use interruptible locking, as the SPROM write might 680*61e115a5SMichael Buesch * be holding the lock for several seconds. So allow userspace 681*61e115a5SMichael Buesch * to cancel operation. */ 682*61e115a5SMichael Buesch err = -ERESTARTSYS; 683*61e115a5SMichael Buesch if (mutex_lock_interruptible(&bus->pci_sprom_mutex)) 684*61e115a5SMichael Buesch goto out_kfree; 685*61e115a5SMichael Buesch err = ssb_devices_freeze(bus); 686*61e115a5SMichael Buesch if (err == -EOPNOTSUPP) { 687*61e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze devices. " 688*61e115a5SMichael Buesch "No suspend support. Is CONFIG_PM enabled?\n"); 689*61e115a5SMichael Buesch goto out_unlock; 690*61e115a5SMichael Buesch } 691*61e115a5SMichael Buesch if (err) { 692*61e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not freeze all devices\n"); 693*61e115a5SMichael Buesch goto out_unlock; 694*61e115a5SMichael Buesch } 695*61e115a5SMichael Buesch res = sprom_do_write(bus, sprom); 696*61e115a5SMichael Buesch err = ssb_devices_thaw(bus); 697*61e115a5SMichael Buesch if (err) 698*61e115a5SMichael Buesch ssb_printk(KERN_ERR PFX "SPROM write: Could not thaw all devices\n"); 699*61e115a5SMichael Buesch out_unlock: 700*61e115a5SMichael Buesch mutex_unlock(&bus->pci_sprom_mutex); 701*61e115a5SMichael Buesch out_kfree: 702*61e115a5SMichael Buesch kfree(sprom); 703*61e115a5SMichael Buesch out: 704*61e115a5SMichael Buesch if (res) 705*61e115a5SMichael Buesch return res; 706*61e115a5SMichael Buesch return err ? err : count; 707*61e115a5SMichael Buesch } 708*61e115a5SMichael Buesch 709*61e115a5SMichael Buesch static DEVICE_ATTR(ssb_sprom, 0600, 710*61e115a5SMichael Buesch ssb_pci_attr_sprom_show, 711*61e115a5SMichael Buesch ssb_pci_attr_sprom_store); 712*61e115a5SMichael Buesch 713*61e115a5SMichael Buesch void ssb_pci_exit(struct ssb_bus *bus) 714*61e115a5SMichael Buesch { 715*61e115a5SMichael Buesch struct pci_dev *pdev; 716*61e115a5SMichael Buesch 717*61e115a5SMichael Buesch if (bus->bustype != SSB_BUSTYPE_PCI) 718*61e115a5SMichael Buesch return; 719*61e115a5SMichael Buesch 720*61e115a5SMichael Buesch pdev = bus->host_pci; 721*61e115a5SMichael Buesch device_remove_file(&pdev->dev, &dev_attr_ssb_sprom); 722*61e115a5SMichael Buesch } 723*61e115a5SMichael Buesch 724*61e115a5SMichael Buesch int ssb_pci_init(struct ssb_bus *bus) 725*61e115a5SMichael Buesch { 726*61e115a5SMichael Buesch struct pci_dev *pdev; 727*61e115a5SMichael Buesch int err; 728*61e115a5SMichael Buesch 729*61e115a5SMichael Buesch if (bus->bustype != SSB_BUSTYPE_PCI) 730*61e115a5SMichael Buesch return 0; 731*61e115a5SMichael Buesch 732*61e115a5SMichael Buesch pdev = bus->host_pci; 733*61e115a5SMichael Buesch mutex_init(&bus->pci_sprom_mutex); 734*61e115a5SMichael Buesch err = device_create_file(&pdev->dev, &dev_attr_ssb_sprom); 735*61e115a5SMichael Buesch if (err) 736*61e115a5SMichael Buesch goto out; 737*61e115a5SMichael Buesch 738*61e115a5SMichael Buesch out: 739*61e115a5SMichael Buesch return err; 740*61e115a5SMichael Buesch } 741