rendered paste body/*
* Sonics Silicon Backplane
* SDIO-Hostbus related functions
*
* Copyright 2009 Albert Herranz
*
* Based on drivers/ssb/sdio.c
* Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2007-2008 Michael Buesch <mb@bu3sch.de>
*
* 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 DBG(fmt, arg...) printk(KERN_DEBUG "%s: " fmt, \
__func__, ## arg)
/* Define the following to 1 to enable a printk on each coreswitch. */
#define SSB_VERBOSE_SDIOCORESWITCH_DEBUG 1
/* Hardware invariants CIS tuples */
#define SSB_SDIO_CIS 0x80
#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
/* SDIO SPROM size. */
#define SSB_SDIO_SPROM_SIZE 256
#define SSB_SDIO_SPROM_SIZE_BYTES (SSB_SDIO_SPROM_SIZE * sizeof(u16))
/*
* 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
* a
*/
/* function 1 miscellaneous registers */
#define SBSDIO_SPROM_CS 0x10000 /* sprom command and status */
#define SBSDIO_SPROM_INFO 0x10001 /* sprom info register */
#define SBSDIO_SPROM_DATA_LOW 0x10002 /* sprom indirect access data byte 0 */
#define SBSDIO_SPROM_DATA_HIGH 0x10003 /* sprom indirect access data byte 1 */
#define SBSDIO_SPROM_ADDR_LOW 0x10004 /* sprom indirect access addr byte 0 */
#define SBSDIO_SPROM_ADDR_HIGH 0x10005 /* sprom indirect access addr byte 1 */
#define SBSDIO_CHIP_CTRL_DATA 0x10006 /* xtal_pu (gpio) output */
#define SBSDIO_CHIP_CTRL_XTAL 0x01
#define SBSDIO_CHIP_CTRL_EN 0x10007 /* xtal_pu (gpio) enable */
#define SBSDIO_WATERMARK 0x10008 /* rev < 7, watermark for sdio device */
#define SBSDIO_DEVICE_CTL 0x10009 /* control busy signal generation */
/* registers introduced in rev 8, some content (mask/bits) defs in sbsdpcmdev.h */
#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) */
#define SBSDIO_FUNC1_FRAMECTRL 0x1000D /* Frame Control (frame term/abort) */
#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E /* ChipClockCSR (ALP/HT ctl/status) */
#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F /* SdioPullUp (on cmd, d0-d2) */
/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */
#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */
#define SBSDIO_SBADDRHIGH_MASK 0xff /* Valid bits in SBADDRHIGH */
#define SBSDIO_SBWINDOW_MASK 0xffff8000 /* Address bits from SBADDR regs */
/* function 1 OCP space */
#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF /* sb offset addr is <= 15 bits, 32k */
#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000
#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 /* with b15, maps to 32-bit SB access */
/* SBSDIO_FUNC1_CHIPCLKCSR */
#define SBSDIO_FORCE_ALP 0x01 /* Force ALP request to backplane */
#define SBSDIO_FORCE_HT 0x02 /* Force HT request to b ackplane */
#define SBSDIO_FORCE_ILP 0x04 /* Force ILP request to backplane */
#define SBSDIO_ALP_AVAIL_REQ 0x08 /* Make ALP ready (power up xtal) */
#define SBSDIO_HT_AVAIL_REQ 0x10 /* Make HT ready (power up PLL) */
#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 /* Squelch clock requests from HW */
#define SBSDIO_ALP_AVAIL 0x40 /* Status: ALP is ready */
#define SBSDIO_HT_AVAIL 0x80 /* Status: HT is ready */
/* SBSDIO_DEVICE_CTL */
#define SBSDIO_DEVCTL_SETBUSY 0x01 /* 1: device will assert busy signal when
* receiving CMD53
*/
#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 /* 1: assertion of sdio interrupt is
* synchronous to the sdio clock
*/
#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 /* 1: mask all interrupts to host
* except the chipActive (rev 8)
*/
#define SBSDIO_DEVCTL_PADS_ISO 0x08 /* 1: isolate internal sdio signals, put
* external pads in tri-state; requires
* sdio bus power cycle to clear (rev 9)
*/
/*
* addr is 17 bits (address space is 128K)
*/
/* SSB I/O via SDIO */
static int ssb_sdio_writeb(struct ssb_bus *bus, unsigned int addr, u8 val)
{
int error = 0;
// DBG("%08X <- %02x\n", addr, val);
sdio_writeb(bus->sdio_func, val, addr, &error);
if (unlikely(error))
DBG("error %d\n", error);
return error;
}
static u8 ssb_sdio_readb(struct ssb_bus *bus, unsigned int addr)
{
u8 val;
int error = 0;
val = sdio_readb(bus->sdio_func, addr, &error);
// DBG("%08X -> %02x\n", addr, val);
if (unlikely(error))
DBG("error %d\n", error);
return val;
}
u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset)
{
u32 val;
int error;
sdio_claim_host(bus->sdio_func);
val = sdio_readl(bus->sdio_func, offset, &error);
sdio_release_host(bus->sdio_func);
// DBG("%04X:%04X > %08x\n", bus->sbaddr >> 16, offset, val);
if (unlikely(error))
DBG("error %d\n", error);
return val;
}
static int ssb_sdio_set_sbaddr_window(struct ssb_bus *bus, u32 address)
{
int error;
// ssb_printk(KERN_INFO PFX "enter %s\n", __func__);
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->sbaddr = address;
out:
if (error)
ssb_printk(KERN_ERR PFX "%s: error %d\n", __func__, error);
return error;
}
int ssb_sdio_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
{
u32 sbaddr;
int error;
sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
sdio_claim_host(bus->sdio_func);
error = ssb_sdio_set_sbaddr_window(bus, sbaddr);
sdio_release_host(bus->sdio_func);
if (error) {
ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n",
coreidx);
goto out;
}
out:
if (error)
ssb_printk(KERN_ERR PFX "%s: error %d\n", __func__, error);
return error;
}
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->sbaddr != sbaddr)) {
#if SSB_VERBOSE_SDIOCORESWITCH_DEBUG
ssb_printk(KERN_INFO PFX
"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) {
ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n",
coreidx);
goto out;
}
bus->mapped_device = dev;
}
out:
if (error)
ssb_printk(KERN_ERR PFX "%s: error %d\n", __func__, error);
return error;
}
/* Enable/disable the on board crystal oscillator and/or PLL. */
int ssb_sdio_xtal(struct ssb_bus *bus, u32 what, int turn_on)
{
#if 0
u8 xtal_data, xtal_enable;
// u8 chipclk;
int error = 0;
sdio_claim_host(bus->sdio_func);
xtal_data = ssb_sdio_readb(bus, SBSDIO_CHIP_CTRL_DATA);
DBG("xtal_data=%02x\n", xtal_data);
xtal_enable = ssb_sdio_readb(bus, SBSDIO_CHIP_CTRL_EN);
DBG("xtal_enable=%02x\n", xtal_enable);
// chipclk = ssb_sdio_readb(bus, SBSDIO_FUNC1_CHIPCLKCSR);
if (what & SSB_GPIO_XTAL) {
if (turn_on) {
xtal_data |= SBSDIO_CHIP_CTRL_XTAL;
xtal_enable |= SBSDIO_CHIP_CTRL_XTAL;
// chipclk |= SBSDIO_ALP_AVAIL_REQ;
} else {
xtal_data &= ~SBSDIO_CHIP_CTRL_XTAL;
xtal_enable &= ~SBSDIO_CHIP_CTRL_XTAL;
// chipclk &= ~SBSDIO_ALP_AVAIL_REQ;
}
error = ssb_sdio_writeb(bus, SBSDIO_CHIP_CTRL_DATA, xtal_data);
if (error)
goto out;
error = ssb_sdio_writeb(bus, SBSDIO_CHIP_CTRL_EN, xtal_enable);
if (error)
goto out;
// ssb_sdio_writeb(bus, SBSDIO_FUNC1_CHIPCLKCSR, chipclk);
}
#if 0
if (what & SSB_GPIO_PLL) {
if (turn_on) {
chipclk |= SBSDIO_HT_AVAIL_REQ;
} else {
chipclk &= ~SBSDIO_HT_AVAIL_REQ;
}
// ssb_sdio_writeb(bus, SBSDIO_FUNC1_CHIPCLKCSR, chipclk);
}
#endif
out:
sdio_release_host(bus->sdio_func);
if (error)
DBG("error %d\n", error);
return error;
#else
return 0;
#endif
}
static u8 ssb_sdio_read8(struct ssb_device *dev, u16 offset)
{
struct ssb_bus *bus = dev->bus;
u8 val = 0xff;
int error = 0;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto err_out;
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
val = sdio_readb(bus->sdio_func, offset, &error);
// DBG("%04X:%04X > %02x\n", bus->sbaddr >> 16, offset, val);
if (error)
ssb_printk(KERN_INFO PFX "%s: error %d\n", __func__, error);
err_out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
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;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto err_out;
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
val = sdio_readw(bus->sdio_func, offset, &error);
// DBG("%04X:%04X > %04x\n", bus->sbaddr >> 16, offset, val);
if (error)
ssb_printk(KERN_INFO PFX "%s: error %d\n", __func__, error);
err_out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
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;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto err_out;
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
#if 1
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
val = sdio_readl(bus->sdio_func, offset, &error);
#else
{
u16 lo, hi;
lo = sdio_readw(bus->sdio_func, offset, &error);
if (error)
ssb_printk(KERN_INFO PFX "%s: 1st sdio_readw error %d\n", __func__, error);
if (!error) {
hi = sdio_readw(bus->sdio_func, offset + 2, &error);
if (error)
ssb_printk(KERN_INFO PFX "%s: 2nd sdio_readw error %d\n", __func__, error);
val = lo | (hi << 16);
}
}
#endif
// DBG("%04X:%04X > %08x\n", bus->sbaddr >> 16, offset, val);
if (error)
ssb_printk(KERN_INFO PFX "%s: error %d\n", __func__, error);
err_out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
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)
{
struct ssb_bus *bus = dev->bus;
int error = 0;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev))) {
error = -EIO;
memset(buffer, 0xff, count);
goto err_out;
}
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
// DBG("%04X:%04X, width=%u, len=%u\n", bus->sbaddr >> 16, offset, reg_width, count);
switch (reg_width) {
case sizeof(u8): {
u8 *buf = buffer;
while (count) {
*buf = sdio_readb(bus->sdio_func, offset, &error);
if (error) {
memset(buf, 0xff, count);
goto err_out;
}
buf++;
count--;
}
break;
}
case sizeof(u16): {
u16 *buf = buffer;
SSB_WARN_ON(count & 1);
while (count) {
*buf = sdio_readw(bus->sdio_func, offset, &error);
if (error) {
memset(buf, 0xff, count);
goto err_out;
}
buf++;
count -= 2;
}
break;
}
case sizeof(u32): {
u32 *buf = buffer;
size_t saved_count = count;
SSB_WARN_ON(count & 3);
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
while (count) {
*buf = sdio_readl(bus->sdio_func, offset, &error);
if (error) {
memset(buf, 0xff, count);
goto err_out;
}
buf++;
count -= 4;
}
// print_hex_dump(KERN_DEBUG, "blkrd32", DUMP_PREFIX_OFFSET,
// 16, 1, buffer, saved_count, 1);
break;
}
default:
SSB_WARN_ON(1);
}
goto out;
err_out:
DBG("%04X:%04X, width=%u, left=%u, ERROR! %d\n", bus->sbaddr >> 16, offset, reg_width, count, error);
out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
}
#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;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto err_out;
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
// DBG("%04X:%04X < %02x\n", bus->sbaddr >> 16, offset, val);
sdio_writeb(bus->sdio_func, val, offset, &error);
if (error)
ssb_printk(KERN_INFO PFX "%s: error %d\n", __func__, error);
err_out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
}
static void ssb_sdio_write16(struct ssb_device *dev, u16 offset, u16 val)
{
struct ssb_bus *bus = dev->bus;
int error = 0;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto err_out;
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
// DBG("%04X:%04X < %04x\n", bus->sbaddr >> 16, offset, val);
sdio_writew(bus->sdio_func, val, offset, &error);
if (error)
ssb_printk(KERN_INFO PFX "%s: error %d\n", __func__, error);
err_out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
}
static void ssb_sdio_write32(struct ssb_device *dev, u16 offset, u32 val)
{
struct ssb_bus *bus = dev->bus;
int error = 0;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev)))
goto err_out;
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
#if 1
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
// DBG("%04X:%04X < %08x\n", bus->sbaddr >> 16, offset, val);
sdio_writel(bus->sdio_func, val, offset, &error);
#else
DBG("%04X:%04X < %08x\n", bus->sbaddr >> 16, offset, val);
sdio_writew(bus->sdio_func, val & 0x0000ffff, offset, &error);
if (!error)
sdio_writew(bus->sdio_func, (val & 0xffff0000) >> 16, offset, &error);
#endif
if (error)
ssb_printk(KERN_INFO PFX "%s: error %d\n", __func__, error);
err_out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
}
#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)
{
struct ssb_bus *bus = dev->bus;
int error = 0;
mutex_lock(&bus->bar_mutex);
sdio_claim_host(bus->sdio_func);
if (unlikely(ssb_sdio_switch_core(bus, dev))) {
error = -EIO;
memset((void *)buffer, 0xff, count);
goto err_out;
}
offset |= bus->sbaddr & 0xffff;
offset &= SBSDIO_SB_OFT_ADDR_MASK;
DBG("%04X:%04X, width=%u, len=%u\n", bus->sbaddr >> 16, offset, reg_width, count);
switch (reg_width) {
case sizeof(u8): {
const u8 *buf = buffer;
while (count) {
sdio_writeb(bus->sdio_func, *buf, offset, &error);
if (error) {
memset((void *)buf, 0xff, count);
goto err_out;
}
buf++;
count--;
}
break;
}
case sizeof(u16): {
const u16 *buf = buffer;
while (count) {
sdio_writew(bus->sdio_func, *buf, offset, &error);
if (error) {
memset((void *)buf, 0xff, count);
goto err_out;
}
buf++;
count -= 2;
}
break;
}
case sizeof(u32): {
const u32 *buf = buffer;
offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */
while (count) {
sdio_writel(bus->sdio_func, *buf, offset, &error);
if (error) {
DBG("error %04X:%04X, width=%u, len=%u\n", bus->sbaddr >> 16, offset, reg_width, count);
memset((void *)buf, 0xff, count);
goto err_out;
}
buf++;
count -= 4;
}
break;
}
default:
SSB_WARN_ON(1);
}
goto out;
err_out:
DBG("error %d\n", error);
out:
sdio_release_host(bus->sdio_func);
mutex_unlock(&bus->bar_mutex);
}
#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
};
#if 0
#define GOTO_ERROR_ON(condition, description) do { \
if (unlikely(condition)) { \
error_description = description; \
goto error; \
} \
} while (0)
#else
#define GOTO_ERROR_ON(condition, description) do { \
if (unlikely(condition)) { \
ssb_printk(KERN_ERR PFX "%s\n", description); \
ssb_printk(KERN_INFO PFX "tuple: code %u size %u\n", \
tuple->data[0], tuple->size - 1); \
continue; \
} \
} while (0)
#endif
int ssb_sdio_get_invariants(struct ssb_bus *bus,
struct ssb_init_invariants *iv)
{
static char mac[] = {0x00,0x1d,0xbc,0x62,0x79,0xfd};
struct sdio_func_tuple *tuple;
struct ssb_sprom *sprom = &iv->sprom;
struct ssb_boardinfo *bi = &iv->boardinfo;
const char *error_description = "none";
memset(sprom, 0xFF, sizeof(*sprom));
sprom->revision = 1;
sprom->boardflags_lo = 0;
sprom->boardflags_hi = 0;
/* First fetch the MAC address. */
memcpy(sprom->il0mac, mac, ETH_ALEN);
memcpy(sprom->et1mac, mac, ETH_ALEN);
tuple = bus->sdio_func->tuples;
while (tuple) {
switch (tuple->code) {
case 0x80: /* vendor specific tuple */
switch (tuple->data[0]) {
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.ghz24.a0 = tuple->data[1];
sprom->antenna_gain.ghz24.a1 = tuple->data[1];
sprom->antenna_gain.ghz24.a2 = tuple->data[1];
sprom->antenna_gain.ghz24.a3 = tuple->data[1];
sprom->antenna_gain.ghz5.a0 = tuple->data[1];
sprom->antenna_gain.ghz5.a1 = tuple->data[1];
sprom->antenna_gain.ghz5.a2 = tuple->data[1];
sprom->antenna_gain.ghz5.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:
ssb_printk(KERN_INFO PFX "tuple: code %u size %u\n",
tuple->data[0], tuple->size - 1);
break;
}
break;
default:
break;
}
tuple = tuple->next;
}
return 0;
error:
ssb_printk(KERN_ERR PFX
"SDIO: Failed to fetch device invariants: %s\n",
error_description);
return -ENODEV;
}
/* Initialize the SDIO hardware. This is called on Init and Resume. */
int ssb_sdio_hardware_setup(struct ssb_bus *bus)
{
int error = 0;
ssb_printk(KERN_INFO PFX "%s\n", __func__);
if (bus->bustype != SSB_BUSTYPE_SDIO)
goto out;
{
int i;
sdio_claim_host(bus->sdio_func);
for (i = 0x10000; i <= 0x10018; i++)
DBG("reg %08x = %08x\n", i, ssb_sdio_readb(bus, i));
ssb_sdio_writeb(bus, SBSDIO_WATERMARK, 64-1);
for (i = 0x10000; i <= 0x10018; i++)
DBG("reg %08x = %08x\n", i, ssb_sdio_readb(bus, i));
sdio_release_host(bus->sdio_func);
}
#if 0
{
u8 clkset;
sdio_claim_host(bus->sdio_func);
clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
ssb_sdio_writeb(bus, SBSDIO_FUNC1_CHIPCLKCSR, clkset);
// ssb_sdio_writeb(bus, SBSDIO_FUNC1_SDIOPULLUP, 0);
sdio_release_host(bus->sdio_func);
}
#endif
out:
if (error)
ssb_printk(KERN_ERR PFX "%s: error %d\n", __func__, error);
ssb_printk(KERN_INFO PFX "exit %s\n", __func__);
return error;
}
void ssb_sdio_exit(struct ssb_bus *bus)
{
ssb_printk(KERN_INFO PFX "%s\n", __func__);
if (bus->bustype != SSB_BUSTYPE_SDIO)
return;
ssb_printk(KERN_INFO PFX "exit %s\n", __func__);
}
int ssb_sdio_init(struct ssb_bus *bus)
{
int error = 0;
ssb_printk(KERN_INFO PFX "%s\n", __func__);
if (bus->bustype != SSB_BUSTYPE_SDIO)
goto out;
bus->sbaddr = ~0;
mutex_init(&bus->bar_mutex);
error = ssb_sdio_hardware_setup(bus);
if (error)
goto err;
bus->sprom_size = 0;
// mutex_init(&bus->sprom_mutex);
goto out;
err:
ssb_printk(KERN_ERR PFX "Failed to initialize SDIO host device\n");
out:
if (error)
ssb_printk(KERN_ERR PFX "%s: error %d\n", __func__, error);
ssb_printk(KERN_INFO PFX "exit %s\n", __func__);
return error;
}