mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 08:48:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
178
drivers/ssb/Kconfig
Normal file
178
drivers/ssb/Kconfig
Normal 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
25
drivers/ssb/Makefile
Normal 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
|
60
drivers/ssb/b43_pci_bridge.c
Normal file
60
drivers/ssb/b43_pci_bridge.c
Normal 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);
|
||||
}
|
697
drivers/ssb/driver_chipcommon.c
Normal file
697
drivers/ssb/driver_chipcommon.c
Normal 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 */
|
716
drivers/ssb/driver_chipcommon_pmu.c
Normal file
716
drivers/ssb/driver_chipcommon_pmu.c
Normal 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);
|
164
drivers/ssb/driver_chipcommon_sflash.c
Normal file
164
drivers/ssb/driver_chipcommon_sflash.c
Normal 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
201
drivers/ssb/driver_extif.c
Normal 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
297
drivers/ssb/driver_gige.c
Normal 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
485
drivers/ssb/driver_gpio.c
Normal 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;
|
||||
}
|
343
drivers/ssb/driver_mipscore.c
Normal file
343
drivers/ssb/driver_mipscore.c
Normal 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);
|
||||
}
|
725
drivers/ssb/driver_pcicore.c
Normal file
725
drivers/ssb/driver_pcicore.c
Normal 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
258
drivers/ssb/embedded.c
Normal 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
1505
drivers/ssb/main.c
Normal file
File diff suppressed because it is too large
Load diff
1189
drivers/ssb/pci.c
Normal file
1189
drivers/ssb/pci.c
Normal file
File diff suppressed because it is too large
Load diff
123
drivers/ssb/pcihost_wrapper.c
Normal file
123
drivers/ssb/pcihost_wrapper.c
Normal 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
842
drivers/ssb/pcmcia.c
Normal 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
445
drivers/ssb/scan.c
Normal 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
606
drivers/ssb/sdio.c
Normal 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
200
drivers/ssb/sprom.c
Normal 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
297
drivers/ssb/ssb_private.h
Normal 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_ */
|
Loading…
Add table
Add a link
Reference in a new issue