18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Sonics Silicon Backplane
38c2ecf20Sopenharmony_ci * SDIO-Hostbus related functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2009 Albert Herranz <albert_herranz@yahoo.es>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Based on drivers/ssb/pcmcia.c
88c2ecf20Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
98c2ecf20Sopenharmony_ci * Copyright 2007-2008 Michael Buesch <m@bues.ch>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "ssb_private.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/ssb/ssb.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/io.h>
208c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
218c2ecf20Sopenharmony_ci#include <linux/mmc/sdio_func.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* Define the following to 1 to enable a printk on each coreswitch. */
248c2ecf20Sopenharmony_ci#define SSB_VERBOSE_SDIOCORESWITCH_DEBUG		0
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* Hardware invariants CIS tuples */
288c2ecf20Sopenharmony_ci#define SSB_SDIO_CIS			0x80
298c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_SROMREV		0x00
308c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_ID		0x01
318c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_BOARDREV		0x02
328c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_PA		0x03
338c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B0_LO	0
348c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B0_HI	1
358c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B1_LO	2
368c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B1_HI	3
378c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B2_LO	4
388c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_PA0B2_HI	5
398c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_ITSSI		6
408c2ecf20Sopenharmony_ci#define   SSB_SDIO_CIS_PA_MAXPOW	7
418c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_OEMNAME		0x04
428c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_CCODE		0x05
438c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_ANTENNA		0x06
448c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_ANTGAIN		0x07
458c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_BFLAGS		0x08
468c2ecf20Sopenharmony_ci#define  SSB_SDIO_CIS_LEDS		0x09
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define CISTPL_FUNCE_LAN_NODE_ID        0x04	/* same as in PCMCIA */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * Function 1 miscellaneous registers.
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * Definitions match src/include/sbsdio.h from the
558c2ecf20Sopenharmony_ci * Android Open Source Project
568c2ecf20Sopenharmony_ci * http://android.git.kernel.org/?p=platform/system/wlan/broadcom.git
578c2ecf20Sopenharmony_ci *
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ci#define SBSDIO_FUNC1_SBADDRLOW	0x1000a	/* SB Address window Low (b15) */
608c2ecf20Sopenharmony_ci#define SBSDIO_FUNC1_SBADDRMID	0x1000b	/* SB Address window Mid (b23-b16) */
618c2ecf20Sopenharmony_ci#define SBSDIO_FUNC1_SBADDRHIGH	0x1000c	/* SB Address window High (b24-b31) */
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
648c2ecf20Sopenharmony_ci#define SBSDIO_SBADDRLOW_MASK	0x80	/* Valid address bits in SBADDRLOW */
658c2ecf20Sopenharmony_ci#define SBSDIO_SBADDRMID_MASK	0xff	/* Valid address bits in SBADDRMID */
668c2ecf20Sopenharmony_ci#define SBSDIO_SBADDRHIGH_MASK	0xff	/* Valid address bits in SBADDRHIGH */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci#define SBSDIO_SB_OFT_ADDR_MASK	0x7FFF	/* sb offset addr is <= 15 bits, 32k */
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* REVISIT: this flag doesn't seem to matter */
718c2ecf20Sopenharmony_ci#define SBSDIO_SB_ACCESS_2_4B_FLAG	0x8000	/* forces 32-bit SB access */
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Address map within the SDIO function address space (128K).
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci *   Start   End     Description
788c2ecf20Sopenharmony_ci *   ------- ------- ------------------------------------------
798c2ecf20Sopenharmony_ci *   0x00000 0x0ffff selected backplane address window (64K)
808c2ecf20Sopenharmony_ci *   0x10000 0x1ffff backplane control registers (max 64K)
818c2ecf20Sopenharmony_ci *
828c2ecf20Sopenharmony_ci * The current address window is configured by writing to registers
838c2ecf20Sopenharmony_ci * SBADDRLOW, SBADDRMID and SBADDRHIGH.
848c2ecf20Sopenharmony_ci *
858c2ecf20Sopenharmony_ci * In order to access the contents of a 32-bit Silicon Backplane address
868c2ecf20Sopenharmony_ci * the backplane address window must be first loaded with the highest
878c2ecf20Sopenharmony_ci * 16 bits of the target address. Then, an access must be done to the
888c2ecf20Sopenharmony_ci * SDIO function address space using the lower 15 bits of the address.
898c2ecf20Sopenharmony_ci * Bit 15 of the address must be set when doing 32 bit accesses.
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * 10987654321098765432109876543210
928c2ecf20Sopenharmony_ci * WWWWWWWWWWWWWWWWW                 SB Address Window
938c2ecf20Sopenharmony_ci *                 OOOOOOOOOOOOOOOO  Offset within SB Address Window
948c2ecf20Sopenharmony_ci *                 a                 32-bit access flag
958c2ecf20Sopenharmony_ci */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * SSB I/O via SDIO.
1008c2ecf20Sopenharmony_ci *
1018c2ecf20Sopenharmony_ci * NOTE: SDIO address @addr is 17 bits long (SDIO address space is 128K).
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic inline struct device *ssb_sdio_dev(struct ssb_bus *bus)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	return &bus->host_sdio->dev;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/* host claimed */
1108c2ecf20Sopenharmony_cistatic int ssb_sdio_writeb(struct ssb_bus *bus, unsigned int addr, u8 val)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	int error = 0;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	sdio_writeb(bus->host_sdio, val, addr, &error);
1158c2ecf20Sopenharmony_ci	if (unlikely(error)) {
1168c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%08X <- %02x, error %d\n",
1178c2ecf20Sopenharmony_ci			addr, val, error);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	return error;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#if 0
1248c2ecf20Sopenharmony_cistatic u8 ssb_sdio_readb(struct ssb_bus *bus, unsigned int addr)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	u8 val;
1278c2ecf20Sopenharmony_ci	int error = 0;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	val = sdio_readb(bus->host_sdio, addr, &error);
1308c2ecf20Sopenharmony_ci	if (unlikely(error)) {
1318c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%08X -> %02x, error %d\n",
1328c2ecf20Sopenharmony_ci			addr, val, error);
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return val;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci#endif
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* host claimed */
1408c2ecf20Sopenharmony_cistatic int ssb_sdio_set_sbaddr_window(struct ssb_bus *bus, u32 address)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	int error;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRLOW,
1458c2ecf20Sopenharmony_ci				(address >> 8) & SBSDIO_SBADDRLOW_MASK);
1468c2ecf20Sopenharmony_ci	if (error)
1478c2ecf20Sopenharmony_ci		goto out;
1488c2ecf20Sopenharmony_ci	error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRMID,
1498c2ecf20Sopenharmony_ci				(address >> 16) & SBSDIO_SBADDRMID_MASK);
1508c2ecf20Sopenharmony_ci	if (error)
1518c2ecf20Sopenharmony_ci		goto out;
1528c2ecf20Sopenharmony_ci	error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRHIGH,
1538c2ecf20Sopenharmony_ci				(address >> 24) & SBSDIO_SBADDRHIGH_MASK);
1548c2ecf20Sopenharmony_ci	if (error)
1558c2ecf20Sopenharmony_ci		goto out;
1568c2ecf20Sopenharmony_ci	bus->sdio_sbaddr = address;
1578c2ecf20Sopenharmony_ciout:
1588c2ecf20Sopenharmony_ci	if (error) {
1598c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "failed to set address window"
1608c2ecf20Sopenharmony_ci			" to 0x%08x, error %d\n", address, error);
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return error;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/* for enumeration use only */
1678c2ecf20Sopenharmony_ciu32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	u32 val;
1708c2ecf20Sopenharmony_ci	int error;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
1738c2ecf20Sopenharmony_ci	val = sdio_readl(bus->host_sdio, offset, &error);
1748c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
1758c2ecf20Sopenharmony_ci	if (unlikely(error)) {
1768c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",
1778c2ecf20Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return val;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/* for enumeration use only */
1848c2ecf20Sopenharmony_ciint ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	u32 sbaddr;
1878c2ecf20Sopenharmony_ci	int error;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
1908c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
1918c2ecf20Sopenharmony_ci	error = ssb_sdio_set_sbaddr_window(bus, sbaddr);
1928c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
1938c2ecf20Sopenharmony_ci	if (error) {
1948c2ecf20Sopenharmony_ci		dev_err(ssb_sdio_dev(bus), "failed to switch to core %u,"
1958c2ecf20Sopenharmony_ci			" error %d\n", coreidx, error);
1968c2ecf20Sopenharmony_ci		goto out;
1978c2ecf20Sopenharmony_ci	}
1988c2ecf20Sopenharmony_ciout:
1998c2ecf20Sopenharmony_ci	return error;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci/* host must be already claimed */
2038c2ecf20Sopenharmony_cistatic int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	u8 coreidx = dev->core_index;
2068c2ecf20Sopenharmony_ci	u32 sbaddr;
2078c2ecf20Sopenharmony_ci	int error = 0;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
2108c2ecf20Sopenharmony_ci	if (unlikely(bus->sdio_sbaddr != sbaddr)) {
2118c2ecf20Sopenharmony_ci#if SSB_VERBOSE_SDIOCORESWITCH_DEBUG
2128c2ecf20Sopenharmony_ci		dev_info(ssb_sdio_dev(bus),
2138c2ecf20Sopenharmony_ci			   "switching to %s core, index %d\n",
2148c2ecf20Sopenharmony_ci			   ssb_core_name(dev->id.coreid), coreidx);
2158c2ecf20Sopenharmony_ci#endif
2168c2ecf20Sopenharmony_ci		error = ssb_sdio_set_sbaddr_window(bus, sbaddr);
2178c2ecf20Sopenharmony_ci		if (error) {
2188c2ecf20Sopenharmony_ci			dev_dbg(ssb_sdio_dev(bus), "failed to switch to"
2198c2ecf20Sopenharmony_ci				" core %u, error %d\n", coreidx, error);
2208c2ecf20Sopenharmony_ci			goto out;
2218c2ecf20Sopenharmony_ci		}
2228c2ecf20Sopenharmony_ci		bus->mapped_device = dev;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ciout:
2268c2ecf20Sopenharmony_ci	return error;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic u8 ssb_sdio_read8(struct ssb_device *dev, u16 offset)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
2328c2ecf20Sopenharmony_ci	u8 val = 0xff;
2338c2ecf20Sopenharmony_ci	int error = 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
2368c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
2378c2ecf20Sopenharmony_ci		goto out;
2388c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
2398c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
2408c2ecf20Sopenharmony_ci	val = sdio_readb(bus->host_sdio, offset, &error);
2418c2ecf20Sopenharmony_ci	if (error) {
2428c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %02x, error %d\n",
2438c2ecf20Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ciout:
2468c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return val;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic u16 ssb_sdio_read16(struct ssb_device *dev, u16 offset)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
2548c2ecf20Sopenharmony_ci	u16 val = 0xffff;
2558c2ecf20Sopenharmony_ci	int error = 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
2588c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
2598c2ecf20Sopenharmony_ci		goto out;
2608c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
2618c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
2628c2ecf20Sopenharmony_ci	val = sdio_readw(bus->host_sdio, offset, &error);
2638c2ecf20Sopenharmony_ci	if (error) {
2648c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %04x, error %d\n",
2658c2ecf20Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ciout:
2688c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return val;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic u32 ssb_sdio_read32(struct ssb_device *dev, u16 offset)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
2768c2ecf20Sopenharmony_ci	u32 val = 0xffffffff;
2778c2ecf20Sopenharmony_ci	int error = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
2808c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
2818c2ecf20Sopenharmony_ci		goto out;
2828c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
2838c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
2848c2ecf20Sopenharmony_ci	offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
2858c2ecf20Sopenharmony_ci	val = sdio_readl(bus->host_sdio, offset, &error);
2868c2ecf20Sopenharmony_ci	if (error) {
2878c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",
2888c2ecf20Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ciout:
2918c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return val;
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO
2978c2ecf20Sopenharmony_cistatic void ssb_sdio_block_read(struct ssb_device *dev, void *buffer,
2988c2ecf20Sopenharmony_ci				  size_t count, u16 offset, u8 reg_width)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	size_t saved_count = count;
3018c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
3028c2ecf20Sopenharmony_ci	int error = 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
3058c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev))) {
3068c2ecf20Sopenharmony_ci		error = -EIO;
3078c2ecf20Sopenharmony_ci		memset(buffer, 0xff, count);
3088c2ecf20Sopenharmony_ci		goto err_out;
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
3118c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	switch (reg_width) {
3148c2ecf20Sopenharmony_ci	case sizeof(u8): {
3158c2ecf20Sopenharmony_ci		error = sdio_readsb(bus->host_sdio, buffer, offset, count);
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci	case sizeof(u16): {
3198c2ecf20Sopenharmony_ci		WARN_ON(count & 1);
3208c2ecf20Sopenharmony_ci		error = sdio_readsb(bus->host_sdio, buffer, offset, count);
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci	case sizeof(u32): {
3248c2ecf20Sopenharmony_ci		WARN_ON(count & 3);
3258c2ecf20Sopenharmony_ci		offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
3268c2ecf20Sopenharmony_ci		error = sdio_readsb(bus->host_sdio, buffer, offset, count);
3278c2ecf20Sopenharmony_ci		break;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci	default:
3308c2ecf20Sopenharmony_ci		WARN_ON(1);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci	if (!error)
3338c2ecf20Sopenharmony_ci		goto out;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cierr_out:
3368c2ecf20Sopenharmony_ci	dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",
3378c2ecf20Sopenharmony_ci		bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);
3388c2ecf20Sopenharmony_ciout:
3398c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_BLOCKIO */
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic void ssb_sdio_write8(struct ssb_device *dev, u16 offset, u8 val)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
3468c2ecf20Sopenharmony_ci	int error = 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
3498c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
3508c2ecf20Sopenharmony_ci		goto out;
3518c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
3528c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
3538c2ecf20Sopenharmony_ci	sdio_writeb(bus->host_sdio, val, offset, &error);
3548c2ecf20Sopenharmony_ci	if (error) {
3558c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %02x, error %d\n",
3568c2ecf20Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ciout:
3598c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic void ssb_sdio_write16(struct ssb_device *dev, u16 offset, u16 val)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
3658c2ecf20Sopenharmony_ci	int error = 0;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
3688c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
3698c2ecf20Sopenharmony_ci		goto out;
3708c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
3718c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
3728c2ecf20Sopenharmony_ci	sdio_writew(bus->host_sdio, val, offset, &error);
3738c2ecf20Sopenharmony_ci	if (error) {
3748c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %04x, error %d\n",
3758c2ecf20Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ciout:
3788c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_cistatic void ssb_sdio_write32(struct ssb_device *dev, u16 offset, u32 val)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
3848c2ecf20Sopenharmony_ci	int error = 0;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
3878c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev)))
3888c2ecf20Sopenharmony_ci		goto out;
3898c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
3908c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
3918c2ecf20Sopenharmony_ci	offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
3928c2ecf20Sopenharmony_ci	sdio_writel(bus->host_sdio, val, offset, &error);
3938c2ecf20Sopenharmony_ci	if (error) {
3948c2ecf20Sopenharmony_ci		dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %08x, error %d\n",
3958c2ecf20Sopenharmony_ci			bus->sdio_sbaddr >> 16, offset, val, error);
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	if (bus->quirks & SSB_QUIRK_SDIO_READ_AFTER_WRITE32)
3988c2ecf20Sopenharmony_ci		sdio_readl(bus->host_sdio, 0, &error);
3998c2ecf20Sopenharmony_ciout:
4008c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO
4048c2ecf20Sopenharmony_cistatic void ssb_sdio_block_write(struct ssb_device *dev, const void *buffer,
4058c2ecf20Sopenharmony_ci				   size_t count, u16 offset, u8 reg_width)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	size_t saved_count = count;
4088c2ecf20Sopenharmony_ci	struct ssb_bus *bus = dev->bus;
4098c2ecf20Sopenharmony_ci	int error = 0;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	sdio_claim_host(bus->host_sdio);
4128c2ecf20Sopenharmony_ci	if (unlikely(ssb_sdio_switch_core(bus, dev))) {
4138c2ecf20Sopenharmony_ci		error = -EIO;
4148c2ecf20Sopenharmony_ci		goto err_out;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci	offset |= bus->sdio_sbaddr & 0xffff;
4178c2ecf20Sopenharmony_ci	offset &= SBSDIO_SB_OFT_ADDR_MASK;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	switch (reg_width) {
4208c2ecf20Sopenharmony_ci	case sizeof(u8):
4218c2ecf20Sopenharmony_ci		error = sdio_writesb(bus->host_sdio, offset,
4228c2ecf20Sopenharmony_ci				     (void *)buffer, count);
4238c2ecf20Sopenharmony_ci		break;
4248c2ecf20Sopenharmony_ci	case sizeof(u16):
4258c2ecf20Sopenharmony_ci		WARN_ON(count & 1);
4268c2ecf20Sopenharmony_ci		error = sdio_writesb(bus->host_sdio, offset,
4278c2ecf20Sopenharmony_ci				     (void *)buffer, count);
4288c2ecf20Sopenharmony_ci		break;
4298c2ecf20Sopenharmony_ci	case sizeof(u32):
4308c2ecf20Sopenharmony_ci		WARN_ON(count & 3);
4318c2ecf20Sopenharmony_ci		offset |= SBSDIO_SB_ACCESS_2_4B_FLAG;	/* 32 bit data access */
4328c2ecf20Sopenharmony_ci		error = sdio_writesb(bus->host_sdio, offset,
4338c2ecf20Sopenharmony_ci				     (void *)buffer, count);
4348c2ecf20Sopenharmony_ci		break;
4358c2ecf20Sopenharmony_ci	default:
4368c2ecf20Sopenharmony_ci		WARN_ON(1);
4378c2ecf20Sopenharmony_ci	}
4388c2ecf20Sopenharmony_ci	if (!error)
4398c2ecf20Sopenharmony_ci		goto out;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_cierr_out:
4428c2ecf20Sopenharmony_ci	dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",
4438c2ecf20Sopenharmony_ci		bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);
4448c2ecf20Sopenharmony_ciout:
4458c2ecf20Sopenharmony_ci	sdio_release_host(bus->host_sdio);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_BLOCKIO */
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/* Not "static", as it's used in main.c */
4518c2ecf20Sopenharmony_ciconst struct ssb_bus_ops ssb_sdio_ops = {
4528c2ecf20Sopenharmony_ci	.read8		= ssb_sdio_read8,
4538c2ecf20Sopenharmony_ci	.read16		= ssb_sdio_read16,
4548c2ecf20Sopenharmony_ci	.read32		= ssb_sdio_read32,
4558c2ecf20Sopenharmony_ci	.write8		= ssb_sdio_write8,
4568c2ecf20Sopenharmony_ci	.write16	= ssb_sdio_write16,
4578c2ecf20Sopenharmony_ci	.write32	= ssb_sdio_write32,
4588c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO
4598c2ecf20Sopenharmony_ci	.block_read	= ssb_sdio_block_read,
4608c2ecf20Sopenharmony_ci	.block_write	= ssb_sdio_block_write,
4618c2ecf20Sopenharmony_ci#endif
4628c2ecf20Sopenharmony_ci};
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci#define GOTO_ERROR_ON(condition, description) do {	\
4658c2ecf20Sopenharmony_ci	if (unlikely(condition)) {			\
4668c2ecf20Sopenharmony_ci		error_description = description;	\
4678c2ecf20Sopenharmony_ci		goto error;				\
4688c2ecf20Sopenharmony_ci	}						\
4698c2ecf20Sopenharmony_ci  } while (0)
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ciint ssb_sdio_get_invariants(struct ssb_bus *bus,
4728c2ecf20Sopenharmony_ci			    struct ssb_init_invariants *iv)
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	struct ssb_sprom *sprom = &iv->sprom;
4758c2ecf20Sopenharmony_ci	struct ssb_boardinfo *bi = &iv->boardinfo;
4768c2ecf20Sopenharmony_ci	const char *error_description = "none";
4778c2ecf20Sopenharmony_ci	struct sdio_func_tuple *tuple;
4788c2ecf20Sopenharmony_ci	void *mac;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	memset(sprom, 0xFF, sizeof(*sprom));
4818c2ecf20Sopenharmony_ci	sprom->boardflags_lo = 0;
4828c2ecf20Sopenharmony_ci	sprom->boardflags_hi = 0;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	tuple = bus->host_sdio->tuples;
4858c2ecf20Sopenharmony_ci	while (tuple) {
4868c2ecf20Sopenharmony_ci		switch (tuple->code) {
4878c2ecf20Sopenharmony_ci		case 0x22: /* extended function */
4888c2ecf20Sopenharmony_ci			switch (tuple->data[0]) {
4898c2ecf20Sopenharmony_ci			case CISTPL_FUNCE_LAN_NODE_ID:
4908c2ecf20Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 7) &&
4918c2ecf20Sopenharmony_ci					      (tuple->data[1] != 6),
4928c2ecf20Sopenharmony_ci					      "mac tpl size");
4938c2ecf20Sopenharmony_ci				/* fetch the MAC address. */
4948c2ecf20Sopenharmony_ci				mac = tuple->data + 2;
4958c2ecf20Sopenharmony_ci				memcpy(sprom->il0mac, mac, ETH_ALEN);
4968c2ecf20Sopenharmony_ci				memcpy(sprom->et1mac, mac, ETH_ALEN);
4978c2ecf20Sopenharmony_ci				break;
4988c2ecf20Sopenharmony_ci			default:
4998c2ecf20Sopenharmony_ci				break;
5008c2ecf20Sopenharmony_ci			}
5018c2ecf20Sopenharmony_ci			break;
5028c2ecf20Sopenharmony_ci		case 0x80: /* vendor specific tuple */
5038c2ecf20Sopenharmony_ci			switch (tuple->data[0]) {
5048c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_SROMREV:
5058c2ecf20Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
5068c2ecf20Sopenharmony_ci					      "sromrev tpl size");
5078c2ecf20Sopenharmony_ci				sprom->revision = tuple->data[1];
5088c2ecf20Sopenharmony_ci				break;
5098c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_ID:
5108c2ecf20Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 5) &&
5118c2ecf20Sopenharmony_ci					      (tuple->size != 7),
5128c2ecf20Sopenharmony_ci					      "id tpl size");
5138c2ecf20Sopenharmony_ci				bi->vendor = tuple->data[1] |
5148c2ecf20Sopenharmony_ci					     (tuple->data[2]<<8);
5158c2ecf20Sopenharmony_ci				break;
5168c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_BOARDREV:
5178c2ecf20Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
5188c2ecf20Sopenharmony_ci					      "boardrev tpl size");
5198c2ecf20Sopenharmony_ci				sprom->board_rev = tuple->data[1];
5208c2ecf20Sopenharmony_ci				break;
5218c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_PA:
5228c2ecf20Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 9) &&
5238c2ecf20Sopenharmony_ci					      (tuple->size != 10),
5248c2ecf20Sopenharmony_ci					      "pa tpl size");
5258c2ecf20Sopenharmony_ci				sprom->pa0b0 = tuple->data[1] |
5268c2ecf20Sopenharmony_ci					 ((u16)tuple->data[2] << 8);
5278c2ecf20Sopenharmony_ci				sprom->pa0b1 = tuple->data[3] |
5288c2ecf20Sopenharmony_ci					 ((u16)tuple->data[4] << 8);
5298c2ecf20Sopenharmony_ci				sprom->pa0b2 = tuple->data[5] |
5308c2ecf20Sopenharmony_ci					 ((u16)tuple->data[6] << 8);
5318c2ecf20Sopenharmony_ci				sprom->itssi_a = tuple->data[7];
5328c2ecf20Sopenharmony_ci				sprom->itssi_bg = tuple->data[7];
5338c2ecf20Sopenharmony_ci				sprom->maxpwr_a = tuple->data[8];
5348c2ecf20Sopenharmony_ci				sprom->maxpwr_bg = tuple->data[8];
5358c2ecf20Sopenharmony_ci				break;
5368c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_OEMNAME:
5378c2ecf20Sopenharmony_ci				/* Not present */
5388c2ecf20Sopenharmony_ci				break;
5398c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_CCODE:
5408c2ecf20Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
5418c2ecf20Sopenharmony_ci					      "ccode tpl size");
5428c2ecf20Sopenharmony_ci				sprom->country_code = tuple->data[1];
5438c2ecf20Sopenharmony_ci				break;
5448c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_ANTENNA:
5458c2ecf20Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
5468c2ecf20Sopenharmony_ci					      "ant tpl size");
5478c2ecf20Sopenharmony_ci				sprom->ant_available_a = tuple->data[1];
5488c2ecf20Sopenharmony_ci				sprom->ant_available_bg = tuple->data[1];
5498c2ecf20Sopenharmony_ci				break;
5508c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_ANTGAIN:
5518c2ecf20Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 2,
5528c2ecf20Sopenharmony_ci					      "antg tpl size");
5538c2ecf20Sopenharmony_ci				sprom->antenna_gain.a0 = tuple->data[1];
5548c2ecf20Sopenharmony_ci				sprom->antenna_gain.a1 = tuple->data[1];
5558c2ecf20Sopenharmony_ci				sprom->antenna_gain.a2 = tuple->data[1];
5568c2ecf20Sopenharmony_ci				sprom->antenna_gain.a3 = tuple->data[1];
5578c2ecf20Sopenharmony_ci				break;
5588c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_BFLAGS:
5598c2ecf20Sopenharmony_ci				GOTO_ERROR_ON((tuple->size != 3) &&
5608c2ecf20Sopenharmony_ci					      (tuple->size != 5),
5618c2ecf20Sopenharmony_ci					      "bfl tpl size");
5628c2ecf20Sopenharmony_ci				sprom->boardflags_lo = tuple->data[1] |
5638c2ecf20Sopenharmony_ci						 ((u16)tuple->data[2] << 8);
5648c2ecf20Sopenharmony_ci				break;
5658c2ecf20Sopenharmony_ci			case SSB_SDIO_CIS_LEDS:
5668c2ecf20Sopenharmony_ci				GOTO_ERROR_ON(tuple->size != 5,
5678c2ecf20Sopenharmony_ci					      "leds tpl size");
5688c2ecf20Sopenharmony_ci				sprom->gpio0 = tuple->data[1];
5698c2ecf20Sopenharmony_ci				sprom->gpio1 = tuple->data[2];
5708c2ecf20Sopenharmony_ci				sprom->gpio2 = tuple->data[3];
5718c2ecf20Sopenharmony_ci				sprom->gpio3 = tuple->data[4];
5728c2ecf20Sopenharmony_ci				break;
5738c2ecf20Sopenharmony_ci			default:
5748c2ecf20Sopenharmony_ci				break;
5758c2ecf20Sopenharmony_ci			}
5768c2ecf20Sopenharmony_ci			break;
5778c2ecf20Sopenharmony_ci		default:
5788c2ecf20Sopenharmony_ci			break;
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci		tuple = tuple->next;
5818c2ecf20Sopenharmony_ci	}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	return 0;
5848c2ecf20Sopenharmony_cierror:
5858c2ecf20Sopenharmony_ci	dev_err(ssb_sdio_dev(bus), "failed to fetch device invariants: %s\n",
5868c2ecf20Sopenharmony_ci		error_description);
5878c2ecf20Sopenharmony_ci	return -ENODEV;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_civoid ssb_sdio_exit(struct ssb_bus *bus)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	if (bus->bustype != SSB_BUSTYPE_SDIO)
5938c2ecf20Sopenharmony_ci		return;
5948c2ecf20Sopenharmony_ci	/* Nothing to do here. */
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ciint ssb_sdio_init(struct ssb_bus *bus)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	if (bus->bustype != SSB_BUSTYPE_SDIO)
6008c2ecf20Sopenharmony_ci		return 0;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	bus->sdio_sbaddr = ~0;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	return 0;
6058c2ecf20Sopenharmony_ci}
606