blob: 7907864586f2101a5d93cf8f7b1976068bff688c [file] [log] [blame]
/*
* Copyright (C) 2013 Google Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __BBSI_H
#define __BBSI_H
#include <linux/netdevice.h>
#include <linux/spi/spi.h>
#include <linux/vmalloc.h>
#ifndef KSEG1
#define KSEG1 0 // just to appease non-MIPS CPUs. Not really used.
#endif
#define BP_MOCA_MAX_NUM 1
/*
* The exact values here don't matter, as they're translated into "real"
* values before talking to mocad. This is just for the device registration
* tables.
*/
enum {
BP_MOCA_TYPE_WAN,
BP_MOCA_TYPE_LAN,
};
enum {
BP_MOCA_RF_BAND_D_LOW,
BP_MOCA_RF_BAND_D_HIGH,
BP_MOCA_RF_BAND_EXT_D,
BP_MOCA_RF_BAND_E,
BP_MOCA_RF_BAND_F,
};
typedef struct BpMocaInfo {
int type;
int rfBand;
} BP_MOCA_INFO;
static void BpGetMocaInfo(BP_MOCA_INFO *chips, int *nchips) {
if (*nchips >= 1) {
*nchips = 1;
chips[0].type = BP_MOCA_TYPE_LAN;
chips[0].rfBand = BP_MOCA_RF_BAND_E;
}
}
static uint32_t _spi_read32(struct spi_device *spi, uint32_t addr);
// TODO(apenwarr): don't make this global.
// Or fix the driver to just only enable/disable interrupts at the right
// times.
static int irq_disabled = 0;
static void kerSysMocaHostIntrEnable(struct spi_device *spi) {
if (irq_disabled == 1) {
irq_disabled = 0;
enable_irq(spi->irq);
}
}
static void kerSysMocaHostIntrDisable(struct spi_device *spi) {
if (irq_disabled == 0) {
disable_irq_nosync(spi->irq);
irq_disabled = 1;
}
}
static uint8_t __pollstatus(struct spi_device *spi) {
uint8_t wclear[] = { 0x80, 0x06 };
uint8_t rdata[1] = { 0 };
struct spi_transfer t[2] = {
{ .tx_buf = wclear, .len = sizeof(wclear) },
{ .rx_buf = rdata, .len = sizeof(rdata) },
};
struct spi_message m;
int i;
spi_message_init(&m);
spi_message_add_tail(&t[0], &m);
spi_message_add_tail(&t[1], &m);
for (i = 0; i < 10; i++) {
if (spi_sync_locked(spi, &m) < 0) {
pr_warn("spi _pollstatus: SPI error\n");
return 0x01; // error code
}
if (rdata[0] & 0x01) {
pr_warn("spi _pollstatus: rbus error: %02X\n", rdata[0]);
return 0x01; // error result; stop polling now
}
if (!(rdata[0] & 0x10)) return 0; // transaction finished
}
// if we get here, the transaction still isn't finished: weird
pr_warn("spi _pollstatus: still busy: %02X\n", rdata[0]);
return rdata[0];
}
static uint32_t __spi_read32a(struct spi_device *spi, uint32_t addr,
int speculative) {
uint8_t waddr[] = {
0x81, 0x07,
0x01 | (speculative ? 0x02 : 0),
0, 0, 0, 0 };
struct spi_transfer addrt[1] = {
{ .tx_buf = waddr, .len = sizeof(waddr) },
};
struct spi_message addrm;
int j, st;
spi_message_init(&addrm);
spi_message_add_tail(&addrt[0], &addrm);
__pollstatus(spi);
for (j = 0; j < 10; j++) {
// write address reg, which triggers the read
writel(cpu_to_be32(addr), waddr + sizeof(waddr) - 4);
if (spi_sync_locked(spi, &addrm) < 0) {
pr_warn("spi_read_addr: error\n");
}
st = __pollstatus(spi);
if (!st) break;
}
return st;
}
static uint32_t __spi_read32d_noswap(struct spi_device *spi) {
uint8_t wdata[] = { 0x80, 0x0c };
uint8_t rdata[4];
struct spi_transfer datat[2] = {
{ .tx_buf = wdata, .len = sizeof(wdata) },
{ .rx_buf = rdata, .len = sizeof(rdata) },
};
struct spi_message datam;
spi_message_init(&datam);
spi_message_add_tail(&datat[0], &datam);
spi_message_add_tail(&datat[1], &datam);
// retrieve actual data bits
if (spi_sync_locked(spi, &datam) < 0) {
pr_warn("spi_read_data: error\n");
}
return readl(rdata);
}
static uint32_t _spi_read32(struct spi_device *spi, uint32_t addr) {
int st;
uint32_t retval;
spi_bus_lock(spi->master);
st = __spi_read32a(spi, addr, 0);
if (st) {
retval = 0x00000000; // error
} else {
retval = be32_to_cpu(__spi_read32d_noswap(spi));
}
spi_bus_unlock(spi->master);
return retval;
}
static void __spi_write32a(struct spi_device *spi, uint32_t addr) {
uint8_t waddr[] = { 0x81, 0x07, 0x00, 0, 0, 0, 0 };
struct spi_transfer t[1] = {
{ .tx_buf = waddr, .len = sizeof(waddr) },
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t[0], &m);
// write address reg
writel(cpu_to_be32(addr), waddr + sizeof(waddr) - 4);
if (spi_sync_locked(spi, &m) < 0) {
pr_warn("spi_write: error\n");
}
}
static void __spi_write32d_noswap(struct spi_device *spi, uint32_t value) {
uint8_t wdata[] = { 0x81, 0x0c, 0, 0, 0, 0 };
struct spi_transfer t[1] = {
{ .tx_buf = wdata, .len = sizeof(wdata) },
};
struct spi_message m;
spi_message_init(&m);
spi_message_add_tail(&t[0], &m);
// write data reg
writel(value, wdata + sizeof(wdata) - 4);
if (spi_sync_locked(spi, &m) < 0) {
pr_warn("spi_write: error\n");
}
}
static void _spi_write32(struct spi_device *spi, uint32_t addr, uint32_t value) {
spi_bus_lock(spi->master);
__pollstatus(spi);
__spi_write32a(spi, addr);
__spi_write32d_noswap(spi, cpu_to_be32(value));
__pollstatus(spi);
spi_bus_unlock(spi->master);
}
static uint32_t kerSysBcmSpiSlaveReadReg32(struct spi_device *spi, uint32_t addr) {
return _spi_read32(spi, addr);
}
static void kerSysBcmSpiSlaveWriteReg32(struct spi_device *spi, uint32_t addr, uint32_t value) {
_spi_write32(spi, addr, value);
}
static void kerSysBcmSpiSlaveReadBuf(struct spi_device *spi, uint32_t addr, void *dst, int len, int wordsize) {
int i;
uint32_t *buf = dst;
spi_bus_lock(spi->master);
if (wordsize != 4) {
pr_info("SPI readbuf: only word size == 4 bytes is supported!\n");
return;
}
__spi_read32a(spi, addr, 1);
for (i = 0; i < len; i += wordsize) {
buf[i/4] = __spi_read32d_noswap(spi);
__pollstatus(spi);
}
spi_bus_unlock(spi->master);
}
static void kerSysBcmSpiSlaveWriteBuf(struct spi_device *spi, uint32_t addr, const void *src, int len, int wordsize) {
int i, nelems = len/4;
const uint32_t *buf = src;
uint8_t wdata[] = { 0x81, 0x0c };
struct spi_transfer *t, *tp;
struct spi_message m;
if (len > 8192) {
pr_warn("spi writebuf: buffer size %d is too large\n", len);
return;
}
if (wordsize != 4) {
pr_err("SPI writebuf: only word size == 4 bytes is supported!\n");
return;
}
t = vmalloc(nelems * sizeof(struct spi_transfer) * 2);
if (!t) {
pr_warn("spi writebuf: out of memory\n");
return;
}
memset(t, 0, nelems * sizeof(struct spi_transfer) * 2);
spi_message_init(&m);
for (i = 0, tp = t; i < nelems; i++) {
tp->tx_buf = wdata;
tp->len = sizeof(wdata);
spi_message_add_tail(tp, &m);
tp++;
tp->tx_buf = &buf[i];
tp->len = 4;
tp->cs_change = 1;
spi_message_add_tail(tp, &m);
tp++;
}
spi_bus_lock(spi->master);
__pollstatus(spi);
writel(cpu_to_be32(addr), wdata + 2);
__spi_write32a(spi, addr);
spi_sync_locked(spi, &m);
__pollstatus(spi);
spi_bus_unlock(spi->master);
vfree(t);
}
#endif // __BBSI_H