| /* |
| * 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(®s->sr) & 0x01)) |
| return 0; |
| udelay(1); |
| } |
| return -1; |
| } |
| |
| static void set_tmod(RkSpiRegs *regs, unsigned int tmod) |
| { |
| clrsetbits_le32(®s->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(®s->sr); |
| int xferred = 0; // in either (or both) directions |
| |
| if (out_buf && !(sr & SR_TF_FULL)) { |
| writel(*out_buf, ®s->txdr); |
| out_buf++; |
| xferred = 1; |
| } |
| |
| if (in_buf && !(sr & SR_RF_EMPT)) { |
| *in_buf = readl(®s->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, ®s->enr); |
| |
| writel(dataframes - 1, ®s->ctrlr1); |
| |
| // Disable transmitter and receiver as needed to avoid |
| // sending or reading spurious bits. |
| set_transfer_mode(regs, in, out); |
| |
| writel(CONTROLLER_ENABLE, ®s->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, ®s->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, ®s->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, ®s->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; |
| } |