--- a/arch/mips/bcm47xx/nvram.c +++ b/arch/mips/bcm47xx/nvram.c @@ -43,8 +43,8 @@ static void early_nvram_init(void) #ifdef CONFIG_BCM47XX_SSB case BCM47XX_BUS_TYPE_SSB: mcore_ssb = &bcm47xx_bus.ssb.mipscore; - base = mcore_ssb->flash_window; - lim = mcore_ssb->flash_window_size; + base = mcore_ssb->pflash.window; + lim = mcore_ssb->pflash.window_size; break; #endif #ifdef CONFIG_BCM47XX_BCMA --- a/arch/mips/bcm47xx/wgt634u.c +++ b/arch/mips/bcm47xx/wgt634u.c @@ -156,10 +156,10 @@ static int __init wgt634u_init(void) SSB_CHIPCO_IRQ_GPIO); } - wgt634u_flash_data.width = mcore->flash_buswidth; - wgt634u_flash_resource.start = mcore->flash_window; - wgt634u_flash_resource.end = mcore->flash_window - + mcore->flash_window_size + wgt634u_flash_data.width = mcore->pflash.buswidth; + wgt634u_flash_resource.start = mcore->pflash.window; + wgt634u_flash_resource.end = mcore->pflash.window + + mcore->pflash.window_size - 1; return platform_add_devices(wgt634u_devices, ARRAY_SIZE(wgt634u_devices)); --- a/drivers/ssb/Kconfig +++ b/drivers/ssb/Kconfig @@ -136,6 +136,11 @@ config SSB_DRIVER_MIPS If unsure, say N +config SSB_SFLASH + bool "SSB serial flash support" + depends on SSB_DRIVER_MIPS && BROKEN + default y + # Assumption: We are on embedded, if we compile the MIPS core. config SSB_EMBEDDED bool @@ -160,4 +165,12 @@ config SSB_DRIVER_GIGE If unsure, say N +config SSB_DRIVER_GPIO + bool "SSB GPIO driver" + depends on SSB && GPIOLIB + help + Driver to provide access to the GPIO pins on the bus. + + If unsure, say N + endmenu --- a/drivers/ssb/Makefile +++ b/drivers/ssb/Makefile @@ -11,10 +11,12 @@ ssb-$(CONFIG_SSB_SDIOHOST) += sdio.o # built-in drivers ssb-y += driver_chipcommon.o ssb-y += driver_chipcommon_pmu.o +ssb-$(CONFIG_SSB_SFLASH) += driver_chipcommon_sflash.o ssb-$(CONFIG_SSB_DRIVER_MIPS) += driver_mipscore.o ssb-$(CONFIG_SSB_DRIVER_EXTIF) += driver_extif.o ssb-$(CONFIG_SSB_DRIVER_PCICORE) += driver_pcicore.o ssb-$(CONFIG_SSB_DRIVER_GIGE) += driver_gige.o +ssb-$(CONFIG_SSB_DRIVER_GPIO) += driver_gpio.o # b43 pci-ssb-bridge driver # Not strictly a part of SSB, but kept here for convenience --- a/drivers/ssb/b43_pci_bridge.c +++ b/drivers/ssb/b43_pci_bridge.c @@ -37,6 +37,7 @@ static const struct pci_device_id b43_pc { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4329) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x432b) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x432c) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4350) }, { 0, }, }; MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl); --- a/drivers/ssb/driver_chipcommon.c +++ b/drivers/ssb/driver_chipcommon.c @@ -4,6 +4,7 @@ * * Copyright 2005, Broadcom Corporation * Copyright 2006, 2007, Michael Buesch + * Copyright 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ @@ -12,6 +13,7 @@ #include #include #include +#include #include "ssb_private.h" @@ -280,10 +282,76 @@ static void calc_fast_powerup_delay(stru cc->fast_pwrup_delay = tmp; } +static u32 ssb_chipco_alp_clock(struct ssb_chipcommon *cc) +{ + if (cc->capabilities & SSB_CHIPCO_CAP_PMU) + return ssb_pmu_get_alp_clock(cc); + + return 20000000; +} + +static u32 ssb_chipco_watchdog_get_max_timer(struct ssb_chipcommon *cc) +{ + u32 nb; + + if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { + if (cc->dev->id.revision < 26) + nb = 16; + else + nb = (cc->dev->id.revision >= 37) ? 32 : 24; + } else { + nb = 28; + } + if (nb == 32) + return 0xffffffff; + else + return (1 << nb) - 1; +} + +u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks) +{ + struct ssb_chipcommon *cc = bcm47xx_wdt_get_drvdata(wdt); + + if (cc->dev->bus->bustype != SSB_BUSTYPE_SSB) + return 0; + + return ssb_chipco_watchdog_timer_set(cc, ticks); +} + +u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms) +{ + struct ssb_chipcommon *cc = bcm47xx_wdt_get_drvdata(wdt); + u32 ticks; + + if (cc->dev->bus->bustype != SSB_BUSTYPE_SSB) + return 0; + + ticks = ssb_chipco_watchdog_timer_set(cc, cc->ticks_per_ms * ms); + return ticks / cc->ticks_per_ms; +} + +static int ssb_chipco_watchdog_ticks_per_ms(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + + if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { + /* based on 32KHz ILP clock */ + return 32; + } else { + if (cc->dev->id.revision < 18) + return ssb_clockspeed(bus) / 1000; + else + return ssb_chipco_alp_clock(cc) / 1000; + } +} + void ssb_chipcommon_init(struct ssb_chipcommon *cc) { if (!cc->dev) return; /* We don't have a ChipCommon */ + + spin_lock_init(&cc->gpio_lock); + if (cc->dev->id.revision >= 11) cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT); ssb_dprintk(KERN_INFO PFX "chipcommon status is 0x%x\n", cc->status); @@ -297,6 +365,11 @@ void ssb_chipcommon_init(struct ssb_chip chipco_powercontrol_init(cc); ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); calc_fast_powerup_delay(cc); + + if (cc->dev->bus->bustype == SSB_BUSTYPE_SSB) { + cc->ticks_per_ms = ssb_chipco_watchdog_ticks_per_ms(cc); + cc->max_timer_ms = ssb_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms; + } } void ssb_chipco_suspend(struct ssb_chipcommon *cc) @@ -395,10 +468,27 @@ void ssb_chipco_timing_init(struct ssb_c } /* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */ -void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks) +u32 ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks) { - /* instant NMI */ - chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks); + u32 maxt; + enum ssb_clkmode clkmode; + + maxt = ssb_chipco_watchdog_get_max_timer(cc); + if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { + if (ticks == 1) + ticks = 2; + else if (ticks > maxt) + ticks = maxt; + chipco_write32(cc, SSB_CHIPCO_PMU_WATCHDOG, ticks); + } else { + clkmode = ticks ? SSB_CLKMODE_FAST : SSB_CLKMODE_DYNAMIC; + ssb_chipco_set_clockmode(cc, clkmode); + if (ticks > maxt) + ticks = maxt; + /* instant NMI */ + chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks); + } + return ticks; } void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value) @@ -418,28 +508,93 @@ u32 ssb_chipco_gpio_in(struct ssb_chipco u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } EXPORT_SYMBOL(ssb_chipco_gpio_control); u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value) { - return chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; +} + +u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value) +{ + unsigned long flags; + u32 res = 0; + + if (cc->dev->id.revision < 20) + return 0xffffffff; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLUP, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; +} + +u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value) +{ + unsigned long flags; + u32 res = 0; + + if (cc->dev->id.revision < 20) + return 0xffffffff; + + spin_lock_irqsave(&cc->gpio_lock, flags); + res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLDOWN, mask, value); + spin_unlock_irqrestore(&cc->gpio_lock, flags); + + return res; } #ifdef CONFIG_SSB_SERIAL @@ -473,12 +628,7 @@ int ssb_chipco_serial_init(struct ssb_ch chipco_read32(cc, SSB_CHIPCO_CORECTL) | SSB_CHIPCO_CORECTL_UARTCLK0); } else if ((ccrev >= 11) && (ccrev != 15)) { - /* Fixed ALP clock */ - baud_base = 20000000; - if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { - /* FIXME: baud_base is different for devices with a PMU */ - SSB_WARN_ON(1); - } + baud_base = ssb_chipco_alp_clock(cc); div = 1; if (ccrev >= 21) { /* Turn off UART clock before switching clocksource. */ --- a/drivers/ssb/driver_chipcommon_pmu.c +++ b/drivers/ssb/driver_chipcommon_pmu.c @@ -346,6 +346,8 @@ static void ssb_pmu_pll_init(struct ssb_ chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, 0x380005C0); } break; + case 43222: + break; default: ssb_printk(KERN_ERR PFX "ERROR: PLL init unknown for device %04X\n", @@ -434,6 +436,7 @@ static void ssb_pmu_resources_init(struc min_msk = 0xCBB; break; case 0x4322: + case 43222: /* We keep the default settings: * min_msk = 0xCBB * max_msk = 0x7FFFF @@ -615,6 +618,33 @@ void ssb_pmu_set_ldo_paref(struct ssb_ch EXPORT_SYMBOL(ssb_pmu_set_ldo_voltage); EXPORT_SYMBOL(ssb_pmu_set_ldo_paref); +static u32 ssb_pmu_get_alp_clock_clk0(struct ssb_chipcommon *cc) +{ + u32 crystalfreq; + const struct pmu0_plltab_entry *e = NULL; + + crystalfreq = chipco_read32(cc, SSB_CHIPCO_PMU_CTL) & + SSB_CHIPCO_PMU_CTL_XTALFREQ >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT; + e = pmu0_plltab_find_entry(crystalfreq); + BUG_ON(!e); + return e->freq * 1000; +} + +u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc) +{ + struct ssb_bus *bus = cc->dev->bus; + + switch (bus->chip_id) { + case 0x5354: + ssb_pmu_get_alp_clock_clk0(cc); + default: + ssb_printk(KERN_ERR PFX + "ERROR: PMU alp clock unknown for device %04X\n", + bus->chip_id); + return 0; + } +} + u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc) { struct ssb_bus *bus = cc->dev->bus; @@ -645,3 +675,32 @@ u32 ssb_pmu_get_controlclock(struct ssb_ return 0; } } + +void ssb_pmu_spuravoid_pllupdate(struct ssb_chipcommon *cc, int spuravoid) +{ + u32 pmu_ctl = 0; + + switch (cc->dev->bus->chip_id) { + case 0x4322: + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, 0x11100070); + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL1, 0x1014140a); + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, 0x88888854); + if (spuravoid == 1) + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x05201828); + else + ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x05001828); + pmu_ctl = SSB_CHIPCO_PMU_CTL_PLL_UPD; + break; + case 43222: + /* TODO: BCM43222 requires updating PLLs too */ + return; + default: + ssb_printk(KERN_ERR PFX + "Unknown spuravoidance settings for chip 0x%04X, not changing PLL\n", + cc->dev->bus->chip_id); + return; + } + + chipco_set32(cc, SSB_CHIPCO_PMU_CTL, pmu_ctl); +} +EXPORT_SYMBOL_GPL(ssb_pmu_spuravoid_pllupdate); --- /dev/null +++ b/drivers/ssb/driver_chipcommon_sflash.c @@ -0,0 +1,18 @@ +/* + * Sonics Silicon Backplane + * ChipCommon serial flash interface + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include + +#include "ssb_private.h" + +/* Initialize serial flash access */ +int ssb_sflash_init(struct ssb_chipcommon *cc) +{ + pr_err("Serial flash support is not implemented yet!\n"); + + return -ENOTSUPP; +} --- a/drivers/ssb/driver_extif.c +++ b/drivers/ssb/driver_extif.c @@ -112,10 +112,37 @@ void ssb_extif_get_clockcontrol(struct s *m = extif_read32(extif, SSB_EXTIF_CLOCK_SB); } -void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, - u32 ticks) +u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks) { + struct ssb_extif *extif = bcm47xx_wdt_get_drvdata(wdt); + + return ssb_extif_watchdog_timer_set(extif, ticks); +} + +u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms) +{ + struct ssb_extif *extif = bcm47xx_wdt_get_drvdata(wdt); + u32 ticks = (SSB_EXTIF_WATCHDOG_CLK / 1000) * ms; + + ticks = ssb_extif_watchdog_timer_set(extif, ticks); + + return (ticks * 1000) / SSB_EXTIF_WATCHDOG_CLK; +} + +u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks) +{ + if (ticks > SSB_EXTIF_WATCHDOG_MAX_TIMER) + ticks = SSB_EXTIF_WATCHDOG_MAX_TIMER; extif_write32(extif, SSB_EXTIF_WATCHDOG, ticks); + + return ticks; +} + +void ssb_extif_init(struct ssb_extif *extif) +{ + if (!extif->dev) + return; /* We don't have a Extif core */ + spin_lock_init(&extif->gpio_lock); } u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask) @@ -125,22 +152,50 @@ u32 ssb_extif_gpio_in(struct ssb_extif * u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0), + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0), mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0), + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0), mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTPOL, mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, u32 value) { - return extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value); + unsigned long flags; + u32 res = 0; + + spin_lock_irqsave(&extif->gpio_lock, flags); + res = extif_write32_masked(extif, SSB_EXTIF_GPIO_INTMASK, mask, value); + spin_unlock_irqrestore(&extif->gpio_lock, flags); + + return res; } --- /dev/null +++ b/drivers/ssb/driver_gpio.c @@ -0,0 +1,176 @@ +/* + * Sonics Silicon Backplane + * GPIO driver + * + * Copyright 2011, Broadcom Corporation + * Copyright 2012, Hauke Mehrtens + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include +#include +#include + +#include "ssb_private.h" + +static struct ssb_bus *ssb_gpio_get_bus(struct gpio_chip *chip) +{ + return container_of(chip, struct ssb_bus, gpio); +} + +static int ssb_gpio_chipco_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + return !!ssb_chipco_gpio_in(&bus->chipco, 1 << gpio); +} + +static void ssb_gpio_chipco_set_value(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0); +} + +static int ssb_gpio_chipco_direction_input(struct gpio_chip *chip, + unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 0); + return 0; +} + +static int ssb_gpio_chipco_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_outen(&bus->chipco, 1 << gpio, 1 << gpio); + ssb_chipco_gpio_out(&bus->chipco, 1 << gpio, value ? 1 << gpio : 0); + return 0; +} + +static int ssb_gpio_chipco_request(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_chipco_gpio_control(&bus->chipco, 1 << gpio, 0); + /* clear pulldown */ + ssb_chipco_gpio_pulldown(&bus->chipco, 1 << gpio, 0); + /* Set pullup */ + ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 1 << gpio); + + return 0; +} + +static void ssb_gpio_chipco_free(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + /* clear pullup */ + ssb_chipco_gpio_pullup(&bus->chipco, 1 << gpio, 0); +} + +static int ssb_gpio_chipco_init(struct ssb_bus *bus) +{ + struct gpio_chip *chip = &bus->gpio; + + chip->label = "ssb_chipco_gpio"; + chip->owner = THIS_MODULE; + chip->request = ssb_gpio_chipco_request; + chip->free = ssb_gpio_chipco_free; + chip->get = ssb_gpio_chipco_get_value; + chip->set = ssb_gpio_chipco_set_value; + chip->direction_input = ssb_gpio_chipco_direction_input; + chip->direction_output = ssb_gpio_chipco_direction_output; + chip->ngpio = 16; + /* There is just one SoC in one device and its GPIO addresses should be + * deterministic to address them more easily. The other buses could get + * a random base number. */ + if (bus->bustype == SSB_BUSTYPE_SSB) + chip->base = 0; + else + chip->base = -1; + + return gpiochip_add(chip); +} + +#ifdef CONFIG_SSB_DRIVER_EXTIF + +static int ssb_gpio_extif_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + return !!ssb_extif_gpio_in(&bus->extif, 1 << gpio); +} + +static void ssb_gpio_extif_set_value(struct gpio_chip *chip, unsigned gpio, + int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0); +} + +static int ssb_gpio_extif_direction_input(struct gpio_chip *chip, + unsigned gpio) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 0); + return 0; +} + +static int ssb_gpio_extif_direction_output(struct gpio_chip *chip, + unsigned gpio, int value) +{ + struct ssb_bus *bus = ssb_gpio_get_bus(chip); + + ssb_extif_gpio_outen(&bus->extif, 1 << gpio, 1 << gpio); + ssb_extif_gpio_out(&bus->extif, 1 << gpio, value ? 1 << gpio : 0); + return 0; +} + +static int ssb_gpio_extif_init(struct ssb_bus *bus) +{ + struct gpio_chip *chip = &bus->gpio; + + chip->label = "ssb_extif_gpio"; + chip->owner = THIS_MODULE; + chip->get = ssb_gpio_extif_get_value; + chip->set = ssb_gpio_extif_set_value; + chip->direction_input = ssb_gpio_extif_direction_input; + chip->direction_output = ssb_gpio_extif_direction_output; + chip->ngpio = 5; + /* There is just one SoC in one device and its GPIO addresses should be + * deterministic to address them more easily. The other buses could get + * a random base number. */ + if (bus->bustype == SSB_BUSTYPE_SSB) + chip->base = 0; + else + chip->base = -1; + + return gpiochip_add(chip); +} + +#else +static int ssb_gpio_extif_init(struct ssb_bus *bus) +{ + return -ENOTSUPP; +} +#endif + +int ssb_gpio_init(struct ssb_bus *bus) +{ + if (ssb_chipco_available(&bus->chipco)) + return ssb_gpio_chipco_init(bus); + else if (ssb_extif_available(&bus->extif)) + return ssb_gpio_extif_init(bus); + else + SSB_WARN_ON(1); + + return -1; +} --- a/drivers/ssb/driver_mipscore.c +++ b/drivers/ssb/driver_mipscore.c @@ -178,9 +178,9 @@ static void ssb_mips_serial_init(struct { struct ssb_bus *bus = mcore->dev->bus; - if (bus->extif.dev) + if (ssb_extif_available(&bus->extif)) mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports); - else if (bus->chipco.dev) + else if (ssb_chipco_available(&bus->chipco)) mcore->nr_serial_ports = ssb_chipco_serial_init(&bus->chipco, mcore->serial_ports); else mcore->nr_serial_ports = 0; @@ -191,10 +191,11 @@ static void ssb_mips_flash_detect(struct struct ssb_bus *bus = mcore->dev->bus; /* When there is no chipcommon on the bus there is 4MB flash */ - if (!bus->chipco.dev) { - mcore->flash_buswidth = 2; - mcore->flash_window = SSB_FLASH1; - mcore->flash_window_size = SSB_FLASH1_SZ; + if (!ssb_chipco_available(&bus->chipco)) { + mcore->pflash.present = true; + mcore->pflash.buswidth = 2; + mcore->pflash.window = SSB_FLASH1; + mcore->pflash.window_size = SSB_FLASH1_SZ; return; } @@ -202,17 +203,19 @@ static void ssb_mips_flash_detect(struct switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) { case SSB_CHIPCO_FLASHT_STSER: case SSB_CHIPCO_FLASHT_ATSER: - pr_err("Serial flash not supported\n"); + pr_debug("Found serial flash\n"); + ssb_sflash_init(&bus->chipco); break; case SSB_CHIPCO_FLASHT_PARA: pr_debug("Found parallel flash\n"); - mcore->flash_window = SSB_FLASH2; - mcore->flash_window_size = SSB_FLASH2_SZ; + mcore->pflash.present = true; + mcore->pflash.window = SSB_FLASH2; + mcore->pflash.window_size = SSB_FLASH2_SZ; if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG) & SSB_CHIPCO_CFG_DS16) == 0) - mcore->flash_buswidth = 1; + mcore->pflash.buswidth = 1; else - mcore->flash_buswidth = 2; + mcore->pflash.buswidth = 2; break; } } @@ -225,9 +228,9 @@ u32 ssb_cpu_clock(struct ssb_mipscore *m if (bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU) return ssb_pmu_get_cpu_clock(&bus->chipco); - if (bus->extif.dev) { + if (ssb_extif_available(&bus->extif)) { ssb_extif_get_clockcontrol(&bus->extif, &pll_type, &n, &m); - } else if (bus->chipco.dev) { + } else if (ssb_chipco_available(&bus->chipco)) { ssb_chipco_get_clockcpu(&bus->chipco, &pll_type, &n, &m); } else return 0; @@ -263,9 +266,9 @@ void ssb_mipscore_init(struct ssb_mipsco hz = 100000000; ns = 1000000000 / hz; - if (bus->extif.dev) + if (ssb_extif_available(&bus->extif)) ssb_extif_timing_init(&bus->extif, ns); - else if (bus->chipco.dev) + else if (ssb_chipco_available(&bus->chipco)) ssb_chipco_timing_init(&bus->chipco, ns); /* Assign IRQs to all cores on the bus, start with irq line 2, because serial usually takes 1 */ --- a/drivers/ssb/embedded.c +++ b/drivers/ssb/embedded.c @@ -4,11 +4,13 @@ * * Copyright 2005-2008, Broadcom Corporation * Copyright 2006-2008, Michael Buesch + * Copyright 2012, Hauke Mehrtens * * Licensed under the GNU/GPL. See COPYING for details. */ #include +#include #include #include #include @@ -32,6 +34,39 @@ int ssb_watchdog_timer_set(struct ssb_bu } EXPORT_SYMBOL(ssb_watchdog_timer_set); +int ssb_watchdog_register(struct ssb_bus *bus) +{ + struct bcm47xx_wdt wdt = {}; + struct platform_device *pdev; + + if (ssb_chipco_available(&bus->chipco)) { + wdt.driver_data = &bus->chipco; + wdt.timer_set = ssb_chipco_watchdog_timer_set_wdt; + wdt.timer_set_ms = ssb_chipco_watchdog_timer_set_ms; + wdt.max_timer_ms = bus->chipco.max_timer_ms; + } else if (ssb_extif_available(&bus->extif)) { + wdt.driver_data = &bus->extif; + wdt.timer_set = ssb_extif_watchdog_timer_set_wdt; + wdt.timer_set_ms = ssb_extif_watchdog_timer_set_ms; + wdt.max_timer_ms = SSB_EXTIF_WATCHDOG_MAX_TIMER_MS; + } else { + return -ENODEV; + } + + pdev = platform_device_register_data(NULL, "bcm47xx-wdt", + bus->busnumber, &wdt, + sizeof(wdt)); + if (IS_ERR(pdev)) { + ssb_dprintk(KERN_INFO PFX + "can not register watchdog device, err: %li\n", + PTR_ERR(pdev)); + return PTR_ERR(pdev); + } + + bus->watchdog = pdev; + return 0; +} + u32 ssb_gpio_in(struct ssb_bus *bus, u32 mask) { unsigned long flags; --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -433,6 +434,11 @@ static void ssb_devices_unregister(struc if (sdev->dev) device_unregister(sdev->dev); } + +#ifdef CONFIG_SSB_EMBEDDED + if (bus->bustype == SSB_BUSTYPE_SSB) + platform_device_unregister(bus->watchdog); +#endif } void ssb_bus_unregister(struct ssb_bus *bus) @@ -561,6 +567,8 @@ static int __devinit ssb_attach_queued_b if (err) goto error; ssb_pcicore_init(&bus->pcicore); + if (bus->bustype == SSB_BUSTYPE_SSB) + ssb_watchdog_register(bus); ssb_bus_may_powerdown(bus); err = ssb_devices_register(bus); @@ -796,7 +804,14 @@ static int __devinit ssb_bus_register(st if (err) goto err_pcmcia_exit; ssb_chipcommon_init(&bus->chipco); + ssb_extif_init(&bus->extif); ssb_mipscore_init(&bus->mipscore); + err = ssb_gpio_init(bus); + if (err == -ENOTSUPP) + ssb_dprintk(KERN_DEBUG PFX "GPIO driver not activated\n"); + else if (err) + ssb_dprintk(KERN_ERR PFX + "Error registering GPIO driver: %i\n", err); err = ssb_fetch_invariants(bus, get_invariants); if (err) { ssb_bus_may_powerdown(bus); @@ -1118,8 +1133,7 @@ static u32 ssb_tmslow_reject_bitmask(str case SSB_IDLOW_SSBREV_27: /* same here */ return SSB_TMSLOW_REJECT; /* this is a guess */ default: - printk(KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev); - WARN_ON(1); + WARN(1, KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev); } return (SSB_TMSLOW_REJECT | SSB_TMSLOW_REJECT_23); } --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -3,6 +3,7 @@ #include #include +#include #define PFX "ssb: " @@ -210,5 +211,63 @@ static inline void b43_pci_ssb_bridge_ex /* driver_chipcommon_pmu.c */ extern u32 ssb_pmu_get_cpu_clock(struct ssb_chipcommon *cc); extern u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc); +extern u32 ssb_pmu_get_alp_clock(struct ssb_chipcommon *cc); + +extern u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, + u32 ticks); +extern u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms); + +/* driver_chipcommon_sflash.c */ +#ifdef CONFIG_SSB_SFLASH +int ssb_sflash_init(struct ssb_chipcommon *cc); +#else +static inline int ssb_sflash_init(struct ssb_chipcommon *cc) +{ + pr_err("Serial flash not supported\n"); + return 0; +} +#endif /* CONFIG_SSB_SFLASH */ + +#ifdef CONFIG_SSB_DRIVER_EXTIF +extern u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks); +extern u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms); +#else +static inline u32 ssb_extif_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, + u32 ticks) +{ + return 0; +} +static inline u32 ssb_extif_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, + u32 ms) +{ + return 0; +} +#endif + +#ifdef CONFIG_SSB_EMBEDDED +extern int ssb_watchdog_register(struct ssb_bus *bus); +#else /* CONFIG_SSB_EMBEDDED */ +static inline int ssb_watchdog_register(struct ssb_bus *bus) +{ + return 0; +} +#endif /* CONFIG_SSB_EMBEDDED */ + +#ifdef CONFIG_SSB_DRIVER_EXTIF +extern void ssb_extif_init(struct ssb_extif *extif); +#else +static inline void ssb_extif_init(struct ssb_extif *extif) +{ +} +#endif + +#ifdef CONFIG_SSB_DRIVER_GPIO +extern int ssb_gpio_init(struct ssb_bus *bus); +#else /* CONFIG_SSB_DRIVER_GPIO */ +static inline int ssb_gpio_init(struct ssb_bus *bus) +{ + return -ENOTSUPP; +} +#endif /* CONFIG_SSB_DRIVER_GPIO */ #endif /* LINUX_SSB_PRIVATE_H_ */ --- a/include/linux/ssb/ssb.h +++ b/include/linux/ssb/ssb.h @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include #include @@ -432,7 +434,11 @@ struct ssb_bus { #ifdef CONFIG_SSB_EMBEDDED /* Lock for GPIO register access. */ spinlock_t gpio_lock; + struct platform_device *watchdog; #endif /* EMBEDDED */ +#ifdef CONFIG_SSB_DRIVER_GPIO + struct gpio_chip gpio; +#endif /* DRIVER_GPIO */ /* Internal-only stuff follows. Do not touch. */ struct list_head list; --- a/include/linux/ssb/ssb_driver_chipcommon.h +++ b/include/linux/ssb/ssb_driver_chipcommon.h @@ -219,6 +219,7 @@ #define SSB_CHIPCO_PMU_CTL 0x0600 /* PMU control */ #define SSB_CHIPCO_PMU_CTL_ILP_DIV 0xFFFF0000 /* ILP div mask */ #define SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT 16 +#define SSB_CHIPCO_PMU_CTL_PLL_UPD 0x00000400 #define SSB_CHIPCO_PMU_CTL_NOILPONW 0x00000200 /* No ILP on wait */ #define SSB_CHIPCO_PMU_CTL_HTREQEN 0x00000100 /* HT req enable */ #define SSB_CHIPCO_PMU_CTL_ALPREQEN 0x00000080 /* ALP req enable */ @@ -590,7 +591,10 @@ struct ssb_chipcommon { u32 status; /* Fast Powerup Delay constant */ u16 fast_pwrup_delay; + spinlock_t gpio_lock; struct ssb_chipcommon_pmu pmu; + u32 ticks_per_ms; + u32 max_timer_ms; }; static inline bool ssb_chipco_available(struct ssb_chipcommon *cc) @@ -630,8 +634,7 @@ enum ssb_clkmode { extern void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc, enum ssb_clkmode mode); -extern void ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, - u32 ticks); +extern u32 ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks); void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value); @@ -644,6 +647,8 @@ u32 ssb_chipco_gpio_outen(struct ssb_chi u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value); u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value); u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value); +u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value); +u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value); #ifdef CONFIG_SSB_SERIAL extern int ssb_chipco_serial_init(struct ssb_chipcommon *cc, @@ -663,5 +668,6 @@ enum ssb_pmu_ldo_volt_id { void ssb_pmu_set_ldo_voltage(struct ssb_chipcommon *cc, enum ssb_pmu_ldo_volt_id id, u32 voltage); void ssb_pmu_set_ldo_paref(struct ssb_chipcommon *cc, bool on); +void ssb_pmu_spuravoid_pllupdate(struct ssb_chipcommon *cc, int spuravoid); #endif /* LINUX_SSB_CHIPCO_H_ */ --- a/include/linux/ssb/ssb_driver_extif.h +++ b/include/linux/ssb/ssb_driver_extif.h @@ -152,12 +152,16 @@ /* watchdog */ #define SSB_EXTIF_WATCHDOG_CLK 48000000 /* Hz */ +#define SSB_EXTIF_WATCHDOG_MAX_TIMER ((1 << 28) - 1) +#define SSB_EXTIF_WATCHDOG_MAX_TIMER_MS (SSB_EXTIF_WATCHDOG_MAX_TIMER \ + / (SSB_EXTIF_WATCHDOG_CLK / 1000)) #ifdef CONFIG_SSB_DRIVER_EXTIF struct ssb_extif { struct ssb_device *dev; + spinlock_t gpio_lock; }; static inline bool ssb_extif_available(struct ssb_extif *extif) @@ -171,8 +175,7 @@ extern void ssb_extif_get_clockcontrol(s extern void ssb_extif_timing_init(struct ssb_extif *extif, unsigned long ns); -extern void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, - u32 ticks); +extern u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks); /* Extif GPIO pin access */ u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask); @@ -205,10 +208,52 @@ void ssb_extif_get_clockcontrol(struct s } static inline -void ssb_extif_watchdog_timer_set(struct ssb_extif *extif, - u32 ticks) +void ssb_extif_timing_init(struct ssb_extif *extif, unsigned long ns) { } +static inline +u32 ssb_extif_watchdog_timer_set(struct ssb_extif *extif, u32 ticks) +{ + return 0; +} + +static inline u32 ssb_extif_gpio_in(struct ssb_extif *extif, u32 mask) +{ + return 0; +} + +static inline u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, + u32 value) +{ + return 0; +} + +static inline u32 ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, + u32 value) +{ + return 0; +} + +static inline u32 ssb_extif_gpio_polarity(struct ssb_extif *extif, u32 mask, + u32 value) +{ + return 0; +} + +static inline u32 ssb_extif_gpio_intmask(struct ssb_extif *extif, u32 mask, + u32 value) +{ + return 0; +} + +#ifdef CONFIG_SSB_SERIAL +static inline int ssb_extif_serial_init(struct ssb_extif *extif, + struct ssb_serial_port *ports) +{ + return 0; +} +#endif /* CONFIG_SSB_SERIAL */ + #endif /* CONFIG_SSB_DRIVER_EXTIF */ #endif /* LINUX_SSB_EXTIFCORE_H_ */ --- a/include/linux/ssb/ssb_driver_mips.h +++ b/include/linux/ssb/ssb_driver_mips.h @@ -13,6 +13,12 @@ struct ssb_serial_port { unsigned int reg_shift; }; +struct ssb_pflash { + bool present; + u8 buswidth; + u32 window; + u32 window_size; +}; struct ssb_mipscore { struct ssb_device *dev; @@ -20,9 +26,7 @@ struct ssb_mipscore { int nr_serial_ports; struct ssb_serial_port serial_ports[4]; - u8 flash_buswidth; - u32 flash_window; - u32 flash_window_size; + struct ssb_pflash pflash; }; extern void ssb_mipscore_init(struct ssb_mipscore *mcore); --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -289,11 +289,11 @@ #define SSB_SPROM4_ETHPHY_ET1A_SHIFT 5 #define SSB_SPROM4_ETHPHY_ET0M (1<<14) /* MDIO for enet0 */ #define SSB_SPROM4_ETHPHY_ET1M (1<<15) /* MDIO for enet1 */ -#define SSB_SPROM4_ANTAVAIL 0x005D /* Antenna available bitfields */ -#define SSB_SPROM4_ANTAVAIL_A 0x00FF /* A-PHY bitfield */ -#define SSB_SPROM4_ANTAVAIL_A_SHIFT 0 -#define SSB_SPROM4_ANTAVAIL_BG 0xFF00 /* B-PHY and G-PHY bitfield */ -#define SSB_SPROM4_ANTAVAIL_BG_SHIFT 8 +#define SSB_SPROM4_ANTAVAIL 0x005C /* Antenna available bitfields */ +#define SSB_SPROM4_ANTAVAIL_BG 0x00FF /* B-PHY and G-PHY bitfield */ +#define SSB_SPROM4_ANTAVAIL_BG_SHIFT 0 +#define SSB_SPROM4_ANTAVAIL_A 0xFF00 /* A-PHY bitfield */ +#define SSB_SPROM4_ANTAVAIL_A_SHIFT 8 #define SSB_SPROM4_AGAIN01 0x005E /* Antenna Gain (in dBm Q5.2) */ #define SSB_SPROM4_AGAIN0 0x00FF /* Antenna 0 */ #define SSB_SPROM4_AGAIN0_SHIFT 0 @@ -485,7 +485,7 @@ #define SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT 4 #define SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL 0x0020 #define SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT 5 -#define SSB_SPROM8_TEMPDELTA 0x00BA +#define SSB_SPROM8_TEMPDELTA 0x00BC #define SSB_SPROM8_TEMPDELTA_PHYCAL 0x00ff #define SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT 0 #define SSB_SPROM8_TEMPDELTA_PERIOD 0x0f00 --- /dev/null +++ b/include/linux/bcm47xx_wdt.h @@ -0,0 +1,19 @@ +#ifndef LINUX_BCM47XX_WDT_H_ +#define LINUX_BCM47XX_WDT_H_ + +#include + + +struct bcm47xx_wdt { + u32 (*timer_set)(struct bcm47xx_wdt *, u32); + u32 (*timer_set_ms)(struct bcm47xx_wdt *, u32); + u32 max_timer_ms; + + void *driver_data; +}; + +static inline void *bcm47xx_wdt_get_drvdata(struct bcm47xx_wdt *wdt) +{ + return wdt->driver_data; +} +#endif /* LINUX_BCM47XX_WDT_H_ */ --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -5165,7 +5165,8 @@ static void b43_nphy_pmu_spur_avoid(stru #endif #ifdef CONFIG_B43_SSB case B43_BUS_SSB: - /* FIXME */ + ssb_pmu_spuravoid_pllupdate(&dev->dev->sdev->bus->chipco, + avoid); break; #endif } --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -339,6 +339,21 @@ static s8 r123_extract_antgain(u8 sprom_ return (s8)gain; } +static void sprom_extract_r23(struct ssb_sprom *out, const u16 *in) +{ + SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); + SPEX(opo, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0); + SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0); + SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0); + SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0); + SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0); + SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0); + SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0); + SPEX(maxpwr_ah, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0); + SPEX(maxpwr_al, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO, + SSB_SPROM2_MAXP_A_LO_SHIFT); +} + static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) { int i; @@ -398,8 +413,7 @@ static void sprom_extract_r123(struct ss SSB_SPROM1_ITSSI_A_SHIFT); SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0); SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0); - if (out->revision >= 2) - SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); + SPEX(alpha2[0], SSB_SPROM1_CCODE, 0xff00, 8); SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0); @@ -410,6 +424,8 @@ static void sprom_extract_r123(struct ss out->antenna_gain.a1 = r123_extract_antgain(out->revision, in, SSB_SPROM1_AGAIN_A, SSB_SPROM1_AGAIN_A_SHIFT); + if (out->revision >= 2) + sprom_extract_r23(out, in); } /* Revs 4 5 and 8 have partially shared layout */