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