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