Files

845 lines
24 KiB
C

// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2025 Amlogic, Inc. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/reset.h>
#include <linux/regmap.h>
#ifdef CONFIG_SPICC_TEST
#include <linux/amlogic/aml_spi.h>
#include "spicc_test.h"
#endif
/* Register Map */
#define SPISG_REG_CFG_READY 0x00
#define CFG_SPI_READY BIT(0)
#define CFG_TEE_OPEN BIT(1)
#define CFG_JOIN_RX BIT(2)
#define CFG_JOIN_TX BIT(3)
#define CFG_THRESH_RX GENMASK(12, 4)
#define CFG_THRESH_TX GENMASK(21, 13)
#define CFG_DMA BIT(22)
#define SPISG_REG_CFG_SPI 0x04
#define CFG_BUS64_EN BIT(0)
#define CFG_SLAVE_EN BIT(1)
#define CFG_SLAVE_SELECT GENMASK(3, 2)
#define CFG_SFLASH_WP BIT(4)
#define CFG_SFLASH_HD BIT(5)
/* start on vsync rising */
#define CFG_HW_POS BIT(6)
/* start on vsync falling */
#define CFG_HW_NEG BIT(7)
#define CFG_WORD_GAP GENMASK(13, 8)
#define CFG_MO_IDLE_OUTPUT GENMASK(15, 14)
/* cs hold time in sclk */
#define CFG_CS_HOLD GENMASK(23, 16)
#define CFG_CS_SETUP GENMASK(31, 24)
#define SPISG_REG_CFG_START 0x08
#define CFG_BLOCK_NUM GENMASK(22, 0)
#define CFG_DATA_COMMAND BIT(23)
#define CFG_OP_MODE GENMASK(25, 24)
#define CFG_RXD_MODE GENMASK(27, 26)
#define CFG_TXD_MODE GENMASK(29, 28)
#define CFG_EOC BIT(30)
#define CFG_PEND BIT(31)
#define SPISG_REG_CFG_BUS 0x0C
#define CFG_CLK_DIV GENMASK(11, 0)
#define CLK_DIV_WIDTH 12
#define CFG_RX_TUNING GENMASK(15, 12)
#define CFG_TX_TUNING GENMASK(19, 16)
#define CFG_LANE GENMASK(21, 20)
#define CFG_HALF_DUPLEX BIT(22)
#define CFG_DC_MODE BIT(23)
#define CFG_NULL_CTL BIT(24)
#define CFG_DUMMY_CTL BIT(25)
#define CFG_READ_TURN GENMASK(28, 26)
#define CFG_KEEP_SS BIT(29)
#define CFG_CPHA BIT(30)
#define CFG_CPOL BIT(31)
#define SPISG_REG_TX_RD_ADDR 0x10
#define SPISG_REG_TX_WR_ADDR 0x14
#define SPISG_REG_RX_RD_ADDR 0x18
#define SPISG_REG_RX_WR_ADDR 0x1C
#define SPISG_REG_TX_FIFO 0x20
#define TX_FIFO_DATA GENMASK(7, 0)
#define TX_FIFO_FULL BIT(8)
#define TX_FIFO_EMPTY BIT(9)
#define TX_FIFO_APB_ERR BIT(10)
#define TX_FIFO_COUNT GENMASK(19, 11)
#define SPISG_REG_RX_FIFO 0x24
#define RX_FIFO_DATA GENMASK(7, 0)
#define RX_FIFO_FULL BIT(8)
#define RX_FIFO_EMPTY BIT(9)
#define RX_FIFO_APB_ERR BIT(10)
#define RX_FIFO_COUNT GENMASK(19, 11)
#define SPISG_REG_SHAKE 0x38
#define SPISG_REG_IRQ_STS 0x30
#define SPISG_REG_IRQ_ENABLE 0x34
#define IRQ_SPI_READY BIT(0)
#define IRQ_APB_RX_EMPTY BIT(1)
#define IRQ_APB_RX_FULL BIT(2)
#define IRQ_APB_TX_EMPTY BIT(3)
#define IRQ_APB_TX_FULL BIT(4)
#define IRQ_RX_FIFO_IRQ BIT(5)
#define IRQ_TX_FIFO_IRQ BIT(6)
#define IRQ_PHY_DONE BIT(7)
#define IRQ_TASK_DONE BIT(8)
#define IRQ_ALL_DONE BIT(9)
#define SPISG_MAX_REG 0x40
#define SPISG_BLOCK_MAX 0x800000
#define SPISG_FIFO_SIZE 128
#define SPISG_OP_MODE_WRITE_CMD 0
#define SPISG_OP_MODE_READ_STS 1
#define SPISG_OP_MODE_WRITE 2
#define SPISG_OP_MODE_READ 3
#define SPISG_DATA_MODE_NONE 0
#define SPISG_DATA_MODE_PIO 1
#define SPISG_DATA_MODE_MEM 2
#define SPISG_DATA_MODE_SG 3
#define SPISG_CLK_DIV_MAX 4096
/* recommended by specification */
#define SPISG_CLK_DIV_MIN 5
#define DIV_NUM (SPISG_CLK_DIV_MAX - SPISG_CLK_DIV_MIN + 1)
#define SPISG_PCLK_RATE_MIN 24000000
#define SPISG_SINGLE_SPI 0
#define SPISG_DUAL_SPI 1
#define SPISG_QUAD_SPI 2
struct spisg_device {
struct spi_controller *controller;
struct platform_device *pdev;
struct regmap *map;
struct clk *pclk;
struct clk *sclk;
struct clk_div_table *tbl;
u32 status;
u32 speed_hz;
u32 effective_speed_hz;
u32 cfg_spi;
u32 cfg_start;
u32 cfg_bus;
struct spi_device *spi;
struct spi_message *msg;
u8 *tx_buf;
u8 *rx_buf;
int tx_remain;
int rx_remain;
bool keep_ss;
int cs_hold_in_sclk;
int fifo_size;
};
#ifdef CONFIG_SPICC_TEST
static unsigned int aml_spisg_flags;
module_param(aml_spisg_flags, uint, 0644);
MODULE_PARM_DESC(aml_spisg_flags, "aml_spisg_flags");
#define dump_en() (aml_spisg_flags & BIT(0))
#endif
#ifdef CONFIG_SPICC_TEST
static void aml_spisg_dump(struct spisg_device *spisg)
{
unsigned char *op_mode_name[] = {"write_cmd", "read_sts", "write", "read"};
unsigned char *data_mode_name[] = {"none", "pio", "mem", "sg"};
u32 val;
if (!dump_en())
return;
regmap_read(spisg->map, SPISG_REG_CFG_READY, &val);
pr_info("cfg_ready = 0x%08x\n", val);
pr_info("\tspi_ready[0] %d\n", (u32)FIELD_GET(CFG_SPI_READY, val));
pr_info("\ttee_open[1] %d\n", (u32)FIELD_GET(CFG_TEE_OPEN, val));
pr_info("\tjoin_rx[2] %d\n", (u32)FIELD_GET(CFG_JOIN_RX, val));
pr_info("\tjoin_tx[3] %d\n", (u32)FIELD_GET(CFG_JOIN_TX, val));
pr_info("\tthres_rx[12:4] %d\n", (u32)FIELD_GET(CFG_THRESH_RX, val));
pr_info("\tthres_tx[21:13] %d\n", (u32)FIELD_GET(CFG_THRESH_TX, val));
pr_info("\tdma[22] %d\n", (u32)FIELD_GET(CFG_DMA, val));
val = spisg->cfg_spi;
pr_info("cfg_spi = 0x%08x\n", val);
pr_info("\tbus64_en[0] %d\n", (u32)FIELD_GET(CFG_BUS64_EN, val));
pr_info("\tslave_en[1] %d\n", (u32)FIELD_GET(CFG_SLAVE_EN, val));
pr_info("\tss[3:2] %d\n", (u32)FIELD_GET(CFG_SLAVE_SELECT, val));
pr_info("\tflash_wp_pin_en[4] %d\n", (u32)FIELD_GET(CFG_SFLASH_WP, val));
pr_info("\tflash_hold_pin_en[5] %d\n", (u32)FIELD_GET(CFG_SFLASH_HD, val));
pr_info("\thw_pos[6] %d\n", (u32)FIELD_GET(CFG_HW_POS, val));
pr_info("\thw_neg[7] %d\n", (u32)FIELD_GET(CFG_HW_NEG, val));
pr_info("\tword_gap[13:8] %d\n", (u32)FIELD_GET(CFG_WORD_GAP, val));
pr_info("\tmoso_idle_level[15:14] %d\n", (u32)FIELD_GET(CFG_MO_IDLE_OUTPUT, val));
pr_info("\tcs_hold[23:16] %d\n", (u32)FIELD_GET(CFG_CS_HOLD, val));
pr_info("\tcs_setup_extend[31:24] %d\n", (u32)FIELD_GET(CFG_CS_SETUP, val));
val = spisg->cfg_start;
pr_info("cfg_start = 0x%08x\n", val);
pr_info("\tblock_num[22:0] %d\n", (u32)FIELD_GET(CFG_BLOCK_NUM, val));
pr_info("\tdc_level[23] %d\n", (u32)FIELD_GET(CFG_DATA_COMMAND, val));
pr_info("\top_mode[25:24] %s\n", op_mode_name[FIELD_GET(CFG_OP_MODE, val)]);
pr_info("\trx_data_mode[27:26] %s\n", data_mode_name[FIELD_GET(CFG_RXD_MODE, val)]);
pr_info("\ttx_data_mode[29:28] %s\n", data_mode_name[FIELD_GET(CFG_TXD_MODE, val)]);
pr_info("\teoc[30] %d\n", (u32)FIELD_GET(CFG_EOC, val));
pr_info("\tpending[31] %d\n", (u32)FIELD_GET(CFG_PEND, val));
val = spisg->cfg_bus;
pr_info("cfg_bus = 0x%08x\n", val);
pr_info("\tclk_div[11:0] %d\n", (u32)FIELD_GET(CFG_CLK_DIV, val));
pr_info("\trx_tuning[15:12] %d\n", (u32)FIELD_GET(CFG_RX_TUNING, val));
pr_info("\ttx_tuning[19:16] %d\n", (u32)FIELD_GET(CFG_TX_TUNING, val));
pr_info("\tlane[21:20] %d\n", (u32)FIELD_GET(CFG_LANE, val));
pr_info("\thalf_duplex_en[22] %d\n", (u32)FIELD_GET(CFG_HALF_DUPLEX, val));
pr_info("\tdc_mode[23] %d\n", (u32)FIELD_GET(CFG_DC_MODE, val));
pr_info("\tnull_ctl[24] %d\n", (u32)FIELD_GET(CFG_NULL_CTL, val));
pr_info("\tdummy_ctl[25] %d\n", (u32)FIELD_GET(CFG_DUMMY_CTL, val));
pr_info("\tread_turn_around[28:26] %d\n", (u32)FIELD_GET(CFG_READ_TURN, val));
pr_info("\tkeep_ss[29] %d\n", (u32)FIELD_GET(CFG_KEEP_SS, val));
pr_info("\tcpha[30] %d\n", (u32)FIELD_GET(CFG_CPHA, val));
pr_info("\tcpol[31] %d\n", (u32)FIELD_GET(CFG_CPOL, val));
}
#endif
static int spi_delay_to_sclk(u32 slck_speed_hz, struct spi_delay *delay)
{
s32 ns;
if (!delay)
return 0;
if (delay->unit == SPI_DELAY_UNIT_SCK)
return delay->value;
ns = spi_delay_to_ns(delay, NULL);
if (ns < 0)
return 0;
return DIV_ROUND_UP_ULL((u64)slck_speed_hz * ns, NSEC_PER_SEC);
}
static inline u32 aml_spisg_sem_down_read(struct spisg_device *spisg)
{
u32 ret;
regmap_read(spisg->map, SPISG_REG_CFG_READY, &ret);
if (ret & CFG_SPI_READY)
regmap_write(spisg->map, SPISG_REG_CFG_READY, ret & ~CFG_SPI_READY);
return (ret & CFG_SPI_READY) || spisg->keep_ss;
}
static inline void aml_spisg_sem_up_write(struct spisg_device *spisg)
{
regmap_update_bits(spisg->map, SPISG_REG_CFG_READY, CFG_SPI_READY, CFG_SPI_READY);
}
static int aml_spisg_set_speed(struct spisg_device *spisg, uint speed_hz)
{
u32 cfg_bus;
if (!speed_hz || speed_hz == spisg->speed_hz)
return 0;
spisg->speed_hz = speed_hz;
clk_set_rate(spisg->sclk, speed_hz);
/* Store the div for the descriptor mode */
regmap_read(spisg->map, SPISG_REG_CFG_BUS, &cfg_bus);
spisg->cfg_bus &= ~CFG_CLK_DIV;
spisg->cfg_bus |= cfg_bus & CFG_CLK_DIV;
spisg->effective_speed_hz = clk_get_rate(spisg->sclk);
dev_dbg(&spisg->pdev->dev,
"desired speed %dHz, effective speed %dHz\n",
speed_hz, spisg->effective_speed_hz);
return 0;
}
static bool aml_spisg_can_dma(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer)
{
return false;
}
static int nbits_to_lane[] = {
SPISG_SINGLE_SPI,
SPISG_SINGLE_SPI,
SPISG_DUAL_SPI,
-EINVAL,
SPISG_QUAD_SPI
};
static int aml_spisg_setup_transfer(struct spisg_device *spisg,
struct spi_transfer *xfer)
{
#ifdef CONFIG_SPICC_TEST
struct spicc_controller_data *cdata = spisg->spi->controller_data;
#endif
u32 val;
/* Setup transfer parameters */
spisg->tx_buf = (u8 *)xfer->tx_buf;
spisg->rx_buf = (u8 *)xfer->rx_buf;
spisg->tx_remain = xfer->len;
spisg->rx_remain = xfer->len;
regmap_read(spisg->map, SPISG_REG_CFG_READY, &val);
val &= ~(CFG_JOIN_TX | CFG_JOIN_RX);
spisg->fifo_size = SPISG_FIFO_SIZE;
if (xfer->len > SPISG_FIFO_SIZE) {
if (!xfer->tx_buf) {
val |= CFG_JOIN_RX;
spisg->fifo_size += SPISG_FIFO_SIZE;
}
if (!xfer->rx_buf) {
val |= CFG_JOIN_TX;
spisg->fifo_size += SPISG_FIFO_SIZE;
}
}
regmap_write(spisg->map, SPISG_REG_CFG_READY, val);
regmap_write(spisg->map, SPISG_REG_TX_RD_ADDR, 0);
regmap_write(spisg->map, SPISG_REG_TX_WR_ADDR, 0);
regmap_write(spisg->map, SPISG_REG_RX_RD_ADDR, 0);
regmap_write(spisg->map, SPISG_REG_RX_WR_ADDR, 0);
aml_spisg_set_speed(spisg, xfer->speed_hz);
xfer->effective_speed_hz = spisg->effective_speed_hz;
/* Setup cs_setup for the first burst of the first xfer */
spisg->cfg_spi &= ~CFG_CS_SETUP;
if (list_is_first(&xfer->transfer_list, &spisg->msg->transfers))
spisg->cfg_spi |= FIELD_PREP(CFG_CS_SETUP, min_t(int, FIELD_MAX(CFG_CS_SETUP),
spi_delay_to_sclk(xfer->effective_speed_hz, &spisg->spi->cs_setup)));
/* Keep ss except for the last burst */
spisg->cfg_bus |= CFG_KEEP_SS;
/* No cs_hold except for the last burst */
spisg->cfg_spi &= ~CFG_CS_HOLD;
/* Save the keep_ss and cs_hold for the last burst */
if (list_is_last(&xfer->transfer_list, &spisg->msg->transfers)) {
spisg->keep_ss = xfer->cs_change;
spisg->cs_hold_in_sclk = spi_delay_to_sclk(xfer->effective_speed_hz,
&spisg->spi->cs_hold);
} else {
spisg->keep_ss = !xfer->cs_change;
spisg->cs_hold_in_sclk = 0;
}
spisg->cfg_bus &= ~(CFG_LANE | CFG_DC_MODE);
spisg->cfg_start &= ~(CFG_OP_MODE | CFG_TXD_MODE | CFG_RXD_MODE | CFG_DATA_COMMAND);
if (xfer->tx_buf) {
spisg->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->tx_nbits]);
spisg->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE);
spisg->cfg_start |= FIELD_PREP(CFG_TXD_MODE, SPISG_DATA_MODE_PIO);
}
if (xfer->rx_buf) {
spisg->cfg_bus |= FIELD_PREP(CFG_LANE, nbits_to_lane[xfer->rx_nbits]);
spisg->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_READ);
spisg->cfg_start |= FIELD_PREP(CFG_RXD_MODE, SPISG_DATA_MODE_PIO);
}
#ifdef CONFIG_SPICC_TEST
if (cdata->dc_mode) {
spisg->cfg_bus |= FIELD_PREP(CFG_DC_MODE, cdata->dc_mode - 1);
spisg->cfg_start |= FIELD_PREP(CFG_DATA_COMMAND, cdata->dc_level);
spisg->cfg_start &= ~CFG_OP_MODE;
if (xfer->tx_buf)
spisg->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_WRITE_CMD);
if (xfer->rx_buf)
spisg->cfg_start |= FIELD_PREP(CFG_OP_MODE, SPISG_OP_MODE_READ_STS);
}
#endif
return 0;
}
static int aml_spisg_fifo_tx(struct spisg_device *spisg)
{
u32 fifo;
int tx_cnt;
if (!spisg->tx_buf) {
tx_cnt = min_t(int, spisg->tx_remain, spisg->fifo_size);
spisg->tx_remain -= tx_cnt;
} else {
tx_cnt = 0;
while (spisg->tx_remain) {
regmap_read(spisg->map, SPISG_REG_TX_FIFO, &fifo);
if (fifo & TX_FIFO_FULL)
break;
fifo &= ~TX_FIFO_DATA;
fifo |= *spisg->tx_buf++;
regmap_write(spisg->map, SPISG_REG_TX_FIFO, fifo);
spisg->tx_remain--;
tx_cnt++;
}
}
spisg->cfg_start &= ~CFG_BLOCK_NUM;
spisg->cfg_start |= FIELD_PREP(CFG_BLOCK_NUM, tx_cnt);
return tx_cnt;
}
static int aml_spisg_fifo_rx(struct spisg_device *spisg)
{
u32 fifo;
int rx_cnt;
if (!spisg->rx_buf) {
rx_cnt = min_t(int, spisg->rx_remain, spisg->fifo_size);
spisg->rx_remain -= rx_cnt;
} else {
rx_cnt = 0;
while (spisg->rx_remain) {
regmap_read(spisg->map, SPISG_REG_RX_FIFO, &fifo);
if (fifo & RX_FIFO_EMPTY)
break;
*spisg->rx_buf++ = FIELD_GET(RX_FIFO_DATA, fifo);
spisg->rx_remain--;
rx_cnt++;
}
}
return rx_cnt;
}
static void aml_spisg_pending(struct spisg_device *spisg,
bool irq_en)
{
/* Setup keep_ss and cs_hold for the last burst */
if (!spisg->tx_remain) {
if (spisg->keep_ss)
spisg->cfg_bus |= CFG_KEEP_SS;
else
spisg->cfg_bus &= ~CFG_KEEP_SS;
spisg->cfg_spi &= ~CFG_CS_HOLD;
spisg->cfg_spi |= FIELD_PREP(CFG_CS_HOLD, min_t(int,
FIELD_MAX(CFG_CS_HOLD), spisg->cs_hold_in_sclk));
}
#ifdef CONFIG_SPICC_TEST
aml_spisg_dump(spisg);
#endif
regmap_write(spisg->map, SPISG_REG_CFG_SPI, spisg->cfg_spi);
regmap_write(spisg->map, SPISG_REG_CFG_BUS, spisg->cfg_bus);
regmap_write(spisg->map, SPISG_REG_IRQ_ENABLE, irq_en ? IRQ_PHY_DONE : 0);
regmap_write(spisg->map, SPISG_REG_CFG_START, spisg->cfg_start | CFG_PEND);
}
static irqreturn_t aml_spisg_irq(int irq, void *data)
{
struct spisg_device *spisg = (void *)data;
u32 sts;
regmap_read(spisg->map, SPISG_REG_IRQ_STS, &sts);
regmap_write(spisg->map, SPISG_REG_IRQ_STS, sts);
if (!(sts & IRQ_PHY_DONE))
return IRQ_NONE;
aml_spisg_fifo_rx(spisg);
if (spisg->tx_remain) {
/* clear cs_setup for the next burst */
spisg->cfg_spi &= ~CFG_CS_SETUP;
aml_spisg_fifo_tx(spisg);
aml_spisg_pending(spisg, true);
} else if (!spisg->rx_remain) {
if (!spisg->keep_ss)
aml_spisg_sem_up_write(spisg);
spi_finalize_current_transfer(spisg->controller);
}
return IRQ_HANDLED;
}
static int aml_spisg_transfer_one(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer)
{
struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
struct device *dev = &spisg->pdev->dev;
if (!aml_spisg_sem_down_read(spisg)) {
dev_err(dev, "controller busy\n");
return -EBUSY;
}
spisg->spi = spi;
aml_spisg_setup_transfer(spisg, xfer);
aml_spisg_fifo_tx(spisg);
aml_spisg_pending(spisg, true);
return 1;
}
static int aml_spisg_prepare_message(struct spi_controller *ctlr,
struct spi_message *message)
{
struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
struct spi_device *spi = message->spi;
#ifdef CONFIG_SPICC_TEST
struct spicc_controller_data *cdata = spi->controller_data;
#endif
if (spi->bits_per_word != 8) {
dev_err(&spisg->pdev->dev, "invalid wordlen %d\n", spi->bits_per_word);
return -EINVAL;
}
spisg->msg = message;
spisg->cfg_spi &= ~CFG_SLAVE_SELECT;
spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_SELECT, spi_get_chipselect(spi, 0));
spisg->cfg_bus &= ~(CFG_CPOL | CFG_CPHA | CFG_HALF_DUPLEX);
spisg->cfg_bus |= FIELD_PREP(CFG_CPOL, !!(spi->mode & SPI_CPOL)) |
FIELD_PREP(CFG_CPHA, !!(spi->mode & SPI_CPHA)) |
FIELD_PREP(CFG_HALF_DUPLEX, !!(spi->mode & SPI_3WIRE));
#ifdef CONFIG_SPICC_TEST
if (cdata && cdata->timing_en) {
spisg->cfg_bus &= ~(CFG_TX_TUNING | CFG_RX_TUNING | CFG_DUMMY_CTL | CFG_READ_TURN);
spisg->cfg_bus |= FIELD_PREP(CFG_TX_TUNING, cdata->tx_tuning);
spisg->cfg_bus |= FIELD_PREP(CFG_RX_TUNING, cdata->rx_tuning);
spisg->cfg_bus |= FIELD_PREP(CFG_DUMMY_CTL, cdata->dummy_ctl);
spisg->cfg_bus |= FIELD_PREP(CFG_READ_TURN, cdata->read_turn_around);
spisg->cfg_spi &= ~(CFG_WORD_GAP | CFG_MO_IDLE_OUTPUT);
spisg->cfg_spi |= FIELD_PREP(CFG_WORD_GAP, cdata->word_gap);
spisg->cfg_spi |= FIELD_PREP(CFG_MO_IDLE_OUTPUT, cdata->mosi_idle_level);
}
#endif
return 0;
}
static int aml_spisg_setup(struct spi_device *spi)
{
if (!spi->controller_state)
spi->controller_state = spi_controller_get_devdata(spi->controller);
return 0;
}
static void aml_spisg_cleanup(struct spi_device *spi)
{
spi->controller_state = NULL;
}
static int aml_spisg_target_abort(struct spi_controller *ctlr)
{
struct spisg_device *spisg = spi_controller_get_devdata(ctlr);
regmap_write(spisg->map, SPISG_REG_IRQ_ENABLE, 0);
regmap_write(spisg->map, SPISG_REG_CFG_START, spisg->cfg_start & ~CFG_PEND);
aml_spisg_sem_up_write(spisg);
spisg->keep_ss = false;
return 0;
}
static void aml_spisg_handle_err(struct spi_controller *ctlr,
struct spi_message *message)
{
aml_spisg_target_abort(ctlr);
}
static int aml_spisg_clk_init(struct spisg_device *spisg, void __iomem *base)
{
struct device *dev = &spisg->pdev->dev;
struct clk_init_data init;
struct clk_divider *div;
struct clk_div_table *tbl;
char name[32];
int ret, i;
spisg->pclk = devm_clk_get_enabled(dev, "pclk");
if (IS_ERR(spisg->pclk)) {
dev_err(dev, "pclk clock request failed\n");
return PTR_ERR(spisg->pclk);
}
clk_set_min_rate(spisg->pclk, SPISG_PCLK_RATE_MIN);
clk_disable_unprepare(spisg->pclk);
tbl = devm_kcalloc(dev, (DIV_NUM + 1), sizeof(*tbl), GFP_KERNEL);
if (!tbl)
return -ENOMEM;
for (i = 0; i < DIV_NUM; i++) {
tbl[i].val = i + SPISG_CLK_DIV_MIN - 1;
tbl[i].div = i + SPISG_CLK_DIV_MIN;
}
spisg->tbl = tbl;
div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
if (!div)
return -ENOMEM;
div->flags = CLK_DIVIDER_ROUND_CLOSEST;
div->reg = base + SPISG_REG_CFG_BUS;
div->shift = __bf_shf(CFG_CLK_DIV);
div->width = CLK_DIV_WIDTH;
div->table = tbl;
/* Register value should not be outside of the table */
regmap_update_bits(spisg->map, SPISG_REG_CFG_BUS, CFG_CLK_DIV,
FIELD_PREP(CFG_CLK_DIV, SPISG_CLK_DIV_MIN - 1));
/* Register clk-divider */
snprintf(name, sizeof(name), "%s_div", dev_name(dev));
init.name = name;
init.ops = &clk_divider_ops;
init.flags = 0;
init.parent_data = &(const struct clk_parent_data) {
.fw_name = "pclk",
};
init.num_parents = 1;
div->hw.init = &init;
ret = devm_clk_hw_register(dev, &div->hw);
if (ret) {
dev_err(dev, "clock registration failed\n");
return ret;
}
spisg->sclk = devm_clk_hw_get_clk(dev, &div->hw, NULL);
if (IS_ERR(spisg->sclk)) {
dev_err(dev, "get clock failed\n");
return PTR_ERR(spisg->sclk);
}
clk_prepare_enable(spisg->sclk);
return 0;
}
#ifdef CONFIG_SPICC_TEST
static DEVICE_ATTR_WO(test);
static DEVICE_ATTR_RW(testdev);
#endif
static int aml_spisg_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr;
struct spisg_device *spisg;
struct device *dev = &pdev->dev;
void __iomem *base;
u8 val;
int ret, irq;
const struct regmap_config aml_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = SPISG_MAX_REG,
};
if (of_property_read_bool(dev->of_node, "spi-slave"))
ctlr = devm_spi_alloc_target(dev, sizeof(*spisg));
else
ctlr = devm_spi_alloc_host(dev, sizeof(*spisg));
if (!ctlr)
return -ENOMEM;
spisg = spi_controller_get_devdata(ctlr);
spisg->controller = ctlr;
spisg->pdev = pdev;
platform_set_drvdata(pdev, spisg);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n");
spisg->map = devm_regmap_init_mmio(dev, base, &aml_regmap_config);
if (IS_ERR(spisg->map))
return dev_err_probe(dev, PTR_ERR(spisg->map), "regmap init failed\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = device_reset_optional(dev);
if (ret)
return dev_err_probe(dev, ret, "reset dev failed\n");
ret = aml_spisg_clk_init(spisg, base);
if (ret)
return dev_err_probe(dev, ret, "clock init failed\n");
spisg->cfg_spi = 0;
spisg->cfg_start = 0;
spisg->cfg_bus = 0;
spisg->cfg_spi = FIELD_PREP(CFG_SFLASH_WP, 1) |
FIELD_PREP(CFG_SFLASH_HD, 1);
if (spi_controller_is_target(ctlr)) {
spisg->cfg_spi |= FIELD_PREP(CFG_SLAVE_EN, 1);
spisg->cfg_bus = FIELD_PREP(CFG_TX_TUNING, 0xf);
}
/* default pending */
spisg->cfg_start = FIELD_PREP(CFG_PEND, 1);
pm_runtime_set_active(&spisg->pdev->dev);
pm_runtime_enable(&spisg->pdev->dev);
pm_runtime_resume_and_get(&spisg->pdev->dev);
if (!of_property_read_u8(dev->of_node, "tx_tuning", &val)) {
spisg->cfg_bus &= ~CFG_TX_TUNING;
spisg->cfg_bus |= FIELD_PREP(CFG_TX_TUNING, val);
}
if (!of_property_read_u8(dev->of_node, "rx_tuning", &val))
spisg->cfg_bus |= FIELD_PREP(CFG_RX_TUNING, val);
if (!of_property_read_u8(dev->of_node, "dummy_ctl", &val))
spisg->cfg_bus |= FIELD_PREP(CFG_DUMMY_CTL, val);
if (!of_property_read_u8(dev->of_node, "read_turn", &val))
spisg->cfg_bus |= FIELD_PREP(CFG_READ_TURN, val);
val = 1;
of_property_read_u8(dev->of_node, "word_gap", &val);
spisg->cfg_spi |= FIELD_PREP(CFG_WORD_GAP, val);
if (!of_property_read_u8(dev->of_node, "mo_idle_output", &val))
spisg->cfg_spi |= FIELD_PREP(CFG_MO_IDLE_OUTPUT, val);
ctlr->dev.of_node = pdev->dev.of_node;
ctlr->num_chipselect = 4;
ctlr->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST |
SPI_3WIRE | SPI_TX_QUAD | SPI_RX_QUAD;
ctlr->max_speed_hz = 1000 * 1000 * 100;
ctlr->min_speed_hz = 1000 * 10;
ctlr->setup = aml_spisg_setup;
ctlr->cleanup = aml_spisg_cleanup;
ctlr->prepare_message = aml_spisg_prepare_message;
ctlr->transfer_one = aml_spisg_transfer_one;
ctlr->target_abort = aml_spisg_target_abort;
ctlr->handle_err = aml_spisg_handle_err;
ctlr->can_dma = aml_spisg_can_dma;
ctlr->max_dma_len = SPISG_BLOCK_MAX;
ctlr->auto_runtime_pm = true;
dma_set_max_seg_size(&pdev->dev, SPISG_BLOCK_MAX);
ret = devm_request_irq(&pdev->dev, irq, aml_spisg_irq, 0, NULL, spisg);
if (ret) {
dev_err(&pdev->dev, "irq request failed\n");
goto out_clk;
}
ret = spi_register_controller(ctlr);
if (ret) {
dev_err(&pdev->dev, "spi controller registration failed\n");
goto out_clk;
}
pm_runtime_put(&spisg->pdev->dev);
#ifdef CONFIG_SPICC_TEST
device_create_file(&ctlr->dev, &dev_attr_test);
device_create_file(&ctlr->dev, &dev_attr_testdev);
#endif
return 0;
out_clk:
clk_disable_unprepare(spisg->pclk);
return ret;
}
static void aml_spisg_remove(struct platform_device *pdev)
{
struct spisg_device *spisg = platform_get_drvdata(pdev);
spi_unregister_controller(spisg->controller);
if (!pm_runtime_suspended(&pdev->dev)) {
pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
clk_disable_unprepare(spisg->pclk);
}
#ifdef CONFIG_SPICC_TEST
device_remove_file(&spisg->controller->dev, &dev_attr_test);
device_remove_file(&spisg->controller->dev, &dev_attr_testdev);
#endif
}
static int spisg_suspend_runtime(struct device *dev)
{
struct spisg_device *spisg = dev_get_drvdata(dev);
pinctrl_pm_select_sleep_state(&spisg->pdev->dev);
clk_disable_unprepare(spisg->sclk);
return 0;
}
static int spisg_resume_runtime(struct device *dev)
{
struct spisg_device *spisg = dev_get_drvdata(dev);
clk_prepare_enable(spisg->sclk);
pinctrl_pm_select_default_state(&spisg->pdev->dev);
return 0;
}
static const struct dev_pm_ops amlogic_spiao_pm_ops = {
.runtime_suspend = spisg_suspend_runtime,
.runtime_resume = spisg_resume_runtime,
};
static const struct of_device_id amlogic_spiao_of_match[] = {
{
.compatible = "amlogic,a9-spiao",
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, amlogic_spiao_of_match);
struct platform_driver amlogic_spiao_driver = {
.probe = aml_spisg_probe,
.remove = aml_spisg_remove,
.driver = {
.name = "amlogic-spiao",
.of_match_table = amlogic_spiao_of_match,
.pm = &amlogic_spiao_pm_ops,
},
};
#ifndef MODULE
module_platform_driver(amlogic_spiao_driver);
#endif
MODULE_DESCRIPTION("Amlogic SPI AO Controller driver");
MODULE_AUTHOR("Sunny Luo <[email protected]>");
MODULE_LICENSE("GPL");