Commit 61e115a5 authored by Michael Buesch's avatar Michael Buesch Committed by David S. Miller

[SSB]: add Sonics Silicon Backplane bus support

SSB is an SoC bus used in a number of embedded devices.  The most
well-known of these devices is probably the Linksys WRT54G, but there
are others as well.  The bus is also used internally on the BCM43xx
and BCM44xx devices from Broadcom.

This patch also includes support for SSB ID tables in modules, so
that SSB drivers can be loaded automatically.
Signed-off-by: default avatarMichael Buesch <mb@bu3sch.de>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5ee3afba
......@@ -3446,6 +3446,12 @@ M: tsbogend@alpha.franken.de
L: netdev@vger.kernel.org
S: Maintained
SONICS SILICON BACKPLANE DRIVER (SSB)
P: Michael Buesch
M: mb@bu3sch.de
L: netdev@vger.kernel.org
S: Maintained
SONY VAIO CONTROL DEVICE DRIVER
P: Mattia Dongili
M: malattia@linux.it
......
......@@ -58,6 +58,8 @@ source "drivers/power/Kconfig"
source "drivers/hwmon/Kconfig"
source "drivers/ssb/Kconfig"
source "drivers/mfd/Kconfig"
source "drivers/media/Kconfig"
......
......@@ -88,3 +88,4 @@ obj-$(CONFIG_DMA_ENGINE) += dma/
obj-$(CONFIG_HID) += hid/
obj-$(CONFIG_PPC_PS3) += ps3/
obj-$(CONFIG_OF) += of/
obj-$(CONFIG_SSB) += ssb/
menu "Sonics Silicon Backplane"
config SSB_POSSIBLE
bool
depends on HAS_IOMEM
default y
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.
config SSB_PCIHOST_POSSIBLE
bool
depends on SSB && PCI
default y
config SSB_PCIHOST
bool "Support for SSB on PCI-bus host"
depends on SSB_PCIHOST_POSSIBLE
default y
help
Support for a Sonics Silicon Backplane on top
of a PCI device.
If unsure, say Y
config SSB_PCMCIAHOST_POSSIBLE
bool
depends on SSB && PCMCIA && EXPERIMENTAL
default y
config SSB_PCMCIAHOST
bool "Support for SSB on PCMCIA-bus host (EXPERIMENTAL)"
depends on SSB_PCMCIAHOST_POSSIBLE
help
Support for a Sonics Silicon Backplane on top
of a PCMCIA device.
If unsure, say N
config SSB_SILENT
bool "No SSB kernel messages"
depends on SSB && EMBEDDED
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 (EXPERIMENTAL)"
depends on SSB_DRIVER_PCICORE && SSB_DRIVER_MIPS && EXPERIMENTAL
help
PCIcore hostmode operation (external PCI bus).
config SSB_DRIVER_MIPS
bool "SSB Broadcom MIPS core driver (EXPERIMENTAL)"
depends on SSB && MIPS && EXPERIMENTAL
select SSB_SERIAL
help
Driver for the Sonics Silicon Backplane attached
Broadcom MIPS core.
If unsure, say N
config SSB_DRIVER_EXTIF
bool "SSB Broadcom EXTIF core driver (EXPERIMENTAL)"
depends on SSB_DRIVER_MIPS && EXPERIMENTAL
help
Driver for the Sonics Silicon Backplane attached
Broadcom EXTIF core.
If unsure, say N
endmenu
# core
ssb-y += main.o scan.o
# host support
ssb-$(CONFIG_SSB_PCIHOST) += pci.o pcihost_wrapper.o
ssb-$(CONFIG_SSB_PCMCIAHOST) += pcmcia.o
# built-in drivers
ssb-y += driver_chipcommon.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
# b43 pci-ssb-bridge driver
# Not strictly a part of SSB, but kept here for convenience
ssb-$(CONFIG_SSB_PCIHOST) += b43_pci_bridge.o
obj-$(CONFIG_SSB) += ssb.o
/*
* Broadcom 43xx PCI-SSB bridge module
*
* This technically is a seperate 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 <mb@bu3sch.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/pci.h>
#include <linux/ssb/ssb.h>
static const struct pci_device_id b43_pci_bridge_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4301) },
{ 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, 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, 0x4324) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4325) },
{ 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);
}
This diff is collapsed.
/*
* Sonics Silicon Backplane
* Broadcom EXTIF core driver
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
* 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 void 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);
}
#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_gpio_in(struct ssb_extif *extif, u32 mask)
{
return extif_read32(extif, SSB_EXTIF_GPIO_IN) & mask;
}
void ssb_extif_gpio_out(struct ssb_extif *extif, u32 mask, u32 value)
{
return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUT(0),
mask, value);
}
void ssb_extif_gpio_outen(struct ssb_extif *extif, u32 mask, u32 value)
{
return extif_write32_masked(extif, SSB_EXTIF_GPIO_OUTEN(0),
mask, value);
}
/*
* Sonics Silicon Backplane
* Broadcom MIPS core driver
*
* Copyright 2005, Broadcom Corporation
* Copyright 2006, 2007, Michael Buesch <mb@bu3sch.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/time.h>
#include "ssb_private.h"
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)
{
return ssb_read32(dev, SSB_TPSFLAG) & SSB_TPSFLAG_BPFLAG;
}
/* Get the MIPS IRQ assignment for a specified device.
* If unassigned, 0 is returned.
*/
unsigned int ssb_mips_irq(struct ssb_device *dev)
{
struct ssb_bus *bus = dev->bus;
u32 irqflag;
u32 ipsflag;
u32 tmp;
unsigned int irq;
irqflag = ssb_irqflag(dev);
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)
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);
dev->irq = irq + 2;
ssb_dprintk(KERN_INFO PFX
"set_irq: core 0x%04x, irq %d => %d\n",
dev->id.coreid, oldirq, irq);
/* clear the old irq */
if (oldirq == 0)
ssb_write32(mdev, SSB_INTVEC, (~(1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
else
clear_irq(bus, oldirq);
/* assign the new one */
if (irq == 0)
ssb_write32(mdev, SSB_INTVEC, ((1 << irqflag) & ssb_read32(mdev, SSB_INTVEC)));
irqflag <<= ipsflag_irq_shift[irq];
irqflag |= (ssb_read32(mdev, SSB_IPSFLAG) & ~ipsflag_irq_mask[irq]);
ssb_write32(mdev, SSB_IPSFLAG, irqflag);
}
static void ssb_mips_serial_init(struct ssb_mipscore *mcore)
{
struct ssb_bus *bus = mcore->dev->bus;
if (bus->extif.dev)
mcore->nr_serial_ports = ssb_extif_serial_init(&bus->extif, mcore->serial_ports);
else if (bus->chipco.dev)
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;
mcore->flash_buswidth = 2;
if (bus->chipco.dev) {
mcore->flash_window = 0x1c000000;
mcore->flash_window_size = 0x02000000;
if ((ssb_read32(bus->chipco.dev, SSB_CHIPCO_FLASH_CFG)
& SSB_CHIPCO_CFG_DS16) == 0)
mcore->flash_buswidth = 1;
} else {
mcore->flash_window = 0x1fc00000;
mcore->flash_window_size = 0x00400000;
}
}
u32 ssb_cpu_clock(struct ssb_mipscore *mcore)
{
struct ssb_bus *bus = mcore->dev->bus;
u32 pll_type, n, m, rate = 0;
if (bus->extif.dev) {
ssb_extif_get_clockcontrol(&bus->extif, &pll_type, &n, &m);
} else if (bus->chipco.dev) {
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 = mcore->dev->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_dprintk(KERN_INFO PFX "Initializing MIPS core...\n");
hz = ssb_clockspeed(bus);
if (!hz)
hz = 100000000;
ns = 1000000000 / hz;
if (bus->extif.dev)
ssb_extif_timing_init(&bus->extif, ns);
else if (bus->chipco.dev)
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++) {
dev = &(bus->devices[i]);
dev->irq = ssb_mips_irq(dev) + 2;
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;
}
/* fallthrough */
case SSB_DEV_PCI:
case SSB_DEV_ETHERNET:
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;
}
}
}
ssb_mips_serial_init(mcore);
ssb_mips_flash_detect(mcore);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* 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 <mbuesch@freenet.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/pci.h>
#include <linux/ssb/ssb.h>
#ifdef CONFIG_PM
static int ssb_pcihost_suspend(struct pci_dev *dev, pm_message_t state)
{
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)
{
int err;
pci_set_power_state(dev, 0);
err = pci_enable_device(dev);
if (err)
return err;
pci_restore_state(dev);
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;
ssb = kzalloc(sizeof(*ssb), GFP_KERNEL);
if (!ssb)
goto out;
err = pci_enable_device(dev);
if (err)
goto err_kfree_ssb;
name = dev->dev.bus_id;
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);
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);
/*
* Sonics Silicon Backplane
* PCMCIA-Hostbus related functions
*
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2007 Michael Buesch <mb@bu3sch.de>
*
* Licensed under the GNU/GPL. See COPYING for details.
*/
#include <linux/ssb/ssb.h>
#include <linux/delay.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.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
int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
u8 coreidx)
{
struct pcmcia_device *pdev = bus->host_pcmcia;
int err;
int attempts = 0;
u32 cur_core;
conf_reg_t reg;
u32 addr;
u32 read_addr;
addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
while (1) {
reg.Action = CS_WRITE;
reg.Offset = 0x2E;
reg.Value = (addr & 0x0000F000) >> 12;
err = pcmcia_access_configuration_register(pdev, &reg);
if (err != CS_SUCCESS)
goto error;
reg.Offset = 0x30;
reg.Value = (addr & 0x00FF0000) >> 16;
err = pcmcia_access_configuration_register(pdev, &reg);
if (err != CS_SUCCESS)
goto error;
reg.Offset = 0x32;
reg.Value = (addr & 0xFF000000) >> 24;
err = pcmcia_access_configuration_register(pdev, &reg);
if (err != CS_SUCCESS)
goto error;
read_addr = 0;
reg.Action = CS_READ;
reg.Offset = 0x2E;
err = pcmcia_access_configuration_register(pdev, &reg);
if (err != CS_SUCCESS)
goto error;
read_addr |= (reg.Value & 0xF) << 12;
reg.Offset = 0x30;
err = pcmcia_access_configuration_register(pdev, &reg);
if (err != CS_SUCCESS)
goto error;
read_addr |= reg.Value << 16;
reg.Offset = 0x32;
err = pcmcia_access_configuration_register(pdev, &reg);
if (err != CS_SUCCESS)
goto error;
read_addr |= reg.Value << 24;
cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE;
if (cur_core == coreidx)
break;
if (attempts++ > SSB_BAR0_MAX_RETRIES)
goto error;
udelay(10);
}
return 0;
error:
ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
return -ENODEV;
}
int ssb_pcmcia_switch_core(struct ssb_bus *bus,
struct ssb_device *dev)
{
int err;
unsigned long flags;
#if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
ssb_printk(KERN_INFO PFX
"Switching to %s core, index %d\n",
ssb_core_name(dev->id.coreid),
dev->core_index);
#endif
spin_lock_irqsave(&bus->bar_lock, flags);
err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
if (!err)
bus->mapped_device = dev;
spin_unlock_irqrestore(&bus->bar_lock, flags);
return err;
}
int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
{
int attempts = 0;
unsigned long flags;
conf_reg_t reg;
int res, err = 0;
SSB_WARN_ON((seg != 0) && (seg != 1));
reg.Offset = 0x34;
reg.Function = 0;
spin_lock_irqsave(&bus->bar_lock, flags);
while (1) {
reg.Action = CS_WRITE;
reg.Value = seg;
res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
if (unlikely(res != CS_SUCCESS))
goto error;
reg.Value = 0xFF;
reg.Action = CS_READ;
res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
if (unlikely(res != CS_SUCCESS))
goto error;
if (reg.Value == seg)
break;
if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES))
goto error;
udelay(10);
}
bus->mapped_pcmcia_seg = seg;
out_unlock:
spin_unlock_irqrestore(&bus->bar_lock, flags);
return err;
error:
ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n");
err = -ENODEV;
goto out_unlock;
}
/* These are the main device register access functions.
* do_select_core is inline to have the likely hotpath inline.
* All unlikely codepaths are out-of-line. */
static inline int do_select_core(struct ssb_bus *bus,
struct ssb_device *dev,
u16 *offset)
{
int err;
u8 need_seg = (*offset >= 0x800) ? 1 : 0;
if (unlikely(dev != bus->mapped_device)) {
err = ssb_pcmcia_switch_core(bus, dev);
if (unlikely(err))
return err;
}
if (unlikely(need_seg != bus->mapped_pcmcia_seg)) {
err = ssb_pcmcia_switch_segment(bus, need_seg);
if (unlikely(err))
return err;
}
if (need_seg == 1)
*offset -= 0x800;
return 0;
}
static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
u16 x;
if (unlikely(do_select_core(bus, dev, &offset)))
return 0xFFFF;
x = readw(bus->mmio + offset);
return x;
}
static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
u32 x;
if (unlikely(do_select_core(bus, dev, &offset)))
return 0xFFFFFFFF;
x = readl(bus->mmio + offset);
return x;
}
static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
{
struct ssb_bus *bus = dev->bus;
if (unlikely(do_select_core(bus, dev, &offset)))
return;
writew(value, bus->mmio + offset);
}
static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
{
struct ssb_bus *bus = dev->bus;
if (unlikely(do_select_core(bus, dev, &offset)))
return;
readw(bus->mmio + offset);
writew(value >> 16, bus->mmio + offset + 2);
readw(bus->mmio + offset);
writew(value, bus->mmio + offset);
}
/* Not "static", as it's used in main.c */
const struct ssb_bus_ops ssb_pcmcia_ops = {
.read16 = ssb_pcmcia_read16,
.read32 = ssb_pcmcia_read32,
.write16 = ssb_pcmcia_write16,
.write32 = ssb_pcmcia_write32,
};
int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv)
{
//TODO
return 0;
}
int ssb_pcmcia_init(struct ssb_bus *bus)
{
conf_reg_t reg;
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 IRQ routing */
reg.Action = CS_READ;
reg.Function = 0;
if (bus->chip_id == 0x4306)
reg.Offset = 0x00;
else
reg.Offset = 0x80;
err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
if (err != CS_SUCCESS)
goto error;
reg.Action = CS_WRITE;
reg.Value |= 0x04 | 0x01;
err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
if (err != CS_SUCCESS)
goto error;
return 0;
error:
return -ENODEV;
}
/*
* Sonics Silicon Backplane
* Bus scanning
*
* Copyright (C) 2005-2007 Michael Buesch <mb@bu3sch.de>
* 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/cs_types.h>
#include <pcmcia/cs.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";
}
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_printk(KERN_ERR PFX
"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_printk(KERN_ERR PFX
"CHIPID not in nrcores fallback list\n");
}
return 1;
}
static u32 scan_read32(struct ssb_bus *bus, u8 current_coreidx,
u16 offset)
{
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);
break;
}
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);
}
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;
}
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;
}
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 == 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);
pci_read_config_word(bus->host_pci, PCI_REVISION_ID,
&bus->chip_rev);
bus->chip_package = 0;
} else {
bus->chip_id = 0x4710;
bus->chip_rev = 0;
bus->chip_package = 0;
}
}
if (!bus->nr_devices)
bus->nr_devices = chipid_to_nrcores(bus->chip_id);
if (bus->nr_devices > ARRAY_SIZE(bus->devices)) {
ssb_printk(KERN_ERR PFX
"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;
ssb_dprintk(KERN_INFO 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_dprintk(KERN_INFO PFX "Ignoring additional "
"802.11 core\n");
continue;
}
}
break;
case SSB_DEV_EXTIF:
#ifdef CONFIG_SSB_DRIVER_EXTIF
if (bus->extif.dev) {
ssb_printk(KERN_WARNING PFX
"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_printk(KERN_WARNING PFX
"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_printk(KERN_WARNING PFX
"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->pcicore.dev) {
ssb_printk(KERN_WARNING PFX
"WARNING: Multiple PCI(E) cores found\n");
break;
}
bus->pcicore.dev = dev;
#endif /* CONFIG_SSB_DRIVER_PCICORE */
break;
default:
break;
}
dev_i++;
}
bus->nr_devices = dev_i;
err = 0;
out:
return err;
err_unmap:
ssb_iounmap(bus);
goto out;
}
#ifndef LINUX_SSB_PRIVATE_H_
#define LINUX_SSB_PRIVATE_H_
#include <linux/ssb/ssb.h>
#include <linux/types.h>
#define PFX "ssb: "
#ifdef CONFIG_SSB_SILENT
# define ssb_printk(fmt, x...) do { /* nothing */ } while (0)
#else
# define ssb_printk printk
#endif /* CONFIG_SSB_SILENT */
/* dprintk: Debugging printk; vanishes for non-debug compilation */
#ifdef CONFIG_SSB_DEBUG
# define ssb_dprintk(fmt, x...) ssb_printk(fmt , ##x)
#else
# define ssb_dprintk(fmt, x...) do { /* nothing */ } 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_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_init(struct ssb_bus *bus)
{
return 0;
}
#endif /* CONFIG_SSB_PCMCIAHOST */
/* 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);
/* core.c */
extern u32 ssb_calc_clock_rate(u32 plltype, u32 n, u32 m);
extern int ssb_devices_freeze(struct ssb_bus *bus);
extern int ssb_devices_thaw(struct ssb_bus *bus);
extern struct ssb_bus *ssb_pci_dev_to_bus(struct pci_dev *pdev);
/* b43_pci_bridge.c */
#ifdef CONFIG_SSB_PCIHOST
extern int __init b43_pci_ssb_bridge_init(void);
extern void __exit b43_pci_ssb_bridge_exit(void);
#else /* CONFIG_SSB_PCIHOST */
static inline int b43_pci_ssb_bridge_init(void)
{
return 0;
}
static inline void b43_pci_ssb_bridge_exit(void)
{
}
#endif /* CONFIG_SSB_PCIHOST */
#endif /* LINUX_SSB_PRIVATE_H_ */
......@@ -340,4 +340,19 @@ struct parisc_device_id {
#define PA_HVERSION_ANY_ID 0xffff
#define PA_SVERSION_ANY_ID 0xffffffff
/* SSB core, see drivers/ssb/ */
struct ssb_device_id {
__u16 vendor;
__u16 coreid;
__u8 revision;
};
#define SSB_DEVICE(_vendor, _coreid, _revision) \
{ .vendor = _vendor, .coreid = _coreid, .revision = _revision, }
#define SSB_DEVTABLE_END \
{ 0, },
#define SSB_ANY_VENDOR 0xFFFF
#define SSB_ANY_ID 0xFFFF
#define SSB_ANY_REV 0xFF
#endif /* LINUX_MOD_DEVICETABLE_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef LINUX_SSB_MIPSCORE_H_
#define LINUX_SSB_MIPSCORE_H_
#ifdef CONFIG_SSB_DRIVER_MIPS
struct ssb_device;
struct ssb_serial_port {
void *regs;
unsigned long clockspeed;
unsigned int irq;
unsigned int baud_base;
unsigned int reg_shift;
};
struct ssb_mipscore {
struct ssb_device *dev;
int nr_serial_ports;
struct ssb_serial_port serial_ports[4];
u8 flash_buswidth;
u32 flash_window;
u32 flash_window_size;
};
extern void ssb_mipscore_init(struct ssb_mipscore *mcore);
extern u32 ssb_cpu_clock(struct ssb_mipscore *mcore);
extern unsigned int ssb_mips_irq(struct ssb_device *dev);
#else /* CONFIG_SSB_DRIVER_MIPS */
struct ssb_mipscore {
};
static inline
void ssb_mipscore_init(struct ssb_mipscore *mcore)
{
}
#endif /* CONFIG_SSB_DRIVER_MIPS */
#endif /* LINUX_SSB_MIPSCORE_H_ */
#ifndef LINUX_SSB_PCICORE_H_
#define LINUX_SSB_PCICORE_H_
#ifdef CONFIG_SSB_DRIVER_PCICORE
/* PCI core registers. */
#define SSB_PCICORE_CTL 0x0000 /* PCI Control */
#define SSB_PCICORE_CTL_RST_OE 0x00000001 /* PCI_RESET Output Enable */
#define SSB_PCICORE_CTL_RST 0x00000002 /* PCI_RESET driven out to pin */
#define SSB_PCICORE_CTL_CLK_OE 0x00000004 /* Clock gate Output Enable */
#define SSB_PCICORE_CTL_CLK 0x00000008 /* Gate for clock driven out to pin */
#define SSB_PCICORE_ARBCTL 0x0010 /* PCI Arbiter Control */
#define SSB_PCICORE_ARBCTL_INTERN 0x00000001 /* Use internal arbiter */
#define SSB_PCICORE_ARBCTL_EXTERN 0x00000002 /* Use external arbiter */
#define SSB_PCICORE_ARBCTL_PARKID 0x00000006 /* Mask, selects which agent is parked on an idle bus */
#define SSB_PCICORE_ARBCTL_PARKID_LAST 0x00000000 /* Last requestor */
#define SSB_PCICORE_ARBCTL_PARKID_4710 0x00000002 /* 4710 */
#define SSB_PCICORE_ARBCTL_PARKID_EXT0 0x00000004 /* External requestor 0 */
#define SSB_PCICORE_ARBCTL_PARKID_EXT1 0x00000006 /* External requestor 1 */
#define SSB_PCICORE_ISTAT 0x0020 /* Interrupt status */
#define SSB_PCICORE_ISTAT_INTA 0x00000001 /* PCI INTA# */
#define SSB_PCICORE_ISTAT_INTB 0x00000002 /* PCI INTB# */
#define SSB_PCICORE_ISTAT_SERR 0x00000004 /* PCI SERR# (write to clear) */
#define SSB_PCICORE_ISTAT_PERR 0x00000008 /* PCI PERR# (write to clear) */
#define SSB_PCICORE_ISTAT_PME 0x00000010 /* PCI PME# */
#define SSB_PCICORE_IMASK 0x0024 /* Interrupt mask */
#define SSB_PCICORE_IMASK_INTA 0x00000001 /* PCI INTA# */
#define SSB_PCICORE_IMASK_INTB 0x00000002 /* PCI INTB# */
#define SSB_PCICORE_IMASK_SERR 0x00000004 /* PCI SERR# */
#define SSB_PCICORE_IMASK_PERR 0x00000008 /* PCI PERR# */
#define SSB_PCICORE_IMASK_PME 0x00000010 /* PCI PME# */
#define SSB_PCICORE_MBOX 0x0028 /* Backplane to PCI Mailbox */
#define SSB_PCICORE_MBOX_F0_0 0x00000100 /* PCI function 0, INT 0 */
#define SSB_PCICORE_MBOX_F0_1 0x00000200 /* PCI function 0, INT 1 */
#define SSB_PCICORE_MBOX_F1_0 0x00000400 /* PCI function 1, INT 0 */
#define SSB_PCICORE_MBOX_F1_1 0x00000800 /* PCI function 1, INT 1 */
#define SSB_PCICORE_MBOX_F2_0 0x00001000 /* PCI function 2, INT 0 */
#define SSB_PCICORE_MBOX_F2_1 0x00002000 /* PCI function 2, INT 1 */
#define SSB_PCICORE_MBOX_F3_0 0x00004000 /* PCI function 3, INT 0 */
#define SSB_PCICORE_MBOX_F3_1 0x00008000 /* PCI function 3, INT 1 */
#define SSB_PCICORE_BCAST_ADDR 0x0050 /* Backplane Broadcast Address */
#define SSB_PCICORE_BCAST_ADDR_MASK 0x000000FF
#define SSB_PCICORE_BCAST_DATA 0x0054 /* Backplane Broadcast Data */
#define SSB_PCICORE_GPIO_IN 0x0060 /* rev >= 2 only */
#define SSB_PCICORE_GPIO_OUT 0x0064 /* rev >= 2 only */
#define SSB_PCICORE_GPIO_ENABLE 0x0068 /* rev >= 2 only */
#define SSB_PCICORE_GPIO_CTL 0x006C /* rev >= 2 only */
#define SSB_PCICORE_SBTOPCI0 0x0100 /* Backplane to PCI translation 0 (sbtopci0) */
#define SSB_PCICORE_SBTOPCI0_MASK 0xFC000000
#define SSB_PCICORE_SBTOPCI1 0x0104 /* Backplane to PCI translation 1 (sbtopci1) */
#define SSB_PCICORE_SBTOPCI1_MASK 0xFC000000
#define SSB_PCICORE_SBTOPCI2 0x0108 /* Backplane to PCI translation 2 (sbtopci2) */
#define SSB_PCICORE_SBTOPCI2_MASK 0xC0000000
/* SBtoPCIx */
#define SSB_PCICORE_SBTOPCI_MEM 0x00000000
#define SSB_PCICORE_SBTOPCI_IO 0x00000001
#define SSB_PCICORE_SBTOPCI_CFG0 0x00000002
#define SSB_PCICORE_SBTOPCI_CFG1 0x00000003
#define SSB_PCICORE_SBTOPCI_PREF 0x00000004 /* Prefetch enable */
#define SSB_PCICORE_SBTOPCI_BURST 0x00000008 /* Burst enable */
#define SSB_PCICORE_SBTOPCI_MRM 0x00000020 /* Memory Read Multiple */
#define SSB_PCICORE_SBTOPCI_RC 0x00000030 /* Read Command mask (rev >= 11) */
#define SSB_PCICORE_SBTOPCI_RC_READ 0x00000000 /* Memory read */
#define SSB_PCICORE_SBTOPCI_RC_READL 0x00000010 /* Memory read line */
#define SSB_PCICORE_SBTOPCI_RC_READM 0x00000020 /* Memory read multiple */
/* PCIcore specific boardflags */
#define SSB_PCICORE_BFL_NOPCI 0x00000400 /* Board leaves PCI floating */
struct ssb_pcicore {
struct ssb_device *dev;
u8 setup_done:1;
u8 hostmode:1;
u8 cardbusmode:1;
};
extern void ssb_pcicore_init(struct ssb_pcicore *pc);
/* Enable IRQ routing for a specific device */
extern int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
struct ssb_device *dev);
#else /* CONFIG_SSB_DRIVER_PCICORE */
struct ssb_pcicore {
};
static inline
void ssb_pcicore_init(struct ssb_pcicore *pc)
{
}
static inline
int ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc,
struct ssb_device *dev)
{
return 0;
}
#endif /* CONFIG_SSB_DRIVER_PCICORE */
#endif /* LINUX_SSB_PCICORE_H_ */
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment