CoolPi-Armbian-Rockchip-RK3.../drivers/spi/spi-rockchip-flexbus-fspi.c

773 lines
23 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Rockchip Flexbus FSPI mode
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#include <linux/acpi.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/dma-mapping.h>
#include <linux/iopoll.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/spi/spi-mem.h>
#include <dt-bindings/mfd/rockchip-flexbus.h>
#include <linux/mfd/rockchip-flexbus.h>
#define FLEXBUS_FSPI_ERR_ISR (FLEXBUS_DMA_TIMEOUT_ISR | FLEXBUS_DMA_ERR_ISR | \
FLEXBUS_TX_UDF_ISR | FLEXBUS_TX_OVF_ISR)
/* Flexbus definition */
#define FLEXBUS_REVISION_V9 (0x9)
#define FLEXBUS_QSPI_CMD_MAX (0x8)
#define FLEXBUS_MAX_IOSIZE (0x4000)
#define FLEXBUS_DMA_TIMEOUT_MS (0x1000)
#define FLEXBUS_MAX_SPEED (150 * 1000 * 1000)
#define FLEXBUS_DLL_THRESHOLD_RATE (50 * 1000 * 1000)
#define FLEXBUS_DLL_TRANING_STEP (10) /* Training step */
#define FLEXBUS_DLL_TRANING_VALID_WINDOW (80) /* Valid DLL winbow */
#define FLEXBUS_DLL_CELLS (0x7f)
#define FLEXBUS_MAX_CHIPSELECT_NUM (1)
#define FLEXBUS_TX_WIDTH_4 (4)
struct rk_flexbus_fspi {
struct device *dev;
struct rockchip_flexbus *fb;
u32 speed[FLEXBUS_MAX_CHIPSELECT_NUM];
u32 cur_speed;
u32 cur_real_speed;
u8 *tx_buf;
u8 *switch_buf;
u8 *temp_buf;
dma_addr_t dma_temp_buf;
struct completion cp;
u32 version;
u32 max_iosize;
u32 dll_cells[FLEXBUS_MAX_CHIPSELECT_NUM];
struct gpio_desc **cs_gpiods;
struct spi_controller *master;
};
struct rk_flexbus_fspi_xfer {
u8 *cmd_buf;
u32 cmd_cycles;
dma_addr_t buf_addr;
u32 buf_cycles;
};
static const unsigned char bit_reverse_table_256[] = {
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
static void flexbus_bit_revert(struct rk_flexbus_fspi *fspi, const u8 *in, u8 *out, u32 len)
{
int i;
for (i = 0; i < len; i++)
out[i] = bit_reverse_table_256[in[i]];
}
static int flexbus_fspi_data_format(struct rk_flexbus_fspi *fspi,
const u8 *in, u8 in_lines,
u8 *out, u8 out_lines,
u32 cycles, u8 *temp_buf)
{
int in_bits, out_bits, i;
if (in_lines == 4 && out_lines == 1) {
in_bits = cycles * 4;
out_bits = cycles;
memset(out, 0, (out_bits + 7) / 8);
for (i = 0; i < in_bits; i += 4)
if (test_bit(i + 1, (void *)in))
__set_bit(i / 4, (void *)out);
return in_bits;
} else if (in_lines == 1 && out_lines == 4) {
in_bits = cycles;
out_bits = in_bits * 4;
memset(out, 0, (out_bits + 7) / 8);
flexbus_bit_revert(fspi, in, temp_buf, cycles / 8);
for (i = 0; i < in_bits; i++)
if (test_bit(i, (void *)temp_buf))
__set_bit(i * 4, (void *)out);
return in_bits;
}
return 0;
}
static int flexbus_data_format_order(struct rk_flexbus_fspi *fspi, u8 *in, u8 *out, u32 bytes)
{
int i, loops, left;
u32 *in32 = (u32 *)in;
u32 *out32 = (u32 *)out;
loops = bytes / 4;
left = bytes % 4;
for (i = 0; i < loops; i++)
out32[i] = __swab32(in32[i]);
dev_dbg(fspi->fb->dev, "%s %d %d %d\n", __func__, loops, left, i);
switch (left) {
case 1:
out[i * 4] = in[i * 4];
break;
case 2:
out[i * 4] = in[i * 4 + 1];
out[i * 4 + 1] = in[i * 4];
break;
case 3:
out[i * 4] = in[i * 4 + 2];
out[i * 4 + 1] = in[i * 4 + 1];
out[i * 4 + 2] = in[i * 4];
break;
default:
break;
}
return 0;
}
static int rk_flexbus_fspi_init(struct rk_flexbus_fspi *fspi)
{
u32 ctrl;
fspi->fb->config->grf_config(fspi->fb, false, 0, 0);
fspi->version = rockchip_flexbus_readl(fspi->fb, FLEXBUS_REVISION) >> 16;
fspi->max_iosize = FLEXBUS_MAX_IOSIZE;
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TXWAT_START, 0x10);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_DMA_SRC_LEN0,
fspi->max_iosize * FLEXBUS_TX_WIDTH_4);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_DMA_DST_LEN0,
fspi->max_iosize * FLEXBUS_TX_WIDTH_4);
if (fspi->version == FLEXBUS_REVISION_V9)
ctrl = FLEXBUS_TX_CTL_MSB | fspi->fb->dfs_reg->dfs_4bit;
else
ctrl = FLEXBUS_TX_CTL_UNIT_BYTE | FLEXBUS_TX_CTL_MSB | fspi->fb->dfs_reg->dfs_1bit;
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_CTL, ctrl);
/* Using internal clk as sample clock */
rockchip_flexbus_writel(fspi->fb, FLEXBUS_SLAVE_MODE, 0);
return 0;
}
static u16 rk_flexbus_fspi_get_max_dll_cells(struct rk_flexbus_fspi *fspi)
{
return FLEXBUS_DLL_CELLS;
}
static void rk_flexbus_fspi_set_delay_lines(struct rk_flexbus_fspi *fspi, u16 cells, u8 cs)
{
u16 cell_max = 256;
if (cells > cell_max)
cells = cell_max;
if (cells) {
rockchip_flexbus_writel(fspi->fb, FLEXBUS_DLL_EN, 1);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_DLL_NUM, cells);
} else {
rockchip_flexbus_writel(fspi->fb, FLEXBUS_DLL_EN, 0);
}
}
static int rk_flexbus_fspi_clk_set_rate(struct rk_flexbus_fspi *fspi, unsigned long speed)
{
return clk_set_rate(fspi->fb->clks[0].clk, speed * 2);
}
static unsigned long rk_flexbus_fspi_clk_get_rate(struct rk_flexbus_fspi *fspi)
{
return clk_get_rate(fspi->fb->clks[0].clk) / 2;
}
static int rk_flexbus_fspi_send(struct rk_flexbus_fspi *fspi, struct rk_flexbus_fspi_xfer *cfg)
{
int ret = 0, timeout_ms = FLEXBUS_DMA_TIMEOUT_MS;
rockchip_flexbus_writel(fspi->fb, FLEXBUS_CSN_CFG, 0x10001);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_COM_CTL, FLEXBUS_TX_ONLY);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_IMR, FLEXBUS_TX_DONE_ISR);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ICR, FLEXBUS_TX_DONE_ISR);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_CMD_LEN, cfg->cmd_cycles);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_CMD0, ((u32 *)fspi->tx_buf)[0]);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_CMD1, ((u32 *)fspi->tx_buf)[1]);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_NUM, cfg->cmd_cycles + cfg->buf_cycles);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_RX_CTL, 0);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_RX_NUM, 0);
init_completion(&fspi->cp);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_DMA_SRC_ADDR0, cfg->buf_addr >> 2);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ENR, FLEXBUS_TX_ENR);
if (!wait_for_completion_timeout(&fspi->cp, msecs_to_jiffies(timeout_ms))) {
dev_err(fspi->dev, "DMA wait for tx transfer finish timeout\n");
ret = -ETIMEDOUT;
}
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ENR, FLEXBUS_TX_DIS);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ICR, FLEXBUS_TX_DONE_ISR);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_IMR, 0);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_CSN_CFG, 0x10000);
return ret;
}
static int rk_flexbus_fspi_send_then_recv_114(struct rk_flexbus_fspi *fspi,
struct rk_flexbus_fspi_xfer *cfg,
bool trick)
{
int ret = 0, timeout_ms = FLEXBUS_DMA_TIMEOUT_MS;
u32 ctrl;
rockchip_flexbus_writel(fspi->fb, FLEXBUS_CSN_CFG, 0x10001);
ctrl = FLEXBUS_TX_THEN_RX | FLEXBUS_SCLK_SHARE | FLEXBUS_TX_USE_RX;
rockchip_flexbus_writel(fspi->fb, FLEXBUS_COM_CTL, ctrl);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_IMR, FLEXBUS_RX_DONE_ISR);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ICR, FLEXBUS_RX_DONE_ISR);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_CMD_LEN, cfg->cmd_cycles);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_CMD0, ((u32 *)fspi->tx_buf)[0]);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_CMD1, ((u32 *)fspi->tx_buf)[1]);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_TX_NUM, cfg->cmd_cycles);
ctrl = FLEXBUS_RXD_DY | FLEXBUS_AUTOPAD | FLEXBUS_RX_CTL_MSB | fspi->fb->dfs_reg->dfs_4bit;
if (!trick)
ctrl |= FLEXBUS_RX_CTL_UNIT_BYTE | FLEXBUS_RX_CTL_FILL_DUMMY;
rockchip_flexbus_writel(fspi->fb, FLEXBUS_RX_CTL, ctrl);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_RX_NUM, cfg->buf_cycles);
init_completion(&fspi->cp);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_DMA_DST_ADDR0, cfg->buf_addr >> 2);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ENR, FLEXBUS_TX_ENR | FLEXBUS_RX_ENR);
if (!wait_for_completion_timeout(&fspi->cp, msecs_to_jiffies(timeout_ms))) {
dev_err(fspi->dev, "DMA wait for tr transfer finish timeout\n");
ret = -ETIMEDOUT;
}
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ENR, FLEXBUS_TX_DIS | FLEXBUS_RX_DIS);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_ICR, FLEXBUS_RX_DONE_ISR);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_IMR, 0);
rockchip_flexbus_writel(fspi->fb, FLEXBUS_CSN_CFG, 0x10000);
return ret;
}
static int rk_flexbus_fspi_exec_mem(struct rk_flexbus_fspi *fspi, const struct spi_mem_op *op)
{
struct rk_flexbus_fspi_xfer cfg = { 0 };
u32 cmd_cycles, data_cycles;
int ret;
bool format_trick;
/* format cmd_addr_dummy */
switch (op->addr.nbytes) {
case 0:
fspi->tx_buf[3] = op->cmd.opcode;
fspi->tx_buf[2] = 0xFF;
cmd_cycles = 8 + op->dummy.nbytes * 8;
break;
case 3:
fspi->tx_buf[3] = op->cmd.opcode;
fspi->tx_buf[2] = op->addr.val >> 16 & 0xFF;
fspi->tx_buf[1] = op->addr.val >> 8 & 0xFF;
fspi->tx_buf[0] = op->addr.val >> 0 & 0xFF;
fspi->tx_buf[7] = 0xFF;
fspi->tx_buf[6] = 0xFF;
cmd_cycles = 32 + op->dummy.nbytes * 8;
break;
case 4:
fspi->tx_buf[3] = op->cmd.opcode;
fspi->tx_buf[2] = op->addr.val >> 24 & 0xFF;
fspi->tx_buf[1] = op->addr.val >> 16 & 0xFF;
fspi->tx_buf[0] = op->addr.val >> 8 & 0xFF;
if (op->dummy.nbytes)
fspi->tx_buf[7] = op->addr.val >> 0 & 0xFF;
else
fspi->tx_buf[4] = op->addr.val >> 0 & 0xFF;
fspi->tx_buf[6] = 0xFF;
cmd_cycles = 40 + op->dummy.nbytes * 8;
break;
default:
dev_err(fspi->fb->dev, "op->addr.nbytes %d not support!\n", op->addr.nbytes);
return -EINVAL;
}
data_cycles = op->data.nbytes * 8 / op->data.buswidth;
/*
* format data:
* V9:
* rx protocol 111 send_then_recv_114(trick!), then re-format data 4-to-1(trick!)
* rx protocol 114 send_then_recv_114, then re-order data 4-to-4(trick!)
* tx protocol 111 re-format data 1-to-4, then send(114 trick!)
* New version:
* rx protocol 111 send_then_recv_114(trick!), then re-format data 4-to-1(trick!)
* rx protocol 114 send_then_recv_114
* tx protocol 111 send(111)
*/
if ((fspi->version == FLEXBUS_REVISION_V9) ||
(op->data.dir == SPI_MEM_DATA_IN && op->data.buswidth == 1))
format_trick = true;
else
format_trick = false;
if (op->data.dir == SPI_MEM_DATA_OUT) {
if (format_trick) {
flexbus_fspi_data_format(fspi, op->data.buf.out, 1, fspi->temp_buf, 4,
data_cycles, fspi->switch_buf);
dma_sync_single_for_device(fspi->fb->dev, fspi->dma_temp_buf,
op->data.nbytes * 4, DMA_TO_DEVICE);
} else {
memcpy(fspi->temp_buf, op->data.buf.out, op->data.nbytes);
dma_sync_single_for_device(fspi->fb->dev, fspi->dma_temp_buf,
op->data.nbytes, DMA_TO_DEVICE);
}
}
cfg.cmd_cycles = cmd_cycles;
cfg.cmd_buf = fspi->tx_buf;
cfg.buf_cycles = data_cycles;
cfg.buf_addr = fspi->dma_temp_buf;
if (op->data.dir == SPI_MEM_DATA_IN)
ret = rk_flexbus_fspi_send_then_recv_114(fspi, &cfg, format_trick);
else
ret = rk_flexbus_fspi_send(fspi, &cfg);
if (ret) {
dev_err(fspi->fb->dev, "cmd=%x data=%d\n", op->cmd.opcode, op->data.nbytes);
print_hex_dump(KERN_WARNING, "regs:", DUMP_PREFIX_OFFSET, 4, 4, fspi->fb->base, 0x200, 0);
return ret;
}
if (op->data.dir == SPI_MEM_DATA_IN) {
if (op->data.buswidth == 4) {
dma_sync_single_for_cpu(fspi->fb->dev, fspi->dma_temp_buf,
op->data.nbytes, DMA_FROM_DEVICE);
if (format_trick)
flexbus_data_format_order(fspi, fspi->temp_buf,
op->data.buf.in, op->data.nbytes);
else
memcpy(op->data.buf.in, fspi->temp_buf, op->data.nbytes);
} else {
dma_sync_single_for_cpu(fspi->fb->dev, fspi->dma_temp_buf,
op->data.nbytes * 4, DMA_FROM_DEVICE);
flexbus_fspi_data_format(fspi, fspi->temp_buf, 4, op->data.buf.in, 1,
data_cycles, fspi->switch_buf);
}
}
dev_dbg(fspi->fb->dev, "cmd=%x addr=%llx nbytes=%x\n", op->cmd.opcode, op->addr.val, op->data.nbytes);
return ret;
}
static void rk_flexbus_fspi_set_cs_gpio(struct rk_flexbus_fspi *fspi, u8 cs, bool enable)
{
if (fspi->cs_gpiods) {
if (has_acpi_companion(fspi->dev))
gpiod_set_value_cansleep(fspi->cs_gpiods[cs], !enable);
else
/* Polarity handled by GPIO library */
gpiod_set_value_cansleep(fspi->cs_gpiods[cs], enable);
}
}
static int rk_flexbus_fspi_exec_op_bypass(struct rk_flexbus_fspi *fspi,
struct spi_mem *mem,
const struct spi_mem_op *op)
{
u8 cs = mem->spi->chip_select;
u32 ret;
rk_flexbus_fspi_set_cs_gpio(fspi, cs, true);
ret = rk_flexbus_fspi_exec_mem(fspi, op);
if (ret) {
dev_err(fspi->dev, "Failed to exec mem, ret=%d\n", ret);
return ret;
}
rk_flexbus_fspi_set_cs_gpio(fspi, cs, false);
return ret;
}
static void rk_flexbus_fspi_delay_lines_tuning(struct rk_flexbus_fspi *fspi, struct spi_mem *mem)
{
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(4, NULL, 1));
u8 id[4] = { 0 }, id_temp[4] = { 0 };
u16 cell_max = (u16)rk_flexbus_fspi_get_max_dll_cells(fspi);
u16 right, left = 0;
u16 step = FLEXBUS_DLL_TRANING_STEP;
bool dll_valid = false;
u8 cs = mem->spi->chip_select;
rk_flexbus_fspi_clk_set_rate(fspi, FLEXBUS_DLL_THRESHOLD_RATE);
op.data.buf.in = &id;
rk_flexbus_fspi_exec_op_bypass(fspi, mem, &op);
if ((0xFF == id[0] && 0xFF == id[1]) ||
(0x00 == id[0] && 0x00 == id[1])) {
dev_dbg(fspi->fb->dev, "no dev, dll by pass\n");
rk_flexbus_fspi_clk_set_rate(fspi, fspi->speed[cs]);
fspi->speed[cs] = FLEXBUS_DLL_THRESHOLD_RATE;
return;
}
rk_flexbus_fspi_clk_set_rate(fspi, fspi->speed[cs]);
op.data.buf.in = &id_temp;
for (right = 0; right <= cell_max; right += step) {
int ret;
rk_flexbus_fspi_set_delay_lines(fspi, right, cs);
rk_flexbus_fspi_exec_op_bypass(fspi, mem, &op);
dev_dbg(fspi->fb->dev, "dll read flash id:%x %x %x\n",
id_temp[0], id_temp[1], id_temp[2]);
ret = memcmp(&id, &id_temp, 3);
if (dll_valid && ret) {
right -= step;
break;
}
if (!dll_valid && !ret)
left = right;
if (!ret)
dll_valid = true;
/* Add cell_max to loop */
if (right == cell_max)
break;
if (right + step > cell_max)
right = cell_max - step;
}
if (dll_valid && (right - left) >= FLEXBUS_DLL_TRANING_VALID_WINDOW) {
if (left == 0 && right < cell_max)
fspi->dll_cells[cs] = left + (right - left) * 2 / 5;
else
fspi->dll_cells[cs] = left + (right - left) / 2;
} else {
fspi->dll_cells[cs] = 0;
}
if (fspi->dll_cells[cs]) {
dev_dbg(fspi->fb->dev, "%d %d %d dll training pass, %dMHz max_cells=%u ver=%d\n",
left, right, fspi->dll_cells[cs], fspi->speed[cs],
rk_flexbus_fspi_get_max_dll_cells(fspi), fspi->version);
rk_flexbus_fspi_set_delay_lines(fspi, (u16)fspi->dll_cells[cs], cs);
} else {
dev_err(fspi->fb->dev, "%d %d dll training failed, %dMHz, reduce the frequency\n",
left, right, fspi->speed[cs]);
rk_flexbus_fspi_set_delay_lines(fspi, 0, cs);
rk_flexbus_fspi_clk_set_rate(fspi, FLEXBUS_DLL_THRESHOLD_RATE);
mem->spi->max_speed_hz = FLEXBUS_DLL_THRESHOLD_RATE;
fspi->cur_speed = FLEXBUS_DLL_THRESHOLD_RATE;
fspi->cur_real_speed = rk_flexbus_fspi_clk_get_rate(fspi);
fspi->speed[cs] = FLEXBUS_DLL_THRESHOLD_RATE;
}
}
static int rk_flexbus_fspi_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
struct rk_flexbus_fspi *fspi = spi_controller_get_devdata(mem->spi->master);
int ret;
u8 cs = mem->spi->chip_select;
if (unlikely(mem->spi->max_speed_hz != fspi->speed[cs]) &&
!has_acpi_companion(fspi->dev)) {
ret = rk_flexbus_fspi_clk_set_rate(fspi, mem->spi->max_speed_hz);
if (ret)
goto out;
fspi->speed[cs] = mem->spi->max_speed_hz;
fspi->cur_speed = mem->spi->max_speed_hz;
fspi->cur_real_speed = rk_flexbus_fspi_clk_get_rate(fspi);
if (fspi->cur_real_speed > FLEXBUS_DLL_THRESHOLD_RATE && 1)
rk_flexbus_fspi_delay_lines_tuning(fspi, mem);
dev_dbg(fspi->dev, "set_freq=%dHz real_freq=%ldHz\n",
fspi->speed[cs], rk_flexbus_fspi_clk_get_rate(fspi));
}
rk_flexbus_fspi_set_cs_gpio(fspi, cs, true);
ret = rk_flexbus_fspi_exec_mem(fspi, op);
if (ret) {
dev_err(fspi->dev, "Failed to exec mem, ret=%d\n", ret);
return ret;
}
out:
rk_flexbus_fspi_set_cs_gpio(fspi, cs, false);
return ret;
}
static int rk_flexbus_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
struct rk_flexbus_fspi *fspi = spi_controller_get_devdata(mem->spi->master);
op->data.nbytes = min(op->data.nbytes, fspi->max_iosize);
return 0;
}
static bool rk_flexbus_fspi_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 ||
op->dummy.buswidth > 1 || op->data.buswidth > 4)
return false;
return spi_mem_default_supports_op(mem, op);
}
static const struct spi_controller_mem_ops rk_flexbus_fspi_mem_ops = {
.exec_op = rk_flexbus_fspi_exec_mem_op,
.adjust_op_size = rk_flexbus_fspi_adjust_op_size,
.supports_op = rk_flexbus_fspi_supports_op,
};
static void rk_flexbus_fspi_irq_handler(struct rockchip_flexbus *rkfb, u32 isr)
{
struct rk_flexbus_fspi *fspi = (struct rk_flexbus_fspi *)rkfb->fb0_data;
// dev_err(rkfb->dev, "isr=%x\n", isr);
rockchip_flexbus_writel(rkfb, FLEXBUS_ICR, isr);
if (isr & FLEXBUS_FSPI_ERR_ISR) {
if (isr & FLEXBUS_DMA_TIMEOUT_ISR) {
dev_err_ratelimited(rkfb->dev, "dma timeout!\n");
rockchip_flexbus_writel(rkfb, FLEXBUS_ICR, FLEXBUS_DMA_TIMEOUT_ISR);
}
if (isr & FLEXBUS_DMA_ERR_ISR) {
dev_err_ratelimited(rkfb->dev, "dma err!\n");
rockchip_flexbus_writel(rkfb, FLEXBUS_ICR, FLEXBUS_DMA_ERR_ISR);
}
if (isr & FLEXBUS_TX_UDF_ISR) {
dev_err_ratelimited(rkfb->dev, "tx underflow!\n");
rockchip_flexbus_writel(rkfb, FLEXBUS_ICR, FLEXBUS_TX_UDF_ISR);
}
if (isr & FLEXBUS_TX_OVF_ISR) {
dev_err_ratelimited(rkfb->dev, "tx overflow!\n");
rockchip_flexbus_writel(rkfb, FLEXBUS_ICR, FLEXBUS_TX_OVF_ISR);
}
}
if ((isr & FLEXBUS_TX_DONE_ISR) || (isr & FLEXBUS_RX_DONE_ISR))
complete(&fspi->cp);
}
static int rk_flexbus_fspi_get_gpio_descs(struct spi_controller *ctlr, struct rk_flexbus_fspi *fspi)
{
int nb, i;
struct gpio_desc **cs;
struct device *dev = &ctlr->dev;
unsigned int num_cs_gpios = 0;
nb = gpiod_count(dev, "fspi-cs");
ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);
if (nb == 0 || nb == -ENOENT)
return 0;
else if (nb < 0)
return nb;
cs = devm_kcalloc(dev, ctlr->num_chipselect, sizeof(*cs),
GFP_KERNEL);
if (!cs)
return -ENOMEM;
fspi->cs_gpiods = cs;
for (i = 0; i < nb; i++) {
cs[i] = devm_gpiod_get_index_optional(dev, "fspi-cs", i,
GPIOD_OUT_LOW);
if (IS_ERR(cs[i]))
return PTR_ERR(cs[i]);
if (cs[i]) {
/*
* If we find a CS GPIO, name it after the device and
* chip select line.
*/
char *gpioname;
gpioname = devm_kasprintf(dev, GFP_KERNEL, "%s CS%d",
dev_name(dev), i);
if (!gpioname)
return -ENOMEM;
gpiod_set_consumer_name(cs[i], gpioname);
num_cs_gpios++;
continue;
}
}
return 0;
}
static int rk_flexbus_fspi_probe(struct platform_device *pdev)
{
struct rockchip_flexbus *flexbus_dev = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct spi_controller *master;
struct rk_flexbus_fspi *fspi;
int ret;
u32 i, val;
if (flexbus_dev->opmode0 != ROCKCHIP_FLEXBUS0_OPMODE_SPI ||
flexbus_dev->opmode1 != ROCKCHIP_FLEXBUS1_OPMODE_NULL) {
dev_err(&pdev->dev, "flexbus opmode mismatch!, fb0=%d fb1=%d\n",
flexbus_dev->opmode0, flexbus_dev->opmode1);
return -ENODEV;
}
master = devm_spi_alloc_master(&pdev->dev, sizeof(*fspi));
if (!master)
return -ENOMEM;
master->flags = SPI_MASTER_HALF_DUPLEX;
master->mem_ops = &rk_flexbus_fspi_mem_ops;
master->dev.of_node = pdev->dev.of_node;
master->max_speed_hz = FLEXBUS_MAX_SPEED;
master->num_chipselect = FLEXBUS_MAX_CHIPSELECT_NUM;
master->mode_bits = SPI_RX_QUAD;
fspi = spi_controller_get_devdata(master);
fspi->dev = dev;
fspi->master = master;
fspi->fb = flexbus_dev;
rockchip_flexbus_set_fb0(flexbus_dev, fspi, rk_flexbus_fspi_irq_handler);
if (has_acpi_companion(&pdev->dev)) {
ret = device_property_read_u32(&pdev->dev, "clock-frequency", &val);
if (ret) {
dev_err(&pdev->dev, "Failed to find clock-frequency in ACPI\n");
return ret;
}
for (i = 0; i < FLEXBUS_MAX_CHIPSELECT_NUM; i++)
fspi->speed[i] = val;
}
ret = rk_flexbus_fspi_get_gpio_descs(master, fspi);
if (ret) {
dev_err(&pdev->dev, "Failed to get gpio_descs\n");
return ret;
}
platform_set_drvdata(pdev, fspi);
rk_flexbus_fspi_init(fspi);
fspi->tx_buf = devm_kmalloc(dev, FLEXBUS_QSPI_CMD_MAX, GFP_KERNEL);
fspi->switch_buf = devm_kmalloc(dev, fspi->max_iosize * FLEXBUS_TX_WIDTH_4, GFP_KERNEL);
fspi->temp_buf = (u8 *)devm_get_free_pages(dev, GFP_KERNEL | GFP_DMA32,
get_order(fspi->max_iosize *
FLEXBUS_TX_WIDTH_4));
if (!fspi->temp_buf)
return -ENOMEM;
fspi->dma_temp_buf = virt_to_phys(fspi->temp_buf);
ret = devm_spi_register_controller(dev, master);
if (ret)
return -ENOMEM;
return 0;
}
static int __maybe_unused rk_flexbus_fspi_resume(struct device *dev)
{
struct rk_flexbus_fspi *fspi = dev_get_drvdata(dev);
int i;
rk_flexbus_fspi_init(fspi);
for (i = 0; i < FLEXBUS_MAX_CHIPSELECT_NUM; i++) {
if (fspi->dll_cells[i])
rk_flexbus_fspi_set_delay_lines(fspi, (u16)fspi->dll_cells[i], i);
}
return 0;
}
static const struct dev_pm_ops rk_flexbus_fspi_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, rk_flexbus_fspi_resume)
};
static const struct of_device_id rk_flexbus_fspi_dt_ids[] = {
{ .compatible = "rockchip,flexbus-fspi"},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rk_flexbus_fspi_dt_ids);
static struct platform_driver rk_flexbus_fspi_driver = {
.driver = {
.name = "rockchip-flexbus-fspi",
.of_match_table = rk_flexbus_fspi_dt_ids,
.pm = &rk_flexbus_fspi_pm_ops,
},
.probe = rk_flexbus_fspi_probe,
};
module_platform_driver(rk_flexbus_fspi_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Rockchip Flexbus Controller Under SPI Transmission Protocol Driver");
MODULE_AUTHOR("Jon Lin <Jon.lin@rock-chips.com>");