123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838 |
- /*
- * Sonics Silicon Backplane
- * PCMCIA-Hostbus related functions
- *
- * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
- * Copyright 2007-2008 Michael Buesch <m@bues.ch>
- *
- * Licensed under the GNU/GPL. See COPYING for details.
- */
- #include "ssb_private.h"
- #include <linux/ssb/ssb.h>
- #include <linux/delay.h>
- #include <linux/io.h>
- #include <linux/etherdevice.h>
- #include <pcmcia/cistpl.h>
- #include <pcmcia/ciscode.h>
- #include <pcmcia/ds.h>
- #include <pcmcia/cisreg.h>
- /* Define the following to 1 to enable a printk on each coreswitch. */
- #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0
- /* PCMCIA configuration registers */
- #define SSB_PCMCIA_ADDRESS0 0x2E
- #define SSB_PCMCIA_ADDRESS1 0x30
- #define SSB_PCMCIA_ADDRESS2 0x32
- #define SSB_PCMCIA_MEMSEG 0x34
- #define SSB_PCMCIA_SPROMCTL 0x36
- #define SSB_PCMCIA_SPROMCTL_IDLE 0
- #define SSB_PCMCIA_SPROMCTL_WRITE 1
- #define SSB_PCMCIA_SPROMCTL_READ 2
- #define SSB_PCMCIA_SPROMCTL_WRITEEN 4
- #define SSB_PCMCIA_SPROMCTL_WRITEDIS 7
- #define SSB_PCMCIA_SPROMCTL_DONE 8
- #define SSB_PCMCIA_SPROM_DATALO 0x38
- #define SSB_PCMCIA_SPROM_DATAHI 0x3A
- #define SSB_PCMCIA_SPROM_ADDRLO 0x3C
- #define SSB_PCMCIA_SPROM_ADDRHI 0x3E
- /* Hardware invariants CIS tuples */
- #define SSB_PCMCIA_CIS 0x80
- #define SSB_PCMCIA_CIS_ID 0x01
- #define SSB_PCMCIA_CIS_BOARDREV 0x02
- #define SSB_PCMCIA_CIS_PA 0x03
- #define SSB_PCMCIA_CIS_PA_PA0B0_LO 0
- #define SSB_PCMCIA_CIS_PA_PA0B0_HI 1
- #define SSB_PCMCIA_CIS_PA_PA0B1_LO 2
- #define SSB_PCMCIA_CIS_PA_PA0B1_HI 3
- #define SSB_PCMCIA_CIS_PA_PA0B2_LO 4
- #define SSB_PCMCIA_CIS_PA_PA0B2_HI 5
- #define SSB_PCMCIA_CIS_PA_ITSSI 6
- #define SSB_PCMCIA_CIS_PA_MAXPOW 7
- #define SSB_PCMCIA_CIS_OEMNAME 0x04
- #define SSB_PCMCIA_CIS_CCODE 0x05
- #define SSB_PCMCIA_CIS_ANTENNA 0x06
- #define SSB_PCMCIA_CIS_ANTGAIN 0x07
- #define SSB_PCMCIA_CIS_BFLAGS 0x08
- #define SSB_PCMCIA_CIS_LEDS 0x09
- /* PCMCIA SPROM size. */
- #define SSB_PCMCIA_SPROM_SIZE 256
- #define SSB_PCMCIA_SPROM_SIZE_BYTES (SSB_PCMCIA_SPROM_SIZE * sizeof(u16))
- /* Write to a PCMCIA configuration register. */
- static int ssb_pcmcia_cfg_write(struct ssb_bus *bus, u8 offset, u8 value)
- {
- int res;
- res = pcmcia_write_config_byte(bus->host_pcmcia, offset, value);
- if (unlikely(res != 0))
- return -EBUSY;
- return 0;
- }
- /* Read from a PCMCIA configuration register. */
- static int ssb_pcmcia_cfg_read(struct ssb_bus *bus, u8 offset, u8 *value)
- {
- int res;
- res = pcmcia_read_config_byte(bus->host_pcmcia, offset, value);
- if (unlikely(res != 0))
- return -EBUSY;
- return 0;
- }
- int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
- u8 coreidx)
- {
- int err;
- int attempts = 0;
- u32 cur_core;
- u32 addr;
- u32 read_addr;
- u8 val;
- addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
- while (1) {
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS0,
- (addr & 0x0000F000) >> 12);
- if (err)
- goto error;
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS1,
- (addr & 0x00FF0000) >> 16);
- if (err)
- goto error;
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_ADDRESS2,
- (addr & 0xFF000000) >> 24);
- if (err)
- goto error;
- read_addr = 0;
- err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS0, &val);
- if (err)
- goto error;
- read_addr |= ((u32)(val & 0x0F)) << 12;
- err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS1, &val);
- if (err)
- goto error;
- read_addr |= ((u32)val) << 16;
- err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_ADDRESS2, &val);
- if (err)
- goto error;
- read_addr |= ((u32)val) << 24;
- cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE;
- if (cur_core == coreidx)
- break;
- err = -ETIMEDOUT;
- if (attempts++ > SSB_BAR0_MAX_RETRIES)
- goto error;
- udelay(10);
- }
- return 0;
- error:
- pr_err("Failed to switch to core %u\n", coreidx);
- return err;
- }
- static int ssb_pcmcia_switch_core(struct ssb_bus *bus, struct ssb_device *dev)
- {
- int err;
- #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
- pr_info("Switching to %s core, index %d\n",
- ssb_core_name(dev->id.coreid), dev->core_index);
- #endif
- err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
- if (!err)
- bus->mapped_device = dev;
- return err;
- }
- int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
- {
- int attempts = 0;
- int err;
- u8 val;
- WARN_ON((seg != 0) && (seg != 1));
- while (1) {
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_MEMSEG, seg);
- if (err)
- goto error;
- err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_MEMSEG, &val);
- if (err)
- goto error;
- if (val == seg)
- break;
- err = -ETIMEDOUT;
- if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES))
- goto error;
- udelay(10);
- }
- bus->mapped_pcmcia_seg = seg;
- return 0;
- error:
- pr_err("Failed to switch pcmcia segment\n");
- return err;
- }
- static int select_core_and_segment(struct ssb_device *dev,
- u16 *offset)
- {
- struct ssb_bus *bus = dev->bus;
- int err;
- u8 need_segment;
- if (*offset >= 0x800) {
- *offset -= 0x800;
- need_segment = 1;
- } else
- need_segment = 0;
- if (unlikely(dev != bus->mapped_device)) {
- err = ssb_pcmcia_switch_core(bus, dev);
- if (unlikely(err))
- return err;
- }
- if (unlikely(need_segment != bus->mapped_pcmcia_seg)) {
- err = ssb_pcmcia_switch_segment(bus, need_segment);
- if (unlikely(err))
- return err;
- }
- return 0;
- }
- static u8 ssb_pcmcia_read8(struct ssb_device *dev, u16 offset)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- int err;
- u8 value = 0xFF;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (likely(!err))
- value = readb(bus->mmio + offset);
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- return value;
- }
- static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- int err;
- u16 value = 0xFFFF;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (likely(!err))
- value = readw(bus->mmio + offset);
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- return value;
- }
- static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- int err;
- u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (likely(!err)) {
- lo = readw(bus->mmio + offset);
- hi = readw(bus->mmio + offset + 2);
- }
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- return (lo | (hi << 16));
- }
- #ifdef CONFIG_SSB_BLOCKIO
- static void ssb_pcmcia_block_read(struct ssb_device *dev, void *buffer,
- size_t count, u16 offset, u8 reg_width)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- void __iomem *addr = bus->mmio + offset;
- int err;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (unlikely(err)) {
- memset(buffer, 0xFF, count);
- goto unlock;
- }
- switch (reg_width) {
- case sizeof(u8): {
- u8 *buf = buffer;
- while (count) {
- *buf = __raw_readb(addr);
- buf++;
- count--;
- }
- break;
- }
- case sizeof(u16): {
- __le16 *buf = buffer;
- WARN_ON(count & 1);
- while (count) {
- *buf = (__force __le16)__raw_readw(addr);
- buf++;
- count -= 2;
- }
- break;
- }
- case sizeof(u32): {
- __le16 *buf = buffer;
- WARN_ON(count & 3);
- while (count) {
- *buf = (__force __le16)__raw_readw(addr);
- buf++;
- *buf = (__force __le16)__raw_readw(addr + 2);
- buf++;
- count -= 4;
- }
- break;
- }
- default:
- WARN_ON(1);
- }
- unlock:
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- }
- #endif /* CONFIG_SSB_BLOCKIO */
- static void ssb_pcmcia_write8(struct ssb_device *dev, u16 offset, u8 value)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- int err;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (likely(!err))
- writeb(value, bus->mmio + offset);
- mmiowb();
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- }
- static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- int err;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (likely(!err))
- writew(value, bus->mmio + offset);
- mmiowb();
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- }
- static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- int err;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (likely(!err)) {
- writew((value & 0x0000FFFF), bus->mmio + offset);
- writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2);
- }
- mmiowb();
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- }
- #ifdef CONFIG_SSB_BLOCKIO
- static void ssb_pcmcia_block_write(struct ssb_device *dev, const void *buffer,
- size_t count, u16 offset, u8 reg_width)
- {
- struct ssb_bus *bus = dev->bus;
- unsigned long flags;
- void __iomem *addr = bus->mmio + offset;
- int err;
- spin_lock_irqsave(&bus->bar_lock, flags);
- err = select_core_and_segment(dev, &offset);
- if (unlikely(err))
- goto unlock;
- switch (reg_width) {
- case sizeof(u8): {
- const u8 *buf = buffer;
- while (count) {
- __raw_writeb(*buf, addr);
- buf++;
- count--;
- }
- break;
- }
- case sizeof(u16): {
- const __le16 *buf = buffer;
- WARN_ON(count & 1);
- while (count) {
- __raw_writew((__force u16)(*buf), addr);
- buf++;
- count -= 2;
- }
- break;
- }
- case sizeof(u32): {
- const __le16 *buf = buffer;
- WARN_ON(count & 3);
- while (count) {
- __raw_writew((__force u16)(*buf), addr);
- buf++;
- __raw_writew((__force u16)(*buf), addr + 2);
- buf++;
- count -= 4;
- }
- break;
- }
- default:
- WARN_ON(1);
- }
- unlock:
- mmiowb();
- spin_unlock_irqrestore(&bus->bar_lock, flags);
- }
- #endif /* CONFIG_SSB_BLOCKIO */
- /* Not "static", as it's used in main.c */
- const struct ssb_bus_ops ssb_pcmcia_ops = {
- .read8 = ssb_pcmcia_read8,
- .read16 = ssb_pcmcia_read16,
- .read32 = ssb_pcmcia_read32,
- .write8 = ssb_pcmcia_write8,
- .write16 = ssb_pcmcia_write16,
- .write32 = ssb_pcmcia_write32,
- #ifdef CONFIG_SSB_BLOCKIO
- .block_read = ssb_pcmcia_block_read,
- .block_write = ssb_pcmcia_block_write,
- #endif
- };
- static int ssb_pcmcia_sprom_command(struct ssb_bus *bus, u8 command)
- {
- unsigned int i;
- int err;
- u8 value;
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROMCTL, command);
- if (err)
- return err;
- for (i = 0; i < 1000; i++) {
- err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROMCTL, &value);
- if (err)
- return err;
- if (value & SSB_PCMCIA_SPROMCTL_DONE)
- return 0;
- udelay(10);
- }
- return -ETIMEDOUT;
- }
- /* offset is the 16bit word offset */
- static int ssb_pcmcia_sprom_read(struct ssb_bus *bus, u16 offset, u16 *value)
- {
- int err;
- u8 lo, hi;
- offset *= 2; /* Make byte offset */
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO,
- (offset & 0x00FF));
- if (err)
- return err;
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI,
- (offset & 0xFF00) >> 8);
- if (err)
- return err;
- err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_READ);
- if (err)
- return err;
- err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATALO, &lo);
- if (err)
- return err;
- err = ssb_pcmcia_cfg_read(bus, SSB_PCMCIA_SPROM_DATAHI, &hi);
- if (err)
- return err;
- *value = (lo | (((u16)hi) << 8));
- return 0;
- }
- /* offset is the 16bit word offset */
- static int ssb_pcmcia_sprom_write(struct ssb_bus *bus, u16 offset, u16 value)
- {
- int err;
- offset *= 2; /* Make byte offset */
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRLO,
- (offset & 0x00FF));
- if (err)
- return err;
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_ADDRHI,
- (offset & 0xFF00) >> 8);
- if (err)
- return err;
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATALO,
- (value & 0x00FF));
- if (err)
- return err;
- err = ssb_pcmcia_cfg_write(bus, SSB_PCMCIA_SPROM_DATAHI,
- (value & 0xFF00) >> 8);
- if (err)
- return err;
- err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITE);
- if (err)
- return err;
- msleep(20);
- return 0;
- }
- /* Read the SPROM image. bufsize is in 16bit words. */
- static int ssb_pcmcia_sprom_read_all(struct ssb_bus *bus, u16 *sprom)
- {
- int err, i;
- for (i = 0; i < SSB_PCMCIA_SPROM_SIZE; i++) {
- err = ssb_pcmcia_sprom_read(bus, i, &sprom[i]);
- if (err)
- return err;
- }
- return 0;
- }
- /* Write the SPROM image. size is in 16bit words. */
- static int ssb_pcmcia_sprom_write_all(struct ssb_bus *bus, const u16 *sprom)
- {
- int i, err;
- bool failed = 0;
- size_t size = SSB_PCMCIA_SPROM_SIZE;
- pr_notice("Writing SPROM. Do NOT turn off the power! Please stand by...\n");
- err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEEN);
- if (err) {
- pr_notice("Could not enable SPROM write access\n");
- return -EBUSY;
- }
- pr_notice("[ 0%%");
- msleep(500);
- for (i = 0; i < size; i++) {
- if (i == size / 4)
- pr_cont("25%%");
- else if (i == size / 2)
- pr_cont("50%%");
- else if (i == (size * 3) / 4)
- pr_cont("75%%");
- else if (i % 2)
- pr_cont(".");
- err = ssb_pcmcia_sprom_write(bus, i, sprom[i]);
- if (err) {
- pr_notice("Failed to write to SPROM\n");
- failed = 1;
- break;
- }
- }
- err = ssb_pcmcia_sprom_command(bus, SSB_PCMCIA_SPROMCTL_WRITEDIS);
- if (err) {
- pr_notice("Could not disable SPROM write access\n");
- failed = 1;
- }
- msleep(500);
- if (!failed) {
- pr_cont("100%% ]\n");
- pr_notice("SPROM written\n");
- }
- return failed ? -EBUSY : 0;
- }
- static int ssb_pcmcia_sprom_check_crc(const u16 *sprom, size_t size)
- {
- //TODO
- return 0;
- }
- #define GOTO_ERROR_ON(condition, description) do { \
- if (unlikely(condition)) { \
- error_description = description; \
- goto error; \
- } \
- } while (0)
- static int ssb_pcmcia_get_mac(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
- {
- struct ssb_sprom *sprom = priv;
- if (tuple->TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID)
- return -EINVAL;
- if (tuple->TupleDataLen != ETH_ALEN + 2)
- return -EINVAL;
- if (tuple->TupleData[1] != ETH_ALEN)
- return -EINVAL;
- memcpy(sprom->il0mac, &tuple->TupleData[2], ETH_ALEN);
- return 0;
- };
- static int ssb_pcmcia_do_get_invariants(struct pcmcia_device *p_dev,
- tuple_t *tuple,
- void *priv)
- {
- struct ssb_init_invariants *iv = priv;
- struct ssb_sprom *sprom = &iv->sprom;
- struct ssb_boardinfo *bi = &iv->boardinfo;
- const char *error_description;
- GOTO_ERROR_ON(tuple->TupleDataLen < 1, "VEN tpl < 1");
- switch (tuple->TupleData[0]) {
- case SSB_PCMCIA_CIS_ID:
- GOTO_ERROR_ON((tuple->TupleDataLen != 5) &&
- (tuple->TupleDataLen != 7),
- "id tpl size");
- bi->vendor = tuple->TupleData[1] |
- ((u16)tuple->TupleData[2] << 8);
- break;
- case SSB_PCMCIA_CIS_BOARDREV:
- GOTO_ERROR_ON(tuple->TupleDataLen != 2,
- "boardrev tpl size");
- sprom->board_rev = tuple->TupleData[1];
- break;
- case SSB_PCMCIA_CIS_PA:
- GOTO_ERROR_ON((tuple->TupleDataLen != 9) &&
- (tuple->TupleDataLen != 10),
- "pa tpl size");
- sprom->pa0b0 = tuple->TupleData[1] |
- ((u16)tuple->TupleData[2] << 8);
- sprom->pa0b1 = tuple->TupleData[3] |
- ((u16)tuple->TupleData[4] << 8);
- sprom->pa0b2 = tuple->TupleData[5] |
- ((u16)tuple->TupleData[6] << 8);
- sprom->itssi_a = tuple->TupleData[7];
- sprom->itssi_bg = tuple->TupleData[7];
- sprom->maxpwr_a = tuple->TupleData[8];
- sprom->maxpwr_bg = tuple->TupleData[8];
- break;
- case SSB_PCMCIA_CIS_OEMNAME:
- /* We ignore this. */
- break;
- case SSB_PCMCIA_CIS_CCODE:
- GOTO_ERROR_ON(tuple->TupleDataLen != 2,
- "ccode tpl size");
- sprom->country_code = tuple->TupleData[1];
- break;
- case SSB_PCMCIA_CIS_ANTENNA:
- GOTO_ERROR_ON(tuple->TupleDataLen != 2,
- "ant tpl size");
- sprom->ant_available_a = tuple->TupleData[1];
- sprom->ant_available_bg = tuple->TupleData[1];
- break;
- case SSB_PCMCIA_CIS_ANTGAIN:
- GOTO_ERROR_ON(tuple->TupleDataLen != 2,
- "antg tpl size");
- sprom->antenna_gain.a0 = tuple->TupleData[1];
- sprom->antenna_gain.a1 = tuple->TupleData[1];
- sprom->antenna_gain.a2 = tuple->TupleData[1];
- sprom->antenna_gain.a3 = tuple->TupleData[1];
- break;
- case SSB_PCMCIA_CIS_BFLAGS:
- GOTO_ERROR_ON((tuple->TupleDataLen != 3) &&
- (tuple->TupleDataLen != 5),
- "bfl tpl size");
- sprom->boardflags_lo = tuple->TupleData[1] |
- ((u16)tuple->TupleData[2] << 8);
- break;
- case SSB_PCMCIA_CIS_LEDS:
- GOTO_ERROR_ON(tuple->TupleDataLen != 5,
- "leds tpl size");
- sprom->gpio0 = tuple->TupleData[1];
- sprom->gpio1 = tuple->TupleData[2];
- sprom->gpio2 = tuple->TupleData[3];
- sprom->gpio3 = tuple->TupleData[4];
- break;
- }
- return -ENOSPC; /* continue with next entry */
- error:
- pr_err("PCMCIA: Failed to fetch device invariants: %s\n",
- error_description);
- return -ENODEV;
- }
- int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
- struct ssb_init_invariants *iv)
- {
- struct ssb_sprom *sprom = &iv->sprom;
- int res;
- memset(sprom, 0xFF, sizeof(*sprom));
- sprom->revision = 1;
- sprom->boardflags_lo = 0;
- sprom->boardflags_hi = 0;
- /* First fetch the MAC address. */
- res = pcmcia_loop_tuple(bus->host_pcmcia, CISTPL_FUNCE,
- ssb_pcmcia_get_mac, sprom);
- if (res != 0) {
- pr_err("PCMCIA: Failed to fetch MAC address\n");
- return -ENODEV;
- }
- /* Fetch the vendor specific tuples. */
- res = pcmcia_loop_tuple(bus->host_pcmcia, SSB_PCMCIA_CIS,
- ssb_pcmcia_do_get_invariants, iv);
- if ((res == 0) || (res == -ENOSPC))
- return 0;
- pr_err("PCMCIA: Failed to fetch device invariants\n");
- return -ENODEV;
- }
- static ssize_t ssb_pcmcia_attr_sprom_show(struct device *pcmciadev,
- struct device_attribute *attr,
- char *buf)
- {
- struct pcmcia_device *pdev =
- container_of(pcmciadev, struct pcmcia_device, dev);
- struct ssb_bus *bus;
- bus = ssb_pcmcia_dev_to_bus(pdev);
- if (!bus)
- return -ENODEV;
- return ssb_attr_sprom_show(bus, buf,
- ssb_pcmcia_sprom_read_all);
- }
- static ssize_t ssb_pcmcia_attr_sprom_store(struct device *pcmciadev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct pcmcia_device *pdev =
- container_of(pcmciadev, struct pcmcia_device, dev);
- struct ssb_bus *bus;
- bus = ssb_pcmcia_dev_to_bus(pdev);
- if (!bus)
- return -ENODEV;
- return ssb_attr_sprom_store(bus, buf, count,
- ssb_pcmcia_sprom_check_crc,
- ssb_pcmcia_sprom_write_all);
- }
- static DEVICE_ATTR(ssb_sprom, 0600,
- ssb_pcmcia_attr_sprom_show,
- ssb_pcmcia_attr_sprom_store);
- static int ssb_pcmcia_cor_setup(struct ssb_bus *bus, u8 cor)
- {
- u8 val;
- int err;
- err = ssb_pcmcia_cfg_read(bus, cor, &val);
- if (err)
- return err;
- val &= ~COR_SOFT_RESET;
- val |= COR_FUNC_ENA | COR_IREQ_ENA | COR_LEVEL_REQ;
- err = ssb_pcmcia_cfg_write(bus, cor, val);
- if (err)
- return err;
- msleep(40);
- return 0;
- }
- /* Initialize the PCMCIA hardware. This is called on Init and Resume. */
- int ssb_pcmcia_hardware_setup(struct ssb_bus *bus)
- {
- int err;
- if (bus->bustype != SSB_BUSTYPE_PCMCIA)
- return 0;
- /* Switch segment to a known state and sync
- * bus->mapped_pcmcia_seg with hardware state. */
- ssb_pcmcia_switch_segment(bus, 0);
- /* Init the COR register. */
- err = ssb_pcmcia_cor_setup(bus, CISREG_COR);
- if (err)
- return err;
- /* Some cards also need this register to get poked. */
- err = ssb_pcmcia_cor_setup(bus, CISREG_COR + 0x80);
- if (err)
- return err;
- return 0;
- }
- void ssb_pcmcia_exit(struct ssb_bus *bus)
- {
- if (bus->bustype != SSB_BUSTYPE_PCMCIA)
- return;
- device_remove_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom);
- }
- int ssb_pcmcia_init(struct ssb_bus *bus)
- {
- int err;
- if (bus->bustype != SSB_BUSTYPE_PCMCIA)
- return 0;
- err = ssb_pcmcia_hardware_setup(bus);
- if (err)
- goto error;
- bus->sprom_size = SSB_PCMCIA_SPROM_SIZE;
- mutex_init(&bus->sprom_mutex);
- err = device_create_file(&bus->host_pcmcia->dev, &dev_attr_ssb_sprom);
- if (err)
- goto error;
- return 0;
- error:
- pr_err("Failed to initialize PCMCIA host device\n");
- return err;
- }
|