All pastes #1512696 Raw Edit

Something

public text v1 · immutable
#1512696 ·published 2009-07-30 19:47 UTC
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;
}