#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/scatterlist.h>
#include <linux/sizes.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include "internals.h"
#define K1_SPI_MIN_SPEED_HZ 6250
#define K1_SPI_MAX_SPEED_HZ 51200000
#define K1_SPI_DMA_ALIGNMENT 64
#define K1_SPI_MAX_DMA_LEN SZ_512K
#define SSP_TOP_CTRL 0x00
#define TOP_SSE BIT(0)
#define TOP_FRF_MASK GENMASK(2, 1)
#define TOP_FRF_MOTOROLA 0
#define TOP_DSS_MASK GENMASK(9, 5)
#define TOP_SPO BIT(10)
#define TOP_SPH BIT(11)
#define TOP_LBM BIT(12)
#define TOP_TRAIL BIT(13)
#define TOP_HOLD_FRAME_LOW BIT(14)
#define SSP_FIFO_CTRL 0x04
#define FIFO_TFT_MASK GENMASK(4, 0)
#define FIFO_RFT_MASK GENMASK(9, 5)
#define FIFO_TSRE BIT(10)
#define FIFO_RSRE BIT(11)
#define SSP_INT_EN 0x08
#define SSP_INT_EN_TINTE BIT(1)
#define SSP_INT_EN_RIE BIT(2)
#define SSP_INT_EN_TIE BIT(3)
#define SSP_INT_EN_RIM BIT(4)
#define SSP_INT_EN_TIM BIT(5)
#define SSP_INT_EN_EBCEI BIT(6)
#define SSP_INT_EN_TX SSP_INT_EN_TIE
#define SSP_INT_EN_RX \
(SSP_INT_EN_TINTE | SSP_INT_EN_RIE)
#define SSP_INT_EN_ERROR \
(SSP_INT_EN_RIM | SSP_INT_EN_TIM | SSP_INT_EN_EBCEI)
#define SSP_TIMEOUT 0x0c
#define SSP_TIMEOUT_MASK GENMASK(23, 0)
#define SSP_DATAR 0x10
#define SSP_STATUS 0x14
#define SSP_STATUS_BSY BIT(0)
#define SSP_STATUS_TNF BIT(6)
#define SSP_STATUS_TFL GENMASK(11, 7)
#define SSP_STATUS_TUR BIT(12)
#define SSP_STATUS_RNE BIT(14)
#define SSP_STATUS_RFL GENMASK(19, 15)
#define SSP_STATUS_ROR BIT(20)
#define SSP_STATUS_BCE BIT(21)
#define SSP_STATUS_ERROR \
(SSP_STATUS_TUR | SSP_STATUS_ROR | SSP_STATUS_BCE)
#define K1_SPI_FIFO_SIZE 32
#define K1_SPI_THRESH (K1_SPI_FIFO_SIZE / 2)
struct k1_spi_driver_data {
struct spi_controller *host;
void __iomem *base;
phys_addr_t base_addr;
unsigned long bus_rate;
struct clk *clk;
unsigned long rate;
int irq;
u32 bytes;
unsigned int rx_resid;
unsigned int tx_resid;
struct spi_transfer *transfer;
bool dma_enabled;
};
static void
k1_spi_register_reset(struct k1_spi_driver_data *drv_data, bool initial)
{
u32 val = 0;
writel(0, drv_data->base + SSP_TOP_CTRL);
if (initial) {
val = FIELD_PREP(FIFO_RFT_MASK, K1_SPI_THRESH - 1);
val |= FIELD_PREP(FIFO_TFT_MASK, K1_SPI_THRESH - 1);
}
writel(val, drv_data->base + SSP_FIFO_CTRL);
writel(0, drv_data->base + SSP_INT_EN);
writel(0, drv_data->base + SSP_TIMEOUT);
writel(~0, drv_data->base + SSP_STATUS);
}
static int k1_spi_setup(struct spi_device *spi)
{
struct k1_spi_driver_data *drv_data;
u32 val;
drv_data = spi_controller_get_devdata(spi->controller);
val = FIELD_PREP(TOP_FRF_MASK, TOP_FRF_MOTOROLA);
if (spi->mode & SPI_CPHA)
val |= TOP_SPH;
if (spi->mode & SPI_CPOL)
val |= TOP_SPO;
if (spi->mode & SPI_LOOP)
val |= TOP_LBM;
writel(val, drv_data->base + SSP_TOP_CTRL);
return 0;
}
static void k1_spi_cleanup(struct spi_device *spi)
{
struct k1_spi_driver_data *drv_data;
drv_data = spi_controller_get_devdata(spi->controller);
k1_spi_register_reset(drv_data, false);
}
static bool k1_spi_can_dma(struct spi_controller *host, struct spi_device *spi,
struct spi_transfer *transfer)
{
struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
u32 burst_size;
if (!drv_data->dma_enabled)
return false;
if (transfer->len > SZ_2K)
return false;
burst_size = K1_SPI_THRESH * spi_bpw_to_bytes(transfer->bits_per_word);
return transfer->len >= burst_size;
}
static void k1_spi_dma_callback(void *param)
{
struct k1_spi_driver_data *drv_data = param;
u32 val;
val = readl(drv_data->base + SSP_FIFO_CTRL);
val &= ~(FIFO_TSRE | FIFO_RSRE);
writel(val, drv_data->base + SSP_FIFO_CTRL);
val = readl(drv_data->base + SSP_TOP_CTRL);
val &= ~TOP_TRAIL;
writel(val, drv_data->base + SSP_TOP_CTRL);
val = readl(drv_data->base + SSP_STATUS);
if (val & SSP_STATUS_ERROR)
drv_data->transfer->error |= SPI_TRANS_FAIL_IO;
val = readl(drv_data->base + SSP_TOP_CTRL);
val &= ~TOP_SSE;
writel(val, drv_data->base + SSP_TOP_CTRL);
drv_data->transfer = NULL;
spi_finalize_current_transfer(drv_data->host);
}
static struct dma_async_tx_descriptor *
k1_spi_dma_prep(struct k1_spi_driver_data *drv_data,
struct spi_transfer *transfer, bool tx)
{
phys_addr_t addr = drv_data->base_addr + SSP_DATAR;
u32 burst_size = K1_SPI_THRESH * drv_data->bytes;
struct dma_slave_config cfg = { };
enum dma_transfer_direction dir;
enum dma_slave_buswidth width;
struct dma_chan *chan;
struct sg_table *sgt;
switch (drv_data->bytes) {
case 1:
width = DMA_SLAVE_BUSWIDTH_1_BYTE;
break;
case 2:
width = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
default:
width = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
}
if (tx) {
chan = drv_data->host->dma_tx;
sgt = &transfer->tx_sg;
dir = DMA_MEM_TO_DEV;
cfg.dst_addr = addr;
cfg.dst_addr_width = width;
cfg.dst_maxburst = burst_size;
} else {
chan = drv_data->host->dma_rx;
sgt = &transfer->rx_sg;
dir = DMA_DEV_TO_MEM;
cfg.src_addr = addr;
cfg.src_addr_width = width;
cfg.src_maxburst = burst_size;
}
cfg.direction = dir;
if (dmaengine_slave_config(chan, &cfg))
return NULL;
return dmaengine_prep_slave_sg(chan, sgt->sgl, sgt->nents, dir,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
}
static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi,
struct spi_transfer *transfer)
{
struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
struct dma_async_tx_descriptor *desc;
u32 val;
desc = k1_spi_dma_prep(drv_data, transfer, true);
if (!desc)
goto fallback;
dmaengine_submit(desc);
desc = k1_spi_dma_prep(drv_data, transfer, false);
if (!desc)
goto fallback;
desc->callback = k1_spi_dma_callback;
desc->callback_param = drv_data;
dmaengine_submit(desc);
val = readl(drv_data->base + SSP_TOP_CTRL);
val |= TOP_TRAIL;
writel(val, drv_data->base + SSP_TOP_CTRL);
val = readl(drv_data->base + SSP_FIFO_CTRL);
val |= FIFO_TSRE | FIFO_RSRE;
writel(val, drv_data->base + SSP_FIFO_CTRL);
dma_async_issue_pending(host->dma_rx);
dma_async_issue_pending(host->dma_tx);
return 1;
fallback:
transfer->error |= SPI_TRANS_FAIL_NO_START;
return -EAGAIN;
}
static int k1_spi_prepare_message(struct spi_controller *host,
struct spi_message *message)
{
struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
u32 val = readl(drv_data->base + SSP_STATUS);
u32 count;
if (!(val & SSP_STATUS_RNE))
return 0;
count = FIELD_GET(SSP_STATUS_RFL, val) + 1;
do
(void)readl(drv_data->base + SSP_DATAR);
while (--count);
return 0;
}
static void k1_spi_set_cs(struct spi_device *spi, bool high)
{
struct k1_spi_driver_data *drv_data;
u32 val;
drv_data = spi_controller_get_devdata(spi->controller);
val = readl(drv_data->base + SSP_TOP_CTRL);
if (high)
val &= ~TOP_HOLD_FRAME_LOW;
else
val |= TOP_HOLD_FRAME_LOW;
writel(val, drv_data->base + SSP_TOP_CTRL);
}
static int k1_spi_set_speed(struct k1_spi_driver_data *drv_data,
struct spi_transfer *transfer)
{
struct clk *clk = drv_data->clk;
u64 nsec_per_word;
u64 bus_ticks;
u32 timeout;
u32 val;
int ret;
ret = clk_set_rate(clk, transfer->speed_hz);
if (ret)
return ret;
drv_data->rate = clk_get_rate(clk);
if (!transfer->rx_buf)
return 0;
nsec_per_word = (u64)NSEC_PER_SEC * BITS_PER_BYTE * drv_data->bytes;
nsec_per_word = DIV_ROUND_UP_ULL(nsec_per_word, drv_data->rate);
bus_ticks = 3 * nsec_per_word * drv_data->bus_rate;
timeout = DIV_ROUND_UP_ULL(bus_ticks, NSEC_PER_SEC);
val = FIELD_PREP(SSP_TIMEOUT_MASK, timeout);
writel(val, drv_data->base + SSP_TIMEOUT);
return 0;
}
static int k1_spi_transfer_one(struct spi_controller *host,
struct spi_device *spi,
struct spi_transfer *transfer)
{
struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
u32 ctrl;
u32 val;
int ret;
drv_data->bytes = spi_bpw_to_bytes(transfer->bits_per_word);
ret = k1_spi_set_speed(drv_data, transfer);
if (ret) {
dev_err(&host->dev,
"failed to set transfer speed: %d\n", ret);
return ret;
}
drv_data->rx_resid = transfer->len;
drv_data->tx_resid = transfer->len;
drv_data->transfer = transfer;
writel(~0, drv_data->base + SSP_STATUS);
ctrl = readl(drv_data->base + SSP_TOP_CTRL);
ctrl &= ~TOP_DSS_MASK;
ctrl |= FIELD_PREP(TOP_DSS_MASK, transfer->bits_per_word - 1);
ctrl |= TOP_SSE;
writel(ctrl, drv_data->base + SSP_TOP_CTRL);
if (spi_xfer_is_dma_mapped(host, spi, transfer))
return k1_spi_dma_one(host, spi, transfer);
val = SSP_INT_EN_TX | SSP_INT_EN_RX | SSP_INT_EN_ERROR;
writel(val, drv_data->base + SSP_INT_EN);
return 1;
}
static void
k1_spi_handle_err(struct spi_controller *host, struct spi_message *message)
{
struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host);
if (drv_data->dma_enabled) {
dmaengine_terminate_sync(host->dma_rx);
dmaengine_terminate_sync(host->dma_tx);
}
}
static void k1_spi_write_word(struct k1_spi_driver_data *drv_data)
{
struct spi_transfer *transfer = drv_data->transfer;
u32 bytes = drv_data->bytes;
u32 val;
if (transfer->tx_buf) {
const void *buf;
buf = transfer->tx_buf + (transfer->len - drv_data->tx_resid);
if (bytes == 1)
val = *(u8 *)buf;
else if (bytes == 2)
val = *(u16 *)buf;
else
val = *(u32 *)buf;
} else {
val = 0;
}
writel(val, drv_data->base + SSP_DATAR);
drv_data->tx_resid -= bytes;
}
static bool k1_spi_write(struct k1_spi_driver_data *drv_data, u32 val)
{
unsigned int count;
count = K1_SPI_FIFO_SIZE - FIELD_GET(SSP_STATUS_TFL, val);
count = min3(count, K1_SPI_THRESH, drv_data->tx_resid / drv_data->bytes);
do
k1_spi_write_word(drv_data);
while (--count);
return !drv_data->tx_resid;
}
static void k1_spi_read_word(struct k1_spi_driver_data *drv_data)
{
struct spi_transfer *transfer = drv_data->transfer;
u32 bytes = drv_data->bytes;
u32 val;
val = readl(drv_data->base + SSP_DATAR);
if (transfer->rx_buf) {
void *buf;
buf = transfer->rx_buf + (transfer->len - drv_data->rx_resid);
if (bytes == 1)
*(u8 *)buf = val;
else if (bytes == 2)
*(u16 *)buf = val;
else
*(u32 *)buf = val;
}
drv_data->rx_resid -= bytes;
}
static bool k1_spi_read(struct k1_spi_driver_data *drv_data, u32 val)
{
do {
unsigned int count = FIELD_GET(SSP_STATUS_RFL, val) + 1;
count = min(count, drv_data->rx_resid / drv_data->bytes);
do
k1_spi_read_word(drv_data);
while (--count);
if (!drv_data->rx_resid)
return true;
val = readl(drv_data->base + SSP_STATUS);
if (val & SSP_STATUS_RNE)
writel(SSP_STATUS_RNE, drv_data->base + SSP_STATUS);
else
return false;
} while (true);
}
static irqreturn_t k1_spi_ssp_isr(int irq, void *dev_id)
{
struct k1_spi_driver_data *drv_data = dev_id;
u32 status;
u32 top_ctrl;
status = readl(drv_data->base + SSP_STATUS);
writel(status, drv_data->base + SSP_STATUS);
if (!(status & (SSP_STATUS_ERROR | SSP_STATUS_TNF | SSP_STATUS_RNE)))
return IRQ_NONE;
if (status & SSP_STATUS_ERROR) {
if (drv_data->transfer)
drv_data->transfer->error |= SPI_TRANS_FAIL_IO;
goto done;
}
if (drv_data->tx_resid && (status & SSP_STATUS_TNF)) {
if (k1_spi_write(drv_data, status))
writel(SSP_INT_EN_RX | SSP_INT_EN_ERROR,
drv_data->base + SSP_INT_EN);
}
if (drv_data->rx_resid) {
if (status & SSP_STATUS_RNE)
if (k1_spi_read(drv_data, status))
goto done;
return IRQ_HANDLED;
}
done:
top_ctrl = readl(drv_data->base + SSP_TOP_CTRL);
top_ctrl &= ~TOP_SSE;
writel(top_ctrl, drv_data->base + SSP_TOP_CTRL);
writel(0, drv_data->base + SSP_INT_EN);
if (drv_data->transfer) {
drv_data->transfer = NULL;
spi_finalize_current_transfer(drv_data->host);
}
return IRQ_HANDLED;
}
static int
k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev)
{
struct spi_controller *host = drv_data->host;
struct dma_chan *chan;
chan = dma_request_chan(dev, "tx");
if (IS_ERR(chan))
return PTR_ERR(chan);
host->dma_tx = chan;
chan = dma_request_chan(dev, "rx");
if (IS_ERR(chan)) {
dma_release_channel(host->dma_tx);
host->dma_tx = NULL;
return PTR_ERR(chan);
}
host->dma_rx = chan;
drv_data->dma_enabled = true;
return 0;
}
static void k1_spi_dma_cleanup(struct device *dev, void *res)
{
struct k1_spi_driver_data **ptr = res;
struct k1_spi_driver_data *drv_data = *ptr;
struct spi_controller *host = drv_data->host;
if (!drv_data->dma_enabled)
return;
drv_data->dma_enabled = false;
dma_release_channel(host->dma_rx);
host->dma_rx = NULL;
dma_release_channel(host->dma_tx);
host->dma_tx = NULL;
}
static int
devm_k1_spi_dma_setup(struct k1_spi_driver_data *drv_data, struct device *dev)
{
struct k1_spi_driver_data **ptr;
int ret;
if (!IS_ENABLED(CONFIG_MMP_PDMA)) {
dev_info(dev, "DMA not available; using PIO\n");
return 0;
}
ptr = devres_alloc(k1_spi_dma_cleanup, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
ret = k1_spi_dma_setup(drv_data, dev);
if (ret) {
devres_free(ptr);
return ret;
}
*ptr = drv_data;
devres_add(dev, ptr);
return 0;
}
static int k1_spi_probe(struct platform_device *pdev)
{
struct k1_spi_driver_data *drv_data;
struct device *dev = &pdev->dev;
struct reset_control *reset;
struct spi_controller *host;
struct resource *iores;
struct clk *clk_bus;
int ret;
host = devm_spi_alloc_host(dev, sizeof(*drv_data));
if (!host)
return -ENOMEM;
drv_data = spi_controller_get_devdata(host);
drv_data->host = host;
platform_set_drvdata(pdev, drv_data);
ret = devm_k1_spi_dma_setup(drv_data, dev);
if (ret == -EPROBE_DEFER)
return ret;
if (ret)
dev_warn(dev, "DMA setup failed (%d), falling back to PIO\n", ret);
drv_data->base = devm_platform_get_and_ioremap_resource(pdev, 0,
&iores);
if (IS_ERR(drv_data->base))
return dev_err_probe(dev, PTR_ERR(drv_data->base),
"error mapping memory\n");
drv_data->base_addr = iores->start;
clk_bus = devm_clk_get_enabled(dev, "bus");
if (IS_ERR(clk_bus))
return dev_err_probe(dev, PTR_ERR(clk_bus),
"error getting/enabling bus clock\n");
drv_data->bus_rate = clk_get_rate(clk_bus);
drv_data->clk = devm_clk_get_enabled(dev, "core");
if (IS_ERR(drv_data->clk))
return dev_err_probe(dev, PTR_ERR(drv_data->clk),
"error getting/enabling core clock\n");
reset = devm_reset_control_get_exclusive_deasserted(dev, NULL);
if (IS_ERR(reset))
return dev_err_probe(dev, PTR_ERR(reset),
"error getting/deasserting reset\n");
k1_spi_register_reset(drv_data, true);
drv_data->irq = platform_get_irq(pdev, 0);
if (drv_data->irq < 0)
return dev_err_probe(dev, drv_data->irq, "error getting IRQ\n");
ret = devm_request_irq(dev, drv_data->irq, k1_spi_ssp_isr,
IRQF_SHARED, dev_name(dev), drv_data);
if (ret < 0)
return dev_err_probe(dev, ret, "error requesting IRQ\n");
host->dev.of_node = dev_of_node(dev);
host->dev.parent = dev;
host->num_chipselect = 1;
if (drv_data->dma_enabled)
host->dma_alignment = K1_SPI_DMA_ALIGNMENT;
host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
host->min_speed_hz = K1_SPI_MIN_SPEED_HZ;
host->max_speed_hz = K1_SPI_MAX_SPEED_HZ;
host->flags = SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX;
host->max_dma_len = K1_SPI_MAX_DMA_LEN;
host->setup = k1_spi_setup;
host->cleanup = k1_spi_cleanup;
host->can_dma = k1_spi_can_dma;
host->prepare_message = k1_spi_prepare_message;
host->set_cs = k1_spi_set_cs;
host->transfer_one = k1_spi_transfer_one;
host->handle_err = k1_spi_handle_err;
ret = devm_spi_register_controller(dev, host);
if (ret)
dev_err(dev, "error registering controller\n");
return ret;
}
static const struct of_device_id k1_spi_dt_ids[] = {
{ .compatible = "spacemit,k1-spi", },
{}
};
MODULE_DEVICE_TABLE(of, k1_spi_dt_ids);
static struct platform_driver k1_spi_driver = {
.probe = k1_spi_probe,
.driver = {
.name = "k1-spi",
.of_match_table = k1_spi_dt_ids,
},
};
module_platform_driver(k1_spi_driver);
MODULE_DESCRIPTION("SpacemiT K1 SPI controller driver");
MODULE_LICENSE("GPL");