From 072de836f53be56a70cecf70b43ae43b7ce17376 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 11 Dec 2023 10:08:36 +0000
Subject: [PATCH] mk-rootfs.sh
---
kernel/drivers/spi/spi-rockchip.c | 552 +++++++++++++++++++++++++++++++++++-------------------
1 files changed, 359 insertions(+), 193 deletions(-)
diff --git a/kernel/drivers/spi/spi-rockchip.c b/kernel/drivers/spi/spi-rockchip.c
index 8604291..b627663 100644
--- a/kernel/drivers/spi/spi-rockchip.c
+++ b/kernel/drivers/spi/spi-rockchip.c
@@ -1,21 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
* Author: Addy Ke <addy.ke@rock-chips.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/dmaengine.h>
-#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
@@ -181,6 +173,12 @@
#define ROCKCHIP_SPI_REGISTER_SIZE 0x1000
+enum rockchip_spi_xfer_mode {
+ ROCKCHIP_SPI_DMA,
+ ROCKCHIP_SPI_IRQ,
+ ROCKCHIP_SPI_POLL,
+};
+
struct rockchip_spi_quirks {
u32 max_baud_div_in_cpha;
};
@@ -190,6 +188,7 @@
struct clk *spiclk;
struct clk *apb_pclk;
+ struct clk *sclk_in;
void __iomem *regs;
dma_addr_t dma_addr_rx;
@@ -201,7 +200,9 @@
unsigned int rx_left;
atomic_t state;
+ struct completion xfer_done;
+ u32 version;
/*depth of the FIFO buffer */
u32 fifo_len;
/* frequency of spiclk */
@@ -211,13 +212,17 @@
u8 n_bytes;
u8 rsd;
+ u8 csm;
+ bool poll; /* only support transfer data by cpu polling */
bool cs_asserted[ROCKCHIP_SPI_MAX_CS_NUM];
struct pinctrl_state *high_speed_state;
- bool slave_abort;
- bool gpio_requested;
+ bool slave_aborted;
bool cs_inactive; /* spi slave tansmition stop when cs inactive */
+ bool cs_high_supported; /* native CS supports active-high polarity */
+ struct gpio_desc *ready; /* spi slave transmission ready */
+
struct spi_transfer *xfer; /* Store xfer temporarily */
phys_addr_t base_addr_phy;
struct miscdevice miscdev;
@@ -231,17 +236,29 @@
writel_relaxed((enable ? 1U : 0U), rs->regs + ROCKCHIP_SPI_SSIENR);
}
-static inline void wait_for_idle(struct rockchip_spi *rs, bool slave_mode)
+static inline void wait_for_tx_idle(struct rockchip_spi *rs, bool slave_mode)
{
unsigned long timeout = jiffies + msecs_to_jiffies(5);
+ u32 bit_filed = SR_BUSY;
+ u32 idle_val = 0;
+ uint32_t speed, us;
+
+ if (slave_mode && rs->version == ROCKCHIP_SPI_VER2_TYPE2) {
+ bit_filed = SR_SLAVE_TX_BUSY;
+ idle_val = 0;
+ } else if (slave_mode) {
+ bit_filed = SR_TF_EMPTY;
+ idle_val = 1;
+ }
do {
- if (slave_mode) {
- if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_SLAVE_TX_BUSY))
- return;
- } else {
- if (!(readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & SR_BUSY))
- return;
+ if ((readl_relaxed(rs->regs + ROCKCHIP_SPI_SR) & bit_filed) == idle_val) {
+ if (bit_filed == SR_TF_EMPTY) {
+ speed = rs->speed_hz;
+ us = (8 * 1000000 / speed) * 2;
+ udelay(us);
+ }
+ return;
}
} while (!time_after(jiffies, timeout));
@@ -250,11 +267,7 @@
static u32 get_fifo_len(struct rockchip_spi *rs)
{
- u32 ver;
-
- ver = readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION);
-
- switch (ver) {
+ switch (rs->version) {
case ROCKCHIP_SPI_VER2_TYPE1:
case ROCKCHIP_SPI_VER2_TYPE2:
return 64;
@@ -277,12 +290,12 @@
/* Keep things powered as long as CS is asserted */
pm_runtime_get_sync(rs->dev);
- if (gpio_is_valid(spi->cs_gpio))
+ if (spi->cs_gpiod)
ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
else
ROCKCHIP_SPI_SET_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));
} else {
- if (gpio_is_valid(spi->cs_gpio))
+ if (spi->cs_gpiod)
ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, 1);
else
ROCKCHIP_SPI_CLR_BITS(rs->regs + ROCKCHIP_SPI_SER, BIT(spi->chip_select));
@@ -299,6 +312,11 @@
{
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+ dev_err(rs->dev, "state=%x\n", atomic_read(&rs->state));
+ dev_err(rs->dev, "tx_left=%x\n", rs->tx_left);
+ dev_err(rs->dev, "rx_left=%x\n", rs->rx_left);
+ print_hex_dump(KERN_ERR, "regs ", DUMP_PREFIX_OFFSET, 4, 4, rs->regs, 0x4c, 0);
+
/* stop running spi transfer
* this also flushes both rx and tx fifos
*/
@@ -313,6 +331,7 @@
if (atomic_read(&rs->state) & RXDMA)
dmaengine_terminate_async(ctlr->dma_rx);
+ atomic_set(&rs->state, 0);
}
static void rockchip_spi_pio_writer(struct rockchip_spi *rs)
@@ -337,7 +356,7 @@
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
{
u32 words = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
- u32 rx_left = rs->rx_left > words ? rs->rx_left - words : 0;
+ u32 rx_left = (rs->rx_left > words) ? rs->rx_left - words : 0;
/* the hardware doesn't allow us to change fifo threshold
* level while spi is enabled, so instead make sure to leave
@@ -374,7 +393,7 @@
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
/* When int_cs_inactive comes, spi slave abort */
- if (readl_relaxed(rs->regs + ROCKCHIP_SPI_IMR) & INT_CS_INACTIVE) {
+ if (rs->cs_inactive && readl_relaxed(rs->regs + ROCKCHIP_SPI_ISR) & INT_CS_INACTIVE) {
ctlr->slave_abort(ctlr);
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
@@ -390,7 +409,7 @@
spi_enable_chip(rs, false);
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
- spi_finalize_current_transfer(ctlr);
+ complete(&rs->xfer_done);
}
return IRQ_HANDLED;
@@ -400,8 +419,6 @@
struct spi_controller *ctlr,
struct spi_transfer *xfer)
{
- rs->tx = xfer->tx_buf;
- rs->rx = xfer->rx_buf;
rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;
rs->rx_left = xfer->len / rs->n_bytes;
@@ -427,14 +444,16 @@
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
int state = atomic_fetch_andnot(RXDMA, &rs->state);
- if (state & TXDMA && !rs->slave_abort)
+ if (state & TXDMA && !rs->slave_aborted)
return;
if (rs->cs_inactive)
writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
spi_enable_chip(rs, false);
- spi_finalize_current_transfer(ctlr);
+ writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+ writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
+ complete(&rs->xfer_done);
}
static void rockchip_spi_dma_txcb(void *data)
@@ -443,14 +462,16 @@
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
int state = atomic_fetch_andnot(TXDMA, &rs->state);
- if (state & RXDMA && !rs->slave_abort)
+ if (state & RXDMA && !rs->slave_aborted)
return;
/* Wait until the FIFO data completely. */
- wait_for_idle(rs, ctlr->slave);
+ wait_for_tx_idle(rs, ctlr->slave);
spi_enable_chip(rs, false);
- spi_finalize_current_transfer(ctlr);
+ writel_relaxed(0, rs->regs + ROCKCHIP_SPI_IMR);
+ writel_relaxed(0xffffffff, rs->regs + ROCKCHIP_SPI_ICR);
+ complete(&rs->xfer_done);
}
static u32 rockchip_spi_calc_burst_size(u32 data_len)
@@ -472,9 +493,6 @@
struct dma_async_tx_descriptor *rxdesc, *txdesc;
atomic_set(&rs->state, 0);
-
- rs->tx = xfer->tx_buf;
- rs->rx = xfer->rx_buf;
rxdesc = NULL;
if (xfer->rx_buf) {
@@ -504,7 +522,7 @@
.direction = DMA_MEM_TO_DEV,
.dst_addr = rs->dma_addr_tx,
.dst_addr_width = rs->n_bytes,
- .dst_maxburst = 8,
+ .dst_maxburst = rs->fifo_len / 4,
};
dmaengine_slave_config(ctlr->dma_tx, &txconf);
@@ -545,9 +563,59 @@
return 1;
}
-static void rockchip_spi_config(struct rockchip_spi *rs,
+static int rockchip_spi_pio_transfer(struct rockchip_spi *rs,
+ struct spi_controller *ctlr, struct spi_transfer *xfer)
+{
+ unsigned long time, timeout;
+ u32 speed_hz = xfer->speed_hz;
+ unsigned long long ms;
+ int ret = 0;
+
+ if (!speed_hz)
+ speed_hz = 100000;
+
+ ms = 8LL * 1000LL * xfer->len;
+ do_div(ms, speed_hz);
+ ms += ms + 200; /* some tolerance */
+
+ if (ms > UINT_MAX || ctlr->slave)
+ ms = UINT_MAX;
+
+ timeout = jiffies + msecs_to_jiffies(ms);
+ time = jiffies;
+ rs->tx_left = rs->tx ? xfer->len / rs->n_bytes : 0;
+ rs->rx_left = rs->rx ? xfer->len / rs->n_bytes : 0;
+
+ spi_enable_chip(rs, true);
+
+ while (rs->tx_left || rs->rx_left) {
+ if (rs->tx)
+ rockchip_spi_pio_writer(rs);
+
+ if (rs->rx)
+ rockchip_spi_pio_reader(rs);
+
+ cpu_relax();
+
+ if (time_after(time, timeout)) {
+ ret = -EIO;
+ goto out;
+ }
+ };
+
+ /* If tx, wait until the FIFO data completely. */
+ if (rs->tx)
+ wait_for_tx_idle(rs, ctlr->slave);
+
+out:
+ spi_enable_chip(rs, false);
+
+ return ret;
+}
+
+static int rockchip_spi_config(struct rockchip_spi *rs,
struct spi_device *spi, struct spi_transfer *xfer,
- bool use_dma, bool slave_mode)
+ enum rockchip_spi_xfer_mode xfer_mode, bool slave_mode)
{
u32 cr0 = CR0_FRF_SPI << CR0_FRF_OFFSET
| CR0_BHT_8BIT << CR0_BHT_OFFSET
@@ -558,21 +626,33 @@
if (slave_mode)
cr0 |= CR0_OPM_SLAVE << CR0_OPM_OFFSET;
- rs->slave_abort = false;
+ rs->slave_aborted = false;
cr0 |= rs->rsd << CR0_RSD_OFFSET;
+ cr0 |= rs->csm << CR0_CSM_OFFSET;
cr0 |= (spi->mode & 0x3U) << CR0_SCPH_OFFSET;
if (spi->mode & SPI_LSB_FIRST)
cr0 |= CR0_FBM_LSB << CR0_FBM_OFFSET;
if (spi->mode & SPI_CS_HIGH)
cr0 |= BIT(spi->chip_select) << CR0_SOI_OFFSET;
- if (xfer->rx_buf && xfer->tx_buf)
+ if (xfer->rx_buf && xfer->tx_buf) {
cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET;
- else if (xfer->rx_buf)
+ } else if (xfer->rx_buf) {
cr0 |= CR0_XFM_RO << CR0_XFM_OFFSET;
- else if (use_dma)
- cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET;
+ } else if (xfer->tx_buf) {
+ /*
+ * Use the water line of rx fifo in full duplex mode to trigger
+ * the interruption of tx irq transmission completion.
+ */
+ if (xfer_mode == ROCKCHIP_SPI_IRQ)
+ cr0 |= CR0_XFM_TR << CR0_XFM_OFFSET;
+ else
+ cr0 |= CR0_XFM_TO << CR0_XFM_OFFSET;
+ } else {
+ dev_err(rs->dev, "no transmission buffer\n");
+ return -EINVAL;
+ }
switch (xfer->bits_per_word) {
case 4:
@@ -592,10 +672,12 @@
* ctlr->bits_per_word_mask, so this shouldn't
* happen
*/
- unreachable();
+ dev_err(rs->dev, "unknown bits per word: %d\n",
+ xfer->bits_per_word);
+ return -EINVAL;
}
- if (use_dma) {
+ if (xfer_mode == ROCKCHIP_SPI_DMA) {
if (xfer->tx_buf)
dmacr |= TF_DMA_EN;
if (xfer->rx_buf)
@@ -652,6 +734,8 @@
writel_relaxed(2 * DIV_ROUND_UP(rs->freq, 2 * xfer->speed_hz),
rs->regs + ROCKCHIP_SPI_BAUDR);
rs->speed_hz = xfer->speed_hz;
+
+ return 0;
}
static size_t rockchip_spi_max_transfer_size(struct spi_device *spi)
@@ -663,50 +747,54 @@
{
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
u32 rx_fifo_left;
- struct dma_tx_state state;
- enum dma_status status;
- /* Get current dma rx point */
- if (atomic_read(&rs->state) & RXDMA) {
- dmaengine_pause(ctlr->dma_rx);
- status = dmaengine_tx_status(ctlr->dma_rx, ctlr->dma_rx->cookie, &state);
- if (status == DMA_ERROR) {
- rs->rx = rs->xfer->rx_buf;
- rs->xfer->len = 0;
- rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
- for (; rx_fifo_left; rx_fifo_left--)
- readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
- goto out;
- } else {
- rs->rx += rs->xfer->len - rs->n_bytes * state.residue;
- }
- }
+ /* Flush rx fifo */
+ rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
+ for (; rx_fifo_left; rx_fifo_left--)
+ readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
- /* Get the valid data left in rx fifo and set rs->xfer->len real rx size */
- if (rs->rx) {
- rx_fifo_left = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
- for (; rx_fifo_left; rx_fifo_left--) {
- u32 rxw = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXDR);
+ rs->slave_aborted = true;
+ complete(&rs->xfer_done);
- if (rs->n_bytes == 1)
- *(u8 *)rs->rx = (u8)rxw;
- else
- *(u16 *)rs->rx = (u16)rxw;
- rs->rx += rs->n_bytes;
+ return 0;
+}
+
+static int rockchip_spi_transfer_wait(struct spi_controller *ctlr,
+ struct spi_transfer *xfer)
+{
+ struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+ u32 speed_hz = xfer->speed_hz;
+ unsigned long long ms;
+
+ if (spi_controller_is_slave(ctlr)) {
+ if (wait_for_completion_interruptible(&rs->xfer_done)) {
+ dev_dbg(rs->dev, "RK SPI transfer interrupted\n");
+ return -EINTR;
}
- rs->xfer->len = (unsigned int)(rs->rx - rs->xfer->rx_buf);
- }
+ if (rs->slave_aborted) {
+ dev_err(rs->dev, "RK SPI transfer slave abort\n");
+ return -EIO;
+ }
+ } else {
+ if (!speed_hz)
+ speed_hz = 100000;
-out:
- if (atomic_read(&rs->state) & RXDMA)
- dmaengine_terminate_sync(ctlr->dma_rx);
- if (atomic_read(&rs->state) & TXDMA)
- dmaengine_terminate_sync(ctlr->dma_tx);
- atomic_set(&rs->state, 0);
- spi_enable_chip(rs, false);
- rs->slave_abort = true;
- complete(&ctlr->xfer_completion);
+ ms = 8LL * 1000LL * xfer->len;
+ do_div(ms, speed_hz);
+ ms += ms + 200; /* some tolerance */
+
+ if (ms > UINT_MAX)
+ ms = UINT_MAX;
+
+ ms = wait_for_completion_timeout(&rs->xfer_done,
+ msecs_to_jiffies(ms));
+
+ if (ms == 0) {
+ dev_err(rs->dev, "RK SPI transfer timed out\n");
+ return -ETIMEDOUT;
+ }
+ }
return 0;
}
@@ -717,11 +805,13 @@
struct spi_transfer *xfer)
{
struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
+ int ret;
bool use_dma;
+ enum rockchip_spi_xfer_mode xfer_mode;
/* Zero length transfers won't trigger an interrupt on completion */
if (!xfer->len) {
- spi_finalize_current_transfer(ctlr);
+ complete(&rs->xfer_done);
return 1;
}
@@ -740,14 +830,49 @@
rs->n_bytes = xfer->bits_per_word <= 8 ? 1 : 2;
rs->xfer = xfer;
- use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
+ if (rs->poll) {
+ xfer_mode = ROCKCHIP_SPI_POLL;
+ } else {
+ use_dma = ctlr->can_dma ? ctlr->can_dma(ctlr, spi, xfer) : false;
+ if (use_dma)
+ xfer_mode = ROCKCHIP_SPI_DMA;
+ else
+ xfer_mode = ROCKCHIP_SPI_IRQ;
+ }
- rockchip_spi_config(rs, spi, xfer, use_dma, ctlr->slave);
+ ret = rockchip_spi_config(rs, spi, xfer, xfer_mode, ctlr->slave);
+ if (ret)
+ return ret;
- if (use_dma)
- return rockchip_spi_prepare_dma(rs, ctlr, xfer);
+ rs->tx = xfer->tx_buf;
+ rs->rx = xfer->rx_buf;
- return rockchip_spi_prepare_irq(rs, ctlr, xfer);
+ reinit_completion(&rs->xfer_done);
+
+ switch (xfer_mode) {
+ case ROCKCHIP_SPI_POLL:
+ ret = rockchip_spi_pio_transfer(rs, ctlr, xfer);
+ break;
+ case ROCKCHIP_SPI_DMA:
+ ret = rockchip_spi_prepare_dma(rs, ctlr, xfer);
+ break;
+ default:
+ ret = rockchip_spi_prepare_irq(rs, ctlr, xfer);
+ }
+
+ if (rs->ready) {
+ gpiod_set_value(rs->ready, 0);
+ udelay(1);
+ gpiod_set_value(rs->ready, 1);
+ }
+
+ if (ret > 0)
+ ret = rockchip_spi_transfer_wait(ctlr, xfer);
+
+ if (rs->ready)
+ gpiod_set_value(rs->ready, 0);
+
+ return ret;
}
static bool rockchip_spi_can_dma(struct spi_controller *ctlr,
@@ -766,10 +891,13 @@
static int rockchip_spi_setup(struct spi_device *spi)
{
-
- int ret = -EINVAL;
struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller);
u32 cr0;
+
+ if (!spi->cs_gpiod && (spi->mode & SPI_CS_HIGH) && !rs->cs_high_supported) {
+ dev_warn(&spi->dev, "setup: non GPIO CS can't be active-high\n");
+ return -EINVAL;
+ }
pm_runtime_get_sync(rs->dev);
@@ -783,39 +911,7 @@
pm_runtime_put(rs->dev);
- if (spi->cs_gpio == -ENOENT)
- return 0;
-
- if (!rs->gpio_requested && gpio_is_valid(spi->cs_gpio)) {
- ret = gpio_request_one(spi->cs_gpio,
- (spi->mode & SPI_CS_HIGH) ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- dev_name(&spi->dev));
- if (ret)
- dev_err(&spi->dev, "can't request chipselect gpio %d\n",
- spi->cs_gpio);
- else
- rs->gpio_requested = true;
- } else {
- if (gpio_is_valid(spi->cs_gpio)) {
- int mode = ((spi->mode & SPI_CS_HIGH) ? 0 : 1);
-
- ret = gpio_direction_output(spi->cs_gpio, mode);
- if (ret)
- dev_err(&spi->dev, "chipselect gpio %d setup failed (%d)\n",
- spi->cs_gpio, ret);
- }
- }
-
- return ret;
-}
-
-static void rockchip_spi_cleanup(struct spi_device *spi)
-{
- struct rockchip_spi *rs = spi_controller_get_devdata(spi->controller);
-
- if (rs->gpio_requested)
- gpio_free(spi->cs_gpio);
+ return 0;
}
static int rockchip_spi_misc_open(struct inode *inode, struct file *filp)
@@ -879,10 +975,11 @@
struct spi_controller *ctlr;
struct resource *mem;
struct device_node *np = pdev->dev.of_node;
- u32 rsd_nsecs;
+ u32 rsd_nsecs, num_cs, csm;
bool slave_mode;
struct pinctrl *pinctrl = NULL;
const struct rockchip_spi_quirks *quirks_cfg;
+ u32 val;
slave_mode = of_property_read_bool(np, "spi-slave");
@@ -896,6 +993,7 @@
if (!ctlr)
return -ENOMEM;
+ ctlr->rt = device_property_read_bool(&pdev->dev, "rockchip,rt");
platform_set_drvdata(pdev, ctlr);
rs = spi_controller_get_devdata(ctlr);
@@ -910,17 +1008,26 @@
}
rs->base_addr_phy = mem->start;
- rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
+ if (!has_acpi_companion(&pdev->dev))
+ rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk");
if (IS_ERR(rs->apb_pclk)) {
dev_err(&pdev->dev, "Failed to get apb_pclk\n");
ret = PTR_ERR(rs->apb_pclk);
goto err_put_ctlr;
}
- rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
+ if (!has_acpi_companion(&pdev->dev))
+ rs->spiclk = devm_clk_get(&pdev->dev, "spiclk");
if (IS_ERR(rs->spiclk)) {
dev_err(&pdev->dev, "Failed to get spi_pclk\n");
ret = PTR_ERR(rs->spiclk);
+ goto err_put_ctlr;
+ }
+
+ rs->sclk_in = devm_clk_get_optional(&pdev->dev, "sclk_in");
+ if (IS_ERR(rs->sclk_in)) {
+ dev_err(&pdev->dev, "Failed to get sclk_in\n");
+ ret = PTR_ERR(rs->sclk_in);
goto err_put_ctlr;
}
@@ -936,23 +1043,35 @@
goto err_disable_apbclk;
}
+ ret = clk_prepare_enable(rs->sclk_in);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to enable sclk_in\n");
+ goto err_disable_spiclk;
+ }
+
spi_enable_chip(rs, false);
ret = platform_get_irq(pdev, 0);
if (ret < 0)
- goto err_disable_spiclk;
+ goto err_disable_sclk_in;
ret = devm_request_threaded_irq(&pdev->dev, ret, rockchip_spi_isr, NULL,
IRQF_ONESHOT, dev_name(&pdev->dev), ctlr);
if (ret)
- goto err_disable_spiclk;
+ goto err_disable_sclk_in;
rs->dev = &pdev->dev;
- rs->freq = clk_get_rate(rs->spiclk);
- rs->gpio_requested = false;
- if (!of_property_read_u32(pdev->dev.of_node, "rx-sample-delay-ns",
- &rsd_nsecs)) {
+ rs->freq = clk_get_rate(rs->spiclk);
+ if (!rs->freq) {
+ ret = device_property_read_u32(&pdev->dev, "clock-frequency", &rs->freq);
+ if (ret) {
+ dev_warn(rs->dev, "Failed to get clock or clock-frequency property\n");
+ goto err_disable_sclk_in;
+ }
+ }
+
+ if (!device_property_read_u32(&pdev->dev, "rx-sample-delay-ns", &rsd_nsecs)) {
/* rx sample delay is expressed in parent clock cycles (max 3) */
u32 rsd = DIV_ROUND_CLOSEST(rsd_nsecs * (rs->freq >> 8),
1000000000 >> 8);
@@ -968,37 +1087,61 @@
rs->rsd = rsd;
}
+ if (!device_property_read_u32(&pdev->dev, "csm", &csm)) {
+ if (csm > CR0_CSM_ONE) {
+ dev_warn(rs->dev, "The csm value %u exceeds the limit, clamping at %u\n",
+ csm, CR0_CSM_ONE);
+ csm = CR0_CSM_ONE;
+ }
+ rs->csm = csm;
+ }
+
+ rs->version = readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION);
rs->fifo_len = get_fifo_len(rs);
if (!rs->fifo_len) {
dev_err(&pdev->dev, "Failed to get fifo length\n");
ret = -EINVAL;
- goto err_disable_spiclk;
+ goto err_disable_sclk_in;
}
quirks_cfg = device_get_match_data(&pdev->dev);
if (quirks_cfg)
rs->max_baud_div_in_cpha = quirks_cfg->max_baud_div_in_cpha;
+
+ if (!device_property_read_u32(&pdev->dev, "rockchip,autosuspend-delay-ms", &val)) {
+ if (val > 0) {
+ pm_runtime_set_autosuspend_delay(&pdev->dev, val);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ }
+ }
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ctlr->auto_runtime_pm = true;
ctlr->bus_num = pdev->id;
- ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST | SPI_CS_HIGH;
+ ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST;
if (slave_mode) {
ctlr->mode_bits |= SPI_NO_CS;
ctlr->slave_abort = rockchip_spi_slave_abort;
} else {
ctlr->flags = SPI_MASTER_GPIO_SS;
+ ctlr->max_native_cs = ROCKCHIP_SPI_MAX_CS_NUM;
+ /*
+ * rk spi0 has two native cs, spi1..5 one cs only
+ * if num-cs is missing in the dts, default to 1
+ */
+ if (device_property_read_u32(&pdev->dev, "num-cs", &num_cs))
+ num_cs = 1;
+ ctlr->num_chipselect = num_cs;
+ ctlr->use_gpio_descriptors = true;
}
- ctlr->num_chipselect = ROCKCHIP_SPI_MAX_CS_NUM;
ctlr->dev.of_node = pdev->dev.of_node;
ctlr->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8) | SPI_BPW_MASK(4);
ctlr->min_speed_hz = rs->freq / BAUDR_SCKDV_MAX;
ctlr->max_speed_hz = min(rs->freq / BAUDR_SCKDV_MIN, MAX_SCLK_OUT);
- ctlr->set_cs = rockchip_spi_set_cs;
ctlr->setup = rockchip_spi_setup;
- ctlr->cleanup = rockchip_spi_cleanup;
+ ctlr->set_cs = rockchip_spi_set_cs;
ctlr->transfer_one = rockchip_spi_transfer_one;
ctlr->max_transfer_size = rockchip_spi_max_transfer_size;
ctlr->handle_err = rockchip_spi_handle_err;
@@ -1030,17 +1173,29 @@
ctlr->can_dma = rockchip_spi_can_dma;
}
- switch (readl_relaxed(rs->regs + ROCKCHIP_SPI_VERSION)) {
- case ROCKCHIP_SPI_VER2_TYPE1:
+ rs->poll = device_property_read_bool(&pdev->dev, "rockchip,poll-only");
+ init_completion(&rs->xfer_done);
+ if (rs->poll && slave_mode) {
+ dev_err(rs->dev, "only support rockchip,poll-only property in master mode\n");
+ ret = -EINVAL;
+ goto err_free_dma_rx;
+ }
+
+ switch (rs->version) {
case ROCKCHIP_SPI_VER2_TYPE2:
- if (ctlr->can_dma && slave_mode)
+ rs->cs_high_supported = true;
+ ctlr->mode_bits |= SPI_CS_HIGH;
+ if (slave_mode)
rs->cs_inactive = true;
else
rs->cs_inactive = false;
break;
default:
rs->cs_inactive = false;
+ break;
}
+ if (device_property_read_bool(&pdev->dev, "rockchip,cs-inactive-disable"))
+ rs->cs_inactive = false;
pinctrl = devm_pinctrl_get(&pdev->dev);
if (!IS_ERR(pinctrl)) {
@@ -1049,6 +1204,13 @@
dev_warn(&pdev->dev, "no high_speed pinctrl state\n");
rs->high_speed_state = NULL;
}
+ }
+
+ rs->ready = devm_gpiod_get_optional(&pdev->dev, "ready", GPIOD_OUT_HIGH);
+ if (IS_ERR(rs->ready)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->ready),
+ "invalid ready-gpios property in node\n");
+ goto err_free_dma_rx;
}
ret = devm_spi_register_controller(&pdev->dev, ctlr);
@@ -1073,6 +1235,9 @@
dev_info(&pdev->dev, "register misc device %s\n", misc_name);
}
+ dev_info(rs->dev, "probed, poll=%d, rsd=%d, cs-inactive=%d, ready=%d\n",
+ rs->poll, rs->rsd, rs->cs_inactive, rs->ready ? 1 : 0);
+
return 0;
err_free_dma_rx:
@@ -1083,6 +1248,8 @@
dma_release_channel(ctlr->dma_tx);
err_disable_pm_runtime:
pm_runtime_disable(&pdev->dev);
+err_disable_sclk_in:
+ clk_disable_unprepare(rs->sclk_in);
err_disable_spiclk:
clk_disable_unprepare(rs->spiclk);
err_disable_apbclk:
@@ -1103,6 +1270,7 @@
pm_runtime_get_sync(&pdev->dev);
+ clk_disable_unprepare(rs->sclk_in);
clk_disable_unprepare(rs->spiclk);
clk_disable_unprepare(rs->apb_pclk);
@@ -1119,51 +1287,6 @@
return 0;
}
-
-#ifdef CONFIG_PM_SLEEP
-static int rockchip_spi_suspend(struct device *dev)
-{
- int ret;
- struct spi_controller *ctlr = dev_get_drvdata(dev);
- struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
-
- ret = spi_controller_suspend(ctlr);
- if (ret < 0)
- return ret;
-
- clk_disable_unprepare(rs->spiclk);
- clk_disable_unprepare(rs->apb_pclk);
-
- pinctrl_pm_select_sleep_state(dev);
-
- return 0;
-}
-
-static int rockchip_spi_resume(struct device *dev)
-{
- int ret;
- struct spi_controller *ctlr = dev_get_drvdata(dev);
- struct rockchip_spi *rs = spi_controller_get_devdata(ctlr);
-
- pinctrl_pm_select_default_state(dev);
-
- ret = clk_prepare_enable(rs->apb_pclk);
- if (ret < 0)
- return ret;
-
- ret = clk_prepare_enable(rs->spiclk);
- if (ret < 0)
- clk_disable_unprepare(rs->apb_pclk);
-
- ret = spi_controller_resume(ctlr);
- if (ret < 0) {
- clk_disable_unprepare(rs->spiclk);
- clk_disable_unprepare(rs->apb_pclk);
- }
-
- return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM
static int rockchip_spi_runtime_suspend(struct device *dev)
@@ -1195,6 +1318,46 @@
}
#endif /* CONFIG_PM */
+#ifdef CONFIG_PM_SLEEP
+static int rockchip_spi_suspend(struct device *dev)
+{
+ int ret;
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+
+ ret = spi_controller_suspend(ctlr);
+ if (ret < 0)
+ return ret;
+
+ /* Avoid redundant clock disable */
+ if (!pm_runtime_status_suspended(dev))
+ rockchip_spi_runtime_suspend(dev);
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
+}
+
+static int rockchip_spi_resume(struct device *dev)
+{
+ int ret;
+ struct spi_controller *ctlr = dev_get_drvdata(dev);
+
+ pinctrl_pm_select_default_state(dev);
+
+ if (!pm_runtime_status_suspended(dev)) {
+ ret = rockchip_spi_runtime_resume(dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = spi_controller_resume(ctlr);
+ if (ret < 0)
+ rockchip_spi_runtime_suspend(dev);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
static const struct dev_pm_ops rockchip_spi_pm = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
@@ -1210,15 +1373,18 @@
.compatible = "rockchip,px30-spi",
.data = &rockchip_spi_quirks_cfg,
},
- { .compatible = "rockchip,rv1108-spi", },
- { .compatible = "rockchip,rv1126-spi", },
{ .compatible = "rockchip,rk3036-spi", },
{ .compatible = "rockchip,rk3066-spi", },
{ .compatible = "rockchip,rk3188-spi", },
{ .compatible = "rockchip,rk3228-spi", },
{ .compatible = "rockchip,rk3288-spi", },
+ { .compatible = "rockchip,rk3308-spi", },
+ { .compatible = "rockchip,rk3328-spi", },
{ .compatible = "rockchip,rk3368-spi", },
{ .compatible = "rockchip,rk3399-spi", },
+ { .compatible = "rockchip,rv1106-spi", },
+ { .compatible = "rockchip,rv1108-spi", },
+ { .compatible = "rockchip,rv1126-spi", },
{ },
};
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
--
Gitblit v1.6.2