blob: 1e0f22e06a25854e01c7e1c0b3fa61b0675e1a86 [file] [log] [blame]
/*
* Copyright 2014 Rockchip Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* 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
*/
#include <assert.h>
#include <libpayload.h>
#include "base/container_of.h"
#include "drivers/bus/spi/spi.h"
#include "drivers/bus/spi/rockchip.h"
#define spi_info(x...) do {if (0) printf(x); } while (0)
#define spi_err(x...) printf(x)
#define SPI_TIMEOUT_US 100000
typedef struct {
u32 ctrlr0;
u32 ctrlr1;
u32 enr;
u32 ser;
u32 baudr;
u32 txftlr;
u32 rxftlr;
u32 txflr;
u32 rxflr;
u32 sr;
u32 ipr;
u32 imr;
u32 isr;
u32 risr;
u32 icr;
u32 dmacr;
u32 dmatdlr;
u32 damrdlr;
u32 reserved0[(0x400 - 0x48) / 4];
u32 txdr[0x100];
u32 rxdr[0x100];
} RkSpiRegs;
#define FIFO_DEPTH 32
//Control register 0
#define CR0_OP_MODE_BIT 20
#define CR0_TS_MODE_BIT 18
enum {
DUPLEX = 0,
TXONLY,
RXONLY
};
#define SPI_TMOD_OFFSET 18
#define SPI_TMOD_MASK 0x3
#define SPI_TMOD_TR 0x00 // xmit & recv
#define SPI_TMOD_TO 0x01 // xmit only
#define SPI_TMOD_RO 0x02 // recv only
#define SPI_TMOD_RESV 0x03
#define CR0_HALFWORD_TS_BIT 13
#define CR0_SSN_DELAY_BIT 10
#define CR0_CLOCK_POLARITY_BIT 7
#define CR0_CLOCK_PHASE_BIT 6
#define CR0_FRAME_SIZE_BIT 0
enum {
FRAME_SIZE_4BIT,
FRAME_SIZE_8BIT,
FRAME_SIZE_16BIT
};
// Control register 1
#define CR1_FRAME_NUM_MASK 0xffff
//Controller enable register
enum {
CONTROLLER_DISABLE,
CONTROLLER_ENABLE
};
// Bit fields in SR, 7 bits
#define SR_MASK 0x7f
#define SR_BUSY (1 << 0)
#define SR_TF_FULL (1 << 1)
#define SR_TF_EMPT (1 << 2)
#define SR_RF_EMPT (1 << 3)
#define SR_RF_FULL (1 << 4)
//Slave select register
#define MAX_SLAVE 2
static int rockchip_spi_wait_till_not_busy(RkSpi *bus)
{
unsigned int delay = 1000000;
RkSpiRegs *regs = bus->reg_addr;
while (delay--) {
if (!(readl(&regs->sr) & 0x01))
return 0;
udelay(1);
}
return -1;
}
static void set_tmod(RkSpiRegs *regs, unsigned int tmod)
{
clrsetbits_le32(&regs->ctrlr0, SPI_TMOD_MASK << SPI_TMOD_OFFSET,
tmod << SPI_TMOD_OFFSET);
}
static void set_transfer_mode(RkSpiRegs *regs, void *in, const void *out)
{
if (!in && !out)
return;
else if (in && out)
set_tmod(regs, SPI_TMOD_TR); // tx and rx
else if (!in)
set_tmod(regs, SPI_TMOD_TO); // tx only
else if (!out)
set_tmod(regs, SPI_TMOD_RO); // rx only
}
// returns 0 to indicate success, <0 otherwise
static int do_xfer(RkSpi *bus, void *in, const void *out, uint32_t size)
{
RkSpiRegs *regs = bus->reg_addr;
uint8_t *in_buf =in;
uint8_t *out_buf = (uint8_t *)out;
while (size) {
uint32_t sr = readl(&regs->sr);
int xferred = 0; // in either (or both) directions
if (out_buf && !(sr & SR_TF_FULL)) {
writel(*out_buf, &regs->txdr);
out_buf++;
xferred = 1;
}
if (in_buf && !(sr & SR_RF_EMPT)) {
*in_buf = readl(&regs->rxdr) & 0xff;
in_buf++;
xferred = 1;
}
size -= xferred;
}
if (rockchip_spi_wait_till_not_busy(bus)) {
printf("Timed out waiting on SPI transfer\n");
return -1;
}
return 0;
}
static int spi_transfer(SpiOps *me, void *in, const void *out, uint32_t size)
{
int res = 0;
RkSpi *bus = container_of(me, RkSpi, ops);
RkSpiRegs *regs = bus->reg_addr;
spi_info("spi:: transfer\n");
assert((in != NULL) ^ (out != NULL));
// RK3288 SPI controller can transfer up to 65536 data frames (bytes
// in our case) continuously. Break apart large requests as necessary.
//
// FIXME: And by 65536, we really mean 65535. If 0xffff is written to
// ctrlr1, all bytes that we see in rxdr end up being 0x00. 0xffff - 1
// seems to work fine.
while (size) {
unsigned int dataframes = MIN(size, 0xffff);
writel(CONTROLLER_DISABLE, &regs->enr);
writel(dataframes - 1, &regs->ctrlr1);
// Disable transmitter and receiver as needed to avoid
// sending or reading spurious bits.
set_transfer_mode(regs, in, out);
writel(CONTROLLER_ENABLE, &regs->enr);
res = do_xfer(bus, in, out, dataframes);
if (res < 0)
break;
if (in)
in += dataframes;
if (out)
out += dataframes;
size -= dataframes;
}
writel(CONTROLLER_DISABLE, &regs->enr);
return res < 0 ? res : 0;
}
static int spi_start(SpiOps *me)
{
int res = 0;
RkSpi *bus = container_of(me, RkSpi, ops);
RkSpiRegs *regs = bus->reg_addr;
spi_info("spi:: start\n");
writel(1, &regs->ser);
return res;
}
static int spi_stop(SpiOps *me)
{
int res = 0;
RkSpi *bus = container_of(me, RkSpi, ops);
RkSpiRegs *regs = bus->reg_addr;
spi_info("spi:: stop\n");
writel(0, &regs->ser);
return res;
}
RkSpi *new_rockchip_spi(uintptr_t reg_addr)
{
RkSpi *bus = NULL;
bus = xzalloc(sizeof(*bus));
bus->reg_addr = (void *)reg_addr;
bus->ops.start = &spi_start;
bus->ops.stop = &spi_stop;
bus->ops.transfer = &spi_transfer;
return bus;
}