124ea602eSAlbert Herranz /* 224ea602eSAlbert Herranz * Sonics Silicon Backplane 324ea602eSAlbert Herranz * SDIO-Hostbus related functions 424ea602eSAlbert Herranz * 524ea602eSAlbert Herranz * Copyright 2009 Albert Herranz <albert_herranz@yahoo.es> 624ea602eSAlbert Herranz * 724ea602eSAlbert Herranz * Based on drivers/ssb/pcmcia.c 824ea602eSAlbert Herranz * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 924ea602eSAlbert Herranz * Copyright 2007-2008 Michael Buesch <mb@bu3sch.de> 1024ea602eSAlbert Herranz * 1124ea602eSAlbert Herranz * Licensed under the GNU/GPL. See COPYING for details. 1224ea602eSAlbert Herranz * 1324ea602eSAlbert Herranz */ 1424ea602eSAlbert Herranz 1524ea602eSAlbert Herranz #include <linux/ssb/ssb.h> 1624ea602eSAlbert Herranz #include <linux/delay.h> 1724ea602eSAlbert Herranz #include <linux/io.h> 1824ea602eSAlbert Herranz #include <linux/etherdevice.h> 1924ea602eSAlbert Herranz #include <linux/mmc/sdio_func.h> 2024ea602eSAlbert Herranz 2124ea602eSAlbert Herranz #include "ssb_private.h" 2224ea602eSAlbert Herranz 2324ea602eSAlbert Herranz /* Define the following to 1 to enable a printk on each coreswitch. */ 2454a68d14SMichael Buesch #define SSB_VERBOSE_SDIOCORESWITCH_DEBUG 0 2524ea602eSAlbert Herranz 2624ea602eSAlbert Herranz 2724ea602eSAlbert Herranz /* Hardware invariants CIS tuples */ 2824ea602eSAlbert Herranz #define SSB_SDIO_CIS 0x80 2924ea602eSAlbert Herranz #define SSB_SDIO_CIS_SROMREV 0x00 3024ea602eSAlbert Herranz #define SSB_SDIO_CIS_ID 0x01 3124ea602eSAlbert Herranz #define SSB_SDIO_CIS_BOARDREV 0x02 3224ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA 0x03 3324ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_PA0B0_LO 0 3424ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_PA0B0_HI 1 3524ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_PA0B1_LO 2 3624ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_PA0B1_HI 3 3724ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_PA0B2_LO 4 3824ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_PA0B2_HI 5 3924ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_ITSSI 6 4024ea602eSAlbert Herranz #define SSB_SDIO_CIS_PA_MAXPOW 7 4124ea602eSAlbert Herranz #define SSB_SDIO_CIS_OEMNAME 0x04 4224ea602eSAlbert Herranz #define SSB_SDIO_CIS_CCODE 0x05 4324ea602eSAlbert Herranz #define SSB_SDIO_CIS_ANTENNA 0x06 4424ea602eSAlbert Herranz #define SSB_SDIO_CIS_ANTGAIN 0x07 4524ea602eSAlbert Herranz #define SSB_SDIO_CIS_BFLAGS 0x08 4624ea602eSAlbert Herranz #define SSB_SDIO_CIS_LEDS 0x09 4724ea602eSAlbert Herranz 4824ea602eSAlbert Herranz #define CISTPL_FUNCE_LAN_NODE_ID 0x04 /* same as in PCMCIA */ 4924ea602eSAlbert Herranz 5024ea602eSAlbert Herranz 5124ea602eSAlbert Herranz /* 5224ea602eSAlbert Herranz * Function 1 miscellaneous registers. 5324ea602eSAlbert Herranz * 5424ea602eSAlbert Herranz * Definitions match src/include/sbsdio.h from the 5524ea602eSAlbert Herranz * Android Open Source Project 5624ea602eSAlbert Herranz * http://android.git.kernel.org/?p=platform/system/wlan/broadcom.git 5724ea602eSAlbert Herranz * 5824ea602eSAlbert Herranz */ 5924ea602eSAlbert Herranz #define SBSDIO_FUNC1_SBADDRLOW 0x1000a /* SB Address window Low (b15) */ 6024ea602eSAlbert Herranz #define SBSDIO_FUNC1_SBADDRMID 0x1000b /* SB Address window Mid (b23-b16) */ 6124ea602eSAlbert Herranz #define SBSDIO_FUNC1_SBADDRHIGH 0x1000c /* SB Address window High (b24-b31) */ 6224ea602eSAlbert Herranz 6324ea602eSAlbert Herranz /* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ 6424ea602eSAlbert Herranz #define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid address bits in SBADDRLOW */ 6524ea602eSAlbert Herranz #define SBSDIO_SBADDRMID_MASK 0xff /* Valid address bits in SBADDRMID */ 6624ea602eSAlbert Herranz #define SBSDIO_SBADDRHIGH_MASK 0xff /* Valid address bits in SBADDRHIGH */ 6724ea602eSAlbert Herranz 6824ea602eSAlbert Herranz #define SBSDIO_SB_OFT_ADDR_MASK 0x7FFF /* sb offset addr is <= 15 bits, 32k */ 6924ea602eSAlbert Herranz 7024ea602eSAlbert Herranz /* REVISIT: this flag doesn't seem to matter */ 7124ea602eSAlbert Herranz #define SBSDIO_SB_ACCESS_2_4B_FLAG 0x8000 /* forces 32-bit SB access */ 7224ea602eSAlbert Herranz 7324ea602eSAlbert Herranz 7424ea602eSAlbert Herranz /* 7524ea602eSAlbert Herranz * Address map within the SDIO function address space (128K). 7624ea602eSAlbert Herranz * 7724ea602eSAlbert Herranz * Start End Description 7824ea602eSAlbert Herranz * ------- ------- ------------------------------------------ 7924ea602eSAlbert Herranz * 0x00000 0x0ffff selected backplane address window (64K) 8024ea602eSAlbert Herranz * 0x10000 0x1ffff backplane control registers (max 64K) 8124ea602eSAlbert Herranz * 8224ea602eSAlbert Herranz * The current address window is configured by writing to registers 8324ea602eSAlbert Herranz * SBADDRLOW, SBADDRMID and SBADDRHIGH. 8424ea602eSAlbert Herranz * 8524ea602eSAlbert Herranz * In order to access the contents of a 32-bit Silicon Backplane address 8624ea602eSAlbert Herranz * the backplane address window must be first loaded with the highest 8724ea602eSAlbert Herranz * 16 bits of the target address. Then, an access must be done to the 8824ea602eSAlbert Herranz * SDIO function address space using the lower 15 bits of the address. 8924ea602eSAlbert Herranz * Bit 15 of the address must be set when doing 32 bit accesses. 9024ea602eSAlbert Herranz * 9124ea602eSAlbert Herranz * 10987654321098765432109876543210 9224ea602eSAlbert Herranz * WWWWWWWWWWWWWWWWW SB Address Window 9324ea602eSAlbert Herranz * OOOOOOOOOOOOOOOO Offset within SB Address Window 9424ea602eSAlbert Herranz * a 32-bit access flag 9524ea602eSAlbert Herranz */ 9624ea602eSAlbert Herranz 9724ea602eSAlbert Herranz 9824ea602eSAlbert Herranz /* 9924ea602eSAlbert Herranz * SSB I/O via SDIO. 10024ea602eSAlbert Herranz * 10124ea602eSAlbert Herranz * NOTE: SDIO address @addr is 17 bits long (SDIO address space is 128K). 10224ea602eSAlbert Herranz */ 10324ea602eSAlbert Herranz 10424ea602eSAlbert Herranz static inline struct device *ssb_sdio_dev(struct ssb_bus *bus) 10524ea602eSAlbert Herranz { 10624ea602eSAlbert Herranz return &bus->host_sdio->dev; 10724ea602eSAlbert Herranz } 10824ea602eSAlbert Herranz 10924ea602eSAlbert Herranz /* host claimed */ 11024ea602eSAlbert Herranz static int ssb_sdio_writeb(struct ssb_bus *bus, unsigned int addr, u8 val) 11124ea602eSAlbert Herranz { 11224ea602eSAlbert Herranz int error = 0; 11324ea602eSAlbert Herranz 11424ea602eSAlbert Herranz sdio_writeb(bus->host_sdio, val, addr, &error); 11524ea602eSAlbert Herranz if (unlikely(error)) { 11624ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%08X <- %02x, error %d\n", 11724ea602eSAlbert Herranz addr, val, error); 11824ea602eSAlbert Herranz } 11924ea602eSAlbert Herranz 12024ea602eSAlbert Herranz return error; 12124ea602eSAlbert Herranz } 12224ea602eSAlbert Herranz 12324ea602eSAlbert Herranz #if 0 12424ea602eSAlbert Herranz static u8 ssb_sdio_readb(struct ssb_bus *bus, unsigned int addr) 12524ea602eSAlbert Herranz { 12624ea602eSAlbert Herranz u8 val; 12724ea602eSAlbert Herranz int error = 0; 12824ea602eSAlbert Herranz 12924ea602eSAlbert Herranz val = sdio_readb(bus->host_sdio, addr, &error); 13024ea602eSAlbert Herranz if (unlikely(error)) { 13124ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%08X -> %02x, error %d\n", 13224ea602eSAlbert Herranz addr, val, error); 13324ea602eSAlbert Herranz } 13424ea602eSAlbert Herranz 13524ea602eSAlbert Herranz return val; 13624ea602eSAlbert Herranz } 13724ea602eSAlbert Herranz #endif 13824ea602eSAlbert Herranz 13924ea602eSAlbert Herranz /* host claimed */ 14024ea602eSAlbert Herranz static int ssb_sdio_set_sbaddr_window(struct ssb_bus *bus, u32 address) 14124ea602eSAlbert Herranz { 14224ea602eSAlbert Herranz int error; 14324ea602eSAlbert Herranz 14424ea602eSAlbert Herranz error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRLOW, 14524ea602eSAlbert Herranz (address >> 8) & SBSDIO_SBADDRLOW_MASK); 14624ea602eSAlbert Herranz if (error) 14724ea602eSAlbert Herranz goto out; 14824ea602eSAlbert Herranz error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRMID, 14924ea602eSAlbert Herranz (address >> 16) & SBSDIO_SBADDRMID_MASK); 15024ea602eSAlbert Herranz if (error) 15124ea602eSAlbert Herranz goto out; 15224ea602eSAlbert Herranz error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRHIGH, 15324ea602eSAlbert Herranz (address >> 24) & SBSDIO_SBADDRHIGH_MASK); 15424ea602eSAlbert Herranz if (error) 15524ea602eSAlbert Herranz goto out; 15624ea602eSAlbert Herranz bus->sdio_sbaddr = address; 15724ea602eSAlbert Herranz out: 15824ea602eSAlbert Herranz if (error) { 15924ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "failed to set address window" 16024ea602eSAlbert Herranz " to 0x%08x, error %d\n", address, error); 16124ea602eSAlbert Herranz } 16224ea602eSAlbert Herranz 16324ea602eSAlbert Herranz return error; 16424ea602eSAlbert Herranz } 16524ea602eSAlbert Herranz 16624ea602eSAlbert Herranz /* for enumeration use only */ 16724ea602eSAlbert Herranz u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset) 16824ea602eSAlbert Herranz { 16924ea602eSAlbert Herranz u32 val; 17024ea602eSAlbert Herranz int error; 17124ea602eSAlbert Herranz 17224ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 17324ea602eSAlbert Herranz val = sdio_readl(bus->host_sdio, offset, &error); 17424ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 17524ea602eSAlbert Herranz if (unlikely(error)) { 17624ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n", 17724ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, val, error); 17824ea602eSAlbert Herranz } 17924ea602eSAlbert Herranz 18024ea602eSAlbert Herranz return val; 18124ea602eSAlbert Herranz } 18224ea602eSAlbert Herranz 18324ea602eSAlbert Herranz /* for enumeration use only */ 18424ea602eSAlbert Herranz int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx) 18524ea602eSAlbert Herranz { 18624ea602eSAlbert Herranz u32 sbaddr; 18724ea602eSAlbert Herranz int error; 18824ea602eSAlbert Herranz 18924ea602eSAlbert Herranz sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; 19024ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 19124ea602eSAlbert Herranz error = ssb_sdio_set_sbaddr_window(bus, sbaddr); 19224ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 19324ea602eSAlbert Herranz if (error) { 19424ea602eSAlbert Herranz dev_err(ssb_sdio_dev(bus), "failed to switch to core %u," 19524ea602eSAlbert Herranz " error %d\n", coreidx, error); 19624ea602eSAlbert Herranz goto out; 19724ea602eSAlbert Herranz } 19824ea602eSAlbert Herranz out: 19924ea602eSAlbert Herranz return error; 20024ea602eSAlbert Herranz } 20124ea602eSAlbert Herranz 20224ea602eSAlbert Herranz /* host must be already claimed */ 20324ea602eSAlbert Herranz int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev) 20424ea602eSAlbert Herranz { 20524ea602eSAlbert Herranz u8 coreidx = dev->core_index; 20624ea602eSAlbert Herranz u32 sbaddr; 20724ea602eSAlbert Herranz int error = 0; 20824ea602eSAlbert Herranz 20924ea602eSAlbert Herranz sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; 21024ea602eSAlbert Herranz if (unlikely(bus->sdio_sbaddr != sbaddr)) { 21124ea602eSAlbert Herranz #if SSB_VERBOSE_SDIOCORESWITCH_DEBUG 21224ea602eSAlbert Herranz dev_info(ssb_sdio_dev(bus), 21324ea602eSAlbert Herranz "switching to %s core, index %d\n", 21424ea602eSAlbert Herranz ssb_core_name(dev->id.coreid), coreidx); 21524ea602eSAlbert Herranz #endif 21624ea602eSAlbert Herranz error = ssb_sdio_set_sbaddr_window(bus, sbaddr); 21724ea602eSAlbert Herranz if (error) { 21824ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "failed to switch to" 21924ea602eSAlbert Herranz " core %u, error %d\n", coreidx, error); 22024ea602eSAlbert Herranz goto out; 22124ea602eSAlbert Herranz } 22224ea602eSAlbert Herranz bus->mapped_device = dev; 22324ea602eSAlbert Herranz } 22424ea602eSAlbert Herranz 22524ea602eSAlbert Herranz out: 22624ea602eSAlbert Herranz return error; 22724ea602eSAlbert Herranz } 22824ea602eSAlbert Herranz 22924ea602eSAlbert Herranz static u8 ssb_sdio_read8(struct ssb_device *dev, u16 offset) 23024ea602eSAlbert Herranz { 23124ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 23224ea602eSAlbert Herranz u8 val = 0xff; 23324ea602eSAlbert Herranz int error = 0; 23424ea602eSAlbert Herranz 23524ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 23624ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) 23724ea602eSAlbert Herranz goto out; 23824ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 23924ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 24024ea602eSAlbert Herranz val = sdio_readb(bus->host_sdio, offset, &error); 24124ea602eSAlbert Herranz if (error) { 24224ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %02x, error %d\n", 24324ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, val, error); 24424ea602eSAlbert Herranz } 24524ea602eSAlbert Herranz out: 24624ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 24724ea602eSAlbert Herranz 24824ea602eSAlbert Herranz return val; 24924ea602eSAlbert Herranz } 25024ea602eSAlbert Herranz 25124ea602eSAlbert Herranz static u16 ssb_sdio_read16(struct ssb_device *dev, u16 offset) 25224ea602eSAlbert Herranz { 25324ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 25424ea602eSAlbert Herranz u16 val = 0xffff; 25524ea602eSAlbert Herranz int error = 0; 25624ea602eSAlbert Herranz 25724ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 25824ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) 25924ea602eSAlbert Herranz goto out; 26024ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 26124ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 26224ea602eSAlbert Herranz val = sdio_readw(bus->host_sdio, offset, &error); 26324ea602eSAlbert Herranz if (error) { 26424ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %04x, error %d\n", 26524ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, val, error); 26624ea602eSAlbert Herranz } 26724ea602eSAlbert Herranz out: 26824ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 26924ea602eSAlbert Herranz 27024ea602eSAlbert Herranz return val; 27124ea602eSAlbert Herranz } 27224ea602eSAlbert Herranz 27324ea602eSAlbert Herranz static u32 ssb_sdio_read32(struct ssb_device *dev, u16 offset) 27424ea602eSAlbert Herranz { 27524ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 27624ea602eSAlbert Herranz u32 val = 0xffffffff; 27724ea602eSAlbert Herranz int error = 0; 27824ea602eSAlbert Herranz 27924ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 28024ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) 28124ea602eSAlbert Herranz goto out; 28224ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 28324ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 28424ea602eSAlbert Herranz offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ 28524ea602eSAlbert Herranz val = sdio_readl(bus->host_sdio, offset, &error); 28624ea602eSAlbert Herranz if (error) { 28724ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n", 28824ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, val, error); 28924ea602eSAlbert Herranz } 29024ea602eSAlbert Herranz out: 29124ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 29224ea602eSAlbert Herranz 29324ea602eSAlbert Herranz return val; 29424ea602eSAlbert Herranz } 29524ea602eSAlbert Herranz 29624ea602eSAlbert Herranz #ifdef CONFIG_SSB_BLOCKIO 29724ea602eSAlbert Herranz static void ssb_sdio_block_read(struct ssb_device *dev, void *buffer, 29824ea602eSAlbert Herranz size_t count, u16 offset, u8 reg_width) 29924ea602eSAlbert Herranz { 30024ea602eSAlbert Herranz size_t saved_count = count; 30124ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 30224ea602eSAlbert Herranz int error = 0; 30324ea602eSAlbert Herranz 30424ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 30524ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) { 30624ea602eSAlbert Herranz error = -EIO; 30724ea602eSAlbert Herranz memset(buffer, 0xff, count); 30824ea602eSAlbert Herranz goto err_out; 30924ea602eSAlbert Herranz } 31024ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 31124ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 31224ea602eSAlbert Herranz 31324ea602eSAlbert Herranz switch (reg_width) { 31424ea602eSAlbert Herranz case sizeof(u8): { 31524ea602eSAlbert Herranz error = sdio_readsb(bus->host_sdio, buffer, offset, count); 31624ea602eSAlbert Herranz break; 31724ea602eSAlbert Herranz } 31824ea602eSAlbert Herranz case sizeof(u16): { 31924ea602eSAlbert Herranz SSB_WARN_ON(count & 1); 32024ea602eSAlbert Herranz error = sdio_readsb(bus->host_sdio, buffer, offset, count); 32124ea602eSAlbert Herranz break; 32224ea602eSAlbert Herranz } 32324ea602eSAlbert Herranz case sizeof(u32): { 32424ea602eSAlbert Herranz SSB_WARN_ON(count & 3); 32524ea602eSAlbert Herranz offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ 32624ea602eSAlbert Herranz error = sdio_readsb(bus->host_sdio, buffer, offset, count); 32724ea602eSAlbert Herranz break; 32824ea602eSAlbert Herranz } 32924ea602eSAlbert Herranz default: 33024ea602eSAlbert Herranz SSB_WARN_ON(1); 33124ea602eSAlbert Herranz } 33224ea602eSAlbert Herranz if (!error) 33324ea602eSAlbert Herranz goto out; 33424ea602eSAlbert Herranz 33524ea602eSAlbert Herranz err_out: 336*de32cce1SRandy Dunlap dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n", 33724ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error); 33824ea602eSAlbert Herranz out: 33924ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 34024ea602eSAlbert Herranz } 34124ea602eSAlbert Herranz #endif /* CONFIG_SSB_BLOCKIO */ 34224ea602eSAlbert Herranz 34324ea602eSAlbert Herranz static void ssb_sdio_write8(struct ssb_device *dev, u16 offset, u8 val) 34424ea602eSAlbert Herranz { 34524ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 34624ea602eSAlbert Herranz int error = 0; 34724ea602eSAlbert Herranz 34824ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 34924ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) 35024ea602eSAlbert Herranz goto out; 35124ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 35224ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 35324ea602eSAlbert Herranz sdio_writeb(bus->host_sdio, val, offset, &error); 35424ea602eSAlbert Herranz if (error) { 35524ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %02x, error %d\n", 35624ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, val, error); 35724ea602eSAlbert Herranz } 35824ea602eSAlbert Herranz out: 35924ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 36024ea602eSAlbert Herranz } 36124ea602eSAlbert Herranz 36224ea602eSAlbert Herranz static void ssb_sdio_write16(struct ssb_device *dev, u16 offset, u16 val) 36324ea602eSAlbert Herranz { 36424ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 36524ea602eSAlbert Herranz int error = 0; 36624ea602eSAlbert Herranz 36724ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 36824ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) 36924ea602eSAlbert Herranz goto out; 37024ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 37124ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 37224ea602eSAlbert Herranz sdio_writew(bus->host_sdio, val, offset, &error); 37324ea602eSAlbert Herranz if (error) { 37424ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %04x, error %d\n", 37524ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, val, error); 37624ea602eSAlbert Herranz } 37724ea602eSAlbert Herranz out: 37824ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 37924ea602eSAlbert Herranz } 38024ea602eSAlbert Herranz 38124ea602eSAlbert Herranz static void ssb_sdio_write32(struct ssb_device *dev, u16 offset, u32 val) 38224ea602eSAlbert Herranz { 38324ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 38424ea602eSAlbert Herranz int error = 0; 38524ea602eSAlbert Herranz 38624ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 38724ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) 38824ea602eSAlbert Herranz goto out; 38924ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 39024ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 39124ea602eSAlbert Herranz offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ 39224ea602eSAlbert Herranz sdio_writel(bus->host_sdio, val, offset, &error); 39324ea602eSAlbert Herranz if (error) { 39424ea602eSAlbert Herranz dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %08x, error %d\n", 39524ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, val, error); 39624ea602eSAlbert Herranz } 39724ea602eSAlbert Herranz if (bus->quirks & SSB_QUIRK_SDIO_READ_AFTER_WRITE32) 39824ea602eSAlbert Herranz sdio_readl(bus->host_sdio, 0, &error); 39924ea602eSAlbert Herranz out: 40024ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 40124ea602eSAlbert Herranz } 40224ea602eSAlbert Herranz 40324ea602eSAlbert Herranz #ifdef CONFIG_SSB_BLOCKIO 40424ea602eSAlbert Herranz static void ssb_sdio_block_write(struct ssb_device *dev, const void *buffer, 40524ea602eSAlbert Herranz size_t count, u16 offset, u8 reg_width) 40624ea602eSAlbert Herranz { 40724ea602eSAlbert Herranz size_t saved_count = count; 40824ea602eSAlbert Herranz struct ssb_bus *bus = dev->bus; 40924ea602eSAlbert Herranz int error = 0; 41024ea602eSAlbert Herranz 41124ea602eSAlbert Herranz sdio_claim_host(bus->host_sdio); 41224ea602eSAlbert Herranz if (unlikely(ssb_sdio_switch_core(bus, dev))) { 41324ea602eSAlbert Herranz error = -EIO; 41424ea602eSAlbert Herranz memset((void *)buffer, 0xff, count); 41524ea602eSAlbert Herranz goto err_out; 41624ea602eSAlbert Herranz } 41724ea602eSAlbert Herranz offset |= bus->sdio_sbaddr & 0xffff; 41824ea602eSAlbert Herranz offset &= SBSDIO_SB_OFT_ADDR_MASK; 41924ea602eSAlbert Herranz 42024ea602eSAlbert Herranz switch (reg_width) { 42124ea602eSAlbert Herranz case sizeof(u8): 42224ea602eSAlbert Herranz error = sdio_writesb(bus->host_sdio, offset, 42324ea602eSAlbert Herranz (void *)buffer, count); 42424ea602eSAlbert Herranz break; 42524ea602eSAlbert Herranz case sizeof(u16): 42624ea602eSAlbert Herranz SSB_WARN_ON(count & 1); 42724ea602eSAlbert Herranz error = sdio_writesb(bus->host_sdio, offset, 42824ea602eSAlbert Herranz (void *)buffer, count); 42924ea602eSAlbert Herranz break; 43024ea602eSAlbert Herranz case sizeof(u32): 43124ea602eSAlbert Herranz SSB_WARN_ON(count & 3); 43224ea602eSAlbert Herranz offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */ 43324ea602eSAlbert Herranz error = sdio_writesb(bus->host_sdio, offset, 43424ea602eSAlbert Herranz (void *)buffer, count); 43524ea602eSAlbert Herranz break; 43624ea602eSAlbert Herranz default: 43724ea602eSAlbert Herranz SSB_WARN_ON(1); 43824ea602eSAlbert Herranz } 43924ea602eSAlbert Herranz if (!error) 44024ea602eSAlbert Herranz goto out; 44124ea602eSAlbert Herranz 44224ea602eSAlbert Herranz err_out: 443*de32cce1SRandy Dunlap dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n", 44424ea602eSAlbert Herranz bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error); 44524ea602eSAlbert Herranz out: 44624ea602eSAlbert Herranz sdio_release_host(bus->host_sdio); 44724ea602eSAlbert Herranz } 44824ea602eSAlbert Herranz 44924ea602eSAlbert Herranz #endif /* CONFIG_SSB_BLOCKIO */ 45024ea602eSAlbert Herranz 45124ea602eSAlbert Herranz /* Not "static", as it's used in main.c */ 45224ea602eSAlbert Herranz const struct ssb_bus_ops ssb_sdio_ops = { 45324ea602eSAlbert Herranz .read8 = ssb_sdio_read8, 45424ea602eSAlbert Herranz .read16 = ssb_sdio_read16, 45524ea602eSAlbert Herranz .read32 = ssb_sdio_read32, 45624ea602eSAlbert Herranz .write8 = ssb_sdio_write8, 45724ea602eSAlbert Herranz .write16 = ssb_sdio_write16, 45824ea602eSAlbert Herranz .write32 = ssb_sdio_write32, 45924ea602eSAlbert Herranz #ifdef CONFIG_SSB_BLOCKIO 46024ea602eSAlbert Herranz .block_read = ssb_sdio_block_read, 46124ea602eSAlbert Herranz .block_write = ssb_sdio_block_write, 46224ea602eSAlbert Herranz #endif 46324ea602eSAlbert Herranz }; 46424ea602eSAlbert Herranz 46524ea602eSAlbert Herranz #define GOTO_ERROR_ON(condition, description) do { \ 46624ea602eSAlbert Herranz if (unlikely(condition)) { \ 46724ea602eSAlbert Herranz error_description = description; \ 46824ea602eSAlbert Herranz goto error; \ 46924ea602eSAlbert Herranz } \ 47024ea602eSAlbert Herranz } while (0) 47124ea602eSAlbert Herranz 47224ea602eSAlbert Herranz int ssb_sdio_get_invariants(struct ssb_bus *bus, 47324ea602eSAlbert Herranz struct ssb_init_invariants *iv) 47424ea602eSAlbert Herranz { 47524ea602eSAlbert Herranz struct ssb_sprom *sprom = &iv->sprom; 47624ea602eSAlbert Herranz struct ssb_boardinfo *bi = &iv->boardinfo; 47724ea602eSAlbert Herranz const char *error_description = "none"; 47824ea602eSAlbert Herranz struct sdio_func_tuple *tuple; 47924ea602eSAlbert Herranz void *mac; 48024ea602eSAlbert Herranz 48124ea602eSAlbert Herranz memset(sprom, 0xFF, sizeof(*sprom)); 48224ea602eSAlbert Herranz sprom->boardflags_lo = 0; 48324ea602eSAlbert Herranz sprom->boardflags_hi = 0; 48424ea602eSAlbert Herranz 48524ea602eSAlbert Herranz tuple = bus->host_sdio->tuples; 48624ea602eSAlbert Herranz while (tuple) { 48724ea602eSAlbert Herranz switch (tuple->code) { 48824ea602eSAlbert Herranz case 0x22: /* extended function */ 48924ea602eSAlbert Herranz switch (tuple->data[0]) { 49024ea602eSAlbert Herranz case CISTPL_FUNCE_LAN_NODE_ID: 49124ea602eSAlbert Herranz GOTO_ERROR_ON((tuple->size != 7) && 49224ea602eSAlbert Herranz (tuple->data[1] != 6), 49324ea602eSAlbert Herranz "mac tpl size"); 49424ea602eSAlbert Herranz /* fetch the MAC address. */ 49524ea602eSAlbert Herranz mac = tuple->data + 2; 49624ea602eSAlbert Herranz memcpy(sprom->il0mac, mac, ETH_ALEN); 49724ea602eSAlbert Herranz memcpy(sprom->et1mac, mac, ETH_ALEN); 49824ea602eSAlbert Herranz break; 49924ea602eSAlbert Herranz default: 50024ea602eSAlbert Herranz break; 50124ea602eSAlbert Herranz } 50224ea602eSAlbert Herranz break; 50324ea602eSAlbert Herranz case 0x80: /* vendor specific tuple */ 50424ea602eSAlbert Herranz switch (tuple->data[0]) { 50524ea602eSAlbert Herranz case SSB_SDIO_CIS_SROMREV: 50624ea602eSAlbert Herranz GOTO_ERROR_ON(tuple->size != 2, 50724ea602eSAlbert Herranz "sromrev tpl size"); 50824ea602eSAlbert Herranz sprom->revision = tuple->data[1]; 50924ea602eSAlbert Herranz break; 51024ea602eSAlbert Herranz case SSB_SDIO_CIS_ID: 51124ea602eSAlbert Herranz GOTO_ERROR_ON((tuple->size != 5) && 51224ea602eSAlbert Herranz (tuple->size != 7), 51324ea602eSAlbert Herranz "id tpl size"); 51424ea602eSAlbert Herranz bi->vendor = tuple->data[1] | 51524ea602eSAlbert Herranz (tuple->data[2]<<8); 51624ea602eSAlbert Herranz break; 51724ea602eSAlbert Herranz case SSB_SDIO_CIS_BOARDREV: 51824ea602eSAlbert Herranz GOTO_ERROR_ON(tuple->size != 2, 51924ea602eSAlbert Herranz "boardrev tpl size"); 52024ea602eSAlbert Herranz sprom->board_rev = tuple->data[1]; 52124ea602eSAlbert Herranz break; 52224ea602eSAlbert Herranz case SSB_SDIO_CIS_PA: 52324ea602eSAlbert Herranz GOTO_ERROR_ON((tuple->size != 9) && 52424ea602eSAlbert Herranz (tuple->size != 10), 52524ea602eSAlbert Herranz "pa tpl size"); 52624ea602eSAlbert Herranz sprom->pa0b0 = tuple->data[1] | 52724ea602eSAlbert Herranz ((u16)tuple->data[2] << 8); 52824ea602eSAlbert Herranz sprom->pa0b1 = tuple->data[3] | 52924ea602eSAlbert Herranz ((u16)tuple->data[4] << 8); 53024ea602eSAlbert Herranz sprom->pa0b2 = tuple->data[5] | 53124ea602eSAlbert Herranz ((u16)tuple->data[6] << 8); 53224ea602eSAlbert Herranz sprom->itssi_a = tuple->data[7]; 53324ea602eSAlbert Herranz sprom->itssi_bg = tuple->data[7]; 53424ea602eSAlbert Herranz sprom->maxpwr_a = tuple->data[8]; 53524ea602eSAlbert Herranz sprom->maxpwr_bg = tuple->data[8]; 53624ea602eSAlbert Herranz break; 53724ea602eSAlbert Herranz case SSB_SDIO_CIS_OEMNAME: 53824ea602eSAlbert Herranz /* Not present */ 53924ea602eSAlbert Herranz break; 54024ea602eSAlbert Herranz case SSB_SDIO_CIS_CCODE: 54124ea602eSAlbert Herranz GOTO_ERROR_ON(tuple->size != 2, 54224ea602eSAlbert Herranz "ccode tpl size"); 54324ea602eSAlbert Herranz sprom->country_code = tuple->data[1]; 54424ea602eSAlbert Herranz break; 54524ea602eSAlbert Herranz case SSB_SDIO_CIS_ANTENNA: 54624ea602eSAlbert Herranz GOTO_ERROR_ON(tuple->size != 2, 54724ea602eSAlbert Herranz "ant tpl size"); 54824ea602eSAlbert Herranz sprom->ant_available_a = tuple->data[1]; 54924ea602eSAlbert Herranz sprom->ant_available_bg = tuple->data[1]; 55024ea602eSAlbert Herranz break; 55124ea602eSAlbert Herranz case SSB_SDIO_CIS_ANTGAIN: 55224ea602eSAlbert Herranz GOTO_ERROR_ON(tuple->size != 2, 55324ea602eSAlbert Herranz "antg tpl size"); 55424ea602eSAlbert Herranz sprom->antenna_gain.ghz24.a0 = tuple->data[1]; 55524ea602eSAlbert Herranz sprom->antenna_gain.ghz24.a1 = tuple->data[1]; 55624ea602eSAlbert Herranz sprom->antenna_gain.ghz24.a2 = tuple->data[1]; 55724ea602eSAlbert Herranz sprom->antenna_gain.ghz24.a3 = tuple->data[1]; 55824ea602eSAlbert Herranz sprom->antenna_gain.ghz5.a0 = tuple->data[1]; 55924ea602eSAlbert Herranz sprom->antenna_gain.ghz5.a1 = tuple->data[1]; 56024ea602eSAlbert Herranz sprom->antenna_gain.ghz5.a2 = tuple->data[1]; 56124ea602eSAlbert Herranz sprom->antenna_gain.ghz5.a3 = tuple->data[1]; 56224ea602eSAlbert Herranz break; 56324ea602eSAlbert Herranz case SSB_SDIO_CIS_BFLAGS: 56424ea602eSAlbert Herranz GOTO_ERROR_ON((tuple->size != 3) && 56524ea602eSAlbert Herranz (tuple->size != 5), 56624ea602eSAlbert Herranz "bfl tpl size"); 56724ea602eSAlbert Herranz sprom->boardflags_lo = tuple->data[1] | 56824ea602eSAlbert Herranz ((u16)tuple->data[2] << 8); 56924ea602eSAlbert Herranz break; 57024ea602eSAlbert Herranz case SSB_SDIO_CIS_LEDS: 57124ea602eSAlbert Herranz GOTO_ERROR_ON(tuple->size != 5, 57224ea602eSAlbert Herranz "leds tpl size"); 57324ea602eSAlbert Herranz sprom->gpio0 = tuple->data[1]; 57424ea602eSAlbert Herranz sprom->gpio1 = tuple->data[2]; 57524ea602eSAlbert Herranz sprom->gpio2 = tuple->data[3]; 57624ea602eSAlbert Herranz sprom->gpio3 = tuple->data[4]; 57724ea602eSAlbert Herranz break; 57824ea602eSAlbert Herranz default: 57924ea602eSAlbert Herranz break; 58024ea602eSAlbert Herranz } 58124ea602eSAlbert Herranz break; 58224ea602eSAlbert Herranz default: 58324ea602eSAlbert Herranz break; 58424ea602eSAlbert Herranz } 58524ea602eSAlbert Herranz tuple = tuple->next; 58624ea602eSAlbert Herranz } 58724ea602eSAlbert Herranz 58824ea602eSAlbert Herranz return 0; 58924ea602eSAlbert Herranz error: 59024ea602eSAlbert Herranz dev_err(ssb_sdio_dev(bus), "failed to fetch device invariants: %s\n", 59124ea602eSAlbert Herranz error_description); 59224ea602eSAlbert Herranz return -ENODEV; 59324ea602eSAlbert Herranz } 59424ea602eSAlbert Herranz 59524ea602eSAlbert Herranz void ssb_sdio_exit(struct ssb_bus *bus) 59624ea602eSAlbert Herranz { 59724ea602eSAlbert Herranz if (bus->bustype != SSB_BUSTYPE_SDIO) 59824ea602eSAlbert Herranz return; 59924ea602eSAlbert Herranz /* Nothing to do here. */ 60024ea602eSAlbert Herranz } 60124ea602eSAlbert Herranz 60224ea602eSAlbert Herranz int ssb_sdio_init(struct ssb_bus *bus) 60324ea602eSAlbert Herranz { 60424ea602eSAlbert Herranz if (bus->bustype != SSB_BUSTYPE_SDIO) 60524ea602eSAlbert Herranz return 0; 60624ea602eSAlbert Herranz 60724ea602eSAlbert Herranz bus->sdio_sbaddr = ~0; 60824ea602eSAlbert Herranz 60924ea602eSAlbert Herranz return 0; 61024ea602eSAlbert Herranz } 611