| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- /*
- * Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #include <errno.h>
- #include <rtthread.h>
- #include <string.h>
- #include <stdio.h>
- #include "mmio.h"
- #include "dw_spi.h"
- #include <rthw.h>
- #ifdef SPI_DEBUG
- #define SP_DEBUG_LOG(fmt, ...) \
- do { \
- rt_kprintf(fmt, ##__VA_ARGS__); \
- } while(0)
- #else
- #define SP_DEBUG_LOG(fmt, ...)
- #endif
- /* Restart the controller, disable all interrupts, clean rx fifo */
- void spi_hw_init(struct dw_spi *dws)
- {
- /*
- * Try to detect the FIFO depth if not set by interface driver,
- * the depth could be from 2 to 256 from HW spec
- */
- if (!dws->fifo_len) {
- uint32_t fifo;
- for (fifo = 1; fifo < 256; fifo++) {
- dw_writel(dws, CVI_DW_SPI_TXFTLR, fifo);
- if (fifo != dw_readl(dws, CVI_DW_SPI_TXFTLR))
- break;
- }
- dw_writel(dws, CVI_DW_SPI_TXFTLR, 0);
- dws->fifo_len = (fifo == 1) ? 0 : fifo;
- SP_DEBUG_LOG("Detected FIFO size: %u bytes\n", dws->fifo_len);
- }
- }
- uint32_t min3(uint32_t a, uint32_t b, uint32_t c)
- {
- uint32_t tmp;
- tmp = (a < b) ? a : b;
- return (tmp < c) ? tmp : c;
- }
- static inline void cpu_relax(void)
- {
- //asm volatile("" ::: "memory");
- }
- static inline uint32_t tx_max(struct dw_spi *dws)
- {
- uint32_t tx_left, tx_room, rxtx_gap, temp;
- cpu_relax();
- tx_left = dws->tx_len;
- tx_room = dws->fifo_len - dw_readl(dws, CVI_DW_SPI_TXFLR);
- /*
- * Another concern is about the tx/rx mismatch, we
- * though to use (dws->fifo_len - rxflr - txflr) as
- * one maximum value for tx, but it doesn't cover the
- * data which is out of tx/rx fifo and inside the
- * shift registers. So a control from sw point of
- * view is taken.
- */
- SP_DEBUG_LOG("tx left: %#x, tx room: %#x\n", tx_left, tx_room);
- if (dws->rx != NULL && dws->tx != NULL) {
- cpu_relax();
- rxtx_gap = dws->fifo_len - (dws->rx_len - dws->tx_len);
- temp = min3(tx_left, tx_room, (uint32_t)(rxtx_gap));
- } else {
- temp = tx_left < tx_room ? tx_left : tx_room;
- }
- SP_DEBUG_LOG("temp: %#x\n", temp);
- return temp;
- }
- void dw_writer(struct dw_spi *dws)
- {
- uint32_t max;
- uint16_t txw = 0;
- max = tx_max(dws);
- SP_DEBUG_LOG("max: %#x \n", max);
- while (max--) {
- if (dws->tx) {
- if (dws->n_bytes == 1)
- txw = *(uint8_t *)(dws->tx);
- else
- txw = *(uint16_t *)(dws->tx);
- }
- dw_writel(dws, CVI_DW_SPI_DR, txw);
- dws->tx += dws->n_bytes;
- --dws->tx_len;
- }
- }
- static inline uint32_t rx_max(struct dw_spi *dws)
- {
- uint32_t temp;
- uint32_t rx_left = dws->rx_len;
- uint32_t data_in_fifo = dw_readl(dws, CVI_DW_SPI_RXFLR);
- temp = (rx_left < data_in_fifo ? rx_left : data_in_fifo);
- SP_DEBUG_LOG("data_in_fifo:%u temp: %u\n", data_in_fifo, temp);
- return temp;
- }
- int dw_spi_check_status(struct dw_spi *dws, bool raw)
- {
- uint32_t irq_status;
- int ret = 0;
- if (raw)
- irq_status = dw_readl(dws, CVI_DW_SPI_RISR);
- else
- irq_status = dw_readl(dws, CVI_DW_SPI_ISR);
- if (irq_status & CVI_SPI_INT_RXOI) {
- SP_DEBUG_LOG("RX FIFO overflow detected\n");
- ret = -1;
- }
- if (irq_status & CVI_SPI_INT_RXUI) {
- SP_DEBUG_LOG("RX FIFO underflow detected\n");
- ret = -1;
- }
- if (irq_status & CVI_SPI_INT_TXOI) {
- SP_DEBUG_LOG("TX FIFO overflow detected\n");
- ret = -1;
- }
- if (ret)
- spi_reset_chip(dws);
- return ret;
- }
- void dw_reader(struct dw_spi *dws)
- {
- uint32_t max;
- uint16_t rxw;
- max = rx_max(dws);
- SP_DEBUG_LOG("max: %#x \n", max);
- while (max--) {
- rxw = dw_readl(dws, CVI_DW_SPI_DR);
- if (dws->rx) {
- if (dws->n_bytes == 1)
- *(uint8_t *)(dws->rx) = rxw;
- else
- *(uint16_t *)(dws->rx) = rxw;
- dws->rx += dws->n_bytes;
- }
- --dws->rx_len;
- }
- }
- int spi_delay_to_ns(struct spi_delay *_delay, struct dw_spi *dws)
- {
- uint32_t delay = _delay->value;
- uint32_t unit = _delay->unit;
- uint32_t hz;
- if (!delay)
- return 0;
- switch (unit) {
- case SPI_DELAY_UNIT_USECS:
- delay *= 1000;
- break;
- case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
- break;
- case SPI_DELAY_UNIT_SCK:
- /* clock cycles need to be obtained from spi_transfer */
- if (!dws)
- return -1;
- /* if there is no effective speed know, then approximate
- * by underestimating with half the requested hz
- */
- hz = dws->speed_hz / 2;
- if (!hz)
- return -1;
- delay *= DIV_ROUND_UP(1000000000, hz);
- break;
- default:
- return -EINVAL;
- }
- return delay;
- }
- static void _spi_transfer_delay_ns(uint32_t ns)
- {
- if (!ns)
- return;
- if (ns <= 1000) {
- rt_hw_us_delay(1);
- } else {
- uint32_t us = DIV_ROUND_UP(ns, 1000);
- rt_hw_us_delay(us);
- }
- }
- int spi_delay_exec(struct spi_delay *_delay, struct dw_spi *dws)
- {
- int delay;
- if (!_delay)
- return -1;
- delay = spi_delay_to_ns(_delay, dws);
- if (delay < 0)
- return delay;
- _spi_transfer_delay_ns(delay);
- return 0;
- }
- int poll_transfer(struct dw_spi *dws)
- {
- struct spi_delay delay;
- uint16_t nbits;
- delay.unit = SPI_DELAY_UNIT_SCK;
- nbits = dws->n_bytes * BITS_PER_BYTE;
- int ret = 0;
- do
- {
- dw_writer(dws);
- cpu_relax();
- delay.value = nbits * (dws->rx_len - dws->tx_len);
- spi_delay_exec(&delay, dws);
- dw_reader(dws);
- cpu_relax();
- ret = dw_spi_check_status(dws, true);
- if (ret)
- return ret;
- } while (dws->rx_len && dws->tx_len);
- return 0;
- }
- void set_tran_mode(struct dw_spi *dws)
- {
- uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
- uint8_t tmode;
- if (dws->rx && dws->tx) {
- tmode = CVI_SPI_TMOD_TR;
- } else if (dws->rx) {
- tmode = CVI_SPI_TMOD_RO;
- } else {
- tmode = CVI_SPI_TMOD_TO;
- }
- reg &= ~CVI_SPI_TMOD_MASK;
- reg |= (tmode << CVI_SPI_TMOD_OFFSET);
- dw_writel(dws, CVI_DW_SPI_CTRLR0, reg);
- }
- void dw_spi_set_controller_mode(struct dw_spi *dws, uint8_t enable_master)
- {
- /* do not support to switch controller mode, it is default master mode */
- }
- void dw_spi_set_cs(struct dw_spi *dws, bool enable, uint32_t index)
- {
- uint32_t reg = dw_readl(dws, CVI_DW_SPI_SER);
- if (enable)
- dw_writel(dws, CVI_DW_SPI_SER, reg | BIT(index));
- else
- dw_writel(dws, CVI_DW_SPI_SER, reg & ~BIT(index));
- }
- void dw_spi_set_polarity_and_phase(struct dw_spi *dws, uint8_t format)
- {
- uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
- reg &= ~(0x3 << 6);
- switch (format) {
- case SPI_FORMAT_CPOL0_CPHA0:
- reg |= (SPI_MODE_0 << 6);
- break;
- case SPI_FORMAT_CPOL0_CPHA1:
- reg |= (SPI_MODE_1 << 6);
- break;
- case SPI_FORMAT_CPOL1_CPHA0:
- reg |= (SPI_MODE_2 << 6);
- break;
- case SPI_FORMAT_CPOL1_CPHA1:
- reg |= (SPI_MODE_3 << 6);
- break;
- default:
- reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
- break;
- }
- SP_DEBUG_LOG("set phase and polarity: %#x\n", reg);
- dw_writel(dws, CVI_DW_SPI_CTRLR0, reg);
- }
- uint32_t dw_spi_set_clock(struct dw_spi *dws, uint32_t clock_in, uint32_t clock_out)
- {
- uint16_t div;
- div = (DIV_ROUND_UP(clock_in, clock_out) + 1) & 0xfffe;
- dws->speed_hz = clock_in / div;
- SP_DEBUG_LOG("clk div value is: %u, hz:%u\n", div, dws->speed_hz);
- spi_set_clk(dws, div);
- return dws->speed_hz;
- }
- int dw_spi_set_data_frame_len(struct dw_spi *dws, uint32_t size)
- {
- uint32_t temp = dw_readl(dws, CVI_DW_SPI_CTRLR0);
- temp &= ~0xf;
- if (size == 8) {
- dws->n_bytes = 1;
- } else if (size == 16) {
- dws->n_bytes = 2;
- } else {
- SP_DEBUG_LOG("do not support %u bit data!\n", size);
- return -1;
- }
- temp |= (size - 1);
- dw_writel(dws, CVI_DW_SPI_CTRLR0, temp);
- SP_DEBUG_LOG("set data frame len: %#x\n", temp);
- return 0;
- }
- void dw_spi_show_regs(struct dw_spi *dws)
- {
- SP_DEBUG_LOG("CTRLR0: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR0));
- SP_DEBUG_LOG("CTRLR1: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR1));
- SP_DEBUG_LOG("SSIENR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SSIENR));
- SP_DEBUG_LOG("SER: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SER));
- SP_DEBUG_LOG("BAUDR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_BAUDR));
- SP_DEBUG_LOG("TXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFTLR));
- SP_DEBUG_LOG("RXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFTLR));
- SP_DEBUG_LOG("TXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFLR));
- SP_DEBUG_LOG("RXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFLR));
- SP_DEBUG_LOG("SR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SR));
- SP_DEBUG_LOG("IMR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_IMR));
- SP_DEBUG_LOG("ISR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_ISR));
- SP_DEBUG_LOG("DMACR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMACR));
- SP_DEBUG_LOG("DMATDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMATDLR));
- SP_DEBUG_LOG("DMARDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMARDLR));
- }
|