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