606 lines · c
1/*2 * Sonics Silicon Backplane3 * SDIO-Hostbus related functions4 *5 * Copyright 2009 Albert Herranz <albert_herranz@yahoo.es>6 *7 * Based on drivers/ssb/pcmcia.c8 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>9 * Copyright 2007-2008 Michael Buesch <m@bues.ch>10 *11 * Licensed under the GNU/GPL. See COPYING for details.12 *13 */14 15#include "ssb_private.h"16 17#include <linux/ssb/ssb.h>18#include <linux/delay.h>19#include <linux/io.h>20#include <linux/etherdevice.h>21#include <linux/mmc/sdio_func.h>22 23/* Define the following to 1 to enable a printk on each coreswitch. */24#define SSB_VERBOSE_SDIOCORESWITCH_DEBUG 025 26 27/* Hardware invariants CIS tuples */28#define SSB_SDIO_CIS 0x8029#define SSB_SDIO_CIS_SROMREV 0x0030#define SSB_SDIO_CIS_ID 0x0131#define SSB_SDIO_CIS_BOARDREV 0x0232#define SSB_SDIO_CIS_PA 0x0333#define SSB_SDIO_CIS_PA_PA0B0_LO 034#define SSB_SDIO_CIS_PA_PA0B0_HI 135#define SSB_SDIO_CIS_PA_PA0B1_LO 236#define SSB_SDIO_CIS_PA_PA0B1_HI 337#define SSB_SDIO_CIS_PA_PA0B2_LO 438#define SSB_SDIO_CIS_PA_PA0B2_HI 539#define SSB_SDIO_CIS_PA_ITSSI 640#define SSB_SDIO_CIS_PA_MAXPOW 741#define SSB_SDIO_CIS_OEMNAME 0x0442#define SSB_SDIO_CIS_CCODE 0x0543#define SSB_SDIO_CIS_ANTENNA 0x0644#define SSB_SDIO_CIS_ANTGAIN 0x0745#define SSB_SDIO_CIS_BFLAGS 0x0846#define SSB_SDIO_CIS_LEDS 0x0947 48#define CISTPL_FUNCE_LAN_NODE_ID 0x04 /* same as in PCMCIA */49 50 51/*52 * Function 1 miscellaneous registers.53 *54 * Definitions match src/include/sbsdio.h from the55 * Android Open Source Project56 * http://android.git.kernel.org/?p=platform/system/wlan/broadcom.git57 *58 */59#define SBSDIO_FUNC1_SBADDRLOW 0x1000a /* SB Address window Low (b15) */60#define SBSDIO_FUNC1_SBADDRMID 0x1000b /* SB Address window Mid (b23-b16) */61#define SBSDIO_FUNC1_SBADDRHIGH 0x1000c /* SB Address window High (b24-b31) */62 63/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */64#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid address bits in SBADDRLOW */65#define SBSDIO_SBADDRMID_MASK 0xff /* Valid address bits in SBADDRMID */66#define SBSDIO_SBADDRHIGH_MASK 0xff /* Valid address bits in SBADDRHIGH */67 68#define SBSDIO_SB_OFT_ADDR_MASK 0x7FFF /* sb offset addr is <= 15 bits, 32k */69 70/* REVISIT: this flag doesn't seem to matter */71#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x8000 /* forces 32-bit SB access */72 73 74/*75 * Address map within the SDIO function address space (128K).76 *77 * Start End Description78 * ------- ------- ------------------------------------------79 * 0x00000 0x0ffff selected backplane address window (64K)80 * 0x10000 0x1ffff backplane control registers (max 64K)81 *82 * The current address window is configured by writing to registers83 * SBADDRLOW, SBADDRMID and SBADDRHIGH.84 *85 * In order to access the contents of a 32-bit Silicon Backplane address86 * the backplane address window must be first loaded with the highest87 * 16 bits of the target address. Then, an access must be done to the88 * SDIO function address space using the lower 15 bits of the address.89 * Bit 15 of the address must be set when doing 32 bit accesses.90 *91 * 1098765432109876543210987654321092 * WWWWWWWWWWWWWWWWW SB Address Window93 * OOOOOOOOOOOOOOOO Offset within SB Address Window94 * a 32-bit access flag95 */96 97 98/*99 * SSB I/O via SDIO.100 *101 * NOTE: SDIO address @addr is 17 bits long (SDIO address space is 128K).102 */103 104static inline struct device *ssb_sdio_dev(struct ssb_bus *bus)105{106 return &bus->host_sdio->dev;107}108 109/* host claimed */110static int ssb_sdio_writeb(struct ssb_bus *bus, unsigned int addr, u8 val)111{112 int error = 0;113 114 sdio_writeb(bus->host_sdio, val, addr, &error);115 if (unlikely(error)) {116 dev_dbg(ssb_sdio_dev(bus), "%08X <- %02x, error %d\n",117 addr, val, error);118 }119 120 return error;121}122 123#if 0124static u8 ssb_sdio_readb(struct ssb_bus *bus, unsigned int addr)125{126 u8 val;127 int error = 0;128 129 val = sdio_readb(bus->host_sdio, addr, &error);130 if (unlikely(error)) {131 dev_dbg(ssb_sdio_dev(bus), "%08X -> %02x, error %d\n",132 addr, val, error);133 }134 135 return val;136}137#endif138 139/* host claimed */140static int ssb_sdio_set_sbaddr_window(struct ssb_bus *bus, u32 address)141{142 int error;143 144 error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRLOW,145 (address >> 8) & SBSDIO_SBADDRLOW_MASK);146 if (error)147 goto out;148 error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRMID,149 (address >> 16) & SBSDIO_SBADDRMID_MASK);150 if (error)151 goto out;152 error = ssb_sdio_writeb(bus, SBSDIO_FUNC1_SBADDRHIGH,153 (address >> 24) & SBSDIO_SBADDRHIGH_MASK);154 if (error)155 goto out;156 bus->sdio_sbaddr = address;157out:158 if (error) {159 dev_dbg(ssb_sdio_dev(bus), "failed to set address window"160 " to 0x%08x, error %d\n", address, error);161 }162 163 return error;164}165 166/* for enumeration use only */167u32 ssb_sdio_scan_read32(struct ssb_bus *bus, u16 offset)168{169 u32 val;170 int error;171 172 sdio_claim_host(bus->host_sdio);173 val = sdio_readl(bus->host_sdio, offset, &error);174 sdio_release_host(bus->host_sdio);175 if (unlikely(error)) {176 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",177 bus->sdio_sbaddr >> 16, offset, val, error);178 }179 180 return val;181}182 183/* for enumeration use only */184int ssb_sdio_scan_switch_coreidx(struct ssb_bus *bus, u8 coreidx)185{186 u32 sbaddr;187 int error;188 189 sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;190 sdio_claim_host(bus->host_sdio);191 error = ssb_sdio_set_sbaddr_window(bus, sbaddr);192 sdio_release_host(bus->host_sdio);193 if (error) {194 dev_err(ssb_sdio_dev(bus), "failed to switch to core %u,"195 " error %d\n", coreidx, error);196 goto out;197 }198out:199 return error;200}201 202/* host must be already claimed */203static int ssb_sdio_switch_core(struct ssb_bus *bus, struct ssb_device *dev)204{205 u8 coreidx = dev->core_index;206 u32 sbaddr;207 int error = 0;208 209 sbaddr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;210 if (unlikely(bus->sdio_sbaddr != sbaddr)) {211#if SSB_VERBOSE_SDIOCORESWITCH_DEBUG212 dev_info(ssb_sdio_dev(bus),213 "switching to %s core, index %d\n",214 ssb_core_name(dev->id.coreid), coreidx);215#endif216 error = ssb_sdio_set_sbaddr_window(bus, sbaddr);217 if (error) {218 dev_dbg(ssb_sdio_dev(bus), "failed to switch to"219 " core %u, error %d\n", coreidx, error);220 goto out;221 }222 bus->mapped_device = dev;223 }224 225out:226 return error;227}228 229static u8 ssb_sdio_read8(struct ssb_device *dev, u16 offset)230{231 struct ssb_bus *bus = dev->bus;232 u8 val = 0xff;233 int error = 0;234 235 sdio_claim_host(bus->host_sdio);236 if (unlikely(ssb_sdio_switch_core(bus, dev)))237 goto out;238 offset |= bus->sdio_sbaddr & 0xffff;239 offset &= SBSDIO_SB_OFT_ADDR_MASK;240 val = sdio_readb(bus->host_sdio, offset, &error);241 if (error) {242 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %02x, error %d\n",243 bus->sdio_sbaddr >> 16, offset, val, error);244 }245out:246 sdio_release_host(bus->host_sdio);247 248 return val;249}250 251static u16 ssb_sdio_read16(struct ssb_device *dev, u16 offset)252{253 struct ssb_bus *bus = dev->bus;254 u16 val = 0xffff;255 int error = 0;256 257 sdio_claim_host(bus->host_sdio);258 if (unlikely(ssb_sdio_switch_core(bus, dev)))259 goto out;260 offset |= bus->sdio_sbaddr & 0xffff;261 offset &= SBSDIO_SB_OFT_ADDR_MASK;262 val = sdio_readw(bus->host_sdio, offset, &error);263 if (error) {264 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %04x, error %d\n",265 bus->sdio_sbaddr >> 16, offset, val, error);266 }267out:268 sdio_release_host(bus->host_sdio);269 270 return val;271}272 273static u32 ssb_sdio_read32(struct ssb_device *dev, u16 offset)274{275 struct ssb_bus *bus = dev->bus;276 u32 val = 0xffffffff;277 int error = 0;278 279 sdio_claim_host(bus->host_sdio);280 if (unlikely(ssb_sdio_switch_core(bus, dev)))281 goto out;282 offset |= bus->sdio_sbaddr & 0xffff;283 offset &= SBSDIO_SB_OFT_ADDR_MASK;284 offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */285 val = sdio_readl(bus->host_sdio, offset, &error);286 if (error) {287 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X > %08x, error %d\n",288 bus->sdio_sbaddr >> 16, offset, val, error);289 }290out:291 sdio_release_host(bus->host_sdio);292 293 return val;294}295 296#ifdef CONFIG_SSB_BLOCKIO297static void ssb_sdio_block_read(struct ssb_device *dev, void *buffer,298 size_t count, u16 offset, u8 reg_width)299{300 size_t saved_count = count;301 struct ssb_bus *bus = dev->bus;302 int error = 0;303 304 sdio_claim_host(bus->host_sdio);305 if (unlikely(ssb_sdio_switch_core(bus, dev))) {306 error = -EIO;307 memset(buffer, 0xff, count);308 goto err_out;309 }310 offset |= bus->sdio_sbaddr & 0xffff;311 offset &= SBSDIO_SB_OFT_ADDR_MASK;312 313 switch (reg_width) {314 case sizeof(u8): {315 error = sdio_readsb(bus->host_sdio, buffer, offset, count);316 break;317 }318 case sizeof(u16): {319 WARN_ON(count & 1);320 error = sdio_readsb(bus->host_sdio, buffer, offset, count);321 break;322 }323 case sizeof(u32): {324 WARN_ON(count & 3);325 offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */326 error = sdio_readsb(bus->host_sdio, buffer, offset, count);327 break;328 }329 default:330 WARN_ON(1);331 }332 if (!error)333 goto out;334 335err_out:336 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",337 bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);338out:339 sdio_release_host(bus->host_sdio);340}341#endif /* CONFIG_SSB_BLOCKIO */342 343static void ssb_sdio_write8(struct ssb_device *dev, u16 offset, u8 val)344{345 struct ssb_bus *bus = dev->bus;346 int error = 0;347 348 sdio_claim_host(bus->host_sdio);349 if (unlikely(ssb_sdio_switch_core(bus, dev)))350 goto out;351 offset |= bus->sdio_sbaddr & 0xffff;352 offset &= SBSDIO_SB_OFT_ADDR_MASK;353 sdio_writeb(bus->host_sdio, val, offset, &error);354 if (error) {355 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %02x, error %d\n",356 bus->sdio_sbaddr >> 16, offset, val, error);357 }358out:359 sdio_release_host(bus->host_sdio);360}361 362static void ssb_sdio_write16(struct ssb_device *dev, u16 offset, u16 val)363{364 struct ssb_bus *bus = dev->bus;365 int error = 0;366 367 sdio_claim_host(bus->host_sdio);368 if (unlikely(ssb_sdio_switch_core(bus, dev)))369 goto out;370 offset |= bus->sdio_sbaddr & 0xffff;371 offset &= SBSDIO_SB_OFT_ADDR_MASK;372 sdio_writew(bus->host_sdio, val, offset, &error);373 if (error) {374 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %04x, error %d\n",375 bus->sdio_sbaddr >> 16, offset, val, error);376 }377out:378 sdio_release_host(bus->host_sdio);379}380 381static void ssb_sdio_write32(struct ssb_device *dev, u16 offset, u32 val)382{383 struct ssb_bus *bus = dev->bus;384 int error = 0;385 386 sdio_claim_host(bus->host_sdio);387 if (unlikely(ssb_sdio_switch_core(bus, dev)))388 goto out;389 offset |= bus->sdio_sbaddr & 0xffff;390 offset &= SBSDIO_SB_OFT_ADDR_MASK;391 offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */392 sdio_writel(bus->host_sdio, val, offset, &error);393 if (error) {394 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X < %08x, error %d\n",395 bus->sdio_sbaddr >> 16, offset, val, error);396 }397 if (bus->quirks & SSB_QUIRK_SDIO_READ_AFTER_WRITE32)398 sdio_readl(bus->host_sdio, 0, &error);399out:400 sdio_release_host(bus->host_sdio);401}402 403#ifdef CONFIG_SSB_BLOCKIO404static void ssb_sdio_block_write(struct ssb_device *dev, const void *buffer,405 size_t count, u16 offset, u8 reg_width)406{407 size_t saved_count = count;408 struct ssb_bus *bus = dev->bus;409 int error = 0;410 411 sdio_claim_host(bus->host_sdio);412 if (unlikely(ssb_sdio_switch_core(bus, dev))) {413 error = -EIO;414 goto err_out;415 }416 offset |= bus->sdio_sbaddr & 0xffff;417 offset &= SBSDIO_SB_OFT_ADDR_MASK;418 419 switch (reg_width) {420 case sizeof(u8):421 error = sdio_writesb(bus->host_sdio, offset,422 (void *)buffer, count);423 break;424 case sizeof(u16):425 WARN_ON(count & 1);426 error = sdio_writesb(bus->host_sdio, offset,427 (void *)buffer, count);428 break;429 case sizeof(u32):430 WARN_ON(count & 3);431 offset |= SBSDIO_SB_ACCESS_2_4B_FLAG; /* 32 bit data access */432 error = sdio_writesb(bus->host_sdio, offset,433 (void *)buffer, count);434 break;435 default:436 WARN_ON(1);437 }438 if (!error)439 goto out;440 441err_out:442 dev_dbg(ssb_sdio_dev(bus), "%04X:%04X (width=%u, len=%zu), error %d\n",443 bus->sdio_sbaddr >> 16, offset, reg_width, saved_count, error);444out:445 sdio_release_host(bus->host_sdio);446}447 448#endif /* CONFIG_SSB_BLOCKIO */449 450/* Not "static", as it's used in main.c */451const struct ssb_bus_ops ssb_sdio_ops = {452 .read8 = ssb_sdio_read8,453 .read16 = ssb_sdio_read16,454 .read32 = ssb_sdio_read32,455 .write8 = ssb_sdio_write8,456 .write16 = ssb_sdio_write16,457 .write32 = ssb_sdio_write32,458#ifdef CONFIG_SSB_BLOCKIO459 .block_read = ssb_sdio_block_read,460 .block_write = ssb_sdio_block_write,461#endif462};463 464#define GOTO_ERROR_ON(condition, description) do { \465 if (unlikely(condition)) { \466 error_description = description; \467 goto error; \468 } \469 } while (0)470 471int ssb_sdio_get_invariants(struct ssb_bus *bus,472 struct ssb_init_invariants *iv)473{474 struct ssb_sprom *sprom = &iv->sprom;475 struct ssb_boardinfo *bi = &iv->boardinfo;476 const char *error_description = "none";477 struct sdio_func_tuple *tuple;478 void *mac;479 480 memset(sprom, 0xFF, sizeof(*sprom));481 sprom->boardflags_lo = 0;482 sprom->boardflags_hi = 0;483 484 tuple = bus->host_sdio->tuples;485 while (tuple) {486 switch (tuple->code) {487 case 0x22: /* extended function */488 switch (tuple->data[0]) {489 case CISTPL_FUNCE_LAN_NODE_ID:490 GOTO_ERROR_ON((tuple->size != 7) &&491 (tuple->data[1] != 6),492 "mac tpl size");493 /* fetch the MAC address. */494 mac = tuple->data + 2;495 memcpy(sprom->il0mac, mac, ETH_ALEN);496 memcpy(sprom->et1mac, mac, ETH_ALEN);497 break;498 default:499 break;500 }501 break;502 case 0x80: /* vendor specific tuple */503 switch (tuple->data[0]) {504 case SSB_SDIO_CIS_SROMREV:505 GOTO_ERROR_ON(tuple->size != 2,506 "sromrev tpl size");507 sprom->revision = tuple->data[1];508 break;509 case SSB_SDIO_CIS_ID:510 GOTO_ERROR_ON((tuple->size != 5) &&511 (tuple->size != 7),512 "id tpl size");513 bi->vendor = tuple->data[1] |514 (tuple->data[2]<<8);515 break;516 case SSB_SDIO_CIS_BOARDREV:517 GOTO_ERROR_ON(tuple->size != 2,518 "boardrev tpl size");519 sprom->board_rev = tuple->data[1];520 break;521 case SSB_SDIO_CIS_PA:522 GOTO_ERROR_ON((tuple->size != 9) &&523 (tuple->size != 10),524 "pa tpl size");525 sprom->pa0b0 = tuple->data[1] |526 ((u16)tuple->data[2] << 8);527 sprom->pa0b1 = tuple->data[3] |528 ((u16)tuple->data[4] << 8);529 sprom->pa0b2 = tuple->data[5] |530 ((u16)tuple->data[6] << 8);531 sprom->itssi_a = tuple->data[7];532 sprom->itssi_bg = tuple->data[7];533 sprom->maxpwr_a = tuple->data[8];534 sprom->maxpwr_bg = tuple->data[8];535 break;536 case SSB_SDIO_CIS_OEMNAME:537 /* Not present */538 break;539 case SSB_SDIO_CIS_CCODE:540 GOTO_ERROR_ON(tuple->size != 2,541 "ccode tpl size");542 sprom->country_code = tuple->data[1];543 break;544 case SSB_SDIO_CIS_ANTENNA:545 GOTO_ERROR_ON(tuple->size != 2,546 "ant tpl size");547 sprom->ant_available_a = tuple->data[1];548 sprom->ant_available_bg = tuple->data[1];549 break;550 case SSB_SDIO_CIS_ANTGAIN:551 GOTO_ERROR_ON(tuple->size != 2,552 "antg tpl size");553 sprom->antenna_gain.a0 = tuple->data[1];554 sprom->antenna_gain.a1 = tuple->data[1];555 sprom->antenna_gain.a2 = tuple->data[1];556 sprom->antenna_gain.a3 = tuple->data[1];557 break;558 case SSB_SDIO_CIS_BFLAGS:559 GOTO_ERROR_ON((tuple->size != 3) &&560 (tuple->size != 5),561 "bfl tpl size");562 sprom->boardflags_lo = tuple->data[1] |563 ((u16)tuple->data[2] << 8);564 break;565 case SSB_SDIO_CIS_LEDS:566 GOTO_ERROR_ON(tuple->size != 5,567 "leds tpl size");568 sprom->gpio0 = tuple->data[1];569 sprom->gpio1 = tuple->data[2];570 sprom->gpio2 = tuple->data[3];571 sprom->gpio3 = tuple->data[4];572 break;573 default:574 break;575 }576 break;577 default:578 break;579 }580 tuple = tuple->next;581 }582 583 return 0;584error:585 dev_err(ssb_sdio_dev(bus), "failed to fetch device invariants: %s\n",586 error_description);587 return -ENODEV;588}589 590void ssb_sdio_exit(struct ssb_bus *bus)591{592 if (bus->bustype != SSB_BUSTYPE_SDIO)593 return;594 /* Nothing to do here. */595}596 597int ssb_sdio_init(struct ssb_bus *bus)598{599 if (bus->bustype != SSB_BUSTYPE_SDIO)600 return 0;601 602 bus->sdio_sbaddr = ~0;603 604 return 0;605}606