Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

178
drivers/ssb/Kconfig Normal file
View file

@ -0,0 +1,178 @@
config SSB_POSSIBLE
bool
depends on HAS_IOMEM && HAS_DMA
default y
menu "Sonics Silicon Backplane"
depends on SSB_POSSIBLE
config SSB
tristate "Sonics Silicon Backplane support"
depends on SSB_POSSIBLE
help
Support for the Sonics Silicon Backplane bus.
You only need to enable this option, if you are
configuring a kernel for an embedded system with
this bus.
It will be auto-selected if needed in other
environments.
The module will be called ssb.
If unsure, say N.
# Common SPROM support routines
config SSB_SPROM
bool
# Support for Block-I/O. SELECT this from the driver that needs it.
config SSB_BLOCKIO
bool
depends on SSB
config SSB_PCIHOST_POSSIBLE
bool
depends on SSB && (PCI = y || PCI = SSB)
default y
config SSB_PCIHOST
bool "Support for SSB on PCI-bus host"
depends on SSB_PCIHOST_POSSIBLE
select SSB_SPROM
default y
help
Support for a Sonics Silicon Backplane on top
of a PCI device.
If unsure, say Y
config SSB_B43_PCI_BRIDGE
bool
depends on SSB_PCIHOST
default n
config SSB_PCMCIAHOST_POSSIBLE
bool
depends on SSB && (PCMCIA = y || PCMCIA = SSB)
default y
config SSB_PCMCIAHOST
bool "Support for SSB on PCMCIA-bus host"
depends on SSB_PCMCIAHOST_POSSIBLE
select SSB_SPROM
help
Support for a Sonics Silicon Backplane on top
of a PCMCIA device.
If unsure, say N
config SSB_SDIOHOST_POSSIBLE
bool
depends on SSB && (MMC = y || MMC = SSB)
default y
config SSB_SDIOHOST
bool "Support for SSB on SDIO-bus host"
depends on SSB_SDIOHOST_POSSIBLE
help
Support for a Sonics Silicon Backplane on top
of a SDIO device.
If unsure, say N
config SSB_SILENT
bool "No SSB kernel messages"
depends on SSB && EXPERT
help
This option turns off all Sonics Silicon Backplane printks.
Note that you won't be able to identify problems, once
messages are turned off.
This might only be desired for production kernels on
embedded devices to reduce the kernel size.
Say N
config SSB_DEBUG
bool "SSB debugging"
depends on SSB && !SSB_SILENT
help
This turns on additional runtime checks and debugging
messages. Turn this on for SSB troubleshooting.
If unsure, say N
config SSB_SERIAL
bool
depends on SSB
# ChipCommon and ExtIf serial support routines.
config SSB_DRIVER_PCICORE_POSSIBLE
bool
depends on SSB_PCIHOST
default y
config SSB_DRIVER_PCICORE
bool "SSB PCI core driver"
depends on SSB_DRIVER_PCICORE_POSSIBLE
help
Driver for the Sonics Silicon Backplane attached
Broadcom PCI core.
If unsure, say Y
config SSB_PCICORE_HOSTMODE
bool "Hostmode support for SSB PCI core"
depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS
help
PCIcore hostmode operation (external PCI bus).
config SSB_DRIVER_MIPS
bool "SSB Broadcom MIPS core driver"
depends on SSB && MIPS
select SSB_SERIAL
select SSB_SFLASH
help
Driver for the Sonics Silicon Backplane attached
Broadcom MIPS core.
If unsure, say N
config SSB_SFLASH
bool "SSB serial flash support"
depends on SSB_DRIVER_MIPS
default y
# Assumption: We are on embedded, if we compile the MIPS core.
config SSB_EMBEDDED
bool
depends on SSB_DRIVER_MIPS && SSB_PCICORE_HOSTMODE
default y
config SSB_DRIVER_EXTIF
bool "SSB Broadcom EXTIF core driver"
depends on SSB_DRIVER_MIPS
help
Driver for the Sonics Silicon Backplane attached
Broadcom EXTIF core.
If unsure, say N
config SSB_DRIVER_GIGE
bool "SSB Broadcom Gigabit Ethernet driver"
depends on SSB_PCIHOST_POSSIBLE && SSB_EMBEDDED && MIPS
help
Driver for the Sonics Silicon Backplane attached
Broadcom Gigabit Ethernet.
If unsure, say N
config SSB_DRIVER_GPIO
bool "SSB GPIO driver"
depends on SSB && GPIOLIB
select IRQ_DOMAIN if SSB_EMBEDDED
help
Driver to provide access to the GPIO pins on the bus.
If unsure, say N
endmenu

25
drivers/ssb/Makefile Normal file
View file

@ -0,0 +1,25 @@
# core
ssb-y += main.o scan.o
ssb-$(CONFIG_SSB_EMBEDDED) += embedded.o
ssb-$(CONFIG_SSB_SPROM) += sprom.o
# host support
ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o
ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o
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
ssb-$(CONFIG_SSB_B43_PCI_BRIDGE) += b43_pci_bridge.o
obj-$(CONFIG_SSB) += ssb.o

View file

@ -0,0 +1,60 @@
/*
* Broadcom 43xx PCI-SSB bridge module
*
* This technically is a separate PCI driver module, but
* because of its small size we include it in the SSB core
* instead of creating a standalone module.
*
* Copyright 2007 Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/ssb/ssb.h>
#include "ssb_private.h"
static const struct pci_device_id b43_pci_bridge_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4301) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4306) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4307) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4311) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4312) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4315) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4318) },
{ PCI_DEVICE(PCI_VENDOR_ID_BCM_GVC, 0x4318) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4319) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4320) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4321) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4322) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43222) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4325) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4328) },
{ 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) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4351) },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl);
static struct pci_driver b43_pci_bridge_driver = {
.name = "b43-pci-bridge",
.id_table = b43_pci_bridge_tbl,
};
int __init b43_pci_ssb_bridge_init(void)
{
return ssb_pcihost_register(&b43_pci_bridge_driver);
}
void __exit b43_pci_ssb_bridge_exit(void)
{
ssb_pcihost_unregister(&b43_pci_bridge_driver);
}

View file

@ -0,0 +1,697 @@
/*
* Sonics Silicon Backplane
* Broadcom ChipCommon core driver
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <m@bues.ch>
* Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_regs.h>
#include <linux/export.h>
#include <linux/pci.h>
#include <linux/bcm47xx_wdt.h>
#include "ssb_private.h"
/* Clock sources */
enum ssb_clksrc {
/* PCI clock */
SSB_CHIPCO_CLKSRC_PCI,
/* Crystal slow clock oscillator */
SSB_CHIPCO_CLKSRC_XTALOS,
/* Low power oscillator */
SSB_CHIPCO_CLKSRC_LOPWROS,
};
static inline u32 chipco_write32_masked(struct ssb_chipcommon *cc, u16 offset,
u32 mask, u32 value)
{
value &= mask;
value |= chipco_read32(cc, offset) & ~mask;
chipco_write32(cc, offset, value);
return value;
}
void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc,
enum ssb_clkmode mode)
{
struct ssb_device *ccdev = cc->dev;
struct ssb_bus *bus;
u32 tmp;
if (!ccdev)
return;
bus = ccdev->bus;
/* We support SLOW only on 6..9 */
if (ccdev->id.revision >= 10 && mode == SSB_CLKMODE_SLOW)
mode = SSB_CLKMODE_DYNAMIC;
if (cc->capabilities & SSB_CHIPCO_CAP_PMU)
return; /* PMU controls clockmode, separated function needed */
SSB_WARN_ON(ccdev->id.revision >= 20);
/* chipcommon cores prior to rev6 don't support dynamic clock control */
if (ccdev->id.revision < 6)
return;
/* ChipCommon cores rev10+ need testing */
if (ccdev->id.revision >= 10)
return;
if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
return;
switch (mode) {
case SSB_CLKMODE_SLOW: /* For revs 6..9 only */
tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
tmp |= SSB_CHIPCO_SLOWCLKCTL_FSLOW;
chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
break;
case SSB_CLKMODE_FAST:
if (ccdev->id.revision < 10) {
ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */
tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL;
chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
} else {
chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL,
(chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) |
SSB_CHIPCO_SYSCLKCTL_FORCEHT));
/* udelay(150); TODO: not available in early init */
}
break;
case SSB_CLKMODE_DYNAMIC:
if (ccdev->id.revision < 10) {
tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW;
tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL;
tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) !=
SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL)
tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL;
chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp);
/* For dynamic control, we have to release our xtal_pu
* "force on" */
if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL)
ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0);
} else {
chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL,
(chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) &
~SSB_CHIPCO_SYSCLKCTL_FORCEHT));
}
break;
default:
SSB_WARN_ON(1);
}
}
/* Get the Slow Clock Source */
static enum ssb_clksrc chipco_pctl_get_slowclksrc(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
u32 uninitialized_var(tmp);
if (cc->dev->id.revision < 6) {
if (bus->bustype == SSB_BUSTYPE_SSB ||
bus->bustype == SSB_BUSTYPE_PCMCIA)
return SSB_CHIPCO_CLKSRC_XTALOS;
if (bus->bustype == SSB_BUSTYPE_PCI) {
pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &tmp);
if (tmp & 0x10)
return SSB_CHIPCO_CLKSRC_PCI;
return SSB_CHIPCO_CLKSRC_XTALOS;
}
}
if (cc->dev->id.revision < 10) {
tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
tmp &= 0x7;
if (tmp == 0)
return SSB_CHIPCO_CLKSRC_LOPWROS;
if (tmp == 1)
return SSB_CHIPCO_CLKSRC_XTALOS;
if (tmp == 2)
return SSB_CHIPCO_CLKSRC_PCI;
}
return SSB_CHIPCO_CLKSRC_XTALOS;
}
/* Get maximum or minimum (depending on get_max flag) slowclock frequency. */
static int chipco_pctl_clockfreqlimit(struct ssb_chipcommon *cc, int get_max)
{
int uninitialized_var(limit);
enum ssb_clksrc clocksrc;
int divisor = 1;
u32 tmp;
clocksrc = chipco_pctl_get_slowclksrc(cc);
if (cc->dev->id.revision < 6) {
switch (clocksrc) {
case SSB_CHIPCO_CLKSRC_PCI:
divisor = 64;
break;
case SSB_CHIPCO_CLKSRC_XTALOS:
divisor = 32;
break;
default:
SSB_WARN_ON(1);
}
} else if (cc->dev->id.revision < 10) {
switch (clocksrc) {
case SSB_CHIPCO_CLKSRC_LOPWROS:
break;
case SSB_CHIPCO_CLKSRC_XTALOS:
case SSB_CHIPCO_CLKSRC_PCI:
tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL);
divisor = (tmp >> 16) + 1;
divisor *= 4;
break;
}
} else {
tmp = chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL);
divisor = (tmp >> 16) + 1;
divisor *= 4;
}
switch (clocksrc) {
case SSB_CHIPCO_CLKSRC_LOPWROS:
if (get_max)
limit = 43000;
else
limit = 25000;
break;
case SSB_CHIPCO_CLKSRC_XTALOS:
if (get_max)
limit = 20200000;
else
limit = 19800000;
break;
case SSB_CHIPCO_CLKSRC_PCI:
if (get_max)
limit = 34000000;
else
limit = 25000000;
break;
}
limit /= divisor;
return limit;
}
static void chipco_powercontrol_init(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
if (bus->chip_id == 0x4321) {
if (bus->chip_rev == 0)
chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0x3A4);
else if (bus->chip_rev == 1)
chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0xA4);
}
if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
return;
if (cc->dev->id.revision >= 10) {
/* Set Idle Power clock rate to 1Mhz */
chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL,
(chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) &
0x0000FFFF) | 0x00040000);
} else {
int maxfreq;
maxfreq = chipco_pctl_clockfreqlimit(cc, 1);
chipco_write32(cc, SSB_CHIPCO_PLLONDELAY,
(maxfreq * 150 + 999999) / 1000000);
chipco_write32(cc, SSB_CHIPCO_FREFSELDELAY,
(maxfreq * 15 + 999999) / 1000000);
}
}
/* http://bcm-v4.sipsolutions.net/802.11/PmuFastPwrupDelay */
static u16 pmu_fast_powerup_delay(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
switch (bus->chip_id) {
case 0x4312:
case 0x4322:
case 0x4328:
return 7000;
case 0x4325:
/* TODO: */
default:
return 15000;
}
}
/* http://bcm-v4.sipsolutions.net/802.11/ClkctlFastPwrupDelay */
static void calc_fast_powerup_delay(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
int minfreq;
unsigned int tmp;
u32 pll_on_delay;
if (bus->bustype != SSB_BUSTYPE_PCI)
return;
if (cc->capabilities & SSB_CHIPCO_CAP_PMU) {
cc->fast_pwrup_delay = pmu_fast_powerup_delay(cc);
return;
}
if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL))
return;
minfreq = chipco_pctl_clockfreqlimit(cc, 0);
pll_on_delay = chipco_read32(cc, SSB_CHIPCO_PLLONDELAY);
tmp = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;
SSB_WARN_ON(tmp & ~0xFFFF);
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_dbg("chipcommon status is 0x%x\n", cc->status);
if (cc->dev->id.revision >= 20) {
chipco_write32(cc, SSB_CHIPCO_GPIOPULLUP, 0);
chipco_write32(cc, SSB_CHIPCO_GPIOPULLDOWN, 0);
}
ssb_pmu_init(cc);
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)
{
if (!cc->dev)
return;
ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW);
}
void ssb_chipco_resume(struct ssb_chipcommon *cc)
{
if (!cc->dev)
return;
chipco_powercontrol_init(cc);
ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
}
/* Get the processor clock */
void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc,
u32 *plltype, u32 *n, u32 *m)
{
*n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N);
*plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
switch (*plltype) {
case SSB_PLLTYPE_2:
case SSB_PLLTYPE_4:
case SSB_PLLTYPE_6:
case SSB_PLLTYPE_7:
*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS);
break;
case SSB_PLLTYPE_3:
/* 5350 uses m2 to control mips */
*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2);
break;
default:
*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB);
break;
}
}
/* Get the bus clock */
void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc,
u32 *plltype, u32 *n, u32 *m)
{
*n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N);
*plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
switch (*plltype) {
case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */
*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS);
break;
case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */
if (cc->dev->bus->chip_id != 0x5365) {
*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2);
break;
}
/* Fallthough */
default:
*m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB);
}
}
void ssb_chipco_timing_init(struct ssb_chipcommon *cc,
unsigned long ns)
{
struct ssb_device *dev = cc->dev;
struct ssb_bus *bus = dev->bus;
u32 tmp;
/* set register for external IO to control LED. */
chipco_write32(cc, SSB_CHIPCO_PROG_CFG, 0x11);
tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */
tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 40ns */
tmp |= DIV_ROUND_UP(240, ns); /* Waitcount-0 = 240ns */
chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */
/* Set timing for the flash */
tmp = DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_3_SHIFT; /* Waitcount-3 = 10nS */
tmp |= DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_1_SHIFT; /* Waitcount-1 = 10nS */
tmp |= DIV_ROUND_UP(120, ns); /* Waitcount-0 = 120nS */
if ((bus->chip_id == 0x5365) ||
(dev->id.revision < 9))
chipco_write32(cc, SSB_CHIPCO_FLASH_WAITCNT, tmp);
if ((bus->chip_id == 0x5365) ||
(dev->id.revision < 9) ||
((bus->chip_id == 0x5350) && (bus->chip_rev == 0)))
chipco_write32(cc, SSB_CHIPCO_PCMCIA_MEMWAIT, tmp);
if (bus->chip_id == 0x5350) {
/* Enable EXTIF */
tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */
tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT; /* Waitcount-2 = 20ns */
tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 100ns */
tmp |= DIV_ROUND_UP(120, ns); /* Waitcount-0 = 120ns */
chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */
}
}
/* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */
u32 ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 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)
{
chipco_write32_masked(cc, SSB_CHIPCO_IRQMASK, mask, value);
}
u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask)
{
return chipco_read32(cc, SSB_CHIPCO_IRQSTAT) & mask;
}
u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask)
{
return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask;
}
u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 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)
{
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)
{
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)
{
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)
{
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
int ssb_chipco_serial_init(struct ssb_chipcommon *cc,
struct ssb_serial_port *ports)
{
struct ssb_bus *bus = cc->dev->bus;
int nr_ports = 0;
u32 plltype;
unsigned int irq;
u32 baud_base, div;
u32 i, n;
unsigned int ccrev = cc->dev->id.revision;
plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT);
irq = ssb_mips_irq(cc->dev);
if (plltype == SSB_PLLTYPE_1) {
/* PLL clock */
baud_base = ssb_calc_clock_rate(plltype,
chipco_read32(cc, SSB_CHIPCO_CLOCK_N),
chipco_read32(cc, SSB_CHIPCO_CLOCK_M2));
div = 1;
} else {
if (ccrev == 20) {
/* BCM5354 uses constant 25MHz clock */
baud_base = 25000000;
div = 48;
/* Set the override bit so we don't divide it */
chipco_write32(cc, SSB_CHIPCO_CORECTL,
chipco_read32(cc, SSB_CHIPCO_CORECTL)
| SSB_CHIPCO_CORECTL_UARTCLK0);
} else if ((ccrev >= 11) && (ccrev != 15)) {
baud_base = ssb_chipco_alp_clock(cc);
div = 1;
if (ccrev >= 21) {
/* Turn off UART clock before switching clocksource. */
chipco_write32(cc, SSB_CHIPCO_CORECTL,
chipco_read32(cc, SSB_CHIPCO_CORECTL)
& ~SSB_CHIPCO_CORECTL_UARTCLKEN);
}
/* Set the override bit so we don't divide it */
chipco_write32(cc, SSB_CHIPCO_CORECTL,
chipco_read32(cc, SSB_CHIPCO_CORECTL)
| SSB_CHIPCO_CORECTL_UARTCLK0);
if (ccrev >= 21) {
/* Re-enable the UART clock. */
chipco_write32(cc, SSB_CHIPCO_CORECTL,
chipco_read32(cc, SSB_CHIPCO_CORECTL)
| SSB_CHIPCO_CORECTL_UARTCLKEN);
}
} else if (ccrev >= 3) {
/* Internal backplane clock */
baud_base = ssb_clockspeed(bus);
div = chipco_read32(cc, SSB_CHIPCO_CLKDIV)
& SSB_CHIPCO_CLKDIV_UART;
} else {
/* Fixed internal backplane clock */
baud_base = 88000000;
div = 48;
}
/* Clock source depends on strapping if UartClkOverride is unset */
if ((ccrev > 0) &&
!(chipco_read32(cc, SSB_CHIPCO_CORECTL) & SSB_CHIPCO_CORECTL_UARTCLK0)) {
if ((cc->capabilities & SSB_CHIPCO_CAP_UARTCLK) ==
SSB_CHIPCO_CAP_UARTCLK_INT) {
/* Internal divided backplane clock */
baud_base /= div;
} else {
/* Assume external clock of 1.8432 MHz */
baud_base = 1843200;
}
}
}
/* Determine the registers of the UARTs */
n = (cc->capabilities & SSB_CHIPCO_CAP_NRUART);
for (i = 0; i < n; i++) {
void __iomem *cc_mmio;
void __iomem *uart_regs;
cc_mmio = cc->dev->bus->mmio + (cc->dev->core_index * SSB_CORE_SIZE);
uart_regs = cc_mmio + SSB_CHIPCO_UART0_DATA;
/* Offset changed at after rev 0 */
if (ccrev == 0)
uart_regs += (i * 8);
else
uart_regs += (i * 256);
nr_ports++;
ports[i].regs = uart_regs;
ports[i].irq = irq;
ports[i].baud_base = baud_base;
ports[i].reg_shift = 0;
}
return nr_ports;
}
#endif /* CONFIG_SSB_SERIAL */

View file

@ -0,0 +1,716 @@
/*
* Sonics Silicon Backplane
* Broadcom ChipCommon Power Management Unit driver
*
* Copyright 2009, Michael Buesch <m@bues.ch>
* Copyright 2007, Broadcom Corporation
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_regs.h>
#include <linux/ssb/ssb_driver_chipcommon.h>
#include <linux/delay.h>
#include <linux/export.h>
#ifdef CONFIG_BCM47XX
#include <bcm47xx_nvram.h>
#endif
#include "ssb_private.h"
static u32 ssb_chipco_pll_read(struct ssb_chipcommon *cc, u32 offset)
{
chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset);
return chipco_read32(cc, SSB_CHIPCO_PLLCTL_DATA);
}
static void ssb_chipco_pll_write(struct ssb_chipcommon *cc,
u32 offset, u32 value)
{
chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, offset);
chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, value);
}
static void ssb_chipco_regctl_maskset(struct ssb_chipcommon *cc,
u32 offset, u32 mask, u32 set)
{
u32 value;
chipco_read32(cc, SSB_CHIPCO_REGCTL_ADDR);
chipco_write32(cc, SSB_CHIPCO_REGCTL_ADDR, offset);
chipco_read32(cc, SSB_CHIPCO_REGCTL_ADDR);
value = chipco_read32(cc, SSB_CHIPCO_REGCTL_DATA);
value &= mask;
value |= set;
chipco_write32(cc, SSB_CHIPCO_REGCTL_DATA, value);
chipco_read32(cc, SSB_CHIPCO_REGCTL_DATA);
}
struct pmu0_plltab_entry {
u16 freq; /* Crystal frequency in kHz.*/
u8 xf; /* Crystal frequency value for PMU control */
u8 wb_int;
u32 wb_frac;
};
static const struct pmu0_plltab_entry pmu0_plltab[] = {
{ .freq = 12000, .xf = 1, .wb_int = 73, .wb_frac = 349525, },
{ .freq = 13000, .xf = 2, .wb_int = 67, .wb_frac = 725937, },
{ .freq = 14400, .xf = 3, .wb_int = 61, .wb_frac = 116508, },
{ .freq = 15360, .xf = 4, .wb_int = 57, .wb_frac = 305834, },
{ .freq = 16200, .xf = 5, .wb_int = 54, .wb_frac = 336579, },
{ .freq = 16800, .xf = 6, .wb_int = 52, .wb_frac = 399457, },
{ .freq = 19200, .xf = 7, .wb_int = 45, .wb_frac = 873813, },
{ .freq = 19800, .xf = 8, .wb_int = 44, .wb_frac = 466033, },
{ .freq = 20000, .xf = 9, .wb_int = 44, .wb_frac = 0, },
{ .freq = 25000, .xf = 10, .wb_int = 70, .wb_frac = 419430, },
{ .freq = 26000, .xf = 11, .wb_int = 67, .wb_frac = 725937, },
{ .freq = 30000, .xf = 12, .wb_int = 58, .wb_frac = 699050, },
{ .freq = 38400, .xf = 13, .wb_int = 45, .wb_frac = 873813, },
{ .freq = 40000, .xf = 14, .wb_int = 45, .wb_frac = 0, },
};
#define SSB_PMU0_DEFAULT_XTALFREQ 20000
static const struct pmu0_plltab_entry * pmu0_plltab_find_entry(u32 crystalfreq)
{
const struct pmu0_plltab_entry *e;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(pmu0_plltab); i++) {
e = &pmu0_plltab[i];
if (e->freq == crystalfreq)
return e;
}
return NULL;
}
/* Tune the PLL to the crystal speed. crystalfreq is in kHz. */
static void ssb_pmu0_pllinit_r0(struct ssb_chipcommon *cc,
u32 crystalfreq)
{
struct ssb_bus *bus = cc->dev->bus;
const struct pmu0_plltab_entry *e = NULL;
u32 pmuctl, tmp, pllctl;
unsigned int i;
if (crystalfreq)
e = pmu0_plltab_find_entry(crystalfreq);
if (!e)
e = pmu0_plltab_find_entry(SSB_PMU0_DEFAULT_XTALFREQ);
BUG_ON(!e);
crystalfreq = e->freq;
cc->pmu.crystalfreq = e->freq;
/* Check if the PLL already is programmed to this frequency. */
pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) {
/* We're already there... */
return;
}
ssb_info("Programming PLL to %u.%03u MHz\n",
crystalfreq / 1000, crystalfreq % 1000);
/* First turn the PLL off. */
switch (bus->chip_id) {
case 0x4328:
chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK,
~(1 << SSB_PMURES_4328_BB_PLL_PU));
chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK,
~(1 << SSB_PMURES_4328_BB_PLL_PU));
break;
case 0x5354:
chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK,
~(1 << SSB_PMURES_5354_BB_PLL_PU));
chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK,
~(1 << SSB_PMURES_5354_BB_PLL_PU));
break;
default:
SSB_WARN_ON(1);
}
for (i = 1500; i; i--) {
tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT))
break;
udelay(10);
}
tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)
ssb_emerg("Failed to turn the PLL off!\n");
/* Set PDIV in PLL control 0. */
pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL0);
if (crystalfreq >= SSB_PMU0_PLLCTL0_PDIV_FREQ)
pllctl |= SSB_PMU0_PLLCTL0_PDIV_MSK;
else
pllctl &= ~SSB_PMU0_PLLCTL0_PDIV_MSK;
ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL0, pllctl);
/* Set WILD in PLL control 1. */
pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL1);
pllctl &= ~SSB_PMU0_PLLCTL1_STOPMOD;
pllctl &= ~(SSB_PMU0_PLLCTL1_WILD_IMSK | SSB_PMU0_PLLCTL1_WILD_FMSK);
pllctl |= ((u32)e->wb_int << SSB_PMU0_PLLCTL1_WILD_IMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_IMSK;
pllctl |= ((u32)e->wb_frac << SSB_PMU0_PLLCTL1_WILD_FMSK_SHIFT) & SSB_PMU0_PLLCTL1_WILD_FMSK;
if (e->wb_frac == 0)
pllctl |= SSB_PMU0_PLLCTL1_STOPMOD;
ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL1, pllctl);
/* Set WILD in PLL control 2. */
pllctl = ssb_chipco_pll_read(cc, SSB_PMU0_PLLCTL2);
pllctl &= ~SSB_PMU0_PLLCTL2_WILD_IMSKHI;
pllctl |= (((u32)e->wb_int >> 4) << SSB_PMU0_PLLCTL2_WILD_IMSKHI_SHIFT) & SSB_PMU0_PLLCTL2_WILD_IMSKHI;
ssb_chipco_pll_write(cc, SSB_PMU0_PLLCTL2, pllctl);
/* Set the crystalfrequency and the divisor. */
pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
pmuctl &= ~SSB_CHIPCO_PMU_CTL_ILP_DIV;
pmuctl |= (((crystalfreq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT)
& SSB_CHIPCO_PMU_CTL_ILP_DIV;
pmuctl &= ~SSB_CHIPCO_PMU_CTL_XTALFREQ;
pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ;
chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl);
}
struct pmu1_plltab_entry {
u16 freq; /* Crystal frequency in kHz.*/
u8 xf; /* Crystal frequency value for PMU control */
u8 ndiv_int;
u32 ndiv_frac;
u8 p1div;
u8 p2div;
};
static const struct pmu1_plltab_entry pmu1_plltab[] = {
{ .freq = 12000, .xf = 1, .p1div = 3, .p2div = 22, .ndiv_int = 0x9, .ndiv_frac = 0xFFFFEF, },
{ .freq = 13000, .xf = 2, .p1div = 1, .p2div = 6, .ndiv_int = 0xb, .ndiv_frac = 0x483483, },
{ .freq = 14400, .xf = 3, .p1div = 1, .p2div = 10, .ndiv_int = 0xa, .ndiv_frac = 0x1C71C7, },
{ .freq = 15360, .xf = 4, .p1div = 1, .p2div = 5, .ndiv_int = 0xb, .ndiv_frac = 0x755555, },
{ .freq = 16200, .xf = 5, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x6E9E06, },
{ .freq = 16800, .xf = 6, .p1div = 1, .p2div = 10, .ndiv_int = 0x5, .ndiv_frac = 0x3CF3CF, },
{ .freq = 19200, .xf = 7, .p1div = 1, .p2div = 9, .ndiv_int = 0x5, .ndiv_frac = 0x17B425, },
{ .freq = 19800, .xf = 8, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0xA57EB, },
{ .freq = 20000, .xf = 9, .p1div = 1, .p2div = 11, .ndiv_int = 0x4, .ndiv_frac = 0, },
{ .freq = 24000, .xf = 10, .p1div = 3, .p2div = 11, .ndiv_int = 0xa, .ndiv_frac = 0, },
{ .freq = 25000, .xf = 11, .p1div = 5, .p2div = 16, .ndiv_int = 0xb, .ndiv_frac = 0, },
{ .freq = 26000, .xf = 12, .p1div = 1, .p2div = 2, .ndiv_int = 0x10, .ndiv_frac = 0xEC4EC4, },
{ .freq = 30000, .xf = 13, .p1div = 3, .p2div = 8, .ndiv_int = 0xb, .ndiv_frac = 0, },
{ .freq = 38400, .xf = 14, .p1div = 1, .p2div = 5, .ndiv_int = 0x4, .ndiv_frac = 0x955555, },
{ .freq = 40000, .xf = 15, .p1div = 1, .p2div = 2, .ndiv_int = 0xb, .ndiv_frac = 0, },
};
#define SSB_PMU1_DEFAULT_XTALFREQ 15360
static const struct pmu1_plltab_entry * pmu1_plltab_find_entry(u32 crystalfreq)
{
const struct pmu1_plltab_entry *e;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(pmu1_plltab); i++) {
e = &pmu1_plltab[i];
if (e->freq == crystalfreq)
return e;
}
return NULL;
}
/* Tune the PLL to the crystal speed. crystalfreq is in kHz. */
static void ssb_pmu1_pllinit_r0(struct ssb_chipcommon *cc,
u32 crystalfreq)
{
struct ssb_bus *bus = cc->dev->bus;
const struct pmu1_plltab_entry *e = NULL;
u32 buffer_strength = 0;
u32 tmp, pllctl, pmuctl;
unsigned int i;
if (bus->chip_id == 0x4312) {
/* We do not touch the BCM4312 PLL and assume
* the default crystal settings work out-of-the-box. */
cc->pmu.crystalfreq = 20000;
return;
}
if (crystalfreq)
e = pmu1_plltab_find_entry(crystalfreq);
if (!e)
e = pmu1_plltab_find_entry(SSB_PMU1_DEFAULT_XTALFREQ);
BUG_ON(!e);
crystalfreq = e->freq;
cc->pmu.crystalfreq = e->freq;
/* Check if the PLL already is programmed to this frequency. */
pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
if (((pmuctl & SSB_CHIPCO_PMU_CTL_XTALFREQ) >> SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) == e->xf) {
/* We're already there... */
return;
}
ssb_info("Programming PLL to %u.%03u MHz\n",
crystalfreq / 1000, crystalfreq % 1000);
/* First turn the PLL off. */
switch (bus->chip_id) {
case 0x4325:
chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK,
~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) |
(1 << SSB_PMURES_4325_HT_AVAIL)));
chipco_mask32(cc, SSB_CHIPCO_PMU_MAXRES_MSK,
~((1 << SSB_PMURES_4325_BBPLL_PWRSW_PU) |
(1 << SSB_PMURES_4325_HT_AVAIL)));
/* Adjust the BBPLL to 2 on all channels later. */
buffer_strength = 0x222222;
break;
default:
SSB_WARN_ON(1);
}
for (i = 1500; i; i--) {
tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
if (!(tmp & SSB_CHIPCO_CLKCTLST_HAVEHT))
break;
udelay(10);
}
tmp = chipco_read32(cc, SSB_CHIPCO_CLKCTLST);
if (tmp & SSB_CHIPCO_CLKCTLST_HAVEHT)
ssb_emerg("Failed to turn the PLL off!\n");
/* Set p1div and p2div. */
pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL0);
pllctl &= ~(SSB_PMU1_PLLCTL0_P1DIV | SSB_PMU1_PLLCTL0_P2DIV);
pllctl |= ((u32)e->p1div << SSB_PMU1_PLLCTL0_P1DIV_SHIFT) & SSB_PMU1_PLLCTL0_P1DIV;
pllctl |= ((u32)e->p2div << SSB_PMU1_PLLCTL0_P2DIV_SHIFT) & SSB_PMU1_PLLCTL0_P2DIV;
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, pllctl);
/* Set ndiv int and ndiv mode */
pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL2);
pllctl &= ~(SSB_PMU1_PLLCTL2_NDIVINT | SSB_PMU1_PLLCTL2_NDIVMODE);
pllctl |= ((u32)e->ndiv_int << SSB_PMU1_PLLCTL2_NDIVINT_SHIFT) & SSB_PMU1_PLLCTL2_NDIVINT;
pllctl |= (1 << SSB_PMU1_PLLCTL2_NDIVMODE_SHIFT) & SSB_PMU1_PLLCTL2_NDIVMODE;
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, pllctl);
/* Set ndiv frac */
pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL3);
pllctl &= ~SSB_PMU1_PLLCTL3_NDIVFRAC;
pllctl |= ((u32)e->ndiv_frac << SSB_PMU1_PLLCTL3_NDIVFRAC_SHIFT) & SSB_PMU1_PLLCTL3_NDIVFRAC;
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, pllctl);
/* Change the drive strength, if required. */
if (buffer_strength) {
pllctl = ssb_chipco_pll_read(cc, SSB_PMU1_PLLCTL5);
pllctl &= ~SSB_PMU1_PLLCTL5_CLKDRV;
pllctl |= (buffer_strength << SSB_PMU1_PLLCTL5_CLKDRV_SHIFT) & SSB_PMU1_PLLCTL5_CLKDRV;
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, pllctl);
}
/* Tune the crystalfreq and the divisor. */
pmuctl = chipco_read32(cc, SSB_CHIPCO_PMU_CTL);
pmuctl &= ~(SSB_CHIPCO_PMU_CTL_ILP_DIV | SSB_CHIPCO_PMU_CTL_XTALFREQ);
pmuctl |= ((((u32)e->freq + 127) / 128 - 1) << SSB_CHIPCO_PMU_CTL_ILP_DIV_SHIFT)
& SSB_CHIPCO_PMU_CTL_ILP_DIV;
pmuctl |= ((u32)e->xf << SSB_CHIPCO_PMU_CTL_XTALFREQ_SHIFT) & SSB_CHIPCO_PMU_CTL_XTALFREQ;
chipco_write32(cc, SSB_CHIPCO_PMU_CTL, pmuctl);
}
static void ssb_pmu_pll_init(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
u32 crystalfreq = 0; /* in kHz. 0 = keep default freq. */
if (bus->bustype == SSB_BUSTYPE_SSB) {
#ifdef CONFIG_BCM47XX
char buf[20];
if (bcm47xx_nvram_getenv("xtalfreq", buf, sizeof(buf)) >= 0)
crystalfreq = simple_strtoul(buf, NULL, 0);
#endif
}
switch (bus->chip_id) {
case 0x4312:
case 0x4325:
ssb_pmu1_pllinit_r0(cc, crystalfreq);
break;
case 0x4328:
ssb_pmu0_pllinit_r0(cc, crystalfreq);
break;
case 0x5354:
if (crystalfreq == 0)
crystalfreq = 25000;
ssb_pmu0_pllinit_r0(cc, crystalfreq);
break;
case 0x4322:
if (cc->pmu.rev == 2) {
chipco_write32(cc, SSB_CHIPCO_PLLCTL_ADDR, 0x0000000A);
chipco_write32(cc, SSB_CHIPCO_PLLCTL_DATA, 0x380005C0);
}
break;
case 43222:
break;
default:
ssb_err("ERROR: PLL init unknown for device %04X\n",
bus->chip_id);
}
}
struct pmu_res_updown_tab_entry {
u8 resource; /* The resource number */
u16 updown; /* The updown value */
};
enum pmu_res_depend_tab_task {
PMU_RES_DEP_SET = 1,
PMU_RES_DEP_ADD,
PMU_RES_DEP_REMOVE,
};
struct pmu_res_depend_tab_entry {
u8 resource; /* The resource number */
u8 task; /* SET | ADD | REMOVE */
u32 depend; /* The depend mask */
};
static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4328a0[] = {
{ .resource = SSB_PMURES_4328_EXT_SWITCHER_PWM, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_BB_SWITCHER_PWM, .updown = 0x1F01, },
{ .resource = SSB_PMURES_4328_BB_SWITCHER_BURST, .updown = 0x010F, },
{ .resource = SSB_PMURES_4328_BB_EXT_SWITCHER_BURST, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_ILP_REQUEST, .updown = 0x0202, },
{ .resource = SSB_PMURES_4328_RADIO_SWITCHER_PWM, .updown = 0x0F01, },
{ .resource = SSB_PMURES_4328_RADIO_SWITCHER_BURST, .updown = 0x0F01, },
{ .resource = SSB_PMURES_4328_ROM_SWITCH, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_PA_REF_LDO, .updown = 0x0F01, },
{ .resource = SSB_PMURES_4328_RADIO_LDO, .updown = 0x0F01, },
{ .resource = SSB_PMURES_4328_AFE_LDO, .updown = 0x0F01, },
{ .resource = SSB_PMURES_4328_PLL_LDO, .updown = 0x0F01, },
{ .resource = SSB_PMURES_4328_BG_FILTBYP, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_TX_FILTBYP, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_RX_FILTBYP, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_XTAL_PU, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_XTAL_EN, .updown = 0xA001, },
{ .resource = SSB_PMURES_4328_BB_PLL_FILTBYP, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_RF_PLL_FILTBYP, .updown = 0x0101, },
{ .resource = SSB_PMURES_4328_BB_PLL_PU, .updown = 0x0701, },
};
static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4328a0[] = {
{
/* Adjust ILP Request to avoid forcing EXT/BB into burst mode. */
.resource = SSB_PMURES_4328_ILP_REQUEST,
.task = PMU_RES_DEP_SET,
.depend = ((1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) |
(1 << SSB_PMURES_4328_BB_SWITCHER_PWM)),
},
};
static const struct pmu_res_updown_tab_entry pmu_res_updown_tab_4325a0[] = {
{ .resource = SSB_PMURES_4325_XTAL_PU, .updown = 0x1501, },
};
static const struct pmu_res_depend_tab_entry pmu_res_depend_tab_4325a0[] = {
{
/* Adjust HT-Available dependencies. */
.resource = SSB_PMURES_4325_HT_AVAIL,
.task = PMU_RES_DEP_ADD,
.depend = ((1 << SSB_PMURES_4325_RX_PWRSW_PU) |
(1 << SSB_PMURES_4325_TX_PWRSW_PU) |
(1 << SSB_PMURES_4325_LOGEN_PWRSW_PU) |
(1 << SSB_PMURES_4325_AFE_PWRSW_PU)),
},
};
static void ssb_pmu_resources_init(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
u32 min_msk = 0, max_msk = 0;
unsigned int i;
const struct pmu_res_updown_tab_entry *updown_tab = NULL;
unsigned int updown_tab_size = 0;
const struct pmu_res_depend_tab_entry *depend_tab = NULL;
unsigned int depend_tab_size = 0;
switch (bus->chip_id) {
case 0x4312:
min_msk = 0xCBB;
break;
case 0x4322:
case 43222:
/* We keep the default settings:
* min_msk = 0xCBB
* max_msk = 0x7FFFF
*/
break;
case 0x4325:
/* Power OTP down later. */
min_msk = (1 << SSB_PMURES_4325_CBUCK_BURST) |
(1 << SSB_PMURES_4325_LNLDO2_PU);
if (chipco_read32(cc, SSB_CHIPCO_CHIPSTAT) &
SSB_CHIPCO_CHST_4325_PMUTOP_2B)
min_msk |= (1 << SSB_PMURES_4325_CLDO_CBUCK_BURST);
/* The PLL may turn on, if it decides so. */
max_msk = 0xFFFFF;
updown_tab = pmu_res_updown_tab_4325a0;
updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4325a0);
depend_tab = pmu_res_depend_tab_4325a0;
depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4325a0);
break;
case 0x4328:
min_msk = (1 << SSB_PMURES_4328_EXT_SWITCHER_PWM) |
(1 << SSB_PMURES_4328_BB_SWITCHER_PWM) |
(1 << SSB_PMURES_4328_XTAL_EN);
/* The PLL may turn on, if it decides so. */
max_msk = 0xFFFFF;
updown_tab = pmu_res_updown_tab_4328a0;
updown_tab_size = ARRAY_SIZE(pmu_res_updown_tab_4328a0);
depend_tab = pmu_res_depend_tab_4328a0;
depend_tab_size = ARRAY_SIZE(pmu_res_depend_tab_4328a0);
break;
case 0x5354:
/* The PLL may turn on, if it decides so. */
max_msk = 0xFFFFF;
break;
default:
ssb_err("ERROR: PMU resource config unknown for device %04X\n",
bus->chip_id);
}
if (updown_tab) {
for (i = 0; i < updown_tab_size; i++) {
chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL,
updown_tab[i].resource);
chipco_write32(cc, SSB_CHIPCO_PMU_RES_UPDNTM,
updown_tab[i].updown);
}
}
if (depend_tab) {
for (i = 0; i < depend_tab_size; i++) {
chipco_write32(cc, SSB_CHIPCO_PMU_RES_TABSEL,
depend_tab[i].resource);
switch (depend_tab[i].task) {
case PMU_RES_DEP_SET:
chipco_write32(cc, SSB_CHIPCO_PMU_RES_DEPMSK,
depend_tab[i].depend);
break;
case PMU_RES_DEP_ADD:
chipco_set32(cc, SSB_CHIPCO_PMU_RES_DEPMSK,
depend_tab[i].depend);
break;
case PMU_RES_DEP_REMOVE:
chipco_mask32(cc, SSB_CHIPCO_PMU_RES_DEPMSK,
~(depend_tab[i].depend));
break;
default:
SSB_WARN_ON(1);
}
}
}
/* Set the resource masks. */
if (min_msk)
chipco_write32(cc, SSB_CHIPCO_PMU_MINRES_MSK, min_msk);
if (max_msk)
chipco_write32(cc, SSB_CHIPCO_PMU_MAXRES_MSK, max_msk);
}
/* http://bcm-v4.sipsolutions.net/802.11/SSB/PmuInit */
void ssb_pmu_init(struct ssb_chipcommon *cc)
{
u32 pmucap;
if (!(cc->capabilities & SSB_CHIPCO_CAP_PMU))
return;
pmucap = chipco_read32(cc, SSB_CHIPCO_PMU_CAP);
cc->pmu.rev = (pmucap & SSB_CHIPCO_PMU_CAP_REVISION);
ssb_dbg("Found rev %u PMU (capabilities 0x%08X)\n",
cc->pmu.rev, pmucap);
if (cc->pmu.rev == 1)
chipco_mask32(cc, SSB_CHIPCO_PMU_CTL,
~SSB_CHIPCO_PMU_CTL_NOILPONW);
else
chipco_set32(cc, SSB_CHIPCO_PMU_CTL,
SSB_CHIPCO_PMU_CTL_NOILPONW);
ssb_pmu_pll_init(cc);
ssb_pmu_resources_init(cc);
}
void ssb_pmu_set_ldo_voltage(struct ssb_chipcommon *cc,
enum ssb_pmu_ldo_volt_id id, u32 voltage)
{
struct ssb_bus *bus = cc->dev->bus;
u32 addr, shift, mask;
switch (bus->chip_id) {
case 0x4328:
case 0x5354:
switch (id) {
case LDO_VOLT1:
addr = 2;
shift = 25;
mask = 0xF;
break;
case LDO_VOLT2:
addr = 3;
shift = 1;
mask = 0xF;
break;
case LDO_VOLT3:
addr = 3;
shift = 9;
mask = 0xF;
break;
case LDO_PAREF:
addr = 3;
shift = 17;
mask = 0x3F;
break;
default:
SSB_WARN_ON(1);
return;
}
break;
case 0x4312:
if (SSB_WARN_ON(id != LDO_PAREF))
return;
addr = 0;
shift = 21;
mask = 0x3F;
break;
default:
return;
}
ssb_chipco_regctl_maskset(cc, addr, ~(mask << shift),
(voltage & mask) << shift);
}
void ssb_pmu_set_ldo_paref(struct ssb_chipcommon *cc, bool on)
{
struct ssb_bus *bus = cc->dev->bus;
int ldo;
switch (bus->chip_id) {
case 0x4312:
ldo = SSB_PMURES_4312_PA_REF_LDO;
break;
case 0x4328:
ldo = SSB_PMURES_4328_PA_REF_LDO;
break;
case 0x5354:
ldo = SSB_PMURES_5354_PA_REF_LDO;
break;
default:
return;
}
if (on)
chipco_set32(cc, SSB_CHIPCO_PMU_MINRES_MSK, 1 << ldo);
else
chipco_mask32(cc, SSB_CHIPCO_PMU_MINRES_MSK, ~(1 << ldo));
chipco_read32(cc, SSB_CHIPCO_PMU_MINRES_MSK); //SPEC FIXME found via mmiotrace - dummy read?
}
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_err("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;
switch (bus->chip_id) {
case 0x5354:
/* 5354 chip uses a non programmable PLL of frequency 240MHz */
return 240000000;
default:
ssb_err("ERROR: PMU cpu clock unknown for device %04X\n",
bus->chip_id);
return 0;
}
}
u32 ssb_pmu_get_controlclock(struct ssb_chipcommon *cc)
{
struct ssb_bus *bus = cc->dev->bus;
switch (bus->chip_id) {
case 0x5354:
return 120000000;
default:
ssb_err("ERROR: PMU controlclock unknown for device %04X\n",
bus->chip_id);
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:
if (spuravoid == 1) {
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, 0x11500008);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL1, 0x0C000C06);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x0F600a08);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, 0x00000000);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL4, 0x2001E920);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, 0x88888815);
} else {
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL0, 0x11100008);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL1, 0x0c000c06);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL2, 0x03000a08);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL3, 0x00000000);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL4, 0x200005c0);
ssb_chipco_pll_write(cc, SSB_PMU1_PLLCTL5, 0x88888855);
}
pmu_ctl = SSB_CHIPCO_PMU_CTL_PLL_UPD;
break;
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);

View file

@ -0,0 +1,164 @@
/*
* Sonics Silicon Backplane
* ChipCommon serial flash interface
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include "ssb_private.h"
static struct resource ssb_sflash_resource = {
.name = "ssb_sflash",
.start = SSB_FLASH2,
.end = 0,
.flags = IORESOURCE_MEM | IORESOURCE_READONLY,
};
struct platform_device ssb_sflash_dev = {
.name = "ssb_sflash",
.resource = &ssb_sflash_resource,
.num_resources = 1,
};
struct ssb_sflash_tbl_e {
char *name;
u32 id;
u32 blocksize;
u16 numblocks;
};
static const struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
{ "M25P20", 0x11, 0x10000, 4, },
{ "M25P40", 0x12, 0x10000, 8, },
{ "M25P16", 0x14, 0x10000, 32, },
{ "M25P32", 0x15, 0x10000, 64, },
{ "M25P64", 0x16, 0x10000, 128, },
{ "M25FL128", 0x17, 0x10000, 256, },
{ NULL },
};
static const struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
{ "SST25WF512", 1, 0x1000, 16, },
{ "SST25VF512", 0x48, 0x1000, 16, },
{ "SST25WF010", 2, 0x1000, 32, },
{ "SST25VF010", 0x49, 0x1000, 32, },
{ "SST25WF020", 3, 0x1000, 64, },
{ "SST25VF020", 0x43, 0x1000, 64, },
{ "SST25WF040", 4, 0x1000, 128, },
{ "SST25VF040", 0x44, 0x1000, 128, },
{ "SST25VF040B", 0x8d, 0x1000, 128, },
{ "SST25WF080", 5, 0x1000, 256, },
{ "SST25VF080B", 0x8e, 0x1000, 256, },
{ "SST25VF016", 0x41, 0x1000, 512, },
{ "SST25VF032", 0x4a, 0x1000, 1024, },
{ "SST25VF064", 0x4b, 0x1000, 2048, },
{ NULL },
};
static const struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
{ "AT45DB011", 0xc, 256, 512, },
{ "AT45DB021", 0x14, 256, 1024, },
{ "AT45DB041", 0x1c, 256, 2048, },
{ "AT45DB081", 0x24, 256, 4096, },
{ "AT45DB161", 0x2c, 512, 4096, },
{ "AT45DB321", 0x34, 512, 8192, },
{ "AT45DB642", 0x3c, 1024, 8192, },
{ NULL },
};
static void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode)
{
int i;
chipco_write32(cc, SSB_CHIPCO_FLASHCTL,
SSB_CHIPCO_FLASHCTL_START | opcode);
for (i = 0; i < 1000; i++) {
if (!(chipco_read32(cc, SSB_CHIPCO_FLASHCTL) &
SSB_CHIPCO_FLASHCTL_BUSY))
return;
cpu_relax();
}
pr_err("SFLASH control command failed (timeout)!\n");
}
/* Initialize serial flash access */
int ssb_sflash_init(struct ssb_chipcommon *cc)
{
struct ssb_sflash *sflash = &cc->dev->bus->mipscore.sflash;
const struct ssb_sflash_tbl_e *e;
u32 id, id2;
switch (cc->capabilities & SSB_CHIPCO_CAP_FLASHT) {
case SSB_CHIPCO_FLASHT_STSER:
ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_DP);
chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 0);
ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES);
id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA);
chipco_write32(cc, SSB_CHIPCO_FLASHADDR, 1);
ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_ST_RES);
id2 = chipco_read32(cc, SSB_CHIPCO_FLASHDATA);
switch (id) {
case 0xbf:
for (e = ssb_sflash_sst_tbl; e->name; e++) {
if (e->id == id2)
break;
}
break;
case 0x13:
return -ENOTSUPP;
default:
for (e = ssb_sflash_st_tbl; e->name; e++) {
if (e->id == id)
break;
}
break;
}
if (!e->name) {
pr_err("Unsupported ST serial flash (id: 0x%X, id2: 0x%X)\n",
id, id2);
return -ENOTSUPP;
}
break;
case SSB_CHIPCO_FLASHT_ATSER:
ssb_sflash_cmd(cc, SSB_CHIPCO_FLASHCTL_AT_STATUS);
id = chipco_read32(cc, SSB_CHIPCO_FLASHDATA) & 0x3c;
for (e = ssb_sflash_at_tbl; e->name; e++) {
if (e->id == id)
break;
}
if (!e->name) {
pr_err("Unsupported Atmel serial flash (id: 0x%X)\n",
id);
return -ENOTSUPP;
}
break;
default:
pr_err("Unsupported flash type\n");
return -ENOTSUPP;
}
sflash->window = SSB_FLASH2;
sflash->blocksize = e->blocksize;
sflash->numblocks = e->numblocks;
sflash->size = sflash->blocksize * sflash->numblocks;
sflash->present = true;
pr_info("Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n",
e->name, sflash->size / 1024, e->blocksize, e->numblocks);
/* Prepare platform device, but don't register it yet. It's too early,
* malloc (required by device_private_init) is not available yet. */
ssb_sflash_dev.resource[0].end = ssb_sflash_dev.resource[0].start +
sflash->size;
ssb_sflash_dev.dev.platform_data = sflash;
return 0;
}

201
drivers/ssb/driver_extif.c Normal file
View file

@ -0,0 +1,201 @@
/*
* Sonics Silicon Backplane
* Broadcom EXTIF core driver
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <m@bues.ch>
* Copyright 2006, 2007, Felix Fietkau <nbd@openwrt.org>
* Copyright 2007, Aurelien Jarno <aurelien@aurel32.net>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include "ssb_private.h"
static inline u32 extif_read32(struct ssb_extif *extif, u16 offset)
{
return ssb_read32(extif->dev, offset);
}
static inline void extif_write32(struct ssb_extif *extif, u16 offset, u32 value)
{
ssb_write32(extif->dev, offset, value);
}
static inline u32 extif_write32_masked(struct ssb_extif *extif, u16 offset,
u32 mask, u32 value)
{
value &= mask;
value |= extif_read32(extif, offset) & ~mask;
extif_write32(extif, offset, value);
return value;
}
#ifdef CONFIG_SSB_SERIAL
static bool serial_exists(u8 *regs)
{
u8 save_mcr, msr = 0;
if (regs) {
save_mcr = regs[UART_MCR];
regs[UART_MCR] = (UART_MCR_LOOP | UART_MCR_OUT2 | UART_MCR_RTS);
msr = regs[UART_MSR] & (UART_MSR_DCD | UART_MSR_RI
| UART_MSR_CTS | UART_MSR_DSR);
regs[UART_MCR] = save_mcr;
}
return (msr == (UART_MSR_DCD | UART_MSR_CTS));
}
int ssb_extif_serial_init(struct ssb_extif *extif, struct ssb_serial_port *ports)
{
u32 i, nr_ports = 0;
/* Disable GPIO interrupt initially */
extif_write32(extif, SSB_EXTIF_GPIO_INTPOL, 0);
extif_write32(extif, SSB_EXTIF_GPIO_INTMASK, 0);
for (i = 0; i < 2; i++) {
void __iomem *uart_regs;
uart_regs = ioremap_nocache(SSB_EUART, 16);
if (uart_regs) {
uart_regs += (i * 8);
if (serial_exists(uart_regs) && ports) {
extif_write32(extif, SSB_EXTIF_GPIO_INTMASK, 2);
nr_ports++;
ports[i].regs = uart_regs;
ports[i].irq = 2;
ports[i].baud_base = 13500000;
ports[i].reg_shift = 0;
}
iounmap(uart_regs);
}
}
return nr_ports;
}
#endif /* CONFIG_SSB_SERIAL */
void ssb_extif_timing_init(struct ssb_extif *extif, unsigned long ns)
{
u32 tmp;
/* Initialize extif so we can get to the LEDs and external UART */
extif_write32(extif, SSB_EXTIF_PROG_CFG, SSB_EXTCFG_EN);
/* Set timing for the flash */
tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;
tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT;
tmp |= DIV_ROUND_UP(120, ns);
extif_write32(extif, SSB_EXTIF_PROG_WAITCNT, tmp);
/* Set programmable interface timing for external uart */
tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT;
tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT;
tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT;
tmp |= DIV_ROUND_UP(120, ns);
extif_write32(extif, SSB_EXTIF_PROG_WAITCNT, tmp);
}
void ssb_extif_get_clockcontrol(struct ssb_extif *extif,
u32 *pll_type, u32 *n, u32 *m)
{
*pll_type = SSB_PLLTYPE_1;
*n = extif_read32(extif, SSB_EXTIF_CLOCK_N);
*m = extif_read32(extif, SSB_EXTIF_CLOCK_SB);
}
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)
{
return extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask;
}
u32 ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value)
{
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)
{
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)
{
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)
{
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;
}

297
drivers/ssb/driver_gige.c Normal file
View file

@ -0,0 +1,297 @@
/*
* Sonics Silicon Backplane
* Broadcom Gigabit Ethernet core driver
*
* Copyright 2008, Broadcom Corporation
* Copyright 2008, Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_driver_gige.h>
#include <linux/export.h>
#include <linux/pci.h>
#include <linux/pci_regs.h>
#include <linux/slab.h>
/*
MODULE_DESCRIPTION("SSB Broadcom Gigabit Ethernet driver");
MODULE_AUTHOR("Michael Buesch");
MODULE_LICENSE("GPL");
*/
static const struct ssb_device_id ssb_gige_tbl[] = {
SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_ETHERNET_GBIT, SSB_ANY_REV),
SSB_DEVTABLE_END
};
/* MODULE_DEVICE_TABLE(ssb, ssb_gige_tbl); */
static inline u8 gige_read8(struct ssb_gige *dev, u16 offset)
{
return ssb_read8(dev->dev, offset);
}
static inline u16 gige_read16(struct ssb_gige *dev, u16 offset)
{
return ssb_read16(dev->dev, offset);
}
static inline u32 gige_read32(struct ssb_gige *dev, u16 offset)
{
return ssb_read32(dev->dev, offset);
}
static inline void gige_write8(struct ssb_gige *dev,
u16 offset, u8 value)
{
ssb_write8(dev->dev, offset, value);
}
static inline void gige_write16(struct ssb_gige *dev,
u16 offset, u16 value)
{
ssb_write16(dev->dev, offset, value);
}
static inline void gige_write32(struct ssb_gige *dev,
u16 offset, u32 value)
{
ssb_write32(dev->dev, offset, value);
}
static inline
u8 gige_pcicfg_read8(struct ssb_gige *dev, unsigned int offset)
{
BUG_ON(offset >= 256);
return gige_read8(dev, SSB_GIGE_PCICFG + offset);
}
static inline
u16 gige_pcicfg_read16(struct ssb_gige *dev, unsigned int offset)
{
BUG_ON(offset >= 256);
return gige_read16(dev, SSB_GIGE_PCICFG + offset);
}
static inline
u32 gige_pcicfg_read32(struct ssb_gige *dev, unsigned int offset)
{
BUG_ON(offset >= 256);
return gige_read32(dev, SSB_GIGE_PCICFG + offset);
}
static inline
void gige_pcicfg_write8(struct ssb_gige *dev,
unsigned int offset, u8 value)
{
BUG_ON(offset >= 256);
gige_write8(dev, SSB_GIGE_PCICFG + offset, value);
}
static inline
void gige_pcicfg_write16(struct ssb_gige *dev,
unsigned int offset, u16 value)
{
BUG_ON(offset >= 256);
gige_write16(dev, SSB_GIGE_PCICFG + offset, value);
}
static inline
void gige_pcicfg_write32(struct ssb_gige *dev,
unsigned int offset, u32 value)
{
BUG_ON(offset >= 256);
gige_write32(dev, SSB_GIGE_PCICFG + offset, value);
}
static int ssb_gige_pci_read_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 *val)
{
struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
unsigned long flags;
if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
return PCIBIOS_DEVICE_NOT_FOUND;
if (reg >= 256)
return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&dev->lock, flags);
switch (size) {
case 1:
*val = gige_pcicfg_read8(dev, reg);
break;
case 2:
*val = gige_pcicfg_read16(dev, reg);
break;
case 4:
*val = gige_pcicfg_read32(dev, reg);
break;
default:
WARN_ON(1);
}
spin_unlock_irqrestore(&dev->lock, flags);
return PCIBIOS_SUCCESSFUL;
}
static int ssb_gige_pci_write_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 val)
{
struct ssb_gige *dev = container_of(bus->ops, struct ssb_gige, pci_ops);
unsigned long flags;
if ((PCI_SLOT(devfn) > 0) || (PCI_FUNC(devfn) > 0))
return PCIBIOS_DEVICE_NOT_FOUND;
if (reg >= 256)
return PCIBIOS_DEVICE_NOT_FOUND;
spin_lock_irqsave(&dev->lock, flags);
switch (size) {
case 1:
gige_pcicfg_write8(dev, reg, val);
break;
case 2:
gige_pcicfg_write16(dev, reg, val);
break;
case 4:
gige_pcicfg_write32(dev, reg, val);
break;
default:
WARN_ON(1);
}
spin_unlock_irqrestore(&dev->lock, flags);
return PCIBIOS_SUCCESSFUL;
}
static int ssb_gige_probe(struct ssb_device *sdev,
const struct ssb_device_id *id)
{
struct ssb_gige *dev;
u32 base, tmslow, tmshigh;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
dev->dev = sdev;
spin_lock_init(&dev->lock);
dev->pci_controller.pci_ops = &dev->pci_ops;
dev->pci_controller.io_resource = &dev->io_resource;
dev->pci_controller.mem_resource = &dev->mem_resource;
dev->pci_controller.io_map_base = 0x800;
dev->pci_ops.read = ssb_gige_pci_read_config;
dev->pci_ops.write = ssb_gige_pci_write_config;
dev->io_resource.name = SSB_GIGE_IO_RES_NAME;
dev->io_resource.start = 0x800;
dev->io_resource.end = 0x8FF;
dev->io_resource.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED;
if (!ssb_device_is_enabled(sdev))
ssb_device_enable(sdev, 0);
/* Setup BAR0. This is a 64k MMIO region. */
base = ssb_admatch_base(ssb_read32(sdev, SSB_ADMATCH1));
gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_0, base);
gige_pcicfg_write32(dev, PCI_BASE_ADDRESS_1, 0);
dev->mem_resource.name = SSB_GIGE_MEM_RES_NAME;
dev->mem_resource.start = base;
dev->mem_resource.end = base + 0x10000 - 1;
dev->mem_resource.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
/* Enable the memory region. */
gige_pcicfg_write16(dev, PCI_COMMAND,
gige_pcicfg_read16(dev, PCI_COMMAND)
| PCI_COMMAND_MEMORY);
/* Write flushing is controlled by the Flush Status Control register.
* We want to flush every register write with a timeout and we want
* to disable the IRQ mask while flushing to avoid concurrency.
* Note that automatic write flushing does _not_ work from
* an IRQ handler. The driver must flush manually by reading a register.
*/
gige_write32(dev, SSB_GIGE_SHIM_FLUSHSTAT, 0x00000068);
/* Check if we have an RGMII or GMII PHY-bus.
* On RGMII do not bypass the DLLs */
tmslow = ssb_read32(sdev, SSB_TMSLOW);
tmshigh = ssb_read32(sdev, SSB_TMSHIGH);
if (tmshigh & SSB_GIGE_TMSHIGH_RGMII) {
tmslow &= ~SSB_GIGE_TMSLOW_TXBYPASS;
tmslow &= ~SSB_GIGE_TMSLOW_RXBYPASS;
dev->has_rgmii = 1;
} else {
tmslow |= SSB_GIGE_TMSLOW_TXBYPASS;
tmslow |= SSB_GIGE_TMSLOW_RXBYPASS;
dev->has_rgmii = 0;
}
tmslow |= SSB_GIGE_TMSLOW_DLLEN;
ssb_write32(sdev, SSB_TMSLOW, tmslow);
ssb_set_drvdata(sdev, dev);
register_pci_controller(&dev->pci_controller);
return 0;
}
bool pdev_is_ssb_gige_core(struct pci_dev *pdev)
{
if (!pdev->resource[0].name)
return 0;
return (strcmp(pdev->resource[0].name, SSB_GIGE_MEM_RES_NAME) == 0);
}
EXPORT_SYMBOL(pdev_is_ssb_gige_core);
int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev,
struct pci_dev *pdev)
{
struct ssb_gige *dev = ssb_get_drvdata(sdev);
struct resource *res;
if (pdev->bus->ops != &dev->pci_ops) {
/* The PCI device is not on this SSB GigE bridge device. */
return -ENODEV;
}
/* Fixup the PCI resources. */
res = &(pdev->resource[0]);
res->flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED;
res->name = dev->mem_resource.name;
res->start = dev->mem_resource.start;
res->end = dev->mem_resource.end;
/* Fixup interrupt lines. */
pdev->irq = ssb_mips_irq(sdev) + 2;
pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, pdev->irq);
return 0;
}
int ssb_gige_map_irq(struct ssb_device *sdev,
const struct pci_dev *pdev)
{
struct ssb_gige *dev = ssb_get_drvdata(sdev);
if (pdev->bus->ops != &dev->pci_ops) {
/* The PCI device is not on this SSB GigE bridge device. */
return -ENODEV;
}
return ssb_mips_irq(sdev) + 2;
}
static struct ssb_driver ssb_gige_driver = {
.name = "BCM-GigE",
.id_table = ssb_gige_tbl,
.probe = ssb_gige_probe,
};
int ssb_gige_init(void)
{
return ssb_driver_register(&ssb_gige_driver);
}

485
drivers/ssb/driver_gpio.c Normal file
View file

@ -0,0 +1,485 @@
/*
* Sonics Silicon Backplane
* GPIO driver
*
* Copyright 2011, Broadcom Corporation
* Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
#include <linux/export.h>
#include <linux/ssb/ssb.h>
#include "ssb_private.h"
/**************************************************
* Shared
**************************************************/
static struct ssb_bus *ssb_gpio_get_bus(struct gpio_chip *chip)
{
return container_of(chip, struct ssb_bus, gpio);
}
#if IS_ENABLED(CONFIG_SSB_EMBEDDED)
static int ssb_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
{
struct ssb_bus *bus = ssb_gpio_get_bus(chip);
if (bus->bustype == SSB_BUSTYPE_SSB)
return irq_find_mapping(bus->irq_domain, gpio);
else
return -EINVAL;
}
#endif
/**************************************************
* ChipCommon
**************************************************/
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);
}
#if IS_ENABLED(CONFIG_SSB_EMBEDDED)
static void ssb_gpio_irq_chipco_mask(struct irq_data *d)
{
struct ssb_bus *bus = irq_data_get_irq_chip_data(d);
int gpio = irqd_to_hwirq(d);
ssb_chipco_gpio_intmask(&bus->chipco, BIT(gpio), 0);
}
static void ssb_gpio_irq_chipco_unmask(struct irq_data *d)
{
struct ssb_bus *bus = irq_data_get_irq_chip_data(d);
int gpio = irqd_to_hwirq(d);
u32 val = ssb_chipco_gpio_in(&bus->chipco, BIT(gpio));
ssb_chipco_gpio_polarity(&bus->chipco, BIT(gpio), val);
ssb_chipco_gpio_intmask(&bus->chipco, BIT(gpio), BIT(gpio));
}
static struct irq_chip ssb_gpio_irq_chipco_chip = {
.name = "SSB-GPIO-CC",
.irq_mask = ssb_gpio_irq_chipco_mask,
.irq_unmask = ssb_gpio_irq_chipco_unmask,
};
static irqreturn_t ssb_gpio_irq_chipco_handler(int irq, void *dev_id)
{
struct ssb_bus *bus = dev_id;
struct ssb_chipcommon *chipco = &bus->chipco;
u32 val = chipco_read32(chipco, SSB_CHIPCO_GPIOIN);
u32 mask = chipco_read32(chipco, SSB_CHIPCO_GPIOIRQ);
u32 pol = chipco_read32(chipco, SSB_CHIPCO_GPIOPOL);
unsigned long irqs = (val ^ pol) & mask;
int gpio;
if (!irqs)
return IRQ_NONE;
for_each_set_bit(gpio, &irqs, bus->gpio.ngpio)
generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio));
ssb_chipco_gpio_polarity(chipco, irqs, val & irqs);
return IRQ_HANDLED;
}
static int ssb_gpio_irq_chipco_domain_init(struct ssb_bus *bus)
{
struct ssb_chipcommon *chipco = &bus->chipco;
struct gpio_chip *chip = &bus->gpio;
int gpio, hwirq, err;
if (bus->bustype != SSB_BUSTYPE_SSB)
return 0;
bus->irq_domain = irq_domain_add_linear(NULL, chip->ngpio,
&irq_domain_simple_ops, chipco);
if (!bus->irq_domain) {
err = -ENODEV;
goto err_irq_domain;
}
for (gpio = 0; gpio < chip->ngpio; gpio++) {
int irq = irq_create_mapping(bus->irq_domain, gpio);
irq_set_chip_data(irq, bus);
irq_set_chip_and_handler(irq, &ssb_gpio_irq_chipco_chip,
handle_simple_irq);
}
hwirq = ssb_mips_irq(bus->chipco.dev) + 2;
err = request_irq(hwirq, ssb_gpio_irq_chipco_handler, IRQF_SHARED,
"gpio", bus);
if (err)
goto err_req_irq;
ssb_chipco_gpio_intmask(&bus->chipco, ~0, 0);
chipco_set32(chipco, SSB_CHIPCO_IRQMASK, SSB_CHIPCO_IRQ_GPIO);
return 0;
err_req_irq:
for (gpio = 0; gpio < chip->ngpio; gpio++) {
int irq = irq_find_mapping(bus->irq_domain, gpio);
irq_dispose_mapping(irq);
}
irq_domain_remove(bus->irq_domain);
err_irq_domain:
return err;
}
static void ssb_gpio_irq_chipco_domain_exit(struct ssb_bus *bus)
{
struct ssb_chipcommon *chipco = &bus->chipco;
struct gpio_chip *chip = &bus->gpio;
int gpio;
if (bus->bustype != SSB_BUSTYPE_SSB)
return;
chipco_mask32(chipco, SSB_CHIPCO_IRQMASK, ~SSB_CHIPCO_IRQ_GPIO);
free_irq(ssb_mips_irq(bus->chipco.dev) + 2, chipco);
for (gpio = 0; gpio < chip->ngpio; gpio++) {
int irq = irq_find_mapping(bus->irq_domain, gpio);
irq_dispose_mapping(irq);
}
irq_domain_remove(bus->irq_domain);
}
#else
static int ssb_gpio_irq_chipco_domain_init(struct ssb_bus *bus)
{
return 0;
}
static void ssb_gpio_irq_chipco_domain_exit(struct ssb_bus *bus)
{
}
#endif
static int ssb_gpio_chipco_init(struct ssb_bus *bus)
{
struct gpio_chip *chip = &bus->gpio;
int err;
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;
#if IS_ENABLED(CONFIG_SSB_EMBEDDED)
chip->to_irq = ssb_gpio_to_irq;
#endif
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;
err = ssb_gpio_irq_chipco_domain_init(bus);
if (err)
return err;
err = gpiochip_add(chip);
if (err) {
ssb_gpio_irq_chipco_domain_exit(bus);
return err;
}
return 0;
}
/**************************************************
* EXTIF
**************************************************/
#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;
}
#if IS_ENABLED(CONFIG_SSB_EMBEDDED)
static void ssb_gpio_irq_extif_mask(struct irq_data *d)
{
struct ssb_bus *bus = irq_data_get_irq_chip_data(d);
int gpio = irqd_to_hwirq(d);
ssb_extif_gpio_intmask(&bus->extif, BIT(gpio), 0);
}
static void ssb_gpio_irq_extif_unmask(struct irq_data *d)
{
struct ssb_bus *bus = irq_data_get_irq_chip_data(d);
int gpio = irqd_to_hwirq(d);
u32 val = ssb_extif_gpio_in(&bus->extif, BIT(gpio));
ssb_extif_gpio_polarity(&bus->extif, BIT(gpio), val);
ssb_extif_gpio_intmask(&bus->extif, BIT(gpio), BIT(gpio));
}
static struct irq_chip ssb_gpio_irq_extif_chip = {
.name = "SSB-GPIO-EXTIF",
.irq_mask = ssb_gpio_irq_extif_mask,
.irq_unmask = ssb_gpio_irq_extif_unmask,
};
static irqreturn_t ssb_gpio_irq_extif_handler(int irq, void *dev_id)
{
struct ssb_bus *bus = dev_id;
struct ssb_extif *extif = &bus->extif;
u32 val = ssb_read32(extif->dev, SSB_EXTIF_GPIO_IN);
u32 mask = ssb_read32(extif->dev, SSB_EXTIF_GPIO_INTMASK);
u32 pol = ssb_read32(extif->dev, SSB_EXTIF_GPIO_INTPOL);
unsigned long irqs = (val ^ pol) & mask;
int gpio;
if (!irqs)
return IRQ_NONE;
for_each_set_bit(gpio, &irqs, bus->gpio.ngpio)
generic_handle_irq(ssb_gpio_to_irq(&bus->gpio, gpio));
ssb_extif_gpio_polarity(extif, irqs, val & irqs);
return IRQ_HANDLED;
}
static int ssb_gpio_irq_extif_domain_init(struct ssb_bus *bus)
{
struct ssb_extif *extif = &bus->extif;
struct gpio_chip *chip = &bus->gpio;
int gpio, hwirq, err;
if (bus->bustype != SSB_BUSTYPE_SSB)
return 0;
bus->irq_domain = irq_domain_add_linear(NULL, chip->ngpio,
&irq_domain_simple_ops, extif);
if (!bus->irq_domain) {
err = -ENODEV;
goto err_irq_domain;
}
for (gpio = 0; gpio < chip->ngpio; gpio++) {
int irq = irq_create_mapping(bus->irq_domain, gpio);
irq_set_chip_data(irq, bus);
irq_set_chip_and_handler(irq, &ssb_gpio_irq_extif_chip,
handle_simple_irq);
}
hwirq = ssb_mips_irq(bus->extif.dev) + 2;
err = request_irq(hwirq, ssb_gpio_irq_extif_handler, IRQF_SHARED,
"gpio", bus);
if (err)
goto err_req_irq;
ssb_extif_gpio_intmask(&bus->extif, ~0, 0);
return 0;
err_req_irq:
for (gpio = 0; gpio < chip->ngpio; gpio++) {
int irq = irq_find_mapping(bus->irq_domain, gpio);
irq_dispose_mapping(irq);
}
irq_domain_remove(bus->irq_domain);
err_irq_domain:
return err;
}
static void ssb_gpio_irq_extif_domain_exit(struct ssb_bus *bus)
{
struct ssb_extif *extif = &bus->extif;
struct gpio_chip *chip = &bus->gpio;
int gpio;
if (bus->bustype != SSB_BUSTYPE_SSB)
return;
free_irq(ssb_mips_irq(bus->extif.dev) + 2, extif);
for (gpio = 0; gpio < chip->ngpio; gpio++) {
int irq = irq_find_mapping(bus->irq_domain, gpio);
irq_dispose_mapping(irq);
}
irq_domain_remove(bus->irq_domain);
}
#else
static int ssb_gpio_irq_extif_domain_init(struct ssb_bus *bus)
{
return 0;
}
static void ssb_gpio_irq_extif_domain_exit(struct ssb_bus *bus)
{
}
#endif
static int ssb_gpio_extif_init(struct ssb_bus *bus)
{
struct gpio_chip *chip = &bus->gpio;
int err;
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;
#if IS_ENABLED(CONFIG_SSB_EMBEDDED)
chip->to_irq = ssb_gpio_to_irq;
#endif
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;
err = ssb_gpio_irq_extif_domain_init(bus);
if (err)
return err;
err = gpiochip_add(chip);
if (err) {
ssb_gpio_irq_extif_domain_exit(bus);
return err;
}
return 0;
}
#else
static int ssb_gpio_extif_init(struct ssb_bus *bus)
{
return -ENOTSUPP;
}
#endif
/**************************************************
* Init
**************************************************/
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;
}
int ssb_gpio_unregister(struct ssb_bus *bus)
{
if (ssb_chipco_available(&bus->chipco) ||
ssb_extif_available(&bus->extif)) {
gpiochip_remove(&bus->gpio);
return 0;
} else {
SSB_WARN_ON(1);
}
return -1;
}

View file

@ -0,0 +1,343 @@
/*
* Sonics Silicon Backplane
* Broadcom MIPS core driver
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/mtd/physmap.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/time.h>
#include "ssb_private.h"
static const char * const part_probes[] = { "bcm47xxpart", NULL };
static struct physmap_flash_data ssb_pflash_data = {
.part_probe_types = part_probes,
};
static struct resource ssb_pflash_resource = {
.name = "ssb_pflash",
.flags = IORESOURCE_MEM,
};
struct platform_device ssb_pflash_dev = {
.name = "physmap-flash",
.dev = {
.platform_data = &ssb_pflash_data,
},
.resource = &ssb_pflash_resource,
.num_resources = 1,
};
static inline u32 mips_read32(struct ssb_mipscore *mcore,
u16 offset)
{
return ssb_read32(mcore->dev, offset);
}
static inline void mips_write32(struct ssb_mipscore *mcore,
u16 offset,
u32 value)
{
ssb_write32(mcore->dev, offset, value);
}
static const u32 ipsflag_irq_mask[] = {
0,
SSB_IPSFLAG_IRQ1,
SSB_IPSFLAG_IRQ2,
SSB_IPSFLAG_IRQ3,
SSB_IPSFLAG_IRQ4,
};
static const u32 ipsflag_irq_shift[] = {
0,
SSB_IPSFLAG_IRQ1_SHIFT,
SSB_IPSFLAG_IRQ2_SHIFT,
SSB_IPSFLAG_IRQ3_SHIFT,
SSB_IPSFLAG_IRQ4_SHIFT,
};
static inline u32 ssb_irqflag(struct ssb_device *dev)
{
u32 tpsflag = ssb_read32(dev, SSB_TPSFLAG);
if (tpsflag)
return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
else
/* not irq supported */
return 0x3f;
}
static struct ssb_device *find_device(struct ssb_device *rdev, int irqflag)
{
struct ssb_bus *bus = rdev->bus;
int i;
for (i = 0; i < bus->nr_devices; i++) {
struct ssb_device *dev;
dev = &(bus->devices[i]);
if (ssb_irqflag(dev) == irqflag)
return dev;
}
return NULL;
}
/* Get the MIPS IRQ assignment for a specified device.
* If unassigned, 0 is returned.
* If disabled, 5 is returned.
* If not supported, 6 is returned.
*/
unsigned int ssb_mips_irq(struct ssb_device *dev)
{
struct ssb_bus *bus = dev->bus;
struct ssb_device *mdev = bus->mipscore.dev;
u32 irqflag;
u32 ipsflag;
u32 tmp;
unsigned int irq;
irqflag = ssb_irqflag(dev);
if (irqflag == 0x3f)
return 6;
ipsflag = ssb_read32(bus->mipscore.dev, SSB_IPSFLAG);
for (irq = 1; irq <= 4; irq++) {
tmp = ((ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq]);
if (tmp == irqflag)
break;
}
if (irq == 5) {
if ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC))
irq = 0;
}
return irq;
}
static void clear_irq(struct ssb_bus *bus, unsigned int irq)
{
struct ssb_device *dev = bus->mipscore.dev;
/* Clear the IRQ in the MIPScore backplane registers */
if (irq == 0) {
ssb_write32(dev, SSB_INTVEC, 0);
} else {
ssb_write32(dev, SSB_IPSFLAG,
ssb_read32(dev, SSB_IPSFLAG) |
ipsflag_irq_mask[irq]);
}
}
static void set_irq(struct ssb_device *dev, unsigned int irq)
{
unsigned int oldirq = ssb_mips_irq(dev);
struct ssb_bus *bus = dev->bus;
struct ssb_device *mdev = bus->mipscore.dev;
u32 irqflag = ssb_irqflag(dev);
BUG_ON(oldirq == 6);
dev->irq = irq + 2;
/* clear the old irq */
if (oldirq == 0)
ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
else if (oldirq != 5)
clear_irq(bus, oldirq);
/* assign the new one */
if (irq == 0) {
ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) | ssb_read32(mdev, SSB_INTVEC)));
} else {
u32 ipsflag = ssb_read32(mdev, SSB_IPSFLAG);
if ((ipsflag & ipsflag_irq_mask[irq]) != ipsflag_irq_mask[irq]) {
u32 oldipsflag = (ipsflag & ipsflag_irq_mask[irq]) >> ipsflag_irq_shift[irq];
struct ssb_device *olddev = find_device(dev, oldipsflag);
if (olddev)
set_irq(olddev, 0);
}
irqflag <<= ipsflag_irq_shift[irq];
irqflag |= (ipsflag & ~ipsflag_irq_mask[irq]);
ssb_write32(mdev, SSB_IPSFLAG, irqflag);
}
ssb_dbg("set_irq: core 0x%04x, irq %d => %d\n",
dev->id.coreid, oldirq+2, irq+2);
}
static void print_irq(struct ssb_device *dev, unsigned int irq)
{
static const char *irq_name[] = {"2(S)", "3", "4", "5", "6", "D", "I"};
ssb_dbg("core 0x%04x, irq : %s%s %s%s %s%s %s%s %s%s %s%s %s%s\n",
dev->id.coreid,
irq_name[0], irq == 0 ? "*" : " ",
irq_name[1], irq == 1 ? "*" : " ",
irq_name[2], irq == 2 ? "*" : " ",
irq_name[3], irq == 3 ? "*" : " ",
irq_name[4], irq == 4 ? "*" : " ",
irq_name[5], irq == 5 ? "*" : " ",
irq_name[6], irq == 6 ? "*" : " ");
}
static void dump_irq(struct ssb_bus *bus)
{
int i;
for (i = 0; i < bus->nr_devices; i++) {
struct ssb_device *dev;
dev = &(bus->devices[i]);
print_irq(dev, ssb_mips_irq(dev));
}
}
static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
{
struct ssb_bus *bus = mcore->dev->bus;
if (ssb_extif_available(&bus->extif))
mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports);
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;
}
static void ssb_mips_flash_detect(struct ssb_mipscore *mcore)
{
struct ssb_bus *bus = mcore->dev->bus;
struct ssb_pflash *pflash = &mcore->pflash;
/* When there is no chipcommon on the bus there is 4MB flash */
if (!ssb_chipco_available(&bus->chipco)) {
pflash->present = true;
pflash->buswidth = 2;
pflash->window = SSB_FLASH1;
pflash->window_size = SSB_FLASH1_SZ;
goto ssb_pflash;
}
/* There is ChipCommon, so use it to read info about flash */
switch (bus->chipco.capabilities & SSB_CHIPCO_CAP_FLASHT) {
case SSB_CHIPCO_FLASHT_STSER:
case SSB_CHIPCO_FLASHT_ATSER:
pr_debug("Found serial flash\n");
ssb_sflash_init(&bus->chipco);
break;
case SSB_CHIPCO_FLASHT_PARA:
pr_debug("Found parallel flash\n");
pflash->present = true;
pflash->window = SSB_FLASH2;
pflash->window_size = SSB_FLASH2_SZ;
if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG)
& SSB_CHIPCO_CFG_DS16) == 0)
pflash->buswidth = 1;
else
pflash->buswidth = 2;
break;
}
ssb_pflash:
if (pflash->present) {
ssb_pflash_data.width = pflash->buswidth;
ssb_pflash_resource.start = pflash->window;
ssb_pflash_resource.end = pflash->window + pflash->window_size;
}
}
u32 ssb_cpu_clock(struct ssb_mipscore *mcore)
{
struct ssb_bus *bus = mcore->dev->bus;
u32 pll_type, n, m, rate = 0;
if (bus->chipco.capabilities & SSB_CHIPCO_CAP_PMU)
return ssb_pmu_get_cpu_clock(&bus->chipco);
if (ssb_extif_available(&bus->extif)) {
ssb_extif_get_clockcontrol(&bus->extif, &pll_type, &n, &m);
} else if (ssb_chipco_available(&bus->chipco)) {
ssb_chipco_get_clockcpu(&bus->chipco, &pll_type, &n, &m);
} else
return 0;
if ((pll_type == SSB_PLLTYPE_5) || (bus->chip_id == 0x5365)) {
rate = 200000000;
} else {
rate = ssb_calc_clock_rate(pll_type, n, m);
}
if (pll_type == SSB_PLLTYPE_6) {
rate *= 2;
}
return rate;
}
void ssb_mipscore_init(struct ssb_mipscore *mcore)
{
struct ssb_bus *bus;
struct ssb_device *dev;
unsigned long hz, ns;
unsigned int irq, i;
if (!mcore->dev)
return; /* We don't have a MIPS core */
ssb_dbg("Initializing MIPS core...\n");
bus = mcore->dev->bus;
hz = ssb_clockspeed(bus);
if (!hz)
hz = 100000000;
ns = 1000000000 / hz;
if (ssb_extif_available(&bus->extif))
ssb_extif_timing_init(&bus->extif, ns);
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 */
for (irq = 2, i = 0; i < bus->nr_devices; i++) {
int mips_irq;
dev = &(bus->devices[i]);
mips_irq = ssb_mips_irq(dev);
if (mips_irq > 4)
dev->irq = 0;
else
dev->irq = mips_irq + 2;
if (dev->irq > 5)
continue;
switch (dev->id.coreid) {
case SSB_DEV_USB11_HOST:
/* shouldn't need a separate irq line for non-4710, most of them have a proper
* external usb controller on the pci */
if ((bus->chip_id == 0x4710) && (irq <= 4)) {
set_irq(dev, irq++);
}
break;
case SSB_DEV_PCI:
case SSB_DEV_ETHERNET:
case SSB_DEV_ETHERNET_GBIT:
case SSB_DEV_80211:
case SSB_DEV_USB20_HOST:
/* These devices get their own IRQ line if available, the rest goes on IRQ0 */
if (irq <= 4) {
set_irq(dev, irq++);
break;
}
/* fallthrough */
case SSB_DEV_EXTIF:
set_irq(dev, 0);
break;
}
}
ssb_dbg("after irq reconfiguration\n");
dump_irq(bus);
ssb_mips_serial_init(mcore);
ssb_mips_flash_detect(mcore);
}

View file

@ -0,0 +1,725 @@
/*
* Sonics Silicon Backplane
* Broadcom PCI-core driver
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/pci.h>
#include <linux/export.h>
#include <linux/delay.h>
#include <linux/ssb/ssb_embedded.h>
#include "ssb_private.h"
static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address);
static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data);
static u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address);
static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,
u8 address, u16 data);
static inline
u32 pcicore_read32(struct ssb_pcicore *pc, u16 offset)
{
return ssb_read32(pc->dev, offset);
}
static inline
void pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value)
{
ssb_write32(pc->dev, offset, value);
}
static inline
u16 pcicore_read16(struct ssb_pcicore *pc, u16 offset)
{
return ssb_read16(pc->dev, offset);
}
static inline
void pcicore_write16(struct ssb_pcicore *pc, u16 offset, u16 value)
{
ssb_write16(pc->dev, offset, value);
}
/**************************************************
* Code for hostmode operation.
**************************************************/
#ifdef CONFIG_SSB_PCICORE_HOSTMODE
#include <asm/paccess.h>
/* Probe a 32bit value on the bus and catch bus exceptions.
* Returns nonzero on a bus exception.
* This is MIPS specific */
#define mips_busprobe32(val, addr) get_dbe((val), ((u32 *)(addr)))
/* Assume one-hot slot wiring */
#define SSB_PCI_SLOT_MAX 16
/* Global lock is OK, as we won't have more than one extpci anyway. */
static DEFINE_SPINLOCK(cfgspace_lock);
/* Core to access the external PCI config space. Can only have one. */
static struct ssb_pcicore *extpci_core;
static u32 get_cfgspace_addr(struct ssb_pcicore *pc,
unsigned int bus, unsigned int dev,
unsigned int func, unsigned int off)
{
u32 addr = 0;
u32 tmp;
/* We do only have one cardbus device behind the bridge. */
if (pc->cardbusmode && (dev > 1))
goto out;
if (bus == 0) {
/* Type 0 transaction */
if (unlikely(dev >= SSB_PCI_SLOT_MAX))
goto out;
/* Slide the window */
tmp = SSB_PCICORE_SBTOPCI_CFG0;
tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK);
pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp);
/* Calculate the address */
addr = SSB_PCI_CFG;
addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK);
addr |= (func << 8);
addr |= (off & ~3);
} else {
/* Type 1 transaction */
pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
SSB_PCICORE_SBTOPCI_CFG1);
/* Calculate the address */
addr = SSB_PCI_CFG;
addr |= (bus << 16);
addr |= (dev << 11);
addr |= (func << 8);
addr |= (off & ~3);
}
out:
return addr;
}
static int ssb_extpci_read_config(struct ssb_pcicore *pc,
unsigned int bus, unsigned int dev,
unsigned int func, unsigned int off,
void *buf, int len)
{
int err = -EINVAL;
u32 addr, val;
void __iomem *mmio;
SSB_WARN_ON(!pc->hostmode);
if (unlikely(len != 1 && len != 2 && len != 4))
goto out;
addr = get_cfgspace_addr(pc, bus, dev, func, off);
if (unlikely(!addr))
goto out;
err = -ENOMEM;
mmio = ioremap_nocache(addr, len);
if (!mmio)
goto out;
if (mips_busprobe32(val, mmio)) {
val = 0xffffffff;
goto unmap;
}
val = readl(mmio);
val >>= (8 * (off & 3));
switch (len) {
case 1:
*((u8 *)buf) = (u8)val;
break;
case 2:
*((u16 *)buf) = (u16)val;
break;
case 4:
*((u32 *)buf) = (u32)val;
break;
}
err = 0;
unmap:
iounmap(mmio);
out:
return err;
}
static int ssb_extpci_write_config(struct ssb_pcicore *pc,
unsigned int bus, unsigned int dev,
unsigned int func, unsigned int off,
const void *buf, int len)
{
int err = -EINVAL;
u32 addr, val = 0;
void __iomem *mmio;
SSB_WARN_ON(!pc->hostmode);
if (unlikely(len != 1 && len != 2 && len != 4))
goto out;
addr = get_cfgspace_addr(pc, bus, dev, func, off);
if (unlikely(!addr))
goto out;
err = -ENOMEM;
mmio = ioremap_nocache(addr, len);
if (!mmio)
goto out;
if (mips_busprobe32(val, mmio)) {
val = 0xffffffff;
goto unmap;
}
switch (len) {
case 1:
val = readl(mmio);
val &= ~(0xFF << (8 * (off & 3)));
val |= *((const u8 *)buf) << (8 * (off & 3));
break;
case 2:
val = readl(mmio);
val &= ~(0xFFFF << (8 * (off & 3)));
val |= *((const u16 *)buf) << (8 * (off & 3));
break;
case 4:
val = *((const u32 *)buf);
break;
}
writel(val, mmio);
err = 0;
unmap:
iounmap(mmio);
out:
return err;
}
static int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 *val)
{
unsigned long flags;
int err;
spin_lock_irqsave(&cfgspace_lock, flags);
err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), reg, val, size);
spin_unlock_irqrestore(&cfgspace_lock, flags);
return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
}
static int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn,
int reg, int size, u32 val)
{
unsigned long flags;
int err;
spin_lock_irqsave(&cfgspace_lock, flags);
err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn), reg, &val, size);
spin_unlock_irqrestore(&cfgspace_lock, flags);
return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL;
}
static struct pci_ops ssb_pcicore_pciops = {
.read = ssb_pcicore_read_config,
.write = ssb_pcicore_write_config,
};
static struct resource ssb_pcicore_mem_resource = {
.name = "SSB PCIcore external memory",
.start = SSB_PCI_DMA,
.end = SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1,
.flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED,
};
static struct resource ssb_pcicore_io_resource = {
.name = "SSB PCIcore external I/O",
.start = 0x100,
.end = 0x7FF,
.flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED,
};
static struct pci_controller ssb_pcicore_controller = {
.pci_ops = &ssb_pcicore_pciops,
.io_resource = &ssb_pcicore_io_resource,
.mem_resource = &ssb_pcicore_mem_resource,
};
/* This function is called when doing a pci_enable_device().
* We must first check if the device is a device on the PCI-core bridge. */
int ssb_pcicore_plat_dev_init(struct pci_dev *d)
{
if (d->bus->ops != &ssb_pcicore_pciops) {
/* This is not a device on the PCI-core bridge. */
return -ENODEV;
}
ssb_info("PCI: Fixing up device %s\n", pci_name(d));
/* Fix up interrupt lines */
d->irq = ssb_mips_irq(extpci_core->dev) + 2;
pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq);
return 0;
}
/* Early PCI fixup for a device on the PCI-core bridge. */
static void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev)
{
u8 lat;
if (dev->bus->ops != &ssb_pcicore_pciops) {
/* This is not a device on the PCI-core bridge. */
return;
}
if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0)
return;
ssb_info("PCI: Fixing up bridge %s\n", pci_name(dev));
/* Enable PCI bridge bus mastering and memory space */
pci_set_master(dev);
if (pcibios_enable_device(dev, ~0) < 0) {
ssb_err("PCI: SSB bridge enable failed\n");
return;
}
/* Enable PCI bridge BAR1 prefetch and burst */
pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3);
/* Make sure our latency is high enough to handle the devices behind us */
lat = 168;
ssb_info("PCI: Fixing latency timer of device %s to %u\n",
pci_name(dev), lat);
pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
}
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge);
/* PCI device IRQ mapping. */
int ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
if (dev->bus->ops != &ssb_pcicore_pciops) {
/* This is not a device on the PCI-core bridge. */
return -ENODEV;
}
return ssb_mips_irq(extpci_core->dev) + 2;
}
static void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc)
{
u32 val;
if (WARN_ON(extpci_core))
return;
extpci_core = pc;
ssb_dbg("PCIcore in host mode found\n");
/* Reset devices on the external PCI bus */
val = SSB_PCICORE_CTL_RST_OE;
val |= SSB_PCICORE_CTL_CLK_OE;
pcicore_write32(pc, SSB_PCICORE_CTL, val);
val |= SSB_PCICORE_CTL_CLK; /* Clock on */
pcicore_write32(pc, SSB_PCICORE_CTL, val);
udelay(150); /* Assertion time demanded by the PCI standard */
val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */
pcicore_write32(pc, SSB_PCICORE_CTL, val);
val = SSB_PCICORE_ARBCTL_INTERN;
pcicore_write32(pc, SSB_PCICORE_ARBCTL, val);
udelay(1); /* Assertion time demanded by the PCI standard */
if (pc->dev->bus->has_cardbus_slot) {
ssb_dbg("CardBus slot detected\n");
pc->cardbusmode = 1;
/* GPIO 1 resets the bridge */
ssb_gpio_out(pc->dev->bus, 1, 1);
ssb_gpio_outen(pc->dev->bus, 1, 1);
pcicore_write16(pc, SSB_PCICORE_SPROM(0),
pcicore_read16(pc, SSB_PCICORE_SPROM(0))
| 0x0400);
}
/* 64MB I/O window */
pcicore_write32(pc, SSB_PCICORE_SBTOPCI0,
SSB_PCICORE_SBTOPCI_IO);
/* 64MB config space */
pcicore_write32(pc, SSB_PCICORE_SBTOPCI1,
SSB_PCICORE_SBTOPCI_CFG0);
/* 1GB memory window */
pcicore_write32(pc, SSB_PCICORE_SBTOPCI2,
SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA);
/* Enable PCI bridge BAR0 prefetch and burst */
val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2);
/* Clear error conditions */
val = 0;
ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2);
/* Enable PCI interrupts */
pcicore_write32(pc, SSB_PCICORE_IMASK,
SSB_PCICORE_IMASK_INTA);
/* Ok, ready to run, register it to the system.
* The following needs change, if we want to port hostmode
* to non-MIPS platform. */
ssb_pcicore_controller.io_map_base = (unsigned long)ioremap_nocache(SSB_PCI_MEM, 0x04000000);
set_io_port_base(ssb_pcicore_controller.io_map_base);
/* Give some time to the PCI controller to configure itself with the new
* values. Not waiting at this point causes crashes of the machine. */
mdelay(10);
register_pci_controller(&ssb_pcicore_controller);
}
static int pcicore_is_in_hostmode(struct ssb_pcicore *pc)
{
struct ssb_bus *bus = pc->dev->bus;
u16 chipid_top;
u32 tmp;
chipid_top = (bus->chip_id & 0xFF00);
if (chipid_top != 0x4700 &&
chipid_top != 0x5300)
return 0;
if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI)
return 0;
/* The 200-pin BCM4712 package does not bond out PCI. Even when
* PCI is bonded out, some boards may leave the pins floating. */
if (bus->chip_id == 0x4712) {
if (bus->chip_package == SSB_CHIPPACK_BCM4712S)
return 0;
if (bus->chip_package == SSB_CHIPPACK_BCM4712M)
return 0;
}
if (bus->chip_id == 0x5350)
return 0;
return !mips_busprobe32(tmp, (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE)));
}
#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
/**************************************************
* Workarounds.
**************************************************/
static void ssb_pcicore_fix_sprom_core_index(struct ssb_pcicore *pc)
{
u16 tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(0));
if (((tmp & 0xF000) >> 12) != pc->dev->core_index) {
tmp &= ~0xF000;
tmp |= (pc->dev->core_index << 12);
pcicore_write16(pc, SSB_PCICORE_SPROM(0), tmp);
}
}
static u8 ssb_pcicore_polarity_workaround(struct ssb_pcicore *pc)
{
return (ssb_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80;
}
static void ssb_pcicore_serdes_workaround(struct ssb_pcicore *pc)
{
const u8 serdes_pll_device = 0x1D;
const u8 serdes_rx_device = 0x1F;
u16 tmp;
ssb_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */,
ssb_pcicore_polarity_workaround(pc));
tmp = ssb_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */);
if (tmp & 0x4000)
ssb_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000);
}
static void ssb_pcicore_pci_setup_workarounds(struct ssb_pcicore *pc)
{
struct ssb_device *pdev = pc->dev;
struct ssb_bus *bus = pdev->bus;
u32 tmp;
tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
tmp |= SSB_PCICORE_SBTOPCI_PREF;
tmp |= SSB_PCICORE_SBTOPCI_BURST;
pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
if (pdev->id.revision < 5) {
tmp = ssb_read32(pdev, SSB_IMCFGLO);
tmp &= ~SSB_IMCFGLO_SERTO;
tmp |= 2;
tmp &= ~SSB_IMCFGLO_REQTO;
tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT;
ssb_write32(pdev, SSB_IMCFGLO, tmp);
ssb_commit_settings(bus);
} else if (pdev->id.revision >= 11) {
tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2);
tmp |= SSB_PCICORE_SBTOPCI_MRM;
pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp);
}
}
static void ssb_pcicore_pcie_setup_workarounds(struct ssb_pcicore *pc)
{
u32 tmp;
u8 rev = pc->dev->id.revision;
if (rev == 0 || rev == 1) {
/* TLP Workaround register. */
tmp = ssb_pcie_read(pc, 0x4);
tmp |= 0x8;
ssb_pcie_write(pc, 0x4, tmp);
}
if (rev == 1) {
/* DLLP Link Control register. */
tmp = ssb_pcie_read(pc, 0x100);
tmp |= 0x40;
ssb_pcie_write(pc, 0x100, tmp);
}
if (rev == 0) {
const u8 serdes_rx_device = 0x1F;
ssb_pcie_mdio_write(pc, serdes_rx_device,
2 /* Timer */, 0x8128);
ssb_pcie_mdio_write(pc, serdes_rx_device,
6 /* CDR */, 0x0100);
ssb_pcie_mdio_write(pc, serdes_rx_device,
7 /* CDR BW */, 0x1466);
} else if (rev == 3 || rev == 4 || rev == 5) {
/* TODO: DLLP Power Management Threshold */
ssb_pcicore_serdes_workaround(pc);
/* TODO: ASPM */
} else if (rev == 7) {
/* TODO: No PLL down */
}
if (rev >= 6) {
/* Miscellaneous Configuration Fixup */
tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(5));
if (!(tmp & 0x8000))
pcicore_write16(pc, SSB_PCICORE_SPROM(5),
tmp | 0x8000);
}
}
/**************************************************
* Generic and Clientmode operation code.
**************************************************/
static void ssb_pcicore_init_clientmode(struct ssb_pcicore *pc)
{
struct ssb_device *pdev = pc->dev;
struct ssb_bus *bus = pdev->bus;
if (bus->bustype == SSB_BUSTYPE_PCI)
ssb_pcicore_fix_sprom_core_index(pc);
/* Disable PCI interrupts. */
ssb_write32(pdev, SSB_INTVEC, 0);
/* Additional PCIe always once-executed workarounds */
if (pc->dev->id.coreid == SSB_DEV_PCIE) {
ssb_pcicore_serdes_workaround(pc);
/* TODO: ASPM */
/* TODO: Clock Request Update */
}
}
void ssb_pcicore_init(struct ssb_pcicore *pc)
{
struct ssb_device *dev = pc->dev;
if (!dev)
return;
if (!ssb_device_is_enabled(dev))
ssb_device_enable(dev, 0);
#ifdef CONFIG_SSB_PCICORE_HOSTMODE
pc->hostmode = pcicore_is_in_hostmode(pc);
if (pc->hostmode)
ssb_pcicore_init_hostmode(pc);
#endif /* CONFIG_SSB_PCICORE_HOSTMODE */
if (!pc->hostmode)
ssb_pcicore_init_clientmode(pc);
}
static u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address)
{
pcicore_write32(pc, 0x130, address);
return pcicore_read32(pc, 0x134);
}
static void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data)
{
pcicore_write32(pc, 0x130, address);
pcicore_write32(pc, 0x134, data);
}
static void ssb_pcie_mdio_set_phy(struct ssb_pcicore *pc, u8 phy)
{
const u16 mdio_control = 0x128;
const u16 mdio_data = 0x12C;
u32 v;
int i;
v = (1 << 30); /* Start of Transaction */
v |= (1 << 28); /* Write Transaction */
v |= (1 << 17); /* Turnaround */
v |= (0x1F << 18);
v |= (phy << 4);
pcicore_write32(pc, mdio_data, v);
udelay(10);
for (i = 0; i < 200; i++) {
v = pcicore_read32(pc, mdio_control);
if (v & 0x100 /* Trans complete */)
break;
msleep(1);
}
}
static u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address)
{
const u16 mdio_control = 0x128;
const u16 mdio_data = 0x12C;
int max_retries = 10;
u16 ret = 0;
u32 v;
int i;
v = 0x80; /* Enable Preamble Sequence */
v |= 0x2; /* MDIO Clock Divisor */
pcicore_write32(pc, mdio_control, v);
if (pc->dev->id.revision >= 10) {
max_retries = 200;
ssb_pcie_mdio_set_phy(pc, device);
}
v = (1 << 30); /* Start of Transaction */
v |= (1 << 29); /* Read Transaction */
v |= (1 << 17); /* Turnaround */
if (pc->dev->id.revision < 10)
v |= (u32)device << 22;
v |= (u32)address << 18;
pcicore_write32(pc, mdio_data, v);
/* Wait for the device to complete the transaction */
udelay(10);
for (i = 0; i < max_retries; i++) {
v = pcicore_read32(pc, mdio_control);
if (v & 0x100 /* Trans complete */) {
udelay(10);
ret = pcicore_read32(pc, mdio_data);
break;
}
msleep(1);
}
pcicore_write32(pc, mdio_control, 0);
return ret;
}
static void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device,
u8 address, u16 data)
{
const u16 mdio_control = 0x128;
const u16 mdio_data = 0x12C;
int max_retries = 10;
u32 v;
int i;
v = 0x80; /* Enable Preamble Sequence */
v |= 0x2; /* MDIO Clock Divisor */
pcicore_write32(pc, mdio_control, v);
if (pc->dev->id.revision >= 10) {
max_retries = 200;
ssb_pcie_mdio_set_phy(pc, device);
}
v = (1 << 30); /* Start of Transaction */
v |= (1 << 28); /* Write Transaction */
v |= (1 << 17); /* Turnaround */
if (pc->dev->id.revision < 10)
v |= (u32)device << 22;
v |= (u32)address << 18;
v |= data;
pcicore_write32(pc, mdio_data, v);
/* Wait for the device to complete the transaction */
udelay(10);
for (i = 0; i < max_retries; i++) {
v = pcicore_read32(pc, mdio_control);
if (v & 0x100 /* Trans complete */)
break;
msleep(1);
}
pcicore_write32(pc, mdio_control, 0);
}
int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
struct ssb_device *dev)
{
struct ssb_device *pdev = pc->dev;
struct ssb_bus *bus;
int err = 0;
u32 tmp;
if (dev->bus->bustype != SSB_BUSTYPE_PCI) {
/* This SSB device is not on a PCI host-bus. So the IRQs are
* not routed through the PCI core.
* So we must not enable routing through the PCI core. */
goto out;
}
if (!pdev)
goto out;
bus = pdev->bus;
might_sleep_if(pdev->id.coreid != SSB_DEV_PCI);
/* Enable interrupts for this device. */
if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) {
u32 coremask;
/* Calculate the "coremask" for the device. */
coremask = (1 << dev->core_index);
SSB_WARN_ON(bus->bustype != SSB_BUSTYPE_PCI);
err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp);
if (err)
goto out;
tmp |= coremask << 8;
err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp);
if (err)
goto out;
} else {
u32 intvec;
intvec = ssb_read32(pdev, SSB_INTVEC);
tmp = ssb_read32(dev, SSB_TPSFLAG);
tmp &= SSB_TPSFLAG_BPFLAG;
intvec |= (1 << tmp);
ssb_write32(pdev, SSB_INTVEC, intvec);
}
/* Setup PCIcore operation. */
if (pc->setup_done)
goto out;
if (pdev->id.coreid == SSB_DEV_PCI) {
ssb_pcicore_pci_setup_workarounds(pc);
} else {
WARN_ON(pdev->id.coreid != SSB_DEV_PCIE);
ssb_pcicore_pcie_setup_workarounds(pc);
}
pc->setup_done = 1;
out:
return err;
}
EXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable);

258
drivers/ssb/embedded.c Normal file
View file

@ -0,0 +1,258 @@
/*
* Sonics Silicon Backplane
* Embedded systems support code
*
* Copyright 2005-2008, Broadcom Corporation
* Copyright 2006-2008, Michael Buesch <m@bues.ch>
* Copyright 2012, Hauke Mehrtens <hauke@hauke-m.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/export.h>
#include <linux/platform_device.h>
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_embedded.h>
#include <linux/ssb/ssb_driver_pci.h>
#include <linux/ssb/ssb_driver_gige.h>
#include <linux/pci.h>
#include "ssb_private.h"
int ssb_watchdog_timer_set(struct ssb_bus *bus, u32 ticks)
{
if (ssb_chipco_available(&bus->chipco)) {
ssb_chipco_watchdog_timer_set(&bus->chipco, ticks);
return 0;
}
if (ssb_extif_available(&bus->extif)) {
ssb_extif_watchdog_timer_set(&bus->extif, ticks);
return 0;
}
return -ENODEV;
}
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_dbg("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;
u32 res = 0;
spin_lock_irqsave(&bus->gpio_lock, flags);
if (ssb_chipco_available(&bus->chipco))
res = ssb_chipco_gpio_in(&bus->chipco, mask);
else if (ssb_extif_available(&bus->extif))
res = ssb_extif_gpio_in(&bus->extif, mask);
else
SSB_WARN_ON(1);
spin_unlock_irqrestore(&bus->gpio_lock, flags);
return res;
}
EXPORT_SYMBOL(ssb_gpio_in);
u32 ssb_gpio_out(struct ssb_bus *bus, u32 mask, u32 value)
{
unsigned long flags;
u32 res = 0;
spin_lock_irqsave(&bus->gpio_lock, flags);
if (ssb_chipco_available(&bus->chipco))
res = ssb_chipco_gpio_out(&bus->chipco, mask, value);
else if (ssb_extif_available(&bus->extif))
res = ssb_extif_gpio_out(&bus->extif, mask, value);
else
SSB_WARN_ON(1);
spin_unlock_irqrestore(&bus->gpio_lock, flags);
return res;
}
EXPORT_SYMBOL(ssb_gpio_out);
u32 ssb_gpio_outen(struct ssb_bus *bus, u32 mask, u32 value)
{
unsigned long flags;
u32 res = 0;
spin_lock_irqsave(&bus->gpio_lock, flags);
if (ssb_chipco_available(&bus->chipco))
res = ssb_chipco_gpio_outen(&bus->chipco, mask, value);
else if (ssb_extif_available(&bus->extif))
res = ssb_extif_gpio_outen(&bus->extif, mask, value);
else
SSB_WARN_ON(1);
spin_unlock_irqrestore(&bus->gpio_lock, flags);
return res;
}
EXPORT_SYMBOL(ssb_gpio_outen);
u32 ssb_gpio_control(struct ssb_bus *bus, u32 mask, u32 value)
{
unsigned long flags;
u32 res = 0;
spin_lock_irqsave(&bus->gpio_lock, flags);
if (ssb_chipco_available(&bus->chipco))
res = ssb_chipco_gpio_control(&bus->chipco, mask, value);
spin_unlock_irqrestore(&bus->gpio_lock, flags);
return res;
}
EXPORT_SYMBOL(ssb_gpio_control);
u32 ssb_gpio_intmask(struct ssb_bus *bus, u32 mask, u32 value)
{
unsigned long flags;
u32 res = 0;
spin_lock_irqsave(&bus->gpio_lock, flags);
if (ssb_chipco_available(&bus->chipco))
res = ssb_chipco_gpio_intmask(&bus->chipco, mask, value);
else if (ssb_extif_available(&bus->extif))
res = ssb_extif_gpio_intmask(&bus->extif, mask, value);
else
SSB_WARN_ON(1);
spin_unlock_irqrestore(&bus->gpio_lock, flags);
return res;
}
EXPORT_SYMBOL(ssb_gpio_intmask);
u32 ssb_gpio_polarity(struct ssb_bus *bus, u32 mask, u32 value)
{
unsigned long flags;
u32 res = 0;
spin_lock_irqsave(&bus->gpio_lock, flags);
if (ssb_chipco_available(&bus->chipco))
res = ssb_chipco_gpio_polarity(&bus->chipco, mask, value);
else if (ssb_extif_available(&bus->extif))
res = ssb_extif_gpio_polarity(&bus->extif, mask, value);
else
SSB_WARN_ON(1);
spin_unlock_irqrestore(&bus->gpio_lock, flags);
return res;
}
EXPORT_SYMBOL(ssb_gpio_polarity);
#ifdef CONFIG_SSB_DRIVER_GIGE
static int gige_pci_init_callback(struct ssb_bus *bus, unsigned long data)
{
struct pci_dev *pdev = (struct pci_dev *)data;
struct ssb_device *dev;
unsigned int i;
int res;
for (i = 0; i < bus->nr_devices; i++) {
dev = &(bus->devices[i]);
if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
continue;
if (!dev->dev ||
!dev->dev->driver ||
!device_is_registered(dev->dev))
continue;
res = ssb_gige_pcibios_plat_dev_init(dev, pdev);
if (res >= 0)
return res;
}
return -ENODEV;
}
#endif /* CONFIG_SSB_DRIVER_GIGE */
int ssb_pcibios_plat_dev_init(struct pci_dev *dev)
{
int err;
err = ssb_pcicore_plat_dev_init(dev);
if (!err)
return 0;
#ifdef CONFIG_SSB_DRIVER_GIGE
err = ssb_for_each_bus_call((unsigned long)dev, gige_pci_init_callback);
if (err >= 0)
return err;
#endif
/* This is not a PCI device on any SSB device. */
return -ENODEV;
}
#ifdef CONFIG_SSB_DRIVER_GIGE
static int gige_map_irq_callback(struct ssb_bus *bus, unsigned long data)
{
const struct pci_dev *pdev = (const struct pci_dev *)data;
struct ssb_device *dev;
unsigned int i;
int res;
for (i = 0; i < bus->nr_devices; i++) {
dev = &(bus->devices[i]);
if (dev->id.coreid != SSB_DEV_ETHERNET_GBIT)
continue;
if (!dev->dev ||
!dev->dev->driver ||
!device_is_registered(dev->dev))
continue;
res = ssb_gige_map_irq(dev, pdev);
if (res >= 0)
return res;
}
return -ENODEV;
}
#endif /* CONFIG_SSB_DRIVER_GIGE */
int ssb_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
{
int res;
/* Check if this PCI device is a device on a SSB bus or device
* and return the IRQ number for it. */
res = ssb_pcicore_pcibios_map_irq(dev, slot, pin);
if (res >= 0)
return res;
#ifdef CONFIG_SSB_DRIVER_GIGE
res = ssb_for_each_bus_call((unsigned long)dev, gige_map_irq_callback);
if (res >= 0)
return res;
#endif
/* This is not a PCI device on any SSB device. */
return -ENODEV;
}

1505
drivers/ssb/main.c Normal file

File diff suppressed because it is too large Load diff

1189
drivers/ssb/pci.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,123 @@
/*
* Sonics Silicon Backplane
* PCI Hostdevice wrapper
*
* Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>
* Copyright (c) 2005 Stefano Brivio <st3@riseup.net>
* Copyright (c) 2005 Danny van Dyk <kugelfang@gentoo.org>
* Copyright (c) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
* Copyright (c) 2005-2007 Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/pci.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/ssb/ssb.h>
#ifdef CONFIG_PM
static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
{
struct ssb_bus *ssb = pci_get_drvdata(dev);
int err;
err = ssb_bus_suspend(ssb);
if (err)
return err;
pci_save_state(dev);
pci_disable_device(dev);
pci_set_power_state(dev, pci_choose_state(dev, state));
return 0;
}
static int ssb_pcihost_resume(struct pci_dev *dev)
{
struct ssb_bus *ssb = pci_get_drvdata(dev);
int err;
pci_set_power_state(dev, PCI_D0);
err = pci_enable_device(dev);
if (err)
return err;
pci_restore_state(dev);
err = ssb_bus_resume(ssb);
if (err)
return err;
return 0;
}
#else /* CONFIG_PM */
# define ssb_pcihost_suspend NULL
# define ssb_pcihost_resume NULL
#endif /* CONFIG_PM */
static int ssb_pcihost_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
struct ssb_bus *ssb;
int err = -ENOMEM;
const char *name;
u32 val;
ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
if (!ssb)
goto out;
err = pci_enable_device(dev);
if (err)
goto err_kfree_ssb;
name = dev_name(&dev->dev);
if (dev->driver && dev->driver->name)
name = dev->driver->name;
err = pci_request_regions(dev, name);
if (err)
goto err_pci_disable;
pci_set_master(dev);
/* Disable the RETRY_TIMEOUT register (0x41) to keep
* PCI Tx retries from interfering with C3 CPU state */
pci_read_config_dword(dev, 0x40, &val);
if ((val & 0x0000ff00) != 0)
pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
err = ssb_bus_pcibus_register(ssb, dev);
if (err)
goto err_pci_release_regions;
pci_set_drvdata(dev, ssb);
out:
return err;
err_pci_release_regions:
pci_release_regions(dev);
err_pci_disable:
pci_disable_device(dev);
err_kfree_ssb:
kfree(ssb);
return err;
}
static void ssb_pcihost_remove(struct pci_dev *dev)
{
struct ssb_bus *ssb = pci_get_drvdata(dev);
ssb_bus_unregister(ssb);
pci_release_regions(dev);
pci_disable_device(dev);
kfree(ssb);
pci_set_drvdata(dev, NULL);
}
int ssb_pcihost_register(struct pci_driver *driver)
{
driver->probe = ssb_pcihost_probe;
driver->remove = ssb_pcihost_remove;
driver->suspend = ssb_pcihost_suspend;
driver->resume = ssb_pcihost_resume;
return pci_register_driver(driver);
}
EXPORT_SYMBOL(ssb_pcihost_register);

842
drivers/ssb/pcmcia.c Normal file
View file

@ -0,0 +1,842 @@
/*
* Sonics Silicon Backplane
* PCMCIA-Hostbus related functions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2007-2008 Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/etherdevice.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ciscode.h>
#include <pcmcia/ds.h>
#include <pcmcia/cisreg.h>
#include "ssb_private.h"
/* Define the following to 1 to enable a printk on each coreswitch. */
#define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0
/* PCMCIA configuration registers */
#define SSB_PCMCIA_ADDRESS0 0x2E
#define SSB_PCMCIA_ADDRESS1 0x30
#define SSB_PCMCIA_ADDRESS2 0x32
#define SSB_PCMCIA_MEMSEG 0x34
#define SSB_PCMCIA_SPROMCTL 0x36
#define SSB_PCMCIA_SPROMCTL_IDLE 0
#define SSB_PCMCIA_SPROMCTL_WRITE 1
#define SSB_PCMCIA_SPROMCTL_READ 2
#define SSB_PCMCIA_SPROMCTL_WRITEEN 4
#define SSB_PCMCIA_SPROMCTL_WRITEDIS 7
#define SSB_PCMCIA_SPROMCTL_DONE 8
#define SSB_PCMCIA_SPROM_DATALO 0x38
#define SSB_PCMCIA_SPROM_DATAHI 0x3A
#define SSB_PCMCIA_SPROM_ADDRLO 0x3C
#define SSB_PCMCIA_SPROM_ADDRHI 0x3E
/* Hardware invariants CIS tuples */
#define SSB_PCMCIA_CIS 0x80
#define SSB_PCMCIA_CIS_ID 0x01
#define SSB_PCMCIA_CIS_BOARDREV 0x02
#define SSB_PCMCIA_CIS_PA 0x03
#define SSB_PCMCIA_CIS_PA_PA0B0_LO 0
#define SSB_PCMCIA_CIS_PA_PA0B0_HI 1
#define SSB_PCMCIA_CIS_PA_PA0B1_LO 2
#define SSB_PCMCIA_CIS_PA_PA0B1_HI 3
#define SSB_PCMCIA_CIS_PA_PA0B2_LO 4
#define SSB_PCMCIA_CIS_PA_PA0B2_HI 5
#define SSB_PCMCIA_CIS_PA_ITSSI 6
#define SSB_PCMCIA_CIS_PA_MAXPOW 7
#define SSB_PCMCIA_CIS_OEMNAME 0x04
#define SSB_PCMCIA_CIS_CCODE 0x05
#define SSB_PCMCIA_CIS_ANTENNA 0x06
#define SSB_PCMCIA_CIS_ANTGAIN 0x07
#define SSB_PCMCIA_CIS_BFLAGS 0x08
#define SSB_PCMCIA_CIS_LEDS 0x09
/* PCMCIA SPROM size. */
#define SSB_PCMCIA_SPROM_SIZE 256
#define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16))
/* Write to a PCMCIA configuration register. */
static int ssb_pcmcia_cfg_write(struct ssb_bus *bus, u8 offset, u8 value)
{
int res;
res = pcmcia_write_config_byte(bus->host_pcmcia, offset, value);
if (unlikely(res != 0))
return -EBUSY;
return 0;
}
/* Read from a PCMCIA configuration register. */
static int ssb_pcmcia_cfg_read(struct ssb_bus *bus, u8 offset, u8 *value)
{
int res;
res = pcmcia_read_config_byte(bus->host_pcmcia, offset, value);
if (unlikely(res != 0))
return -EBUSY;
return 0;
}
int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
u8 coreidx)
{
int err;
int attempts = 0;
u32 cur_core;
u32 addr;
u32 read_addr;
u8 val;
addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
while (1) {
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS0,
(addr & 0x0000F000) >> 12);
if (err)
goto error;
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS1,
(addr & 0x00FF0000) >> 16);
if (err)
goto error;
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS2,
(addr & 0xFF000000) >> 24);
if (err)
goto error;
read_addr = 0;
err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS0, &val);
if (err)
goto error;
read_addr |= ((u32)(val & 0x0F)) << 12;
err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS1, &val);
if (err)
goto error;
read_addr |= ((u32)val) << 16;
err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS2, &val);
if (err)
goto error;
read_addr |= ((u32)val) << 24;
cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE;
if (cur_core == coreidx)
break;
err = -ETIMEDOUT;
if (attempts++ > SSB_BAR0_MAX_RETRIES)
goto error;
udelay(10);
}
return 0;
error:
ssb_err("Failed to switch to core %u\n", coreidx);
return err;
}
int ssb_pcmcia_switch_core(struct ssb_bus *bus,
struct ssb_device *dev)
{
int err;
#if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
ssb_info("Switching to %s core, index %d\n",
ssb_core_name(dev->id.coreid),
dev->core_index);
#endif
err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
if (!err)
bus->mapped_device = dev;
return err;
}
int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
{
int attempts = 0;
int err;
u8 val;
SSB_WARN_ON((seg != 0) && (seg != 1));
while (1) {
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_MEMSEG, seg);
if (err)
goto error;
err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_MEMSEG, &val);
if (err)
goto error;
if (val == seg)
break;
err = -ETIMEDOUT;
if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES))
goto error;
udelay(10);
}
bus->mapped_pcmcia_seg = seg;
return 0;
error:
ssb_err("Failed to switch pcmcia segment\n");
return err;
}
static int select_core_and_segment(struct ssb_device *dev,
u16 *offset)
{
struct ssb_bus *bus = dev->bus;
int err;
u8 need_segment;
if (*offset >= 0x800) {
*offset -= 0x800;
need_segment = 1;
} else
need_segment = 0;
if (unlikely(dev != bus->mapped_device)) {
err = ssb_pcmcia_switch_core(bus, dev);
if (unlikely(err))
return err;
}
if (unlikely(need_segment != bus->mapped_pcmcia_seg)) {
err = ssb_pcmcia_switch_segment(bus, need_segment);
if (unlikely(err))
return err;
}
return 0;
}
static u8 ssb_pcmcia_read8(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
u8 value = 0xFF;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (likely(!err))
value = readb(bus->mmio + offset);
spin_unlock_irqrestore(&bus->bar_lock, flags);
return value;
}
static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
u16 value = 0xFFFF;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (likely(!err))
value = readw(bus->mmio + offset);
spin_unlock_irqrestore(&bus->bar_lock, flags);
return value;
}
static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (likely(!err)) {
lo = readw(bus->mmio + offset);
hi = readw(bus->mmio + offset + 2);
}
spin_unlock_irqrestore(&bus->bar_lock, flags);
return (lo | (hi << 16));
}
#ifdef CONFIG_SSB_BLOCKIO
static void ssb_pcmcia_block_read(struct ssb_device *dev, void *buffer,
size_t count, u16 offset, u8 reg_width)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
void __iomem *addr = bus->mmio + offset;
int err;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (unlikely(err)) {
memset(buffer, 0xFF, count);
goto unlock;
}
switch (reg_width) {
case sizeof(u8): {
u8 *buf = buffer;
while (count) {
*buf = __raw_readb(addr);
buf++;
count--;
}
break;
}
case sizeof(u16): {
__le16 *buf = buffer;
SSB_WARN_ON(count & 1);
while (count) {
*buf = (__force __le16)__raw_readw(addr);
buf++;
count -= 2;
}
break;
}
case sizeof(u32): {
__le16 *buf = buffer;
SSB_WARN_ON(count & 3);
while (count) {
*buf = (__force __le16)__raw_readw(addr);
buf++;
*buf = (__force __le16)__raw_readw(addr + 2);
buf++;
count -= 4;
}
break;
}
default:
SSB_WARN_ON(1);
}
unlock:
spin_unlock_irqrestore(&bus->bar_lock, flags);
}
#endif /* CONFIG_SSB_BLOCKIO */
static void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (likely(!err))
writeb(value, bus->mmio + offset);
mmiowb();
spin_unlock_irqrestore(&bus->bar_lock, flags);
}
static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (likely(!err))
writew(value, bus->mmio + offset);
mmiowb();
spin_unlock_irqrestore(&bus->bar_lock, flags);
}
static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
int err;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (likely(!err)) {
writew((value & 0x0000FFFF), bus->mmio + offset);
writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2);
}
mmiowb();
spin_unlock_irqrestore(&bus->bar_lock, flags);
}
#ifdef CONFIG_SSB_BLOCKIO
static void ssb_pcmcia_block_write(struct ssb_device *dev, const void *buffer,
size_t count, u16 offset, u8 reg_width)
{
struct ssb_bus *bus = dev->bus;
unsigned long flags;
void __iomem *addr = bus->mmio + offset;
int err;
spin_lock_irqsave(&bus->bar_lock, flags);
err = select_core_and_segment(dev, &offset);
if (unlikely(err))
goto unlock;
switch (reg_width) {
case sizeof(u8): {
const u8 *buf = buffer;
while (count) {
__raw_writeb(*buf, addr);
buf++;
count--;
}
break;
}
case sizeof(u16): {
const __le16 *buf = buffer;
SSB_WARN_ON(count & 1);
while (count) {
__raw_writew((__force u16)(*buf), addr);
buf++;
count -= 2;
}
break;
}
case sizeof(u32): {
const __le16 *buf = buffer;
SSB_WARN_ON(count & 3);
while (count) {
__raw_writew((__force u16)(*buf), addr);
buf++;
__raw_writew((__force u16)(*buf), addr + 2);
buf++;
count -= 4;
}
break;
}
default:
SSB_WARN_ON(1);
}
unlock:
mmiowb();
spin_unlock_irqrestore(&bus->bar_lock, flags);
}
#endif /* CONFIG_SSB_BLOCKIO */
/* Not "static", as it's used in main.c */
const struct ssb_bus_ops ssb_pcmcia_ops = {
.read8 = ssb_pcmcia_read8,
.read16 = ssb_pcmcia_read16,
.read32 = ssb_pcmcia_read32,
.write8 = ssb_pcmcia_write8,
.write16 = ssb_pcmcia_write16,
.write32 = ssb_pcmcia_write32,
#ifdef CONFIG_SSB_BLOCKIO
.block_read = ssb_pcmcia_block_read,
.block_write = ssb_pcmcia_block_write,
#endif
};
static int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command)
{
unsigned int i;
int err;
u8 value;
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROMCTL, command);
if (err)
return err;
for (i = 0; i < 1000; i++) {
err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROMCTL, &value);
if (err)
return err;
if (value & SSB_PCMCIA_SPROMCTL_DONE)
return 0;
udelay(10);
}
return -ETIMEDOUT;
}
/* offset is the 16bit word offset */
static int ssb_pcmcia_sprom_read(struct ssb_bus *bus, u16 offset, u16 *value)
{
int err;
u8 lo, hi;
offset *= 2; /* Make byte offset */
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO,
(offset & 0x00FF));
if (err)
return err;
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI,
(offset & 0xFF00) >> 8);
if (err)
return err;
err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_READ);
if (err)
return err;
err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATALO, &lo);
if (err)
return err;
err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATAHI, &hi);
if (err)
return err;
*value = (lo | (((u16)hi) << 8));
return 0;
}
/* offset is the 16bit word offset */
static int ssb_pcmcia_sprom_write(struct ssb_bus *bus, u16 offset, u16 value)
{
int err;
offset *= 2; /* Make byte offset */
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO,
(offset & 0x00FF));
if (err)
return err;
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI,
(offset & 0xFF00) >> 8);
if (err)
return err;
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATALO,
(value & 0x00FF));
if (err)
return err;
err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATAHI,
(value & 0xFF00) >> 8);
if (err)
return err;
err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITE);
if (err)
return err;
msleep(20);
return 0;
}
/* Read the SPROM image. bufsize is in 16bit words. */
static int ssb_pcmcia_sprom_read_all(struct ssb_bus *bus, u16 *sprom)
{
int err, i;
for (i = 0; i < SSB_PCMCIA_SPROM_SIZE; i++) {
err = ssb_pcmcia_sprom_read(bus, i, &sprom[i]);
if (err)
return err;
}
return 0;
}
/* Write the SPROM image. size is in 16bit words. */
static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom)
{
int i, err;
bool failed = 0;
size_t size = SSB_PCMCIA_SPROM_SIZE;
ssb_notice("Writing SPROM. Do NOT turn off the power! Please stand by...\n");
err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN);
if (err) {
ssb_notice("Could not enable SPROM write access\n");
return -EBUSY;
}
ssb_notice("[ 0%%");
msleep(500);
for (i = 0; i < size; i++) {
if (i == size / 4)
ssb_cont("25%%");
else if (i == size / 2)
ssb_cont("50%%");
else if (i == (size * 3) / 4)
ssb_cont("75%%");
else if (i % 2)
ssb_cont(".");
err = ssb_pcmcia_sprom_write(bus, i, sprom[i]);
if (err) {
ssb_notice("Failed to write to SPROM\n");
failed = 1;
break;
}
}
err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS);
if (err) {
ssb_notice("Could not disable SPROM write access\n");
failed = 1;
}
msleep(500);
if (!failed) {
ssb_cont("100%% ]\n");
ssb_notice("SPROM written\n");
}
return failed ? -EBUSY : 0;
}
static int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size)
{
//TODO
return 0;
}
#define GOTO_ERROR_ON(condition, description) do { \
if (unlikely(condition)) { \
error_description = description; \
goto error; \
} \
} while (0)
static int ssb_pcmcia_get_mac(struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv)
{
struct ssb_sprom *sprom = priv;
if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID)
return -EINVAL;
if (tuple->TupleDataLen != ETH_ALEN + 2)
return -EINVAL;
if (tuple->TupleData[1] != ETH_ALEN)
return -EINVAL;
memcpy(sprom->il0mac, &tuple->TupleData[2], ETH_ALEN);
return 0;
};
static int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv)
{
struct ssb_init_invariants *iv = priv;
struct ssb_sprom *sprom = &iv->sprom;
struct ssb_boardinfo *bi = &iv->boardinfo;
const char *error_description;
GOTO_ERROR_ON(tuple->TupleDataLen < 1, "VEN tpl < 1");
switch (tuple->TupleData[0]) {
case SSB_PCMCIA_CIS_ID:
GOTO_ERROR_ON((tuple->TupleDataLen != 5) &&
(tuple->TupleDataLen != 7),
"id tpl size");
bi->vendor = tuple->TupleData[1] |
((u16)tuple->TupleData[2] << 8);
break;
case SSB_PCMCIA_CIS_BOARDREV:
GOTO_ERROR_ON(tuple->TupleDataLen != 2,
"boardrev tpl size");
sprom->board_rev = tuple->TupleData[1];
break;
case SSB_PCMCIA_CIS_PA:
GOTO_ERROR_ON((tuple->TupleDataLen != 9) &&
(tuple->TupleDataLen != 10),
"pa tpl size");
sprom->pa0b0 = tuple->TupleData[1] |
((u16)tuple->TupleData[2] << 8);
sprom->pa0b1 = tuple->TupleData[3] |
((u16)tuple->TupleData[4] << 8);
sprom->pa0b2 = tuple->TupleData[5] |
((u16)tuple->TupleData[6] << 8);
sprom->itssi_a = tuple->TupleData[7];
sprom->itssi_bg = tuple->TupleData[7];
sprom->maxpwr_a = tuple->TupleData[8];
sprom->maxpwr_bg = tuple->TupleData[8];
break;
case SSB_PCMCIA_CIS_OEMNAME:
/* We ignore this. */
break;
case SSB_PCMCIA_CIS_CCODE:
GOTO_ERROR_ON(tuple->TupleDataLen != 2,
"ccode tpl size");
sprom->country_code = tuple->TupleData[1];
break;
case SSB_PCMCIA_CIS_ANTENNA:
GOTO_ERROR_ON(tuple->TupleDataLen != 2,
"ant tpl size");
sprom->ant_available_a = tuple->TupleData[1];
sprom->ant_available_bg = tuple->TupleData[1];
break;
case SSB_PCMCIA_CIS_ANTGAIN:
GOTO_ERROR_ON(tuple->TupleDataLen != 2,
"antg tpl size");
sprom->antenna_gain.a0 = tuple->TupleData[1];
sprom->antenna_gain.a1 = tuple->TupleData[1];
sprom->antenna_gain.a2 = tuple->TupleData[1];
sprom->antenna_gain.a3 = tuple->TupleData[1];
break;
case SSB_PCMCIA_CIS_BFLAGS:
GOTO_ERROR_ON((tuple->TupleDataLen != 3) &&
(tuple->TupleDataLen != 5),
"bfl tpl size");
sprom->boardflags_lo = tuple->TupleData[1] |
((u16)tuple->TupleData[2] << 8);
break;
case SSB_PCMCIA_CIS_LEDS:
GOTO_ERROR_ON(tuple->TupleDataLen != 5,
"leds tpl size");
sprom->gpio0 = tuple->TupleData[1];
sprom->gpio1 = tuple->TupleData[2];
sprom->gpio2 = tuple->TupleData[3];
sprom->gpio3 = tuple->TupleData[4];
break;
}
return -ENOSPC; /* continue with next entry */
error:
ssb_err(
"PCMCIA: Failed to fetch device invariants: %s\n",
error_description);
return -ENODEV;
}
int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv)
{
struct ssb_sprom *sprom = &iv->sprom;
int res;
memset(sprom, 0xFF, sizeof(*sprom));
sprom->revision = 1;
sprom->boardflags_lo = 0;
sprom->boardflags_hi = 0;
/* First fetch the MAC address. */
res = pcmcia_loop_tuple(bus->host_pcmcia, CISTPL_FUNCE,
ssb_pcmcia_get_mac, sprom);
if (res != 0) {
ssb_err(
"PCMCIA: Failed to fetch MAC address\n");
return -ENODEV;
}
/* Fetch the vendor specific tuples. */
res = pcmcia_loop_tuple(bus->host_pcmcia, SSB_PCMCIA_CIS,
ssb_pcmcia_do_get_invariants, iv);
if ((res == 0) || (res == -ENOSPC))
return 0;
ssb_err(
"PCMCIA: Failed to fetch device invariants\n");
return -ENODEV;
}
static ssize_t ssb_pcmcia_attr_sprom_show(struct device *pcmciadev,
struct device_attribute *attr,
char *buf)
{
struct pcmcia_device *pdev =
container_of(pcmciadev, struct pcmcia_device, dev);
struct ssb_bus *bus;
bus = ssb_pcmcia_dev_to_bus(pdev);
if (!bus)
return -ENODEV;
return ssb_attr_sprom_show(bus, buf,
ssb_pcmcia_sprom_read_all);
}
static ssize_t ssb_pcmcia_attr_sprom_store(struct device *pcmciadev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct pcmcia_device *pdev =
container_of(pcmciadev, struct pcmcia_device, dev);
struct ssb_bus *bus;
bus = ssb_pcmcia_dev_to_bus(pdev);
if (!bus)
return -ENODEV;
return ssb_attr_sprom_store(bus, buf, count,
ssb_pcmcia_sprom_check_crc,
ssb_pcmcia_sprom_write_all);
}
static DEVICE_ATTR(ssb_sprom, 0600,
ssb_pcmcia_attr_sprom_show,
ssb_pcmcia_attr_sprom_store);
static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor)
{
u8 val;
int err;
err = ssb_pcmcia_cfg_read(bus, cor, &val);
if (err)
return err;
val &= ~COR_SOFT_RESET;
val |= COR_FUNC_ENA | COR_IREQ_ENA | COR_LEVEL_REQ;
err = ssb_pcmcia_cfg_write(bus, cor, val);
if (err)
return err;
msleep(40);
return 0;
}
/* Initialize the PCMCIA hardware. This is called on Init and Resume. */
int ssb_pcmcia_hardware_setup(struct ssb_bus *bus)
{
int err;
if (bus->bustype != SSB_BUSTYPE_PCMCIA)
return 0;
/* Switch segment to a known state and sync
* bus->mapped_pcmcia_seg with hardware state. */
ssb_pcmcia_switch_segment(bus, 0);
/* Init the COR register. */
err = ssb_pcmcia_cor_setup(bus, CISREG_COR);
if (err)
return err;
/* Some cards also need this register to get poked. */
err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80);
if (err)
return err;
return 0;
}
void ssb_pcmcia_exit(struct ssb_bus *bus)
{
if (bus->bustype != SSB_BUSTYPE_PCMCIA)
return;
device_remove_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom);
}
int ssb_pcmcia_init(struct ssb_bus *bus)
{
int err;
if (bus->bustype != SSB_BUSTYPE_PCMCIA)
return 0;
err = ssb_pcmcia_hardware_setup(bus);
if (err)
goto error;
bus->sprom_size = SSB_PCMCIA_SPROM_SIZE;
mutex_init(&bus->sprom_mutex);
err = device_create_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom);
if (err)
goto error;
return 0;
error:
ssb_err("Failed to initialize PCMCIA host device\n");
return err;
}

445
drivers/ssb/scan.c Normal file
View file

@ -0,0 +1,445 @@
/*
* Sonics Silicon Backplane
* Bus scanning
*
* Copyright (C) 2005-2007 Michael Buesch <m@bues.ch>
* Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
* Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
* Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
* Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
* Copyright (C) 2006 Broadcom Corporation.
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/ssb/ssb_regs.h>
#include <linux/pci.h>
#include <linux/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include "ssb_private.h"
const char *ssb_core_name(u16 coreid)
{
switch (coreid) {
case SSB_DEV_CHIPCOMMON:
return "ChipCommon";
case SSB_DEV_ILINE20:
return "ILine 20";
case SSB_DEV_SDRAM:
return "SDRAM";
case SSB_DEV_PCI:
return "PCI";
case SSB_DEV_MIPS:
return "MIPS";
case SSB_DEV_ETHERNET:
return "Fast Ethernet";
case SSB_DEV_V90:
return "V90";
case SSB_DEV_USB11_HOSTDEV:
return "USB 1.1 Hostdev";
case SSB_DEV_ADSL:
return "ADSL";
case SSB_DEV_ILINE100:
return "ILine 100";
case SSB_DEV_IPSEC:
return "IPSEC";
case SSB_DEV_PCMCIA:
return "PCMCIA";
case SSB_DEV_INTERNAL_MEM:
return "Internal Memory";
case SSB_DEV_MEMC_SDRAM:
return "MEMC SDRAM";
case SSB_DEV_EXTIF:
return "EXTIF";
case SSB_DEV_80211:
return "IEEE 802.11";
case SSB_DEV_MIPS_3302:
return "MIPS 3302";
case SSB_DEV_USB11_HOST:
return "USB 1.1 Host";
case SSB_DEV_USB11_DEV:
return "USB 1.1 Device";
case SSB_DEV_USB20_HOST:
return "USB 2.0 Host";
case SSB_DEV_USB20_DEV:
return "USB 2.0 Device";
case SSB_DEV_SDIO_HOST:
return "SDIO Host";
case SSB_DEV_ROBOSWITCH:
return "Roboswitch";
case SSB_DEV_PARA_ATA:
return "PATA";
case SSB_DEV_SATA_XORDMA:
return "SATA XOR-DMA";
case SSB_DEV_ETHERNET_GBIT:
return "GBit Ethernet";
case SSB_DEV_PCIE:
return "PCI-E";
case SSB_DEV_MIMO_PHY:
return "MIMO PHY";
case SSB_DEV_SRAM_CTRLR:
return "SRAM Controller";
case SSB_DEV_MINI_MACPHY:
return "Mini MACPHY";
case SSB_DEV_ARM_1176:
return "ARM 1176";
case SSB_DEV_ARM_7TDMI:
return "ARM 7TDMI";
case SSB_DEV_ARM_CM3:
return "ARM Cortex M3";
}
return "UNKNOWN";
}
static u16 pcidev_to_chipid(struct pci_dev *pci_dev)
{
u16 chipid_fallback = 0;
switch (pci_dev->device) {
case 0x4301:
chipid_fallback = 0x4301;
break;
case 0x4305 ... 0x4307:
chipid_fallback = 0x4307;
break;
case 0x4403:
chipid_fallback = 0x4402;
break;
case 0x4610 ... 0x4615:
chipid_fallback = 0x4610;
break;
case 0x4710 ... 0x4715:
chipid_fallback = 0x4710;
break;
case 0x4320 ... 0x4325:
chipid_fallback = 0x4309;
break;
case PCI_DEVICE_ID_BCM4401:
case PCI_DEVICE_ID_BCM4401B0:
case PCI_DEVICE_ID_BCM4401B1:
chipid_fallback = 0x4401;
break;
default:
ssb_err("PCI-ID not in fallback list\n");
}
return chipid_fallback;
}
static u8 chipid_to_nrcores(u16 chipid)
{
switch (chipid) {
case 0x5365:
return 7;
case 0x4306:
return 6;
case 0x4310:
return 8;
case 0x4307:
case 0x4301:
return 5;
case 0x4401:
case 0x4402:
return 3;
case 0x4710:
case 0x4610:
case 0x4704:
return 9;
default:
ssb_err("CHIPID not in nrcores fallback list\n");
}
return 1;
}
static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx,
u16 offset)
{
u32 lo, hi;
switch (bus->bustype) {
case SSB_BUSTYPE_SSB:
offset += current_coreidx * SSB_CORE_SIZE;
break;
case SSB_BUSTYPE_PCI:
break;
case SSB_BUSTYPE_PCMCIA:
if (offset >= 0x800) {
ssb_pcmcia_switch_segment(bus, 1);
offset -= 0x800;
} else
ssb_pcmcia_switch_segment(bus, 0);
lo = readw(bus->mmio + offset);
hi = readw(bus->mmio + offset + 2);
return lo | (hi << 16);
case SSB_BUSTYPE_SDIO:
offset += current_coreidx * SSB_CORE_SIZE;
return ssb_sdio_scan_read32(bus, offset);
}
return readl(bus->mmio + offset);
}
static int scan_switchcore(struct ssb_bus *bus, u8 coreidx)
{
switch (bus->bustype) {
case SSB_BUSTYPE_SSB:
break;
case SSB_BUSTYPE_PCI:
return ssb_pci_switch_coreidx(bus, coreidx);
case SSB_BUSTYPE_PCMCIA:
return ssb_pcmcia_switch_coreidx(bus, coreidx);
case SSB_BUSTYPE_SDIO:
return ssb_sdio_scan_switch_coreidx(bus, coreidx);
}
return 0;
}
void ssb_iounmap(struct ssb_bus *bus)
{
switch (bus->bustype) {
case SSB_BUSTYPE_SSB:
case SSB_BUSTYPE_PCMCIA:
iounmap(bus->mmio);
break;
case SSB_BUSTYPE_PCI:
#ifdef CONFIG_SSB_PCIHOST
pci_iounmap(bus->host_pci, bus->mmio);
#else
SSB_BUG_ON(1); /* Can't reach this code. */
#endif
break;
case SSB_BUSTYPE_SDIO:
break;
}
bus->mmio = NULL;
bus->mapped_device = NULL;
}
static void __iomem *ssb_ioremap(struct ssb_bus *bus,
unsigned long baseaddr)
{
void __iomem *mmio = NULL;
switch (bus->bustype) {
case SSB_BUSTYPE_SSB:
/* Only map the first core for now. */
/* fallthrough... */
case SSB_BUSTYPE_PCMCIA:
mmio = ioremap(baseaddr, SSB_CORE_SIZE);
break;
case SSB_BUSTYPE_PCI:
#ifdef CONFIG_SSB_PCIHOST
mmio = pci_iomap(bus->host_pci, 0, ~0UL);
#else
SSB_BUG_ON(1); /* Can't reach this code. */
#endif
break;
case SSB_BUSTYPE_SDIO:
/* Nothing to ioremap in the SDIO case, just fake it */
mmio = (void __iomem *)baseaddr;
break;
}
return mmio;
}
static int we_support_multiple_80211_cores(struct ssb_bus *bus)
{
/* More than one 802.11 core is only supported by special chips.
* There are chips with two 802.11 cores, but with dangling
* pins on the second core. Be careful and reject them here.
*/
#ifdef CONFIG_SSB_PCIHOST
if (bus->bustype == SSB_BUSTYPE_PCI) {
if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM &&
((bus->host_pci->device == 0x4313) ||
(bus->host_pci->device == 0x431A) ||
(bus->host_pci->device == 0x4321) ||
(bus->host_pci->device == 0x4324)))
return 1;
}
#endif /* CONFIG_SSB_PCIHOST */
return 0;
}
int ssb_bus_scan(struct ssb_bus *bus,
unsigned long baseaddr)
{
int err = -ENOMEM;
void __iomem *mmio;
u32 idhi, cc, rev, tmp;
int dev_i, i;
struct ssb_device *dev;
int nr_80211_cores = 0;
mmio = ssb_ioremap(bus, baseaddr);
if (!mmio)
goto out;
bus->mmio = mmio;
err = scan_switchcore(bus, 0); /* Switch to first core */
if (err)
goto err_unmap;
idhi = scan_read32(bus, 0, SSB_IDHIGH);
cc = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT;
rev = (idhi & SSB_IDHIGH_RCLO);
rev |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT;
bus->nr_devices = 0;
if (cc == SSB_DEV_CHIPCOMMON) {
tmp = scan_read32(bus, 0, SSB_CHIPCO_CHIPID);
bus->chip_id = (tmp & SSB_CHIPCO_IDMASK);
bus->chip_rev = (tmp & SSB_CHIPCO_REVMASK) >>
SSB_CHIPCO_REVSHIFT;
bus->chip_package = (tmp & SSB_CHIPCO_PACKMASK) >>
SSB_CHIPCO_PACKSHIFT;
if (rev >= 4) {
bus->nr_devices = (tmp & SSB_CHIPCO_NRCORESMASK) >>
SSB_CHIPCO_NRCORESSHIFT;
}
tmp = scan_read32(bus, 0, SSB_CHIPCO_CAP);
bus->chipco.capabilities = tmp;
} else {
if (bus->bustype == SSB_BUSTYPE_PCI) {
bus->chip_id = pcidev_to_chipid(bus->host_pci);
bus->chip_rev = bus->host_pci->revision;
bus->chip_package = 0;
} else {
bus->chip_id = 0x4710;
bus->chip_rev = 0;
bus->chip_package = 0;
}
}
ssb_info("Found chip with id 0x%04X, rev 0x%02X and package 0x%02X\n",
bus->chip_id, bus->chip_rev, bus->chip_package);
if (!bus->nr_devices)
bus->nr_devices = chipid_to_nrcores(bus->chip_id);
if (bus->nr_devices > ARRAY_SIZE(bus->devices)) {
ssb_err("More than %d ssb cores found (%d)\n",
SSB_MAX_NR_CORES, bus->nr_devices);
goto err_unmap;
}
if (bus->bustype == SSB_BUSTYPE_SSB) {
/* Now that we know the number of cores,
* remap the whole IO space for all cores.
*/
err = -ENOMEM;
iounmap(mmio);
mmio = ioremap(baseaddr, SSB_CORE_SIZE * bus->nr_devices);
if (!mmio)
goto out;
bus->mmio = mmio;
}
/* Fetch basic information about each core/device */
for (i = 0, dev_i = 0; i < bus->nr_devices; i++) {
err = scan_switchcore(bus, i);
if (err)
goto err_unmap;
dev = &(bus->devices[dev_i]);
idhi = scan_read32(bus, i, SSB_IDHIGH);
dev->id.coreid = (idhi & SSB_IDHIGH_CC) >> SSB_IDHIGH_CC_SHIFT;
dev->id.revision = (idhi & SSB_IDHIGH_RCLO);
dev->id.revision |= (idhi & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT;
dev->id.vendor = (idhi & SSB_IDHIGH_VC) >> SSB_IDHIGH_VC_SHIFT;
dev->core_index = i;
dev->bus = bus;
dev->ops = bus->ops;
printk(KERN_DEBUG PFX
"Core %d found: %s "
"(cc 0x%03X, rev 0x%02X, vendor 0x%04X)\n",
i, ssb_core_name(dev->id.coreid),
dev->id.coreid, dev->id.revision, dev->id.vendor);
switch (dev->id.coreid) {
case SSB_DEV_80211:
nr_80211_cores++;
if (nr_80211_cores > 1) {
if (!we_support_multiple_80211_cores(bus)) {
ssb_dbg("Ignoring additional 802.11 core\n");
continue;
}
}
break;
case SSB_DEV_EXTIF:
#ifdef CONFIG_SSB_DRIVER_EXTIF
if (bus->extif.dev) {
ssb_warn("WARNING: Multiple EXTIFs found\n");
break;
}
bus->extif.dev = dev;
#endif /* CONFIG_SSB_DRIVER_EXTIF */
break;
case SSB_DEV_CHIPCOMMON:
if (bus->chipco.dev) {
ssb_warn("WARNING: Multiple ChipCommon found\n");
break;
}
bus->chipco.dev = dev;
break;
case SSB_DEV_MIPS:
case SSB_DEV_MIPS_3302:
#ifdef CONFIG_SSB_DRIVER_MIPS
if (bus->mipscore.dev) {
ssb_warn("WARNING: Multiple MIPS cores found\n");
break;
}
bus->mipscore.dev = dev;
#endif /* CONFIG_SSB_DRIVER_MIPS */
break;
case SSB_DEV_PCI:
case SSB_DEV_PCIE:
#ifdef CONFIG_SSB_DRIVER_PCICORE
if (bus->bustype == SSB_BUSTYPE_PCI) {
/* Ignore PCI cores on PCI-E cards.
* Ignore PCI-E cores on PCI cards. */
if (dev->id.coreid == SSB_DEV_PCI) {
if (pci_is_pcie(bus->host_pci))
continue;
} else {
if (!pci_is_pcie(bus->host_pci))
continue;
}
}
if (bus->pcicore.dev) {
ssb_warn("WARNING: Multiple PCI(E) cores found\n");
break;
}
bus->pcicore.dev = dev;
#endif /* CONFIG_SSB_DRIVER_PCICORE */
break;
case SSB_DEV_ETHERNET:
if (bus->bustype == SSB_BUSTYPE_PCI) {
if (bus->host_pci->vendor == PCI_VENDOR_ID_BROADCOM &&
(bus->host_pci->device & 0xFF00) == 0x4300) {
/* This is a dangling ethernet core on a
* wireless device. Ignore it. */
continue;
}
}
break;
default:
break;
}
dev_i++;
}
bus->nr_devices = dev_i;
err = 0;
out:
return err;
err_unmap:
ssb_iounmap(bus);
goto out;
}

606
drivers/ssb/sdio.c Normal file
View file

@ -0,0 +1,606 @@
/*
* Sonics Silicon Backplane
* SDIO-Hostbus related functions
*
* Copyright 2009 Albert Herranz <albert_herranz@yahoo.es>
*
* Based on drivers/ssb/pcmcia.c
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2007-2008 Michael Buesch <m@bues.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*
*/
#include <linux/ssb/ssb.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/etherdevice.h>
#include <linux/mmc/sdio_func.h>
#include "ssb_private.h"
/* Define the following to 1 to enable a printk on each coreswitch. */
#define SSB_VERBOSE_SDIOCORESWITCH_DEBUG 0
/* Hardware invariants CIS tuples */
#define SSB_SDIO_CIS 0x80
#define SSB_SDIO_CIS_SROMREV 0x00
#define SSB_SDIO_CIS_ID 0x01
#define SSB_SDIO_CIS_BOARDREV 0x02
#define SSB_SDIO_CIS_PA 0x03
#define SSB_SDIO_CIS_PA_PA0B0_LO 0
#define SSB_SDIO_CIS_PA_PA0B0_HI 1
#define SSB_SDIO_CIS_PA_PA0B1_LO 2
#define SSB_SDIO_CIS_PA_PA0B1_HI 3
#define SSB_SDIO_CIS_PA_PA0B2_LO 4
#define SSB_SDIO_CIS_PA_PA0B2_HI 5
#define SSB_SDIO_CIS_PA_ITSSI 6
#define SSB_SDIO_CIS_PA_MAXPOW 7
#define SSB_SDIO_CIS_OEMNAME 0x04
#define SSB_SDIO_CIS_CCODE 0x05
#define SSB_SDIO_CIS_ANTENNA 0x06
#define SSB_SDIO_CIS_ANTGAIN 0x07
#define SSB_SDIO_CIS_BFLAGS 0x08
#define SSB_SDIO_CIS_LEDS 0x09
#define CISTPL_FUNCE_LAN_NODE_ID 0x04 /* same as in PCMCIA */
/*
* Function 1 miscellaneous registers.
*
* Definitions match src/include/sbsdio.h from the
* Android Open Source Project
* http://android.git.kernel.org/?p=platform/system/wlan/broadcom.git
*
*/
#define SBSDIO_FUNC1_SBADDRLOW 0x1000a /* SB Address window Low (b15) */
#define SBSDIO_FUNC1_SBADDRMID 0x1000b /* SB Address window Mid (b23-b16) */
#define SBSDIO_FUNC1_SBADDRHIGH 0x1000c /* SB Address window High (b24-b31) */
/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid address bits in SBADDRLOW */
#define SBSDIO_SBADDRMID_MASK 0xff /* Valid address bits in SBADDRMID */
#define SBSDIO_SBADDRHIGH_MASK 0xff /* Valid address bits in SBADDRHIGH */
#define SBSDIO_SB_OFT_ADDR_MASK 0x7FFF /* sb offset addr is <= 15 bits, 32k */
/* REVISIT: this flag doesn't seem to matter */
#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x8000 /* forces 32-bit SB access */
/*
* Address map within the SDIO function address space (128K).
*
* Start End Description
* ------- ------- ------------------------------------------
* 0x00000 0x0ffff selected backplane address window (64K)
* 0x10000 0x1ffff backplane control registers (max 64K)
*
* The current address window is configured by writing to registers
* SBADDRLOW, SBADDRMID and SBADDRHIGH.
*
* In order to access the contents of a 32-bit Silicon Backplane address
* the backplane address window must be first loaded with the highest
* 16 bits of the target address. Then, an access must be done to the
* SDIO function address space using the lower 15 bits of the address.
* Bit 15 of the address must be set when doing 32 bit accesses.
*
* 10987654321098765432109876543210
* WWWWWWWWWWWWWWWWW SB Address Window
* OOOOOOOOOOOOOOOO Offset within SB Address Window
* a 32-bit access flag
*/
/*
* SSB I/O via SDIO.
*
* NOTE: SDIO address @addr is 17 bits long (SDIO address space is 128K).
*/
static inline struct device *ssb_sdio_dev(struct ssb_bus *bus)
{
return &bus->host_sdio->dev;
}
/* host claimed */
static int ssb_sdio_writeb(struct ssb_bus *bus, unsigned int addr, u8 val)
{
int error = 0;
sdio_writeb(bus->host_sdio, val, addr, &error);
if (unlikely(error)) {
dev_dbg(ssb_sdio_dev(bus), "%08X <- %02x, error %d\n",
addr, val, error);
}
return error;
}
#if 0
static u8 ssb_sdio_readb(struct ssb_bus *bus, unsigned int addr)
{
u8 val;
int error = 0;
val = sdio_readb(bus->host_sdio, addr, &error);
if (unlikely(error)) {
dev_dbg(ssb_sdio_dev(bus), "%08X -> %02x, error %d\n",
addr, val, error);
}
return val;
}
#endif
/* host claimed */
static int ssb_sdio_set_sbaddr_window(struct ssb_bus *bus, u32 address)
{
int error;
error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRLOW,
(address >> 8) & SBSDIO_SBADDRLOW_MASK);
if (error)
goto out;
error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRMID,
(address >> 16) & SBSDIO_SBADDRMID_MASK);
if (error)
goto out;
error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRHIGH,
(address >> 24) & SBSDIO_SBADDRHIGH_MASK);
if (error)
goto out;
bus->sdio_sbaddr = address;
out:
if (error) {
dev_dbg(ssb_sdio_dev(bus), "failed to set address window"
" to 0x%08x, error %d\n", address, error);
}
return error;
}
/* for enumeration use only */
u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset)
{
u32 val;
int error;
sdio_claim_host(bus->host_sdio);
val = sdio_readl(bus->host_sdio, offset, &error);
sdio_release_host(bus->host_sdio);
if (unlikely(error)) {
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",
bus->sdio_sbaddr >> 16, offset, val, error);
}
return val;
}
/* for enumeration use only */
int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
{
u32 sbaddr;
int error;
sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
sdio_claim_host(bus->host_sdio);
error = ssb_sdio_set_sbaddr_window(bus, sbaddr);
sdio_release_host(bus->host_sdio);
if (error) {
dev_err(ssb_sdio_dev(bus), "failed to switch to core %u,"
" error %d\n", coreidx, error);
goto out;
}
out:
return error;
}
/* host must be already claimed */
int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
{
u8 coreidx = dev->core_index;
u32 sbaddr;
int error = 0;
sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
if (unlikely(bus->sdio_sbaddr != sbaddr)) {
#if SSB_VERBOSE_SDIOCORESWITCH_DEBUG
dev_info(ssb_sdio_dev(bus),
"switching to %s core, index %d\n",
ssb_core_name(dev->id.coreid), coreidx);
#endif
error = ssb_sdio_set_sbaddr_window(bus, sbaddr);
if (error) {
dev_dbg(ssb_sdio_dev(bus), "failed to switch to"
" core %u, error %d\n", coreidx, error);
goto out;
}
bus->mapped_device = dev;
}
out:
return error;
}
static u8 ssb_sdio_read8(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
u8 val = 0xff;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto out;
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
val = sdio_readb(bus->host_sdio, offset, &error);
if (error) {
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %02x, error %d\n",
bus->sdio_sbaddr >> 16, offset, val, error);
}
out:
sdio_release_host(bus->host_sdio);
return val;
}
static u16 ssb_sdio_read16(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
u16 val = 0xffff;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto out;
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
val = sdio_readw(bus->host_sdio, offset, &error);
if (error) {
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %04x, error %d\n",
bus->sdio_sbaddr >> 16, offset, val, error);
}
out:
sdio_release_host(bus->host_sdio);
return val;
}
static u32 ssb_sdio_read32(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
u32 val = 0xffffffff;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto out;
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
val = sdio_readl(bus->host_sdio, offset, &error);
if (error) {
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",
bus->sdio_sbaddr >> 16, offset, val, error);
}
out:
sdio_release_host(bus->host_sdio);
return val;
}
#ifdef CONFIG_SSB_BLOCKIO
static void ssb_sdio_block_read(struct ssb_device *dev, void *buffer,
size_t count, u16 offset, u8 reg_width)
{
size_t saved_count = count;
struct ssb_bus *bus = dev->bus;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev))) {
error = -EIO;
memset(buffer, 0xff, count);
goto err_out;
}
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
switch (reg_width) {
case sizeof(u8): {
error = sdio_readsb(bus->host_sdio, buffer, offset, count);
break;
}
case sizeof(u16): {
SSB_WARN_ON(count & 1);
error = sdio_readsb(bus->host_sdio, buffer, offset, count);
break;
}
case sizeof(u32): {
SSB_WARN_ON(count & 3);
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
error = sdio_readsb(bus->host_sdio, buffer, offset, count);
break;
}
default:
SSB_WARN_ON(1);
}
if (!error)
goto out;
err_out:
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",
bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);
out:
sdio_release_host(bus->host_sdio);
}
#endif /* CONFIG_SSB_BLOCKIO */
static void ssb_sdio_write8(struct ssb_device *dev, u16 offset, u8 val)
{
struct ssb_bus *bus = dev->bus;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto out;
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
sdio_writeb(bus->host_sdio, val, offset, &error);
if (error) {
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %02x, error %d\n",
bus->sdio_sbaddr >> 16, offset, val, error);
}
out:
sdio_release_host(bus->host_sdio);
}
static void ssb_sdio_write16(struct ssb_device *dev, u16 offset, u16 val)
{
struct ssb_bus *bus = dev->bus;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto out;
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
sdio_writew(bus->host_sdio, val, offset, &error);
if (error) {
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %04x, error %d\n",
bus->sdio_sbaddr >> 16, offset, val, error);
}
out:
sdio_release_host(bus->host_sdio);
}
static void ssb_sdio_write32(struct ssb_device *dev, u16 offset, u32 val)
{
struct ssb_bus *bus = dev->bus;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto out;
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
sdio_writel(bus->host_sdio, val, offset, &error);
if (error) {
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %08x, error %d\n",
bus->sdio_sbaddr >> 16, offset, val, error);
}
if (bus->quirks & SSB_QUIRK_SDIO_READ_AFTER_WRITE32)
sdio_readl(bus->host_sdio, 0, &error);
out:
sdio_release_host(bus->host_sdio);
}
#ifdef CONFIG_SSB_BLOCKIO
static void ssb_sdio_block_write(struct ssb_device *dev, const void *buffer,
size_t count, u16 offset, u8 reg_width)
{
size_t saved_count = count;
struct ssb_bus *bus = dev->bus;
int error = 0;
sdio_claim_host(bus->host_sdio);
if (unlikely(ssb_sdio_switch_core(bus, dev))) {
error = -EIO;
memset((void *)buffer, 0xff, count);
goto err_out;
}
offset |= bus->sdio_sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
switch (reg_width) {
case sizeof(u8):
error = sdio_writesb(bus->host_sdio, offset,
(void *)buffer, count);
break;
case sizeof(u16):
SSB_WARN_ON(count & 1);
error = sdio_writesb(bus->host_sdio, offset,
(void *)buffer, count);
break;
case sizeof(u32):
SSB_WARN_ON(count & 3);
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
error = sdio_writesb(bus->host_sdio, offset,
(void *)buffer, count);
break;
default:
SSB_WARN_ON(1);
}
if (!error)
goto out;
err_out:
dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",
bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);
out:
sdio_release_host(bus->host_sdio);
}
#endif /* CONFIG_SSB_BLOCKIO */
/* Not "static", as it's used in main.c */
const struct ssb_bus_ops ssb_sdio_ops = {
.read8 = ssb_sdio_read8,
.read16 = ssb_sdio_read16,
.read32 = ssb_sdio_read32,
.write8 = ssb_sdio_write8,
.write16 = ssb_sdio_write16,
.write32 = ssb_sdio_write32,
#ifdef CONFIG_SSB_BLOCKIO
.block_read = ssb_sdio_block_read,
.block_write = ssb_sdio_block_write,
#endif
};
#define GOTO_ERROR_ON(condition, description) do { \
if (unlikely(condition)) { \
error_description = description; \
goto error; \
} \
} while (0)
int ssb_sdio_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv)
{
struct ssb_sprom *sprom = &iv->sprom;
struct ssb_boardinfo *bi = &iv->boardinfo;
const char *error_description = "none";
struct sdio_func_tuple *tuple;
void *mac;
memset(sprom, 0xFF, sizeof(*sprom));
sprom->boardflags_lo = 0;
sprom->boardflags_hi = 0;
tuple = bus->host_sdio->tuples;
while (tuple) {
switch (tuple->code) {
case 0x22: /* extended function */
switch (tuple->data[0]) {
case CISTPL_FUNCE_LAN_NODE_ID:
GOTO_ERROR_ON((tuple->size != 7) &&
(tuple->data[1] != 6),
"mac tpl size");
/* fetch the MAC address. */
mac = tuple->data + 2;
memcpy(sprom->il0mac, mac, ETH_ALEN);
memcpy(sprom->et1mac, mac, ETH_ALEN);
break;
default:
break;
}
break;
case 0x80: /* vendor specific tuple */
switch (tuple->data[0]) {
case SSB_SDIO_CIS_SROMREV:
GOTO_ERROR_ON(tuple->size != 2,
"sromrev tpl size");
sprom->revision = tuple->data[1];
break;
case SSB_SDIO_CIS_ID:
GOTO_ERROR_ON((tuple->size != 5) &&
(tuple->size != 7),
"id tpl size");
bi->vendor = tuple->data[1] |
(tuple->data[2]<<8);
break;
case SSB_SDIO_CIS_BOARDREV:
GOTO_ERROR_ON(tuple->size != 2,
"boardrev tpl size");
sprom->board_rev = tuple->data[1];
break;
case SSB_SDIO_CIS_PA:
GOTO_ERROR_ON((tuple->size != 9) &&
(tuple->size != 10),
"pa tpl size");
sprom->pa0b0 = tuple->data[1] |
((u16)tuple->data[2] << 8);
sprom->pa0b1 = tuple->data[3] |
((u16)tuple->data[4] << 8);
sprom->pa0b2 = tuple->data[5] |
((u16)tuple->data[6] << 8);
sprom->itssi_a = tuple->data[7];
sprom->itssi_bg = tuple->data[7];
sprom->maxpwr_a = tuple->data[8];
sprom->maxpwr_bg = tuple->data[8];
break;
case SSB_SDIO_CIS_OEMNAME:
/* Not present */
break;
case SSB_SDIO_CIS_CCODE:
GOTO_ERROR_ON(tuple->size != 2,
"ccode tpl size");
sprom->country_code = tuple->data[1];
break;
case SSB_SDIO_CIS_ANTENNA:
GOTO_ERROR_ON(tuple->size != 2,
"ant tpl size");
sprom->ant_available_a = tuple->data[1];
sprom->ant_available_bg = tuple->data[1];
break;
case SSB_SDIO_CIS_ANTGAIN:
GOTO_ERROR_ON(tuple->size != 2,
"antg tpl size");
sprom->antenna_gain.a0 = tuple->data[1];
sprom->antenna_gain.a1 = tuple->data[1];
sprom->antenna_gain.a2 = tuple->data[1];
sprom->antenna_gain.a3 = tuple->data[1];
break;
case SSB_SDIO_CIS_BFLAGS:
GOTO_ERROR_ON((tuple->size != 3) &&
(tuple->size != 5),
"bfl tpl size");
sprom->boardflags_lo = tuple->data[1] |
((u16)tuple->data[2] << 8);
break;
case SSB_SDIO_CIS_LEDS:
GOTO_ERROR_ON(tuple->size != 5,
"leds tpl size");
sprom->gpio0 = tuple->data[1];
sprom->gpio1 = tuple->data[2];
sprom->gpio2 = tuple->data[3];
sprom->gpio3 = tuple->data[4];
break;
default:
break;
}
break;
default:
break;
}
tuple = tuple->next;
}
return 0;
error:
dev_err(ssb_sdio_dev(bus), "failed to fetch device invariants: %s\n",
error_description);
return -ENODEV;
}
void ssb_sdio_exit(struct ssb_bus *bus)
{
if (bus->bustype != SSB_BUSTYPE_SDIO)
return;
/* Nothing to do here. */
}
int ssb_sdio_init(struct ssb_bus *bus)
{
if (bus->bustype != SSB_BUSTYPE_SDIO)
return 0;
bus->sdio_sbaddr = ~0;
return 0;
}

200
drivers/ssb/sprom.c Normal file
View file

@ -0,0 +1,200 @@
/*
* Sonics Silicon Backplane
* Common SPROM support routines
*
* Copyright (C) 2005-2008 Michael Buesch <m@bues.ch>
* Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>
* Copyright (C) 2005 Stefano Brivio <st3@riseup.net>
* Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>
* Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include "ssb_private.h"
#include <linux/ctype.h>
#include <linux/slab.h>
static int(*get_fallback_sprom)(struct ssb_bus *dev, struct ssb_sprom *out);
static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len,
size_t sprom_size_words)
{
int i, pos = 0;
for (i = 0; i < sprom_size_words; i++)
pos += snprintf(buf + pos, buf_len - pos - 1,
"%04X", swab16(sprom[i]) & 0xFFFF);
pos += snprintf(buf + pos, buf_len - pos - 1, "\n");
return pos + 1;
}
static int hex2sprom(u16 *sprom, const char *dump, size_t len,
size_t sprom_size_words)
{
char c, tmp[5] = { 0 };
int err, cnt = 0;
unsigned long parsed;
/* Strip whitespace at the end. */
while (len) {
c = dump[len - 1];
if (!isspace(c) && c != '\0')
break;
len--;
}
/* Length must match exactly. */
if (len != sprom_size_words * 4)
return -EINVAL;
while (cnt < sprom_size_words) {
memcpy(tmp, dump, 4);
dump += 4;
err = kstrtoul(tmp, 16, &parsed);
if (err)
return err;
sprom[cnt++] = swab16((u16)parsed);
}
return 0;
}
/* Common sprom device-attribute show-handler */
ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf,
int (*sprom_read)(struct ssb_bus *bus, u16 *sprom))
{
u16 *sprom;
int err = -ENOMEM;
ssize_t count = 0;
size_t sprom_size_words = bus->sprom_size;
sprom = kcalloc(sprom_size_words, sizeof(u16), GFP_KERNEL);
if (!sprom)
goto out;
/* Use interruptible locking, as the SPROM write might
* be holding the lock for several seconds. So allow userspace
* to cancel operation. */
err = -ERESTARTSYS;
if (mutex_lock_interruptible(&bus->sprom_mutex))
goto out_kfree;
err = sprom_read(bus, sprom);
mutex_unlock(&bus->sprom_mutex);
if (!err)
count = sprom2hex(sprom, buf, PAGE_SIZE, sprom_size_words);
out_kfree:
kfree(sprom);
out:
return err ? err : count;
}
/* Common sprom device-attribute store-handler */
ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
const char *buf, size_t count,
int (*sprom_check_crc)(const u16 *sprom, size_t size),
int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom))
{
u16 *sprom;
int res = 0, err = -ENOMEM;
size_t sprom_size_words = bus->sprom_size;
struct ssb_freeze_context freeze;
sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL);
if (!sprom)
goto out;
err = hex2sprom(sprom, buf, count, sprom_size_words);
if (err) {
err = -EINVAL;
goto out_kfree;
}
err = sprom_check_crc(sprom, sprom_size_words);
if (err) {
err = -EINVAL;
goto out_kfree;
}
/* Use interruptible locking, as the SPROM write might
* be holding the lock for several seconds. So allow userspace
* to cancel operation. */
err = -ERESTARTSYS;
if (mutex_lock_interruptible(&bus->sprom_mutex))
goto out_kfree;
err = ssb_devices_freeze(bus, &freeze);
if (err) {
ssb_err("SPROM write: Could not freeze all devices\n");
goto out_unlock;
}
res = sprom_write(bus, sprom);
err = ssb_devices_thaw(&freeze);
if (err)
ssb_err("SPROM write: Could not thaw all devices\n");
out_unlock:
mutex_unlock(&bus->sprom_mutex);
out_kfree:
kfree(sprom);
out:
if (res)
return res;
return err ? err : count;
}
/**
* ssb_arch_register_fallback_sprom - Registers a method providing a
* fallback SPROM if no SPROM is found.
*
* @sprom_callback: The callback function.
*
* With this function the architecture implementation may register a
* callback handler which fills the SPROM data structure. The fallback is
* only used for PCI based SSB devices, where no valid SPROM can be found
* in the shadow registers.
*
* This function is useful for weird architectures that have a half-assed
* SSB device hardwired to their PCI bus.
*
* Note that it does only work with PCI attached SSB devices. PCMCIA
* devices currently don't use this fallback.
* Architectures must provide the SPROM for native SSB devices anyway, so
* the fallback also isn't used for native devices.
*
* This function is available for architecture code, only. So it is not
* exported.
*/
int ssb_arch_register_fallback_sprom(int (*sprom_callback)(struct ssb_bus *bus,
struct ssb_sprom *out))
{
if (get_fallback_sprom)
return -EEXIST;
get_fallback_sprom = sprom_callback;
return 0;
}
int ssb_fill_sprom_with_fallback(struct ssb_bus *bus, struct ssb_sprom *out)
{
if (!get_fallback_sprom)
return -ENOENT;
return get_fallback_sprom(bus, out);
}
/* http://bcm-v4.sipsolutions.net/802.11/IsSpromAvailable */
bool ssb_is_sprom_available(struct ssb_bus *bus)
{
/* status register only exists on chipcomon rev >= 11 and we need check
for >= 31 only */
/* this routine differs from specs as we do not access SPROM directly
on PCMCIA */
if (bus->bustype == SSB_BUSTYPE_PCI &&
bus->chipco.dev && /* can be unavailable! */
bus->chipco.dev->id.revision >= 31)
return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
return true;
}

297
drivers/ssb/ssb_private.h Normal file
View file

@ -0,0 +1,297 @@
#ifndef LINUX_SSB_PRIVATE_H_
#define LINUX_SSB_PRIVATE_H_
#include <linux/ssb/ssb.h>
#include <linux/types.h>
#include <linux/bcm47xx_wdt.h>
#define PFX "ssb: "
#ifdef CONFIG_SSB_SILENT
# define ssb_printk(fmt, ...) \
do { if (0) printk(fmt, ##__VA_ARGS__); } while (0)
#else
# define ssb_printk(fmt, ...) \
printk(fmt, ##__VA_ARGS__)
#endif /* CONFIG_SSB_SILENT */
#define ssb_emerg(fmt, ...) ssb_printk(KERN_EMERG PFX fmt, ##__VA_ARGS__)
#define ssb_err(fmt, ...) ssb_printk(KERN_ERR PFX fmt, ##__VA_ARGS__)
#define ssb_warn(fmt, ...) ssb_printk(KERN_WARNING PFX fmt, ##__VA_ARGS__)
#define ssb_notice(fmt, ...) ssb_printk(KERN_NOTICE PFX fmt, ##__VA_ARGS__)
#define ssb_info(fmt, ...) ssb_printk(KERN_INFO PFX fmt, ##__VA_ARGS__)
#define ssb_cont(fmt, ...) ssb_printk(KERN_CONT fmt, ##__VA_ARGS__)
/* dprintk: Debugging printk; vanishes for non-debug compilation */
#ifdef CONFIG_SSB_DEBUG
# define ssb_dbg(fmt, ...) \
ssb_printk(KERN_DEBUG PFX fmt, ##__VA_ARGS__)
#else
# define ssb_dbg(fmt, ...) \
do { if (0) printk(KERN_DEBUG PFX fmt, ##__VA_ARGS__); } while (0)
#endif
#ifdef CONFIG_SSB_DEBUG
# define SSB_WARN_ON(x) WARN_ON(x)
# define SSB_BUG_ON(x) BUG_ON(x)
#else
static inline int __ssb_do_nothing(int x) { return x; }
# define SSB_WARN_ON(x) __ssb_do_nothing(unlikely(!!(x)))
# define SSB_BUG_ON(x) __ssb_do_nothing(unlikely(!!(x)))
#endif
/* pci.c */
#ifdef CONFIG_SSB_PCIHOST
extern int ssb_pci_switch_core(struct ssb_bus *bus,
struct ssb_device *dev);
extern int ssb_pci_switch_coreidx(struct ssb_bus *bus,
u8 coreidx);
extern int ssb_pci_xtal(struct ssb_bus *bus, u32 what,
int turn_on);
extern int ssb_pci_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv);
extern void ssb_pci_exit(struct ssb_bus *bus);
extern int ssb_pci_init(struct ssb_bus *bus);
extern const struct ssb_bus_ops ssb_pci_ops;
#else /* CONFIG_SSB_PCIHOST */
static inline int ssb_pci_switch_core(struct ssb_bus *bus,
struct ssb_device *dev)
{
return 0;
}
static inline int ssb_pci_switch_coreidx(struct ssb_bus *bus,
u8 coreidx)
{
return 0;
}
static inline int ssb_pci_xtal(struct ssb_bus *bus, u32 what,
int turn_on)
{
return 0;
}
static inline void ssb_pci_exit(struct ssb_bus *bus)
{
}
static inline int ssb_pci_init(struct ssb_bus *bus)
{
return 0;
}
#endif /* CONFIG_SSB_PCIHOST */
/* pcmcia.c */
#ifdef CONFIG_SSB_PCMCIAHOST
extern int ssb_pcmcia_switch_core(struct ssb_bus *bus,
struct ssb_device *dev);
extern int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
u8 coreidx);
extern int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
u8 seg);
extern int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv);
extern int ssb_pcmcia_hardware_setup(struct ssb_bus *bus);
extern void ssb_pcmcia_exit(struct ssb_bus *bus);
extern int ssb_pcmcia_init(struct ssb_bus *bus);
extern const struct ssb_bus_ops ssb_pcmcia_ops;
#else /* CONFIG_SSB_PCMCIAHOST */
static inline int ssb_pcmcia_switch_core(struct ssb_bus *bus,
struct ssb_device *dev)
{
return 0;
}
static inline int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
u8 coreidx)
{
return 0;
}
static inline int ssb_pcmcia_switch_segment(struct ssb_bus *bus,
u8 seg)
{
return 0;
}
static inline int ssb_pcmcia_hardware_setup(struct ssb_bus *bus)
{
return 0;
}
static inline void ssb_pcmcia_exit(struct ssb_bus *bus)
{
}
static inline int ssb_pcmcia_init(struct ssb_bus *bus)
{
return 0;
}
#endif /* CONFIG_SSB_PCMCIAHOST */
/* sdio.c */
#ifdef CONFIG_SSB_SDIOHOST
extern int ssb_sdio_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv);
extern u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset);
extern int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev);
extern int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx);
extern int ssb_sdio_hardware_setup(struct ssb_bus *bus);
extern void ssb_sdio_exit(struct ssb_bus *bus);
extern int ssb_sdio_init(struct ssb_bus *bus);
extern const struct ssb_bus_ops ssb_sdio_ops;
#else /* CONFIG_SSB_SDIOHOST */
static inline u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset)
{
return 0;
}
static inline int ssb_sdio_switch_core(struct ssb_bus *bus,
struct ssb_device *dev)
{
return 0;
}
static inline int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
{
return 0;
}
static inline int ssb_sdio_hardware_setup(struct ssb_bus *bus)
{
return 0;
}
static inline void ssb_sdio_exit(struct ssb_bus *bus)
{
}
static inline int ssb_sdio_init(struct ssb_bus *bus)
{
return 0;
}
#endif /* CONFIG_SSB_SDIOHOST */
/* scan.c */
extern const char *ssb_core_name(u16 coreid);
extern int ssb_bus_scan(struct ssb_bus *bus,
unsigned long baseaddr);
extern void ssb_iounmap(struct ssb_bus *ssb);
/* sprom.c */
extern
ssize_t ssb_attr_sprom_show(struct ssb_bus *bus, char *buf,
int (*sprom_read)(struct ssb_bus *bus, u16 *sprom));
extern
ssize_t ssb_attr_sprom_store(struct ssb_bus *bus,
const char *buf, size_t count,
int (*sprom_check_crc)(const u16 *sprom, size_t size),
int (*sprom_write)(struct ssb_bus *bus, const u16 *sprom));
extern int ssb_fill_sprom_with_fallback(struct ssb_bus *bus,
struct ssb_sprom *out);
/* core.c */
extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
int ssb_for_each_bus_call(unsigned long data,
int (*func)(struct ssb_bus *bus, unsigned long data));
extern struct ssb_bus *ssb_pcmcia_dev_to_bus(struct pcmcia_device *pdev);
struct ssb_freeze_context {
/* Pointer to the bus */
struct ssb_bus *bus;
/* Boolean list to indicate whether a device is frozen on this bus. */
bool device_frozen[SSB_MAX_NR_CORES];
};
extern int ssb_devices_freeze(struct ssb_bus *bus, struct ssb_freeze_context *ctx);
extern int ssb_devices_thaw(struct ssb_freeze_context *ctx);
/* b43_pci_bridge.c */
#ifdef CONFIG_SSB_B43_PCI_BRIDGE
extern int __init b43_pci_ssb_bridge_init(void);
extern void __exit b43_pci_ssb_bridge_exit(void);
#else /* CONFIG_SSB_B43_PCI_BRIDGE */
static inline int b43_pci_ssb_bridge_init(void)
{
return 0;
}
static inline void b43_pci_ssb_bridge_exit(void)
{
}
#endif /* CONFIG_SSB_B43_PCI_BRIDGE */
/* 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_MIPS
extern struct platform_device ssb_pflash_dev;
#endif
#ifdef CONFIG_SSB_SFLASH
extern struct platform_device ssb_sflash_dev;
#endif
#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);
extern int ssb_gpio_unregister(struct ssb_bus *bus);
#else /* CONFIG_SSB_DRIVER_GPIO */
static inline int ssb_gpio_init(struct ssb_bus *bus)
{
return -ENOTSUPP;
}
static inline int ssb_gpio_unregister(struct ssb_bus *bus)
{
return 0;
}
#endif /* CONFIG_SSB_DRIVER_GPIO */
#endif /* LINUX_SSB_PRIVATE_H_ */