18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Sonics Silicon Backplane 38c2ecf20Sopenharmony_ci * PCMCIA-Hostbus related functions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> 68c2ecf20Sopenharmony_ci * Copyright 2007-2008 Michael Buesch <m@bues.ch> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "ssb_private.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/ssb/ssb.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <pcmcia/cistpl.h> 198c2ecf20Sopenharmony_ci#include <pcmcia/ciscode.h> 208c2ecf20Sopenharmony_ci#include <pcmcia/ds.h> 218c2ecf20Sopenharmony_ci#include <pcmcia/cisreg.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Define the following to 1 to enable a printk on each coreswitch. */ 258c2ecf20Sopenharmony_ci#define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* PCMCIA configuration registers */ 298c2ecf20Sopenharmony_ci#define SSB_PCMCIA_ADDRESS0 0x2E 308c2ecf20Sopenharmony_ci#define SSB_PCMCIA_ADDRESS1 0x30 318c2ecf20Sopenharmony_ci#define SSB_PCMCIA_ADDRESS2 0x32 328c2ecf20Sopenharmony_ci#define SSB_PCMCIA_MEMSEG 0x34 338c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROMCTL 0x36 348c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROMCTL_IDLE 0 358c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROMCTL_WRITE 1 368c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROMCTL_READ 2 378c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROMCTL_WRITEEN 4 388c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROMCTL_WRITEDIS 7 398c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROMCTL_DONE 8 408c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROM_DATALO 0x38 418c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROM_DATAHI 0x3A 428c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROM_ADDRLO 0x3C 438c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROM_ADDRHI 0x3E 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Hardware invariants CIS tuples */ 468c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS 0x80 478c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_ID 0x01 488c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_BOARDREV 0x02 498c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA 0x03 508c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_PA0B0_LO 0 518c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_PA0B0_HI 1 528c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_PA0B1_LO 2 538c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_PA0B1_HI 3 548c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_PA0B2_LO 4 558c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_PA0B2_HI 5 568c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_ITSSI 6 578c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_PA_MAXPOW 7 588c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_OEMNAME 0x04 598c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_CCODE 0x05 608c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_ANTENNA 0x06 618c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_ANTGAIN 0x07 628c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_BFLAGS 0x08 638c2ecf20Sopenharmony_ci#define SSB_PCMCIA_CIS_LEDS 0x09 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* PCMCIA SPROM size. */ 668c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROM_SIZE 256 678c2ecf20Sopenharmony_ci#define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16)) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Write to a PCMCIA configuration register. */ 718c2ecf20Sopenharmony_cistatic int ssb_pcmcia_cfg_write(struct ssb_bus *bus, u8 offset, u8 value) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int res; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci res = pcmcia_write_config_byte(bus->host_pcmcia, offset, value); 768c2ecf20Sopenharmony_ci if (unlikely(res != 0)) 778c2ecf20Sopenharmony_ci return -EBUSY; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Read from a PCMCIA configuration register. */ 838c2ecf20Sopenharmony_cistatic int ssb_pcmcia_cfg_read(struct ssb_bus *bus, u8 offset, u8 *value) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int res; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci res = pcmcia_read_config_byte(bus->host_pcmcia, offset, value); 888c2ecf20Sopenharmony_ci if (unlikely(res != 0)) 898c2ecf20Sopenharmony_ci return -EBUSY; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return 0; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ciint ssb_pcmcia_switch_coreidx(struct ssb_bus *bus, 958c2ecf20Sopenharmony_ci u8 coreidx) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int err; 988c2ecf20Sopenharmony_ci int attempts = 0; 998c2ecf20Sopenharmony_ci u32 cur_core; 1008c2ecf20Sopenharmony_ci u32 addr; 1018c2ecf20Sopenharmony_ci u32 read_addr; 1028c2ecf20Sopenharmony_ci u8 val; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE; 1058c2ecf20Sopenharmony_ci while (1) { 1068c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS0, 1078c2ecf20Sopenharmony_ci (addr & 0x0000F000) >> 12); 1088c2ecf20Sopenharmony_ci if (err) 1098c2ecf20Sopenharmony_ci goto error; 1108c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS1, 1118c2ecf20Sopenharmony_ci (addr & 0x00FF0000) >> 16); 1128c2ecf20Sopenharmony_ci if (err) 1138c2ecf20Sopenharmony_ci goto error; 1148c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS2, 1158c2ecf20Sopenharmony_ci (addr & 0xFF000000) >> 24); 1168c2ecf20Sopenharmony_ci if (err) 1178c2ecf20Sopenharmony_ci goto error; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci read_addr = 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS0, &val); 1228c2ecf20Sopenharmony_ci if (err) 1238c2ecf20Sopenharmony_ci goto error; 1248c2ecf20Sopenharmony_ci read_addr |= ((u32)(val & 0x0F)) << 12; 1258c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS1, &val); 1268c2ecf20Sopenharmony_ci if (err) 1278c2ecf20Sopenharmony_ci goto error; 1288c2ecf20Sopenharmony_ci read_addr |= ((u32)val) << 16; 1298c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS2, &val); 1308c2ecf20Sopenharmony_ci if (err) 1318c2ecf20Sopenharmony_ci goto error; 1328c2ecf20Sopenharmony_ci read_addr |= ((u32)val) << 24; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE; 1358c2ecf20Sopenharmony_ci if (cur_core == coreidx) 1368c2ecf20Sopenharmony_ci break; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 1398c2ecf20Sopenharmony_ci if (attempts++ > SSB_BAR0_MAX_RETRIES) 1408c2ecf20Sopenharmony_ci goto error; 1418c2ecf20Sopenharmony_ci udelay(10); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_cierror: 1468c2ecf20Sopenharmony_ci pr_err("Failed to switch to core %u\n", coreidx); 1478c2ecf20Sopenharmony_ci return err; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int err; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 1558c2ecf20Sopenharmony_ci pr_info("Switching to %s core, index %d\n", 1568c2ecf20Sopenharmony_ci ssb_core_name(dev->id.coreid), dev->core_index); 1578c2ecf20Sopenharmony_ci#endif 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); 1608c2ecf20Sopenharmony_ci if (!err) 1618c2ecf20Sopenharmony_ci bus->mapped_device = dev; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return err; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ciint ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci int attempts = 0; 1698c2ecf20Sopenharmony_ci int err; 1708c2ecf20Sopenharmony_ci u8 val; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci WARN_ON((seg != 0) && (seg != 1)); 1738c2ecf20Sopenharmony_ci while (1) { 1748c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_MEMSEG, seg); 1758c2ecf20Sopenharmony_ci if (err) 1768c2ecf20Sopenharmony_ci goto error; 1778c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_MEMSEG, &val); 1788c2ecf20Sopenharmony_ci if (err) 1798c2ecf20Sopenharmony_ci goto error; 1808c2ecf20Sopenharmony_ci if (val == seg) 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 1848c2ecf20Sopenharmony_ci if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES)) 1858c2ecf20Sopenharmony_ci goto error; 1868c2ecf20Sopenharmony_ci udelay(10); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci bus->mapped_pcmcia_seg = seg; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_cierror: 1928c2ecf20Sopenharmony_ci pr_err("Failed to switch pcmcia segment\n"); 1938c2ecf20Sopenharmony_ci return err; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int select_core_and_segment(struct ssb_device *dev, 1978c2ecf20Sopenharmony_ci u16 *offset) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 2008c2ecf20Sopenharmony_ci int err; 2018c2ecf20Sopenharmony_ci u8 need_segment; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (*offset >= 0x800) { 2048c2ecf20Sopenharmony_ci *offset -= 0x800; 2058c2ecf20Sopenharmony_ci need_segment = 1; 2068c2ecf20Sopenharmony_ci } else 2078c2ecf20Sopenharmony_ci need_segment = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (unlikely(dev != bus->mapped_device)) { 2108c2ecf20Sopenharmony_ci err = ssb_pcmcia_switch_core(bus, dev); 2118c2ecf20Sopenharmony_ci if (unlikely(err)) 2128c2ecf20Sopenharmony_ci return err; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci if (unlikely(need_segment != bus->mapped_pcmcia_seg)) { 2158c2ecf20Sopenharmony_ci err = ssb_pcmcia_switch_segment(bus, need_segment); 2168c2ecf20Sopenharmony_ci if (unlikely(err)) 2178c2ecf20Sopenharmony_ci return err; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic u8 ssb_pcmcia_read8(struct ssb_device *dev, u16 offset) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 2268c2ecf20Sopenharmony_ci unsigned long flags; 2278c2ecf20Sopenharmony_ci int err; 2288c2ecf20Sopenharmony_ci u8 value = 0xFF; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 2318c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 2328c2ecf20Sopenharmony_ci if (likely(!err)) 2338c2ecf20Sopenharmony_ci value = readb(bus->mmio + offset); 2348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return value; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 2428c2ecf20Sopenharmony_ci unsigned long flags; 2438c2ecf20Sopenharmony_ci int err; 2448c2ecf20Sopenharmony_ci u16 value = 0xFFFF; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 2478c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 2488c2ecf20Sopenharmony_ci if (likely(!err)) 2498c2ecf20Sopenharmony_ci value = readw(bus->mmio + offset); 2508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return value; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 2588c2ecf20Sopenharmony_ci unsigned long flags; 2598c2ecf20Sopenharmony_ci int err; 2608c2ecf20Sopenharmony_ci u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 2638c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 2648c2ecf20Sopenharmony_ci if (likely(!err)) { 2658c2ecf20Sopenharmony_ci lo = readw(bus->mmio + offset); 2668c2ecf20Sopenharmony_ci hi = readw(bus->mmio + offset + 2); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return (lo | (hi << 16)); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO 2748c2ecf20Sopenharmony_cistatic void ssb_pcmcia_block_read(struct ssb_device *dev, void *buffer, 2758c2ecf20Sopenharmony_ci size_t count, u16 offset, u8 reg_width) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 2788c2ecf20Sopenharmony_ci unsigned long flags; 2798c2ecf20Sopenharmony_ci void __iomem *addr = bus->mmio + offset; 2808c2ecf20Sopenharmony_ci int err; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 2838c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 2848c2ecf20Sopenharmony_ci if (unlikely(err)) { 2858c2ecf20Sopenharmony_ci memset(buffer, 0xFF, count); 2868c2ecf20Sopenharmony_ci goto unlock; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci switch (reg_width) { 2898c2ecf20Sopenharmony_ci case sizeof(u8): { 2908c2ecf20Sopenharmony_ci u8 *buf = buffer; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci while (count) { 2938c2ecf20Sopenharmony_ci *buf = __raw_readb(addr); 2948c2ecf20Sopenharmony_ci buf++; 2958c2ecf20Sopenharmony_ci count--; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci case sizeof(u16): { 3008c2ecf20Sopenharmony_ci __le16 *buf = buffer; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci WARN_ON(count & 1); 3038c2ecf20Sopenharmony_ci while (count) { 3048c2ecf20Sopenharmony_ci *buf = (__force __le16)__raw_readw(addr); 3058c2ecf20Sopenharmony_ci buf++; 3068c2ecf20Sopenharmony_ci count -= 2; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci case sizeof(u32): { 3118c2ecf20Sopenharmony_ci __le16 *buf = buffer; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci WARN_ON(count & 3); 3148c2ecf20Sopenharmony_ci while (count) { 3158c2ecf20Sopenharmony_ci *buf = (__force __le16)__raw_readw(addr); 3168c2ecf20Sopenharmony_ci buf++; 3178c2ecf20Sopenharmony_ci *buf = (__force __le16)__raw_readw(addr + 2); 3188c2ecf20Sopenharmony_ci buf++; 3198c2ecf20Sopenharmony_ci count -= 4; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci default: 3248c2ecf20Sopenharmony_ci WARN_ON(1); 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ciunlock: 3278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_BLOCKIO */ 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 3348c2ecf20Sopenharmony_ci unsigned long flags; 3358c2ecf20Sopenharmony_ci int err; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 3388c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 3398c2ecf20Sopenharmony_ci if (likely(!err)) 3408c2ecf20Sopenharmony_ci writeb(value, bus->mmio + offset); 3418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 3478c2ecf20Sopenharmony_ci unsigned long flags; 3488c2ecf20Sopenharmony_ci int err; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 3518c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 3528c2ecf20Sopenharmony_ci if (likely(!err)) 3538c2ecf20Sopenharmony_ci writew(value, bus->mmio + offset); 3548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 3608c2ecf20Sopenharmony_ci unsigned long flags; 3618c2ecf20Sopenharmony_ci int err; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 3648c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 3658c2ecf20Sopenharmony_ci if (likely(!err)) { 3668c2ecf20Sopenharmony_ci writew((value & 0x0000FFFF), bus->mmio + offset); 3678c2ecf20Sopenharmony_ci writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 3708c2ecf20Sopenharmony_ci} 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO 3738c2ecf20Sopenharmony_cistatic void ssb_pcmcia_block_write(struct ssb_device *dev, const void *buffer, 3748c2ecf20Sopenharmony_ci size_t count, u16 offset, u8 reg_width) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct ssb_bus *bus = dev->bus; 3778c2ecf20Sopenharmony_ci unsigned long flags; 3788c2ecf20Sopenharmony_ci void __iomem *addr = bus->mmio + offset; 3798c2ecf20Sopenharmony_ci int err; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci spin_lock_irqsave(&bus->bar_lock, flags); 3828c2ecf20Sopenharmony_ci err = select_core_and_segment(dev, &offset); 3838c2ecf20Sopenharmony_ci if (unlikely(err)) 3848c2ecf20Sopenharmony_ci goto unlock; 3858c2ecf20Sopenharmony_ci switch (reg_width) { 3868c2ecf20Sopenharmony_ci case sizeof(u8): { 3878c2ecf20Sopenharmony_ci const u8 *buf = buffer; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci while (count) { 3908c2ecf20Sopenharmony_ci __raw_writeb(*buf, addr); 3918c2ecf20Sopenharmony_ci buf++; 3928c2ecf20Sopenharmony_ci count--; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci case sizeof(u16): { 3978c2ecf20Sopenharmony_ci const __le16 *buf = buffer; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci WARN_ON(count & 1); 4008c2ecf20Sopenharmony_ci while (count) { 4018c2ecf20Sopenharmony_ci __raw_writew((__force u16)(*buf), addr); 4028c2ecf20Sopenharmony_ci buf++; 4038c2ecf20Sopenharmony_ci count -= 2; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci case sizeof(u32): { 4088c2ecf20Sopenharmony_ci const __le16 *buf = buffer; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci WARN_ON(count & 3); 4118c2ecf20Sopenharmony_ci while (count) { 4128c2ecf20Sopenharmony_ci __raw_writew((__force u16)(*buf), addr); 4138c2ecf20Sopenharmony_ci buf++; 4148c2ecf20Sopenharmony_ci __raw_writew((__force u16)(*buf), addr + 2); 4158c2ecf20Sopenharmony_ci buf++; 4168c2ecf20Sopenharmony_ci count -= 4; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci default: 4218c2ecf20Sopenharmony_ci WARN_ON(1); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ciunlock: 4248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&bus->bar_lock, flags); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_BLOCKIO */ 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci/* Not "static", as it's used in main.c */ 4298c2ecf20Sopenharmony_ciconst struct ssb_bus_ops ssb_pcmcia_ops = { 4308c2ecf20Sopenharmony_ci .read8 = ssb_pcmcia_read8, 4318c2ecf20Sopenharmony_ci .read16 = ssb_pcmcia_read16, 4328c2ecf20Sopenharmony_ci .read32 = ssb_pcmcia_read32, 4338c2ecf20Sopenharmony_ci .write8 = ssb_pcmcia_write8, 4348c2ecf20Sopenharmony_ci .write16 = ssb_pcmcia_write16, 4358c2ecf20Sopenharmony_ci .write32 = ssb_pcmcia_write32, 4368c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_BLOCKIO 4378c2ecf20Sopenharmony_ci .block_read = ssb_pcmcia_block_read, 4388c2ecf20Sopenharmony_ci .block_write = ssb_pcmcia_block_write, 4398c2ecf20Sopenharmony_ci#endif 4408c2ecf20Sopenharmony_ci}; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci unsigned int i; 4458c2ecf20Sopenharmony_ci int err; 4468c2ecf20Sopenharmony_ci u8 value; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROMCTL, command); 4498c2ecf20Sopenharmony_ci if (err) 4508c2ecf20Sopenharmony_ci return err; 4518c2ecf20Sopenharmony_ci for (i = 0; i < 1000; i++) { 4528c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROMCTL, &value); 4538c2ecf20Sopenharmony_ci if (err) 4548c2ecf20Sopenharmony_ci return err; 4558c2ecf20Sopenharmony_ci if (value & SSB_PCMCIA_SPROMCTL_DONE) 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci udelay(10); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci/* offset is the 16bit word offset */ 4648c2ecf20Sopenharmony_cistatic int ssb_pcmcia_sprom_read(struct ssb_bus *bus, u16 offset, u16 *value) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci int err; 4678c2ecf20Sopenharmony_ci u8 lo, hi; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci offset *= 2; /* Make byte offset */ 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, 4728c2ecf20Sopenharmony_ci (offset & 0x00FF)); 4738c2ecf20Sopenharmony_ci if (err) 4748c2ecf20Sopenharmony_ci return err; 4758c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, 4768c2ecf20Sopenharmony_ci (offset & 0xFF00) >> 8); 4778c2ecf20Sopenharmony_ci if (err) 4788c2ecf20Sopenharmony_ci return err; 4798c2ecf20Sopenharmony_ci err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_READ); 4808c2ecf20Sopenharmony_ci if (err) 4818c2ecf20Sopenharmony_ci return err; 4828c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATALO, &lo); 4838c2ecf20Sopenharmony_ci if (err) 4848c2ecf20Sopenharmony_ci return err; 4858c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATAHI, &hi); 4868c2ecf20Sopenharmony_ci if (err) 4878c2ecf20Sopenharmony_ci return err; 4888c2ecf20Sopenharmony_ci *value = (lo | (((u16)hi) << 8)); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci return 0; 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* offset is the 16bit word offset */ 4948c2ecf20Sopenharmony_cistatic int ssb_pcmcia_sprom_write(struct ssb_bus *bus, u16 offset, u16 value) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci int err; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci offset *= 2; /* Make byte offset */ 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO, 5018c2ecf20Sopenharmony_ci (offset & 0x00FF)); 5028c2ecf20Sopenharmony_ci if (err) 5038c2ecf20Sopenharmony_ci return err; 5048c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI, 5058c2ecf20Sopenharmony_ci (offset & 0xFF00) >> 8); 5068c2ecf20Sopenharmony_ci if (err) 5078c2ecf20Sopenharmony_ci return err; 5088c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATALO, 5098c2ecf20Sopenharmony_ci (value & 0x00FF)); 5108c2ecf20Sopenharmony_ci if (err) 5118c2ecf20Sopenharmony_ci return err; 5128c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATAHI, 5138c2ecf20Sopenharmony_ci (value & 0xFF00) >> 8); 5148c2ecf20Sopenharmony_ci if (err) 5158c2ecf20Sopenharmony_ci return err; 5168c2ecf20Sopenharmony_ci err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITE); 5178c2ecf20Sopenharmony_ci if (err) 5188c2ecf20Sopenharmony_ci return err; 5198c2ecf20Sopenharmony_ci msleep(20); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci return 0; 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/* Read the SPROM image. bufsize is in 16bit words. */ 5258c2ecf20Sopenharmony_cistatic int ssb_pcmcia_sprom_read_all(struct ssb_bus *bus, u16 *sprom) 5268c2ecf20Sopenharmony_ci{ 5278c2ecf20Sopenharmony_ci int err, i; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci for (i = 0; i < SSB_PCMCIA_SPROM_SIZE; i++) { 5308c2ecf20Sopenharmony_ci err = ssb_pcmcia_sprom_read(bus, i, &sprom[i]); 5318c2ecf20Sopenharmony_ci if (err) 5328c2ecf20Sopenharmony_ci return err; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci return 0; 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci/* Write the SPROM image. size is in 16bit words. */ 5398c2ecf20Sopenharmony_cistatic int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom) 5408c2ecf20Sopenharmony_ci{ 5418c2ecf20Sopenharmony_ci int i, err; 5428c2ecf20Sopenharmony_ci bool failed = 0; 5438c2ecf20Sopenharmony_ci size_t size = SSB_PCMCIA_SPROM_SIZE; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci pr_notice("Writing SPROM. Do NOT turn off the power! Please stand by...\n"); 5468c2ecf20Sopenharmony_ci err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN); 5478c2ecf20Sopenharmony_ci if (err) { 5488c2ecf20Sopenharmony_ci pr_notice("Could not enable SPROM write access\n"); 5498c2ecf20Sopenharmony_ci return -EBUSY; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci pr_notice("[ 0%%"); 5528c2ecf20Sopenharmony_ci msleep(500); 5538c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 5548c2ecf20Sopenharmony_ci if (i == size / 4) 5558c2ecf20Sopenharmony_ci pr_cont("25%%"); 5568c2ecf20Sopenharmony_ci else if (i == size / 2) 5578c2ecf20Sopenharmony_ci pr_cont("50%%"); 5588c2ecf20Sopenharmony_ci else if (i == (size * 3) / 4) 5598c2ecf20Sopenharmony_ci pr_cont("75%%"); 5608c2ecf20Sopenharmony_ci else if (i % 2) 5618c2ecf20Sopenharmony_ci pr_cont("."); 5628c2ecf20Sopenharmony_ci err = ssb_pcmcia_sprom_write(bus, i, sprom[i]); 5638c2ecf20Sopenharmony_ci if (err) { 5648c2ecf20Sopenharmony_ci pr_notice("Failed to write to SPROM\n"); 5658c2ecf20Sopenharmony_ci failed = 1; 5668c2ecf20Sopenharmony_ci break; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS); 5708c2ecf20Sopenharmony_ci if (err) { 5718c2ecf20Sopenharmony_ci pr_notice("Could not disable SPROM write access\n"); 5728c2ecf20Sopenharmony_ci failed = 1; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci msleep(500); 5758c2ecf20Sopenharmony_ci if (!failed) { 5768c2ecf20Sopenharmony_ci pr_cont("100%% ]\n"); 5778c2ecf20Sopenharmony_ci pr_notice("SPROM written\n"); 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return failed ? -EBUSY : 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci //TODO 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci#define GOTO_ERROR_ON(condition, description) do { \ 5908c2ecf20Sopenharmony_ci if (unlikely(condition)) { \ 5918c2ecf20Sopenharmony_ci error_description = description; \ 5928c2ecf20Sopenharmony_ci goto error; \ 5938c2ecf20Sopenharmony_ci } \ 5948c2ecf20Sopenharmony_ci } while (0) 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int ssb_pcmcia_get_mac(struct pcmcia_device *p_dev, 5978c2ecf20Sopenharmony_ci tuple_t *tuple, 5988c2ecf20Sopenharmony_ci void *priv) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct ssb_sprom *sprom = priv; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID) 6038c2ecf20Sopenharmony_ci return -EINVAL; 6048c2ecf20Sopenharmony_ci if (tuple->TupleDataLen != ETH_ALEN + 2) 6058c2ecf20Sopenharmony_ci return -EINVAL; 6068c2ecf20Sopenharmony_ci if (tuple->TupleData[1] != ETH_ALEN) 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci memcpy(sprom->il0mac, &tuple->TupleData[2], ETH_ALEN); 6098c2ecf20Sopenharmony_ci return 0; 6108c2ecf20Sopenharmony_ci}; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev, 6138c2ecf20Sopenharmony_ci tuple_t *tuple, 6148c2ecf20Sopenharmony_ci void *priv) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci struct ssb_init_invariants *iv = priv; 6178c2ecf20Sopenharmony_ci struct ssb_sprom *sprom = &iv->sprom; 6188c2ecf20Sopenharmony_ci struct ssb_boardinfo *bi = &iv->boardinfo; 6198c2ecf20Sopenharmony_ci const char *error_description; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci GOTO_ERROR_ON(tuple->TupleDataLen < 1, "VEN tpl < 1"); 6228c2ecf20Sopenharmony_ci switch (tuple->TupleData[0]) { 6238c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_ID: 6248c2ecf20Sopenharmony_ci GOTO_ERROR_ON((tuple->TupleDataLen != 5) && 6258c2ecf20Sopenharmony_ci (tuple->TupleDataLen != 7), 6268c2ecf20Sopenharmony_ci "id tpl size"); 6278c2ecf20Sopenharmony_ci bi->vendor = tuple->TupleData[1] | 6288c2ecf20Sopenharmony_ci ((u16)tuple->TupleData[2] << 8); 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_BOARDREV: 6318c2ecf20Sopenharmony_ci GOTO_ERROR_ON(tuple->TupleDataLen != 2, 6328c2ecf20Sopenharmony_ci "boardrev tpl size"); 6338c2ecf20Sopenharmony_ci sprom->board_rev = tuple->TupleData[1]; 6348c2ecf20Sopenharmony_ci break; 6358c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_PA: 6368c2ecf20Sopenharmony_ci GOTO_ERROR_ON((tuple->TupleDataLen != 9) && 6378c2ecf20Sopenharmony_ci (tuple->TupleDataLen != 10), 6388c2ecf20Sopenharmony_ci "pa tpl size"); 6398c2ecf20Sopenharmony_ci sprom->pa0b0 = tuple->TupleData[1] | 6408c2ecf20Sopenharmony_ci ((u16)tuple->TupleData[2] << 8); 6418c2ecf20Sopenharmony_ci sprom->pa0b1 = tuple->TupleData[3] | 6428c2ecf20Sopenharmony_ci ((u16)tuple->TupleData[4] << 8); 6438c2ecf20Sopenharmony_ci sprom->pa0b2 = tuple->TupleData[5] | 6448c2ecf20Sopenharmony_ci ((u16)tuple->TupleData[6] << 8); 6458c2ecf20Sopenharmony_ci sprom->itssi_a = tuple->TupleData[7]; 6468c2ecf20Sopenharmony_ci sprom->itssi_bg = tuple->TupleData[7]; 6478c2ecf20Sopenharmony_ci sprom->maxpwr_a = tuple->TupleData[8]; 6488c2ecf20Sopenharmony_ci sprom->maxpwr_bg = tuple->TupleData[8]; 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_OEMNAME: 6518c2ecf20Sopenharmony_ci /* We ignore this. */ 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_CCODE: 6548c2ecf20Sopenharmony_ci GOTO_ERROR_ON(tuple->TupleDataLen != 2, 6558c2ecf20Sopenharmony_ci "ccode tpl size"); 6568c2ecf20Sopenharmony_ci sprom->country_code = tuple->TupleData[1]; 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_ANTENNA: 6598c2ecf20Sopenharmony_ci GOTO_ERROR_ON(tuple->TupleDataLen != 2, 6608c2ecf20Sopenharmony_ci "ant tpl size"); 6618c2ecf20Sopenharmony_ci sprom->ant_available_a = tuple->TupleData[1]; 6628c2ecf20Sopenharmony_ci sprom->ant_available_bg = tuple->TupleData[1]; 6638c2ecf20Sopenharmony_ci break; 6648c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_ANTGAIN: 6658c2ecf20Sopenharmony_ci GOTO_ERROR_ON(tuple->TupleDataLen != 2, 6668c2ecf20Sopenharmony_ci "antg tpl size"); 6678c2ecf20Sopenharmony_ci sprom->antenna_gain.a0 = tuple->TupleData[1]; 6688c2ecf20Sopenharmony_ci sprom->antenna_gain.a1 = tuple->TupleData[1]; 6698c2ecf20Sopenharmony_ci sprom->antenna_gain.a2 = tuple->TupleData[1]; 6708c2ecf20Sopenharmony_ci sprom->antenna_gain.a3 = tuple->TupleData[1]; 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_BFLAGS: 6738c2ecf20Sopenharmony_ci GOTO_ERROR_ON((tuple->TupleDataLen != 3) && 6748c2ecf20Sopenharmony_ci (tuple->TupleDataLen != 5), 6758c2ecf20Sopenharmony_ci "bfl tpl size"); 6768c2ecf20Sopenharmony_ci sprom->boardflags_lo = tuple->TupleData[1] | 6778c2ecf20Sopenharmony_ci ((u16)tuple->TupleData[2] << 8); 6788c2ecf20Sopenharmony_ci break; 6798c2ecf20Sopenharmony_ci case SSB_PCMCIA_CIS_LEDS: 6808c2ecf20Sopenharmony_ci GOTO_ERROR_ON(tuple->TupleDataLen != 5, 6818c2ecf20Sopenharmony_ci "leds tpl size"); 6828c2ecf20Sopenharmony_ci sprom->gpio0 = tuple->TupleData[1]; 6838c2ecf20Sopenharmony_ci sprom->gpio1 = tuple->TupleData[2]; 6848c2ecf20Sopenharmony_ci sprom->gpio2 = tuple->TupleData[3]; 6858c2ecf20Sopenharmony_ci sprom->gpio3 = tuple->TupleData[4]; 6868c2ecf20Sopenharmony_ci break; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci return -ENOSPC; /* continue with next entry */ 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cierror: 6918c2ecf20Sopenharmony_ci pr_err("PCMCIA: Failed to fetch device invariants: %s\n", 6928c2ecf20Sopenharmony_ci error_description); 6938c2ecf20Sopenharmony_ci return -ENODEV; 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ciint ssb_pcmcia_get_invariants(struct ssb_bus *bus, 6988c2ecf20Sopenharmony_ci struct ssb_init_invariants *iv) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci struct ssb_sprom *sprom = &iv->sprom; 7018c2ecf20Sopenharmony_ci int res; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci memset(sprom, 0xFF, sizeof(*sprom)); 7048c2ecf20Sopenharmony_ci sprom->revision = 1; 7058c2ecf20Sopenharmony_ci sprom->boardflags_lo = 0; 7068c2ecf20Sopenharmony_ci sprom->boardflags_hi = 0; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci /* First fetch the MAC address. */ 7098c2ecf20Sopenharmony_ci res = pcmcia_loop_tuple(bus->host_pcmcia, CISTPL_FUNCE, 7108c2ecf20Sopenharmony_ci ssb_pcmcia_get_mac, sprom); 7118c2ecf20Sopenharmony_ci if (res != 0) { 7128c2ecf20Sopenharmony_ci pr_err("PCMCIA: Failed to fetch MAC address\n"); 7138c2ecf20Sopenharmony_ci return -ENODEV; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* Fetch the vendor specific tuples. */ 7178c2ecf20Sopenharmony_ci res = pcmcia_loop_tuple(bus->host_pcmcia, SSB_PCMCIA_CIS, 7188c2ecf20Sopenharmony_ci ssb_pcmcia_do_get_invariants, iv); 7198c2ecf20Sopenharmony_ci if ((res == 0) || (res == -ENOSPC)) 7208c2ecf20Sopenharmony_ci return 0; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci pr_err("PCMCIA: Failed to fetch device invariants\n"); 7238c2ecf20Sopenharmony_ci return -ENODEV; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_cistatic ssize_t ssb_pcmcia_attr_sprom_show(struct device *pcmciadev, 7278c2ecf20Sopenharmony_ci struct device_attribute *attr, 7288c2ecf20Sopenharmony_ci char *buf) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci struct pcmcia_device *pdev = 7318c2ecf20Sopenharmony_ci container_of(pcmciadev, struct pcmcia_device, dev); 7328c2ecf20Sopenharmony_ci struct ssb_bus *bus; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci bus = ssb_pcmcia_dev_to_bus(pdev); 7358c2ecf20Sopenharmony_ci if (!bus) 7368c2ecf20Sopenharmony_ci return -ENODEV; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci return ssb_attr_sprom_show(bus, buf, 7398c2ecf20Sopenharmony_ci ssb_pcmcia_sprom_read_all); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic ssize_t ssb_pcmcia_attr_sprom_store(struct device *pcmciadev, 7438c2ecf20Sopenharmony_ci struct device_attribute *attr, 7448c2ecf20Sopenharmony_ci const char *buf, size_t count) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct pcmcia_device *pdev = 7478c2ecf20Sopenharmony_ci container_of(pcmciadev, struct pcmcia_device, dev); 7488c2ecf20Sopenharmony_ci struct ssb_bus *bus; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci bus = ssb_pcmcia_dev_to_bus(pdev); 7518c2ecf20Sopenharmony_ci if (!bus) 7528c2ecf20Sopenharmony_ci return -ENODEV; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci return ssb_attr_sprom_store(bus, buf, count, 7558c2ecf20Sopenharmony_ci ssb_pcmcia_sprom_check_crc, 7568c2ecf20Sopenharmony_ci ssb_pcmcia_sprom_write_all); 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(ssb_sprom, 0600, 7608c2ecf20Sopenharmony_ci ssb_pcmcia_attr_sprom_show, 7618c2ecf20Sopenharmony_ci ssb_pcmcia_attr_sprom_store); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_cistatic int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci u8 val; 7668c2ecf20Sopenharmony_ci int err; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_read(bus, cor, &val); 7698c2ecf20Sopenharmony_ci if (err) 7708c2ecf20Sopenharmony_ci return err; 7718c2ecf20Sopenharmony_ci val &= ~COR_SOFT_RESET; 7728c2ecf20Sopenharmony_ci val |= COR_FUNC_ENA | COR_IREQ_ENA | COR_LEVEL_REQ; 7738c2ecf20Sopenharmony_ci err = ssb_pcmcia_cfg_write(bus, cor, val); 7748c2ecf20Sopenharmony_ci if (err) 7758c2ecf20Sopenharmony_ci return err; 7768c2ecf20Sopenharmony_ci msleep(40); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return 0; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci/* Initialize the PCMCIA hardware. This is called on Init and Resume. */ 7828c2ecf20Sopenharmony_ciint ssb_pcmcia_hardware_setup(struct ssb_bus *bus) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci int err; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (bus->bustype != SSB_BUSTYPE_PCMCIA) 7878c2ecf20Sopenharmony_ci return 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci /* Switch segment to a known state and sync 7908c2ecf20Sopenharmony_ci * bus->mapped_pcmcia_seg with hardware state. */ 7918c2ecf20Sopenharmony_ci ssb_pcmcia_switch_segment(bus, 0); 7928c2ecf20Sopenharmony_ci /* Init the COR register. */ 7938c2ecf20Sopenharmony_ci err = ssb_pcmcia_cor_setup(bus, CISREG_COR); 7948c2ecf20Sopenharmony_ci if (err) 7958c2ecf20Sopenharmony_ci return err; 7968c2ecf20Sopenharmony_ci /* Some cards also need this register to get poked. */ 7978c2ecf20Sopenharmony_ci err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80); 7988c2ecf20Sopenharmony_ci if (err) 7998c2ecf20Sopenharmony_ci return err; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci} 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_civoid ssb_pcmcia_exit(struct ssb_bus *bus) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci if (bus->bustype != SSB_BUSTYPE_PCMCIA) 8078c2ecf20Sopenharmony_ci return; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci device_remove_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ciint ssb_pcmcia_init(struct ssb_bus *bus) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci int err; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (bus->bustype != SSB_BUSTYPE_PCMCIA) 8178c2ecf20Sopenharmony_ci return 0; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci err = ssb_pcmcia_hardware_setup(bus); 8208c2ecf20Sopenharmony_ci if (err) 8218c2ecf20Sopenharmony_ci goto error; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci bus->sprom_size = SSB_PCMCIA_SPROM_SIZE; 8248c2ecf20Sopenharmony_ci mutex_init(&bus->sprom_mutex); 8258c2ecf20Sopenharmony_ci err = device_create_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom); 8268c2ecf20Sopenharmony_ci if (err) 8278c2ecf20Sopenharmony_ci goto error; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci return 0; 8308c2ecf20Sopenharmony_cierror: 8318c2ecf20Sopenharmony_ci pr_err("Failed to initialize PCMCIA host device\n"); 8328c2ecf20Sopenharmony_ci return err; 8338c2ecf20Sopenharmony_ci} 834