162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Sonics Silicon Backplane
362306a36Sopenharmony_ci * SDIO-Hostbus related functions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2009 Albert Herranz <albert_herranz@yahoo.es>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on drivers/ssb/pcmcia.c
862306a36Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
962306a36Sopenharmony_ci * Copyright 2007-2008 Michael Buesch <m@bues.ch>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "ssb_private.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/ssb/ssb.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/etherdevice.h>
2162306a36Sopenharmony_ci#include <linux/mmc/sdio_func.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Define the following to 1 to enable a printk on each coreswitch. */
2462306a36Sopenharmony_ci#define SSB_VERBOSE_SDIOCORESWITCH_DEBUG		0
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Hardware invariants CIS tuples */
2862306a36Sopenharmony_ci#define SSB_SDIO_CIS			0x80
2962306a36Sopenharmony_ci#define  SSB_SDIO_CIS_SROMREV		0x00
3062306a36Sopenharmony_ci#define  SSB_SDIO_CIS_ID		0x01
3162306a36Sopenharmony_ci#define  SSB_SDIO_CIS_BOARDREV		0x02
3262306a36Sopenharmony_ci#define  SSB_SDIO_CIS_PA		0x03
3362306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B0_LO	0
3462306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B0_HI	1
3562306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B1_LO	2
3662306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B1_HI	3
3762306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B2_LO	4
3862306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B2_HI	5
3962306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_ITSSI		6
4062306a36Sopenharmony_ci#define   SSB_SDIO_CIS_PA_MAXPOW	7
4162306a36Sopenharmony_ci#define  SSB_SDIO_CIS_OEMNAME		0x04
4262306a36Sopenharmony_ci#define  SSB_SDIO_CIS_CCODE		0x05
4362306a36Sopenharmony_ci#define  SSB_SDIO_CIS_ANTENNA		0x06
4462306a36Sopenharmony_ci#define  SSB_SDIO_CIS_ANTGAIN		0x07
4562306a36Sopenharmony_ci#define  SSB_SDIO_CIS_BFLAGS		0x08
4662306a36Sopenharmony_ci#define  SSB_SDIO_CIS_LEDS		0x09
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define CISTPL_FUNCE_LAN_NODE_ID        0x04	/* same as in PCMCIA */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Function 1 miscellaneous registers.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Definitions match src/include/sbsdio.h from the
5562306a36Sopenharmony_ci * Android Open Source Project
5662306a36Sopenharmony_ci * http://android.git.kernel.org/?p=platform/system/wlan/broadcom.git
5762306a36Sopenharmony_ci *
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ci#define SBSDIO_FUNC1_SBADDRLOW	0x1000a	/* SB Address window Low (b15) */
6062306a36Sopenharmony_ci#define SBSDIO_FUNC1_SBADDRMID	0x1000b	/* SB Address window Mid (b23-b16) */
6162306a36Sopenharmony_ci#define SBSDIO_FUNC1_SBADDRHIGH	0x1000c	/* SB Address window High (b24-b31) */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
6462306a36Sopenharmony_ci#define SBSDIO_SBADDRLOW_MASK	0x80	/* Valid address bits in SBADDRLOW */
6562306a36Sopenharmony_ci#define SBSDIO_SBADDRMID_MASK	0xff	/* Valid address bits in SBADDRMID */
6662306a36Sopenharmony_ci#define SBSDIO_SBADDRHIGH_MASK	0xff	/* Valid address bits in SBADDRHIGH */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define SBSDIO_SB_OFT_ADDR_MASK	0x7FFF	/* sb offset addr is <= 15 bits, 32k */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/* REVISIT: this flag doesn't seem to matter */
7162306a36Sopenharmony_ci#define SBSDIO_SB_ACCESS_2_4B_FLAG	0x8000	/* forces 32-bit SB access */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * Address map within the SDIO function address space (128K).
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci *   Start   End     Description
7862306a36Sopenharmony_ci *   ------- ------- ------------------------------------------
7962306a36Sopenharmony_ci *   0x00000 0x0ffff selected backplane address window (64K)
8062306a36Sopenharmony_ci *   0x10000 0x1ffff backplane control registers (max 64K)
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * The current address window is configured by writing to registers
8362306a36Sopenharmony_ci * SBADDRLOW, SBADDRMID and SBADDRHIGH.
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * In order to access the contents of a 32-bit Silicon Backplane address
8662306a36Sopenharmony_ci * the backplane address window must be first loaded with the highest
8762306a36Sopenharmony_ci * 16 bits of the target address. Then, an access must be done to the
8862306a36Sopenharmony_ci * SDIO function address space using the lower 15 bits of the address.
8962306a36Sopenharmony_ci * Bit 15 of the address must be set when doing 32 bit accesses.
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * 10987654321098765432109876543210
9262306a36Sopenharmony_ci * WWWWWWWWWWWWWWWWW                 SB Address Window
9362306a36Sopenharmony_ci *                 OOOOOOOOOOOOOOOO  Offset within SB Address Window
9462306a36Sopenharmony_ci *                 a                 32-bit access flag
9562306a36Sopenharmony_ci */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/*
9962306a36Sopenharmony_ci * SSB I/O via SDIO.
10062306a36Sopenharmony_ci *
10162306a36Sopenharmony_ci * NOTE: SDIO address @addr is 17 bits long (SDIO address space is 128K).
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic inline struct device *ssb_sdio_dev(struct ssb_bus *bus)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	return &bus->host_sdio->dev;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/* host claimed */
11062306a36Sopenharmony_cistatic int ssb_sdio_writeb(struct ssb_bus *bus, unsigned int addr, u8 val)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	int error = 0;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	sdio_writeb(bus->host_sdio, val, addr, &error);
11562306a36Sopenharmony_ci	if (unlikely(error)) {
11662306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%08X <- %02x, error %d\n",
11762306a36Sopenharmony_ci			addr, val, error);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return error;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#if 0
12462306a36Sopenharmony_cistatic u8 ssb_sdio_readb(struct ssb_bus *bus, unsigned int addr)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	u8 val;
12762306a36Sopenharmony_ci	int error = 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	val = sdio_readb(bus->host_sdio, addr, &error);
13062306a36Sopenharmony_ci	if (unlikely(error)) {
13162306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%08X -> %02x, error %d\n",
13262306a36Sopenharmony_ci			addr, val, error);
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return val;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci#endif
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* host claimed */
14062306a36Sopenharmony_cistatic int ssb_sdio_set_sbaddr_window(struct ssb_bus *bus, u32 address)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	int error;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRLOW,
14562306a36Sopenharmony_ci				(address >> 8) & SBSDIO_SBADDRLOW_MASK);
14662306a36Sopenharmony_ci	if (error)
14762306a36Sopenharmony_ci		goto out;
14862306a36Sopenharmony_ci	error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRMID,
14962306a36Sopenharmony_ci				(address >> 16) & SBSDIO_SBADDRMID_MASK);
15062306a36Sopenharmony_ci	if (error)
15162306a36Sopenharmony_ci		goto out;
15262306a36Sopenharmony_ci	error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRHIGH,
15362306a36Sopenharmony_ci				(address >> 24) & SBSDIO_SBADDRHIGH_MASK);
15462306a36Sopenharmony_ci	if (error)
15562306a36Sopenharmony_ci		goto out;
15662306a36Sopenharmony_ci	bus->sdio_sbaddr = address;
15762306a36Sopenharmony_ciout:
15862306a36Sopenharmony_ci	if (error) {
15962306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "failed to set address window"
16062306a36Sopenharmony_ci			" to 0x%08x, error %d\n", address, error);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return error;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/* for enumeration use only */
16762306a36Sopenharmony_ciu32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	u32 val;
17062306a36Sopenharmony_ci	int error;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
17362306a36Sopenharmony_ci	val = sdio_readl(bus->host_sdio, offset, &error);
17462306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
17562306a36Sopenharmony_ci	if (unlikely(error)) {
17662306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",
17762306a36Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return val;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* for enumeration use only */
18462306a36Sopenharmony_ciint ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	u32 sbaddr;
18762306a36Sopenharmony_ci	int error;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
19062306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
19162306a36Sopenharmony_ci	error = ssb_sdio_set_sbaddr_window(bus, sbaddr);
19262306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
19362306a36Sopenharmony_ci	if (error) {
19462306a36Sopenharmony_ci		dev_err(ssb_sdio_dev(bus), "failed to switch to core %u,"
19562306a36Sopenharmony_ci			" error %d\n", coreidx, error);
19662306a36Sopenharmony_ci		goto out;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ciout:
19962306a36Sopenharmony_ci	return error;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/* host must be already claimed */
20362306a36Sopenharmony_cistatic int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	u8 coreidx = dev->core_index;
20662306a36Sopenharmony_ci	u32 sbaddr;
20762306a36Sopenharmony_ci	int error = 0;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
21062306a36Sopenharmony_ci	if (unlikely(bus->sdio_sbaddr != sbaddr)) {
21162306a36Sopenharmony_ci#if SSB_VERBOSE_SDIOCORESWITCH_DEBUG
21262306a36Sopenharmony_ci		dev_info(ssb_sdio_dev(bus),
21362306a36Sopenharmony_ci			   "switching to %s core, index %d\n",
21462306a36Sopenharmony_ci			   ssb_core_name(dev->id.coreid), coreidx);
21562306a36Sopenharmony_ci#endif
21662306a36Sopenharmony_ci		error = ssb_sdio_set_sbaddr_window(bus, sbaddr);
21762306a36Sopenharmony_ci		if (error) {
21862306a36Sopenharmony_ci			dev_dbg(ssb_sdio_dev(bus), "failed to switch to"
21962306a36Sopenharmony_ci				" core %u, error %d\n", coreidx, error);
22062306a36Sopenharmony_ci			goto out;
22162306a36Sopenharmony_ci		}
22262306a36Sopenharmony_ci		bus->mapped_device = dev;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ciout:
22662306a36Sopenharmony_ci	return error;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic u8 ssb_sdio_read8(struct ssb_device *dev, u16 offset)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
23262306a36Sopenharmony_ci	u8 val = 0xff;
23362306a36Sopenharmony_ci	int error = 0;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
23662306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
23762306a36Sopenharmony_ci		goto out;
23862306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
23962306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
24062306a36Sopenharmony_ci	val = sdio_readb(bus->host_sdio, offset, &error);
24162306a36Sopenharmony_ci	if (error) {
24262306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %02x, error %d\n",
24362306a36Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ciout:
24662306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return val;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic u16 ssb_sdio_read16(struct ssb_device *dev, u16 offset)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
25462306a36Sopenharmony_ci	u16 val = 0xffff;
25562306a36Sopenharmony_ci	int error = 0;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
25862306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
25962306a36Sopenharmony_ci		goto out;
26062306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
26162306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
26262306a36Sopenharmony_ci	val = sdio_readw(bus->host_sdio, offset, &error);
26362306a36Sopenharmony_ci	if (error) {
26462306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %04x, error %d\n",
26562306a36Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ciout:
26862306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return val;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic u32 ssb_sdio_read32(struct ssb_device *dev, u16 offset)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
27662306a36Sopenharmony_ci	u32 val = 0xffffffff;
27762306a36Sopenharmony_ci	int error = 0;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
28062306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
28162306a36Sopenharmony_ci		goto out;
28262306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
28362306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
28462306a36Sopenharmony_ci	offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
28562306a36Sopenharmony_ci	val = sdio_readl(bus->host_sdio, offset, &error);
28662306a36Sopenharmony_ci	if (error) {
28762306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",
28862306a36Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ciout:
29162306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return val;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO
29762306a36Sopenharmony_cistatic void ssb_sdio_block_read(struct ssb_device *dev, void *buffer,
29862306a36Sopenharmony_ci				  size_t count, u16 offset, u8 reg_width)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	size_t saved_count = count;
30162306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
30262306a36Sopenharmony_ci	int error = 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
30562306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev))) {
30662306a36Sopenharmony_ci		error = -EIO;
30762306a36Sopenharmony_ci		memset(buffer, 0xff, count);
30862306a36Sopenharmony_ci		goto err_out;
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
31162306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	switch (reg_width) {
31462306a36Sopenharmony_ci	case sizeof(u8): {
31562306a36Sopenharmony_ci		error = sdio_readsb(bus->host_sdio, buffer, offset, count);
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci	case sizeof(u16): {
31962306a36Sopenharmony_ci		WARN_ON(count & 1);
32062306a36Sopenharmony_ci		error = sdio_readsb(bus->host_sdio, buffer, offset, count);
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	case sizeof(u32): {
32462306a36Sopenharmony_ci		WARN_ON(count & 3);
32562306a36Sopenharmony_ci		offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
32662306a36Sopenharmony_ci		error = sdio_readsb(bus->host_sdio, buffer, offset, count);
32762306a36Sopenharmony_ci		break;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	default:
33062306a36Sopenharmony_ci		WARN_ON(1);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci	if (!error)
33362306a36Sopenharmony_ci		goto out;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cierr_out:
33662306a36Sopenharmony_ci	dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",
33762306a36Sopenharmony_ci		bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);
33862306a36Sopenharmony_ciout:
33962306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci#endif /* CONFIG_SSB_BLOCKIO */
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic void ssb_sdio_write8(struct ssb_device *dev, u16 offset, u8 val)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
34662306a36Sopenharmony_ci	int error = 0;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
34962306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
35062306a36Sopenharmony_ci		goto out;
35162306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
35262306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
35362306a36Sopenharmony_ci	sdio_writeb(bus->host_sdio, val, offset, &error);
35462306a36Sopenharmony_ci	if (error) {
35562306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %02x, error %d\n",
35662306a36Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ciout:
35962306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void ssb_sdio_write16(struct ssb_device *dev, u16 offset, u16 val)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
36562306a36Sopenharmony_ci	int error = 0;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
36862306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
36962306a36Sopenharmony_ci		goto out;
37062306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
37162306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
37262306a36Sopenharmony_ci	sdio_writew(bus->host_sdio, val, offset, &error);
37362306a36Sopenharmony_ci	if (error) {
37462306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %04x, error %d\n",
37562306a36Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ciout:
37862306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void ssb_sdio_write32(struct ssb_device *dev, u16 offset, u32 val)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
38462306a36Sopenharmony_ci	int error = 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
38762306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
38862306a36Sopenharmony_ci		goto out;
38962306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
39062306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
39162306a36Sopenharmony_ci	offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
39262306a36Sopenharmony_ci	sdio_writel(bus->host_sdio, val, offset, &error);
39362306a36Sopenharmony_ci	if (error) {
39462306a36Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %08x, error %d\n",
39562306a36Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci	if (bus->quirks & SSB_QUIRK_SDIO_READ_AFTER_WRITE32)
39862306a36Sopenharmony_ci		sdio_readl(bus->host_sdio, 0, &error);
39962306a36Sopenharmony_ciout:
40062306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO
40462306a36Sopenharmony_cistatic void ssb_sdio_block_write(struct ssb_device *dev, const void *buffer,
40562306a36Sopenharmony_ci				   size_t count, u16 offset, u8 reg_width)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	size_t saved_count = count;
40862306a36Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
40962306a36Sopenharmony_ci	int error = 0;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
41262306a36Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev))) {
41362306a36Sopenharmony_ci		error = -EIO;
41462306a36Sopenharmony_ci		goto err_out;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
41762306a36Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	switch (reg_width) {
42062306a36Sopenharmony_ci	case sizeof(u8):
42162306a36Sopenharmony_ci		error = sdio_writesb(bus->host_sdio, offset,
42262306a36Sopenharmony_ci				     (void *)buffer, count);
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci	case sizeof(u16):
42562306a36Sopenharmony_ci		WARN_ON(count & 1);
42662306a36Sopenharmony_ci		error = sdio_writesb(bus->host_sdio, offset,
42762306a36Sopenharmony_ci				     (void *)buffer, count);
42862306a36Sopenharmony_ci		break;
42962306a36Sopenharmony_ci	case sizeof(u32):
43062306a36Sopenharmony_ci		WARN_ON(count & 3);
43162306a36Sopenharmony_ci		offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
43262306a36Sopenharmony_ci		error = sdio_writesb(bus->host_sdio, offset,
43362306a36Sopenharmony_ci				     (void *)buffer, count);
43462306a36Sopenharmony_ci		break;
43562306a36Sopenharmony_ci	default:
43662306a36Sopenharmony_ci		WARN_ON(1);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	if (!error)
43962306a36Sopenharmony_ci		goto out;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cierr_out:
44262306a36Sopenharmony_ci	dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",
44362306a36Sopenharmony_ci		bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);
44462306a36Sopenharmony_ciout:
44562306a36Sopenharmony_ci	sdio_release_host(bus->host_sdio);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci#endif /* CONFIG_SSB_BLOCKIO */
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci/* Not "static", as it's used in main.c */
45162306a36Sopenharmony_ciconst struct ssb_bus_ops ssb_sdio_ops = {
45262306a36Sopenharmony_ci	.read8		= ssb_sdio_read8,
45362306a36Sopenharmony_ci	.read16		= ssb_sdio_read16,
45462306a36Sopenharmony_ci	.read32		= ssb_sdio_read32,
45562306a36Sopenharmony_ci	.write8		= ssb_sdio_write8,
45662306a36Sopenharmony_ci	.write16	= ssb_sdio_write16,
45762306a36Sopenharmony_ci	.write32	= ssb_sdio_write32,
45862306a36Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO
45962306a36Sopenharmony_ci	.block_read	= ssb_sdio_block_read,
46062306a36Sopenharmony_ci	.block_write	= ssb_sdio_block_write,
46162306a36Sopenharmony_ci#endif
46262306a36Sopenharmony_ci};
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci#define GOTO_ERROR_ON(condition, description) do {	\
46562306a36Sopenharmony_ci	if (unlikely(condition)) {			\
46662306a36Sopenharmony_ci		error_description = description;	\
46762306a36Sopenharmony_ci		goto error;				\
46862306a36Sopenharmony_ci	}						\
46962306a36Sopenharmony_ci  } while (0)
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ciint ssb_sdio_get_invariants(struct ssb_bus *bus,
47262306a36Sopenharmony_ci			    struct ssb_init_invariants *iv)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct ssb_sprom *sprom = &iv->sprom;
47562306a36Sopenharmony_ci	struct ssb_boardinfo *bi = &iv->boardinfo;
47662306a36Sopenharmony_ci	const char *error_description = "none";
47762306a36Sopenharmony_ci	struct sdio_func_tuple *tuple;
47862306a36Sopenharmony_ci	void *mac;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	memset(sprom, 0xFF, sizeof(*sprom));
48162306a36Sopenharmony_ci	sprom->boardflags_lo = 0;
48262306a36Sopenharmony_ci	sprom->boardflags_hi = 0;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	tuple = bus->host_sdio->tuples;
48562306a36Sopenharmony_ci	while (tuple) {
48662306a36Sopenharmony_ci		switch (tuple->code) {
48762306a36Sopenharmony_ci		case 0x22: /* extended function */
48862306a36Sopenharmony_ci			switch (tuple->data[0]) {
48962306a36Sopenharmony_ci			case CISTPL_FUNCE_LAN_NODE_ID:
49062306a36Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 7) &&
49162306a36Sopenharmony_ci					      (tuple->data[1] != 6),
49262306a36Sopenharmony_ci					      "mac tpl size");
49362306a36Sopenharmony_ci				/* fetch the MAC address. */
49462306a36Sopenharmony_ci				mac = tuple->data + 2;
49562306a36Sopenharmony_ci				memcpy(sprom->il0mac, mac, ETH_ALEN);
49662306a36Sopenharmony_ci				memcpy(sprom->et1mac, mac, ETH_ALEN);
49762306a36Sopenharmony_ci				break;
49862306a36Sopenharmony_ci			default:
49962306a36Sopenharmony_ci				break;
50062306a36Sopenharmony_ci			}
50162306a36Sopenharmony_ci			break;
50262306a36Sopenharmony_ci		case 0x80: /* vendor specific tuple */
50362306a36Sopenharmony_ci			switch (tuple->data[0]) {
50462306a36Sopenharmony_ci			case SSB_SDIO_CIS_SROMREV:
50562306a36Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
50662306a36Sopenharmony_ci					      "sromrev tpl size");
50762306a36Sopenharmony_ci				sprom->revision = tuple->data[1];
50862306a36Sopenharmony_ci				break;
50962306a36Sopenharmony_ci			case SSB_SDIO_CIS_ID:
51062306a36Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 5) &&
51162306a36Sopenharmony_ci					      (tuple->size != 7),
51262306a36Sopenharmony_ci					      "id tpl size");
51362306a36Sopenharmony_ci				bi->vendor = tuple->data[1] |
51462306a36Sopenharmony_ci					     (tuple->data[2]<<8);
51562306a36Sopenharmony_ci				break;
51662306a36Sopenharmony_ci			case SSB_SDIO_CIS_BOARDREV:
51762306a36Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
51862306a36Sopenharmony_ci					      "boardrev tpl size");
51962306a36Sopenharmony_ci				sprom->board_rev = tuple->data[1];
52062306a36Sopenharmony_ci				break;
52162306a36Sopenharmony_ci			case SSB_SDIO_CIS_PA:
52262306a36Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 9) &&
52362306a36Sopenharmony_ci					      (tuple->size != 10),
52462306a36Sopenharmony_ci					      "pa tpl size");
52562306a36Sopenharmony_ci				sprom->pa0b0 = tuple->data[1] |
52662306a36Sopenharmony_ci					 ((u16)tuple->data[2] << 8);
52762306a36Sopenharmony_ci				sprom->pa0b1 = tuple->data[3] |
52862306a36Sopenharmony_ci					 ((u16)tuple->data[4] << 8);
52962306a36Sopenharmony_ci				sprom->pa0b2 = tuple->data[5] |
53062306a36Sopenharmony_ci					 ((u16)tuple->data[6] << 8);
53162306a36Sopenharmony_ci				sprom->itssi_a = tuple->data[7];
53262306a36Sopenharmony_ci				sprom->itssi_bg = tuple->data[7];
53362306a36Sopenharmony_ci				sprom->maxpwr_a = tuple->data[8];
53462306a36Sopenharmony_ci				sprom->maxpwr_bg = tuple->data[8];
53562306a36Sopenharmony_ci				break;
53662306a36Sopenharmony_ci			case SSB_SDIO_CIS_OEMNAME:
53762306a36Sopenharmony_ci				/* Not present */
53862306a36Sopenharmony_ci				break;
53962306a36Sopenharmony_ci			case SSB_SDIO_CIS_CCODE:
54062306a36Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
54162306a36Sopenharmony_ci					      "ccode tpl size");
54262306a36Sopenharmony_ci				sprom->country_code = tuple->data[1];
54362306a36Sopenharmony_ci				break;
54462306a36Sopenharmony_ci			case SSB_SDIO_CIS_ANTENNA:
54562306a36Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
54662306a36Sopenharmony_ci					      "ant tpl size");
54762306a36Sopenharmony_ci				sprom->ant_available_a = tuple->data[1];
54862306a36Sopenharmony_ci				sprom->ant_available_bg = tuple->data[1];
54962306a36Sopenharmony_ci				break;
55062306a36Sopenharmony_ci			case SSB_SDIO_CIS_ANTGAIN:
55162306a36Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
55262306a36Sopenharmony_ci					      "antg tpl size");
55362306a36Sopenharmony_ci				sprom->antenna_gain.a0 = tuple->data[1];
55462306a36Sopenharmony_ci				sprom->antenna_gain.a1 = tuple->data[1];
55562306a36Sopenharmony_ci				sprom->antenna_gain.a2 = tuple->data[1];
55662306a36Sopenharmony_ci				sprom->antenna_gain.a3 = tuple->data[1];
55762306a36Sopenharmony_ci				break;
55862306a36Sopenharmony_ci			case SSB_SDIO_CIS_BFLAGS:
55962306a36Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 3) &&
56062306a36Sopenharmony_ci					      (tuple->size != 5),
56162306a36Sopenharmony_ci					      "bfl tpl size");
56262306a36Sopenharmony_ci				sprom->boardflags_lo = tuple->data[1] |
56362306a36Sopenharmony_ci						 ((u16)tuple->data[2] << 8);
56462306a36Sopenharmony_ci				break;
56562306a36Sopenharmony_ci			case SSB_SDIO_CIS_LEDS:
56662306a36Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 5,
56762306a36Sopenharmony_ci					      "leds tpl size");
56862306a36Sopenharmony_ci				sprom->gpio0 = tuple->data[1];
56962306a36Sopenharmony_ci				sprom->gpio1 = tuple->data[2];
57062306a36Sopenharmony_ci				sprom->gpio2 = tuple->data[3];
57162306a36Sopenharmony_ci				sprom->gpio3 = tuple->data[4];
57262306a36Sopenharmony_ci				break;
57362306a36Sopenharmony_ci			default:
57462306a36Sopenharmony_ci				break;
57562306a36Sopenharmony_ci			}
57662306a36Sopenharmony_ci			break;
57762306a36Sopenharmony_ci		default:
57862306a36Sopenharmony_ci			break;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci		tuple = tuple->next;
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return 0;
58462306a36Sopenharmony_cierror:
58562306a36Sopenharmony_ci	dev_err(ssb_sdio_dev(bus), "failed to fetch device invariants: %s\n",
58662306a36Sopenharmony_ci		error_description);
58762306a36Sopenharmony_ci	return -ENODEV;
58862306a36Sopenharmony_ci}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_civoid ssb_sdio_exit(struct ssb_bus *bus)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	if (bus->bustype != SSB_BUSTYPE_SDIO)
59362306a36Sopenharmony_ci		return;
59462306a36Sopenharmony_ci	/* Nothing to do here. */
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ciint ssb_sdio_init(struct ssb_bus *bus)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	if (bus->bustype != SSB_BUSTYPE_SDIO)
60062306a36Sopenharmony_ci		return 0;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	bus->sdio_sbaddr = ~0;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	return 0;
60562306a36Sopenharmony_ci}
606