1210 lines
31 KiB
C
1210 lines
31 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (c) 2022-2025, ArtInChip Technology Co., Ltd
|
|
*
|
|
* Dehuang Wu <dehuang.wu@artinchip.com>
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <clk.h>
|
|
#include <dm.h>
|
|
#include <dm/device.h>
|
|
#include <dm/device_compat.h>
|
|
#include <dt-structs.h>
|
|
#include <mapmem.h>
|
|
#include <spi.h>
|
|
#include <spi-mem.h>
|
|
#include <dma.h>
|
|
#include <errno.h>
|
|
#include <fdt_support.h>
|
|
#include <reset.h>
|
|
#include <wait_bit.h>
|
|
#include <asm/bitops.h>
|
|
#include <asm/gpio.h>
|
|
#include <asm/cache.h>
|
|
#include <asm/io.h>
|
|
#include <linux/iopoll.h>
|
|
#include <hexdump.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/* AIC spi registers */
|
|
#define SPI_REG_VER(priv) (priv->base + 0x00)
|
|
#define SPI_REG_GCR(priv) (priv->base + 0x04)
|
|
#define SPI_REG_TCR(priv) (priv->base + 0x08)
|
|
#define SPI_REG_PHC(priv) (priv->base + 0x0c)
|
|
#define SPI_REG_ICR(priv) (priv->base + 0x10)
|
|
#define SPI_REG_ISR(priv) (priv->base + 0x14)
|
|
#define SPI_REG_FCR(priv) (priv->base + 0x18)
|
|
#define SPI_REG_FSR(priv) (priv->base + 0x1c)
|
|
#define SPI_REG_WCR(priv) (priv->base + 0x20)
|
|
#define SPI_REG_CCR(priv) (priv->base + 0x24)
|
|
#define SPI_REG_BCR(priv) (priv->base + 0x30)
|
|
#define SPI_REG_MTC(priv) (priv->base + 0x34)
|
|
#define SPI_REG_BCC(priv) (priv->base + 0x38)
|
|
#define SPI_REG_DCR(priv) (priv->base + 0x88)
|
|
#define SPI_REG_TXD(priv) (priv->base + 0x200)
|
|
#define SPI_REG_RXD(priv) (priv->base + 0x300)
|
|
|
|
#define GCR_BIT_EN BIT(0)
|
|
#define GCR_BIT_MODE BIT(1)
|
|
#define GCR_BIT_PAUSE BIT(7)
|
|
#define GCR_BIT_SRST BIT(31)
|
|
|
|
#define TCR_BIT_CPHA BIT(0)
|
|
#define TCR_BIT_CPOL BIT(1)
|
|
#define TCR_BIT_SPOL BIT(2)
|
|
#define TCR_BIT_SSCTL BIT(3)
|
|
#define TCR_BIT_SS_OWNER BIT(6)
|
|
#define TCR_BIT_SS_LEVEL BIT(7)
|
|
#define TCR_BIT_DHB BIT(8)
|
|
#define TCR_BIT_DDB BIT(9)
|
|
#define TCR_BIT_RPSM BIT(10)
|
|
#define TCR_BIT_RXINDLY_EN BIT(11)
|
|
#define TCR_BIT_FBS BIT(12)
|
|
#define TCR_BIT_RXDLY_DIS BIT(13)
|
|
#define TCR_BIT_TXDLY_EN BIT(14)
|
|
#define TCR_BIT_XCH BIT(31)
|
|
#define TCR_BIT_SS_SEL_OFF (4)
|
|
#define TCR_BIT_SS_SEL_MSK GENMASK(5, 4)
|
|
|
|
#define TCR_RX_SAMPDLY_MSK (TCR_BIT_RXINDLY_EN | TCR_BIT_RXDLY_DIS)
|
|
#define TCR_RX_SAMPDLY_NONE (TCR_BIT_RXDLY_DIS)
|
|
#define TCR_RX_SAMPDLY_HALF (0)
|
|
#define TCR_RX_SAMPDLY_ONE (TCR_BIT_RXINDLY_EN)
|
|
|
|
#define PHC_BIT_TX_CLK_MSK GENMASK(2, 0)
|
|
#define PHC_BIT_RX_DATA_OFF (4)
|
|
#define PHC_BIT_RX_DATA_MSK GENMASK(7, 4)
|
|
#define PHC_BIT_RX_PHA_MSK BIT(12)
|
|
#define PHC_BIT_TX_DATA_MSK BIT(14)
|
|
#define PHC_BIT_TX_PHA_MSK BIT(15)
|
|
|
|
#define FCR_BIT_RX_LEVEL_MSK GENMASK(7, 0)
|
|
#define FCR_BIT_RX_DMA_EN BIT(8)
|
|
#define FCR_BIT_RX_RST BIT(15)
|
|
#define FCR_BIT_TX_LEVEL_MSK GENMASK(23, 16)
|
|
#define FCR_BIT_TX_DMA_EN BIT(24)
|
|
#define FCR_BIT_TX_RST BIT(31)
|
|
#define FCR_BIT_DMA_EN_MSK (FCR_BIT_TX_DMA_EN | FCR_BIT_RX_DMA_EN)
|
|
|
|
#define FSR_BIT_RX_CNT_MSK GENMASK(7, 0)
|
|
#define FSR_BIT_RB_CNT_MSK GENMASK(14, 12)
|
|
#define FSR_BIT_RB_WR_EN BIT(15)
|
|
#define FSR_BIT_TX_CNT_MSK GENMASK(23, 16)
|
|
#define FSR_BIT_TB_CNT_MSK GENMASK(30, 28)
|
|
#define FSR_BIT_TB_WR_EN BIT(31)
|
|
#define FSR_BIT_RXCNT_OFF (0)
|
|
#define FSR_BIT_TXCNT_OFF (16)
|
|
|
|
#define ICR_BIT_RX_RDY BIT(0)
|
|
#define ICR_BIT_RX_EMP BIT(1)
|
|
#define ICR_BIT_RX_FULL BIT(2)
|
|
#define ICR_BIT_TX_ERQ BIT(4)
|
|
#define ICR_BIT_TX_EMP BIT(5)
|
|
#define ICR_BIT_TX_FULL BIT(6)
|
|
#define ICR_BIT_RX_OVF BIT(8)
|
|
#define ICR_BIT_RX_UDR BIT(9)
|
|
#define ICR_BIT_TX_OVF BIT(10)
|
|
#define ICR_BIT_TX_UDR BIT(11)
|
|
#define ICR_BIT_TC BIT(12)
|
|
#define ICR_BIT_SSI BIT(13)
|
|
#define ICR_BIT_ERRS (ICR_BIT_TX_OVF | ICR_BIT_RX_UDR | ICR_BIT_RX_OVF)
|
|
#define ICR_BIT_ALL_MSK (0x77 | (0x3f << 8))
|
|
|
|
#define ISR_BIT_RX_RDY BIT(0)
|
|
#define ISR_BIT_RX_EMP BIT(1)
|
|
#define ISR_BIT_RX_FULL BIT(2)
|
|
#define ISR_BIT_TX_RDY BIT(4)
|
|
#define ISR_BIT_TX_EMP BIT(5)
|
|
#define ISR_BIT_TX_FULL BIT(6)
|
|
#define ISR_BIT_RX_OVF BIT(8)
|
|
#define ISR_BIT_RX_UDR BIT(9)
|
|
#define ISR_BIT_TX_OVF BIT(10)
|
|
#define ISR_BIT_TX_UDR BIT(11)
|
|
#define ISR_BIT_TC BIT(12)
|
|
#define ISR_BIT_SSI BIT(13)
|
|
#define ISR_BIT_ERRS (ISR_BIT_TX_OVF | ISR_BIT_RX_UDR | ISR_BIT_RX_OVF)
|
|
#define ISR_BIT_ALL_MSK (0x77 | (0x3f << 8))
|
|
|
|
#define WCR_BIT_WCC_MSK GENMASK(15, 0)
|
|
#define WCR_BIT_SWC_MSK GENMASK(19, 16)
|
|
|
|
#define CCR_BIT_CDR2_OFF (0)
|
|
#define CCR_BIT_CDR1_OFF (8)
|
|
#define CCR_BIT_CDR2_MSK GENMASK(7, 0)
|
|
#define CCR_BIT_CDR1_MSK GENMASK(11, 8)
|
|
#define CCR_BIT_DRS BIT(12)
|
|
|
|
#define BCR_BIT_CNT_MSK GENMASK(23, 0)
|
|
#define MTC_BIT_CNT_MSK GENMASK(23, 0)
|
|
|
|
#define BCC_BIT_STC_MSK GENMASK(23, 0)
|
|
#define BCC_BIT_DBC_MSK GENMASK(27, 24)
|
|
#define BCC_BIT_DUAL_MODE BIT(28)
|
|
#define BCC_BIT_QUAD_MODE BIT(29)
|
|
|
|
#define AIC_SPI_CS(cs) (((cs) << TCR_BIT_SS_SEL_OFF) & TCR_BIT_SS_SEL_MSK)
|
|
#define AIC_SPI_MAX_XFER_SIZE 0xffffff
|
|
#define AIC_SPI_BURST_CNT(cnt) ((cnt)&AIC_SPI_MAX_XFER_SIZE)
|
|
#define AIC_SPI_XMIT_CNT(cnt) ((cnt)&AIC_SPI_MAX_XFER_SIZE)
|
|
#define SPI_FIFO_DEPTH 64
|
|
#define AIC_SPI_RX_WL 0x20
|
|
#define AIC_SPI_TX_WL 0x20
|
|
|
|
#define AIC_SPI_MAX_RATE 200000000
|
|
#define AIC_SPI_MIN_RATE 3000
|
|
#define AIC_SPI_DEFAULT_RATE 24000000
|
|
#define AIC_SPI_TIMEOUT_US 1000000
|
|
|
|
#define RX_SAMP_DLY_AUTO 0
|
|
#define RX_SAMP_DLY_NONE 1
|
|
#define RX_SAMP_DLY_HALF_CYCLE 2
|
|
#define RX_SAMP_DLY_ONE_CYCLE 3
|
|
|
|
#define SPI_DUAL_MODE 1
|
|
#define SPI_QUAD_MODE 2
|
|
#define SPI_SINGLE_MODE 4
|
|
|
|
#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OPCODE 0xbb
|
|
#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OPCODE 0xeb
|
|
|
|
enum aic_spi_rx_delay_mode {
|
|
RX_SAMP_DLY_0P,
|
|
RX_SAMP_DLY_90P,
|
|
RX_SAMP_DLY_180P,
|
|
RX_SAMP_DLY_270P,
|
|
RX_SAMP_DLY_360P,
|
|
RX_SAMP_DLY_450P,
|
|
RX_SAMP_DLY_540P,
|
|
RX_SAMP_DLY_630P,
|
|
RX_SAMP_DLY_720P,
|
|
RX_SAMP_DLY_810P,
|
|
RX_SAMP_DLY_900P,
|
|
RX_SAMP_DLY_990P,
|
|
RX_SAMP_DLY_1080P,
|
|
RX_SAMP_DLY_AUTOP
|
|
};
|
|
|
|
struct aic_spi_data {
|
|
u32 fifo_depth;
|
|
bool has_soft_reset;
|
|
bool has_burst_ctl;
|
|
bool has_pahse_ctl;
|
|
};
|
|
|
|
struct aic_spi_platdata {
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
struct dtd_artinchip_aic_spi dtplat;
|
|
#endif
|
|
struct aic_spi_data *spi_data;
|
|
void __iomem *base;
|
|
u32 max_hz;
|
|
u32 rx_samp_dly;
|
|
u32 tx_samp_dly;
|
|
u32 tx_clk_dly;
|
|
};
|
|
|
|
struct aic_spi_priv {
|
|
struct aic_spi_data *spi_data;
|
|
struct clk clk;
|
|
struct reset_ctl reset;
|
|
void __iomem *base;
|
|
u32 freq;
|
|
u32 mode;
|
|
u32 rx_samp_dly;
|
|
u32 tx_samp_dly;
|
|
u32 tx_clk_dly;
|
|
|
|
#ifdef CONFIG_ARTINCHIP_DMA
|
|
/* DMA */
|
|
struct dma rx_dma;
|
|
struct dma tx_dma;
|
|
#endif
|
|
|
|
u8 *tx_buf;
|
|
u8 *rx_buf;
|
|
};
|
|
|
|
static void aic_spi_set_cs(struct udevice *bus, u8 cs, bool enable)
|
|
{
|
|
struct aic_spi_priv *priv = dev_get_priv(bus);
|
|
u32 reg;
|
|
|
|
reg = readl(SPI_REG_TCR(priv));
|
|
|
|
reg &= ~TCR_BIT_SS_SEL_MSK;
|
|
reg |= AIC_SPI_CS(cs);
|
|
|
|
if (enable)
|
|
reg &= ~TCR_BIT_SS_LEVEL;
|
|
else
|
|
reg |= TCR_BIT_SS_LEVEL;
|
|
|
|
writel(reg, SPI_REG_TCR(priv));
|
|
}
|
|
|
|
static inline int aic_spi_set_clock(struct udevice *dev, bool enable)
|
|
{
|
|
int ret = 0;
|
|
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_CLK_ARTINCHIP)
|
|
struct aic_spi_priv *priv = dev_get_priv(dev);
|
|
|
|
dev_dbg(dev, "enable %d\n", enable);
|
|
if (!enable) {
|
|
clk_disable(&priv->clk);
|
|
if (reset_valid(&priv->reset))
|
|
reset_assert(&priv->reset);
|
|
return 0;
|
|
}
|
|
|
|
ret = clk_enable(&priv->clk);
|
|
if (ret) {
|
|
dev_err(dev, "failed to enable clock (ret=%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
udelay(500);
|
|
clk_set_rate(&priv->clk, priv->freq);
|
|
if (reset_valid(&priv->reset)) {
|
|
ret = reset_deassert(&priv->reset);
|
|
if (ret) {
|
|
dev_err(dev, "failed to deassert reset\n");
|
|
goto err;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
err:
|
|
clk_disable(&priv->clk);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static int aic_spi_claim_bus(struct udevice *dev)
|
|
{
|
|
struct aic_spi_priv *priv = dev_get_priv(dev->parent);
|
|
|
|
setbits_le32(SPI_REG_GCR(priv),
|
|
GCR_BIT_EN | GCR_BIT_MODE | GCR_BIT_PAUSE);
|
|
|
|
if (priv->spi_data->has_soft_reset)
|
|
setbits_le32(SPI_REG_GCR(priv), GCR_BIT_SRST);
|
|
|
|
setbits_le32(SPI_REG_TCR(priv), TCR_BIT_SS_OWNER);
|
|
|
|
#ifdef CONFIG_ARTINCHIP_DMA
|
|
/* enable dma rx channel */
|
|
dma_enable(&priv->rx_dma);
|
|
|
|
/* enable dma tx channel */
|
|
dma_enable(&priv->tx_dma);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static bool aic_spi_in_single_mode(struct aic_spi_priv *priv)
|
|
{
|
|
u32 val;
|
|
|
|
val = readl(SPI_REG_BCC(priv));
|
|
if ((val & BCC_BIT_QUAD_MODE) || (val & BCC_BIT_DUAL_MODE))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void aic_spi_reset_fifo(struct aic_spi_priv *priv)
|
|
{
|
|
u32 val = readl(SPI_REG_FCR(priv));
|
|
|
|
val |= (FCR_BIT_RX_RST | FCR_BIT_TX_RST);
|
|
|
|
/* Set the trigger level of RxFIFO/TxFIFO. */
|
|
val &= ~(FCR_BIT_RX_LEVEL_MSK | FCR_BIT_TX_LEVEL_MSK);
|
|
val |= (AIC_SPI_TX_WL << 16) | AIC_SPI_RX_WL;
|
|
writel(val, SPI_REG_FCR(priv));
|
|
}
|
|
|
|
static void aic_spi_set_xfer_cnt(struct aic_spi_priv *priv, u32 tx_len,
|
|
u32 rx_len, u32 single_len, u32 dummy_cnt)
|
|
{
|
|
u32 reg_val = readl(SPI_REG_BCR(priv));
|
|
|
|
pr_debug("xfer cnt: tx %d, rx %d, dummy %d\n", tx_len, rx_len, dummy_cnt);
|
|
|
|
/* Total transfer length */
|
|
reg_val &= ~BCR_BIT_CNT_MSK;
|
|
reg_val |= (BCR_BIT_CNT_MSK & (tx_len + rx_len + dummy_cnt));
|
|
writel(reg_val, SPI_REG_BCR(priv));
|
|
|
|
/* Total send data length */
|
|
reg_val = readl(SPI_REG_MTC(priv));
|
|
reg_val &= ~MTC_BIT_CNT_MSK;
|
|
reg_val |= (MTC_BIT_CNT_MSK & tx_len);
|
|
writel(reg_val, SPI_REG_MTC(priv));
|
|
|
|
/*
|
|
* Special configuration:
|
|
* # Data length need to send in single mode
|
|
* - If the transfer is already single mode, the STC length should be
|
|
* the same with total send data length
|
|
* - If the transfer is DUAL/QUAD mode, the STC specifies the length
|
|
* that need to send out in single mode, usually for cmd,addr,...
|
|
* # Dummy count for DUAL/QUAD mode
|
|
* - DBC specifies how many dummy burst should be sent before receive
|
|
* data in DUAL/QUAD mode
|
|
*/
|
|
reg_val = readl(SPI_REG_BCC(priv));
|
|
reg_val &= ~BCC_BIT_STC_MSK;
|
|
reg_val |= (BCC_BIT_STC_MSK & single_len);
|
|
reg_val &= ~(0xf << 24);
|
|
reg_val |= (dummy_cnt << 24);
|
|
writel(reg_val, SPI_REG_BCC(priv));
|
|
}
|
|
|
|
static int aic_spi_release_bus(struct udevice *dev)
|
|
{
|
|
struct aic_spi_priv *priv = dev_get_priv(dev->parent);
|
|
|
|
clrbits_le32(SPI_REG_GCR(priv), GCR_BIT_EN);
|
|
|
|
#ifdef CONFIG_ARTINCHIP_DMA
|
|
/* disable dma rx channel */
|
|
dma_disable(&priv->rx_dma);
|
|
|
|
/* disable dma tx channel */
|
|
dma_disable(&priv->tx_dma);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_ARTINCHIP_DMA
|
|
static int aic_spi_xfer_dma(struct udevice *dev, unsigned int bitlen,
|
|
const void *dout, void *din, unsigned long flags)
|
|
{
|
|
u32 len = bitlen / 8, val, tx_len, rx_len, single_len;
|
|
struct dm_spi_slave_plat *slave_plat;
|
|
struct udevice *bus = dev->parent;
|
|
struct aic_spi_priv *priv;
|
|
int ret = 0;
|
|
|
|
tx_len = 0;
|
|
rx_len = 0;
|
|
single_len = 0;
|
|
slave_plat = dev_get_parent_plat(dev);
|
|
priv = dev_get_priv(bus);
|
|
priv->tx_buf = (u8 *)dout;
|
|
priv->rx_buf = (u8 *)din;
|
|
|
|
if (bitlen % 8) {
|
|
dev_err(bus, "%s: non byte-aligned SPI transfer.\n", __func__);
|
|
return -ENAVAIL;
|
|
}
|
|
|
|
if (flags & SPI_XFER_BEGIN)
|
|
aic_spi_set_cs(bus, slave_plat->cs, true);
|
|
|
|
aic_spi_reset_fifo(priv);
|
|
|
|
if (priv->tx_buf) {
|
|
val = readl(SPI_REG_FCR(priv));
|
|
val |= (FCR_BIT_TX_DMA_EN);
|
|
writel(val, SPI_REG_FCR(priv));
|
|
|
|
tx_len = len;
|
|
if (aic_spi_in_single_mode(priv))
|
|
single_len = tx_len;
|
|
aic_spi_set_xfer_cnt(priv, tx_len, 0, single_len, 0);
|
|
setbits_le32(SPI_REG_TCR(priv), TCR_BIT_XCH);
|
|
|
|
ret = dma_send(&priv->tx_dma, (void *)priv->tx_buf, len,
|
|
(void *)SPI_REG_TXD(priv));
|
|
if (ret < 0)
|
|
goto send_err;
|
|
ret = readl_poll_timeout(SPI_REG_ISR(priv), val,
|
|
(val & ISR_BIT_TC),
|
|
AIC_SPI_TIMEOUT_US);
|
|
send_err:
|
|
if (ret < 0) {
|
|
dev_err(bus, "%s: Timeout to send data\n", __func__);
|
|
clrbits_le32(SPI_REG_FCR(priv), FCR_BIT_TX_DMA_EN);
|
|
aic_spi_set_cs(bus, slave_plat->cs, false);
|
|
return ret;
|
|
}
|
|
/* Write 1 to clear */
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_TC);
|
|
clrbits_le32(SPI_REG_FCR(priv), FCR_BIT_TX_DMA_EN);
|
|
}
|
|
|
|
if (priv->rx_buf) {
|
|
val = readl(SPI_REG_FCR(priv));
|
|
val |= (FCR_BIT_RX_DMA_EN);
|
|
writel(val, SPI_REG_FCR(priv));
|
|
|
|
rx_len = len;
|
|
aic_spi_set_xfer_cnt(priv, 0, rx_len, 0, 0);
|
|
setbits_le32(SPI_REG_TCR(priv), TCR_BIT_XCH);
|
|
// DMA recieve
|
|
dma_prepare_rcv_buf(&priv->rx_dma, (void *)priv->rx_buf, len);
|
|
ret = dma_receive(&priv->rx_dma, (void **)&priv->rx_buf,
|
|
(void *)SPI_REG_RXD(priv));
|
|
if (ret < 0)
|
|
goto recv_err;
|
|
|
|
ret = readl_poll_timeout(SPI_REG_ISR(priv), val,
|
|
(val & ISR_BIT_TC),
|
|
AIC_SPI_TIMEOUT_US);
|
|
recv_err:
|
|
if (ret < 0) {
|
|
dev_err(bus, "%s: Timeout to recv data\n", __func__);
|
|
clrbits_le32(SPI_REG_FCR(priv), FCR_BIT_RX_DMA_EN);
|
|
aic_spi_set_cs(bus, slave_plat->cs, false);
|
|
return ret;
|
|
}
|
|
/* Write 1 to clear */
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_TC);
|
|
clrbits_le32(SPI_REG_FCR(priv), FCR_BIT_RX_DMA_EN);
|
|
}
|
|
|
|
if (flags & SPI_XFER_END)
|
|
aic_spi_set_cs(bus, slave_plat->cs, false);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static u32 aic_spi_txfifo_query(struct aic_spi_priv *priv)
|
|
{
|
|
u32 val = (FSR_BIT_TX_CNT_MSK & readl(SPI_REG_FSR(priv)));
|
|
|
|
val >>= FSR_BIT_TXCNT_OFF;
|
|
return val;
|
|
}
|
|
|
|
static u32 aic_spi_rxfifo_query(struct aic_spi_priv *priv)
|
|
{
|
|
u32 val = (FSR_BIT_RX_CNT_MSK & readl(SPI_REG_FSR(priv)));
|
|
|
|
val >>= FSR_BIT_RXCNT_OFF;
|
|
return val;
|
|
}
|
|
|
|
static int aic_spi_fifo_write(struct aic_spi_priv *priv, u8 *tx_buf, u32 len)
|
|
{
|
|
unsigned int poll_time = 0x7ffffff;
|
|
unsigned int dolen, fifo_len;
|
|
|
|
while (len) {
|
|
while (aic_spi_txfifo_query(priv) > (SPI_FIFO_DEPTH / 2))
|
|
;
|
|
fifo_len = SPI_FIFO_DEPTH - aic_spi_txfifo_query(priv);
|
|
dolen = min(fifo_len, len);
|
|
while (dolen) {
|
|
writeb(*tx_buf++, SPI_REG_TXD(priv));
|
|
len--;
|
|
dolen--;
|
|
}
|
|
}
|
|
|
|
poll_time = 0x7ffffff;
|
|
while (aic_spi_txfifo_query(priv) && (--poll_time > 0))
|
|
;
|
|
if (poll_time <= 0) {
|
|
pr_err("cpu transfer data time out!\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_spi_fifo_read(struct aic_spi_priv *priv, u8 *rx_buf, u32 len)
|
|
{
|
|
unsigned int poll_time = 0x7ffffff;
|
|
unsigned int dolen;
|
|
|
|
while (len) {
|
|
dolen = aic_spi_rxfifo_query(priv);
|
|
if (dolen == 0) {
|
|
poll_time--;
|
|
if (poll_time == 0)
|
|
break;
|
|
continue;
|
|
}
|
|
while (dolen) {
|
|
*rx_buf++ = readb(SPI_REG_RXD(priv));
|
|
--dolen;
|
|
--len;
|
|
}
|
|
}
|
|
if (poll_time == 0) {
|
|
pr_err("cpu receive data time out!\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_spi_xfer_fifo(struct udevice *dev, unsigned int bitlen,
|
|
const void *dout, void *din, unsigned long flags)
|
|
{
|
|
u32 val, len = bitlen / 8, tx_len, rx_len, single_len;
|
|
struct dm_spi_slave_plat *slave_plat;
|
|
struct udevice *bus = dev->parent;
|
|
struct aic_spi_priv *priv;
|
|
int ret = 0;
|
|
tx_len = 0;
|
|
rx_len = 0;
|
|
single_len = 0;
|
|
|
|
slave_plat = dev_get_parent_plat(dev);
|
|
priv = dev_get_priv(bus);
|
|
priv->tx_buf = (u8 *)dout;
|
|
priv->rx_buf = (u8 *)din;
|
|
|
|
if (bitlen % 8) {
|
|
dev_err(bus, "%s: non byte-aligned SPI transfer.\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (flags & SPI_XFER_BEGIN)
|
|
aic_spi_set_cs(bus, slave_plat->cs, true);
|
|
|
|
/* Reset FIFOs */
|
|
aic_spi_reset_fifo(priv);
|
|
|
|
if (priv->tx_buf) {
|
|
tx_len = len;
|
|
if (aic_spi_in_single_mode(priv))
|
|
single_len = tx_len;
|
|
aic_spi_set_xfer_cnt(priv, tx_len, 0, single_len, 0);
|
|
/* Start transfer */
|
|
setbits_le32(SPI_REG_TCR(priv), TCR_BIT_XCH);
|
|
ret = aic_spi_fifo_write(priv, priv->tx_buf, len);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
ret = readl_poll_timeout(SPI_REG_ISR(priv), val,
|
|
(val & ISR_BIT_TC),
|
|
AIC_SPI_TIMEOUT_US);
|
|
if (ret < 0) {
|
|
dev_err(bus, "%s: Timeout to send data\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_TX_EMP);
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_TX_FULL);
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_TX_RDY);
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_TC);
|
|
}
|
|
|
|
if (priv->rx_buf) {
|
|
rx_len = len;
|
|
aic_spi_set_xfer_cnt(priv, 0, rx_len, 0, 0);
|
|
/* Start transfer */
|
|
setbits_le32(SPI_REG_TCR(priv), TCR_BIT_XCH);
|
|
|
|
ret = aic_spi_fifo_read(priv, priv->rx_buf, len);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto out;
|
|
}
|
|
ret = readl_poll_timeout(SPI_REG_ISR(priv), val,
|
|
(val & ISR_BIT_TC),
|
|
AIC_SPI_TIMEOUT_US);
|
|
if (ret < 0) {
|
|
dev_err(bus, "%s: Timeout to recv data\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_TC);
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_RX_EMP);
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_RX_FULL);
|
|
setbits_le32(SPI_REG_ISR(priv), ISR_BIT_RX_RDY);
|
|
}
|
|
|
|
out:
|
|
if (flags & SPI_XFER_END)
|
|
aic_spi_set_cs(bus, slave_plat->cs, false);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aic_spi_xfer(struct udevice *dev, unsigned int bitlen,
|
|
const void *dout, void *din, unsigned long flags)
|
|
{
|
|
u32 len = bitlen / 8;
|
|
u32 addr_align = 0;
|
|
u32 len_unalign = 0;
|
|
|
|
/* Half-duplex only */
|
|
if (din && dout) {
|
|
dev_err(dev, "Half-duplex support only\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
#ifdef CONFIG_ARTINCHIP_DMA
|
|
len_unalign = len % (SPI_FIFO_DEPTH / 2);
|
|
|
|
if (dout)
|
|
addr_align = (((unsigned long)dout) & (ARCH_DMA_MINALIGN - 1))
|
|
== 0;
|
|
else
|
|
addr_align = (((unsigned long)din) & (ARCH_DMA_MINALIGN - 1))
|
|
== 0;
|
|
|
|
if ((len >= SPI_FIFO_DEPTH) && (len >= ARCH_DMA_MINALIGN) &&
|
|
addr_align && !len_unalign)
|
|
return aic_spi_xfer_dma(dev, bitlen, dout, din, flags);
|
|
else
|
|
return aic_spi_xfer_fifo(dev, bitlen, dout, din, flags);
|
|
#else
|
|
(void)len;
|
|
(void)addr_align;
|
|
(void)len_unalign;
|
|
return aic_spi_xfer_fifo(dev, bitlen, dout, din, flags);
|
|
#endif
|
|
}
|
|
|
|
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_CLK_ARTINCHIP)
|
|
static u32 spi_get_best_div_param(u32 spiclk, u32 mclk, u32 *div)
|
|
{
|
|
u32 cdr1_clk, cdr2_clk, cdr1_clkt, cdr2_clkt;
|
|
s32 cdr2, cdr1;
|
|
|
|
/* Get the best cdr1 param if going to use cdr1 */
|
|
cdr1 = 0;
|
|
while ((mclk >> cdr1) > spiclk)
|
|
cdr1++;
|
|
if (cdr1 > 0xF)
|
|
cdr1 = 0xF;
|
|
|
|
/* Get the best cdr1 value around bus clk*/
|
|
cdr1_clk = mclk >> cdr1;
|
|
if (cdr1 > 0 && (cdr1_clk < spiclk)) {
|
|
cdr1_clkt = mclk >> (cdr1 - 1);
|
|
if ((cdr1_clkt - spiclk) < (spiclk - cdr1_clk))
|
|
cdr1--;
|
|
}
|
|
|
|
/* Get the best cdr2 param if going to use cdr2 */
|
|
cdr2 = (s32)(mclk / (spiclk * 2)) - 1;
|
|
if (cdr2 < 0)
|
|
cdr2 = 0;
|
|
if (cdr2 > 0xFF)
|
|
cdr2 = 0xFF;
|
|
|
|
cdr2_clk = mclk / (2 * (cdr2 + 1));
|
|
if ((cdr2 < 0xFF) && (cdr2_clk > spiclk)) {
|
|
cdr2_clkt = mclk / (2 * (cdr2 + 1));
|
|
if ((spiclk - cdr2_clkt) < (cdr2_clk - spiclk))
|
|
cdr2++;
|
|
}
|
|
|
|
cdr1_clk = mclk >> cdr1;
|
|
cdr2_clk = mclk / (2 * (cdr2 + 1));
|
|
|
|
/* cdr1 param vs cdr2 param, use the best */
|
|
if (cdr1_clk == spiclk) {
|
|
*div = cdr1;
|
|
return 0;
|
|
} else if (cdr2_clk == spiclk) {
|
|
*div = cdr2;
|
|
return 1;
|
|
} else if ((cdr2_clk < spiclk) && (cdr1_clk < spiclk)) {
|
|
/* Two clks less than expect clk, use the larger one */
|
|
if (cdr2_clk > cdr1_clk) {
|
|
*div = cdr2;
|
|
return 1;
|
|
}
|
|
*div = cdr1;
|
|
return 0;
|
|
}
|
|
/*
|
|
* 1. Two clks great than expect clk, use least one
|
|
* 2. There is one clk less than expect clk, use it
|
|
*/
|
|
if (cdr2_clk < cdr1_clk) {
|
|
*div = cdr2;
|
|
return 1;
|
|
}
|
|
*div = cdr1;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int aic_spi_set_speed(struct udevice *dev, uint speed)
|
|
{
|
|
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_CLK_ARTINCHIP)
|
|
struct aic_spi_platdata *plat = dev_get_plat(dev);
|
|
struct aic_spi_priv *priv = dev_get_priv(dev);
|
|
u32 reg, cdr, div, mclk;
|
|
|
|
dev_info(dev, "speed %d plat->max_hz %d\n", speed, plat->max_hz);
|
|
if (speed > plat->max_hz)
|
|
speed = plat->max_hz;
|
|
|
|
if (speed < AIC_SPI_MIN_RATE)
|
|
speed = AIC_SPI_MIN_RATE;
|
|
|
|
if (priv->spi_data->has_pahse_ctl)
|
|
mclk = plat->max_hz / 2;
|
|
else
|
|
mclk = plat->max_hz;
|
|
|
|
cdr = spi_get_best_div_param(speed, mclk, &div);
|
|
|
|
reg = readl(SPI_REG_CCR(priv));
|
|
if (cdr == 0) {
|
|
reg &= ~(CCR_BIT_CDR1_MSK | CCR_BIT_DRS);
|
|
reg |= (div << CCR_BIT_CDR1_OFF);
|
|
} else {
|
|
reg &= ~CCR_BIT_CDR2_MSK;
|
|
reg |= (div | CCR_BIT_DRS);
|
|
}
|
|
priv->freq = speed;
|
|
writel(reg, SPI_REG_CCR(priv));
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aic_spi_config_bus_mode(struct aic_spi_priv *priv, u32 busmode)
|
|
{
|
|
u32 val;
|
|
|
|
val = readl(SPI_REG_BCC(priv));
|
|
|
|
if (busmode == SPI_QUAD_MODE)
|
|
val |= BCC_BIT_QUAD_MODE;
|
|
else
|
|
val &= ~BCC_BIT_QUAD_MODE;
|
|
|
|
if (busmode == SPI_DUAL_MODE)
|
|
val |= BCC_BIT_DUAL_MODE;
|
|
else
|
|
val &= ~BCC_BIT_DUAL_MODE;
|
|
|
|
writel(val, SPI_REG_BCC(priv));
|
|
}
|
|
|
|
static int aic_spi_set_mode(struct udevice *dev, uint mode)
|
|
{
|
|
struct aic_spi_priv *priv = dev_get_priv(dev);
|
|
u32 reg, val, busmode;
|
|
|
|
dev_info(dev, "mode = 0x%x\n", mode);
|
|
reg = readl(SPI_REG_TCR(priv));
|
|
reg &= ~(TCR_BIT_CPOL | TCR_BIT_CPHA);
|
|
|
|
if (mode & SPI_CPOL)
|
|
reg |= TCR_BIT_CPOL;
|
|
|
|
if (mode & SPI_CPHA)
|
|
reg |= TCR_BIT_CPHA;
|
|
|
|
if (mode & SPI_CS_HIGH)
|
|
reg &= ~TCR_BIT_SPOL;
|
|
else
|
|
reg |= TCR_BIT_SPOL;
|
|
/*
|
|
* Should set this bit to discard unused RX burst, otherwise unused
|
|
* data will full fill RXFIFO when sending data.
|
|
*/
|
|
reg |= TCR_BIT_DHB;
|
|
reg |= TCR_BIT_TXDLY_EN;
|
|
reg &= ~TCR_RX_SAMPDLY_MSK;
|
|
if (priv->rx_samp_dly == RX_SAMP_DLY_AUTO) {
|
|
if (priv->freq <= 24000000) {
|
|
/* Normal mode */
|
|
reg |= TCR_RX_SAMPDLY_NONE;
|
|
} else if (priv->freq <= 60000000) {
|
|
/* Delay half cycle to sample input */
|
|
reg |= TCR_RX_SAMPDLY_HALF;
|
|
} else {
|
|
/* Delay 1 cycle to sample input */
|
|
reg |= TCR_RX_SAMPDLY_ONE;
|
|
}
|
|
} else if (priv->rx_samp_dly == RX_SAMP_DLY_NONE) {
|
|
reg |= TCR_RX_SAMPDLY_NONE;
|
|
} else if (priv->rx_samp_dly == RX_SAMP_DLY_HALF_CYCLE) {
|
|
reg |= TCR_RX_SAMPDLY_HALF;
|
|
} else {
|
|
reg |= TCR_RX_SAMPDLY_ONE;
|
|
}
|
|
writel(reg, SPI_REG_TCR(priv));
|
|
|
|
reg = readl(SPI_REG_PHC(priv));
|
|
if (priv->spi_data->has_pahse_ctl) {
|
|
if (priv->rx_samp_dly == RX_SAMP_DLY_AUTOP) {
|
|
if (priv->freq <= 24000000) {
|
|
priv->rx_samp_dly = RX_SAMP_DLY_0P;
|
|
} else if (priv->freq <= 60000000) {
|
|
/* Delay half cycle to sample input */
|
|
priv->rx_samp_dly = RX_SAMP_DLY_180P;
|
|
} else {
|
|
/* Delay 1 cycle to sample input */
|
|
priv->rx_samp_dly = RX_SAMP_DLY_360P;
|
|
}
|
|
}
|
|
reg &= ~(PHC_BIT_TX_CLK_MSK | PHC_BIT_RX_DATA_MSK | PHC_BIT_TX_DATA_MSK);
|
|
reg |= priv->rx_samp_dly << PHC_BIT_RX_DATA_OFF;
|
|
reg |= priv->tx_clk_dly & PHC_BIT_TX_CLK_MSK;
|
|
if (priv->tx_samp_dly)
|
|
reg |= PHC_BIT_TX_DATA_MSK;
|
|
} else {
|
|
reg |= PHC_BIT_TX_PHA_MSK | PHC_BIT_RX_PHA_MSK;
|
|
}
|
|
writel(reg, SPI_REG_PHC(priv));
|
|
|
|
val = (mode & (SPI_TX_DUAL | SPI_RX_DUAL));
|
|
if ((val > 0) && val != (SPI_TX_DUAL | SPI_RX_DUAL)) {
|
|
dev_warn(dev, "TX RX bus width should be the same.\n");
|
|
mode &= ~(SPI_TX_DUAL | SPI_RX_DUAL);
|
|
}
|
|
|
|
val = (mode & (SPI_TX_QUAD | SPI_RX_QUAD));
|
|
if ((val > 0) && val != (SPI_TX_QUAD | SPI_RX_QUAD)) {
|
|
dev_warn(dev, "TX RX bus width should be the same.\n");
|
|
mode &= ~(SPI_TX_QUAD | SPI_RX_QUAD);
|
|
}
|
|
|
|
priv->mode = mode;
|
|
|
|
if (mode & SPI_RX_QUAD)
|
|
busmode = SPI_QUAD_MODE;
|
|
else if (mode & SPI_RX_DUAL)
|
|
busmode = SPI_DUAL_MODE;
|
|
else
|
|
busmode = SPI_SINGLE_MODE;
|
|
|
|
aic_spi_config_bus_mode(priv, busmode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_spi_check_buswidth(struct spi_slave *slave, u8 buswidth, bool tx)
|
|
{
|
|
u32 mode = slave->mode;
|
|
|
|
switch (buswidth) {
|
|
case 1:
|
|
return 0;
|
|
|
|
case 2:
|
|
if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
|
|
(!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
if ((tx && (mode & SPI_TX_QUAD)) ||
|
|
(!tx && (mode & SPI_RX_QUAD)))
|
|
return 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return -ENOTSUPP;
|
|
}
|
|
|
|
static int aic_spi_check_opcode(u8 opcode)
|
|
{
|
|
int ret = 0;
|
|
|
|
/*
|
|
* ArtInChip SPI controller not support DUAL IO, QUAD IO
|
|
*/
|
|
switch (opcode) {
|
|
case SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OPCODE:
|
|
case SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OPCODE:
|
|
ret = -ENOTSUPP;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static bool aic_spi_mem_supports_op(struct spi_slave *slave,
|
|
const struct spi_mem_op *op)
|
|
{
|
|
if (aic_spi_check_opcode(op->cmd.opcode))
|
|
return false;
|
|
|
|
if (aic_spi_check_buswidth(slave, op->cmd.buswidth, true))
|
|
return false;
|
|
|
|
if (op->addr.nbytes &&
|
|
aic_spi_check_buswidth(slave, op->addr.buswidth, true))
|
|
return false;
|
|
|
|
if (op->dummy.nbytes &&
|
|
aic_spi_check_buswidth(slave, op->dummy.buswidth, true))
|
|
return false;
|
|
|
|
if (op->data.nbytes &&
|
|
aic_spi_check_buswidth(slave, op->data.buswidth,
|
|
op->data.dir == SPI_MEM_DATA_OUT))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int aic_spi_mem_exec_op(struct spi_slave *slave,
|
|
const struct spi_mem_op *op)
|
|
{
|
|
struct udevice *dev = slave->dev->parent;
|
|
struct aic_spi_priv *priv = dev_get_priv(dev);
|
|
u32 flag, busmode, pos = 0;
|
|
const u8 *tx_buf = NULL;
|
|
int ret, i, op_len;
|
|
u8 *rx_buf = NULL;
|
|
|
|
if (op->data.nbytes) {
|
|
if (op->data.dir == SPI_MEM_DATA_IN)
|
|
rx_buf = op->data.buf.in;
|
|
else
|
|
tx_buf = op->data.buf.out;
|
|
}
|
|
|
|
op_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
|
|
|
/*
|
|
* Avoid using malloc() here so that we can use this code in SPL where
|
|
* simple malloc may be used. That implementation does not allow free()
|
|
* so repeated calls to this code can exhaust the space.
|
|
*
|
|
* The value of op_len is small, since it does not include the actual
|
|
* data being sent, only the op-code and address. In fact, it should be
|
|
* possible to just use a small fixed value here instead of op_len.
|
|
*/
|
|
u8 op_buf[op_len];
|
|
|
|
op_buf[pos++] = op->cmd.opcode;
|
|
|
|
if (op->addr.nbytes) {
|
|
for (i = 0; i < op->addr.nbytes; i++)
|
|
op_buf[pos + i] =
|
|
op->addr.val >> (8 * (op->addr.nbytes - i - 1));
|
|
|
|
pos += op->addr.nbytes;
|
|
}
|
|
|
|
if (op->dummy.nbytes)
|
|
memset(op_buf + pos, 0xff, op->dummy.nbytes);
|
|
|
|
/* 1st transfer: opcode + address + dummy cycles */
|
|
flag = SPI_XFER_BEGIN;
|
|
/* Make sure to set END bit if no tx or rx data messages follow */
|
|
if (!tx_buf && !rx_buf)
|
|
flag |= SPI_XFER_END;
|
|
|
|
/* Command phase, should run in single mode */
|
|
aic_spi_config_bus_mode(priv, SPI_SINGLE_MODE);
|
|
ret = aic_spi_xfer(slave->dev, op_len * 8, op_buf, NULL, flag);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/* Data phase, check bus width */
|
|
if (op->data.buswidth == 4)
|
|
busmode = SPI_QUAD_MODE;
|
|
else if (op->data.buswidth == 2)
|
|
busmode = SPI_DUAL_MODE;
|
|
else
|
|
busmode = SPI_SINGLE_MODE;
|
|
|
|
aic_spi_config_bus_mode(priv, busmode);
|
|
|
|
/* 2nd transfer: rx or tx data path */
|
|
if (tx_buf || rx_buf)
|
|
ret = aic_spi_xfer(slave->dev, op->data.nbytes * 8, tx_buf,
|
|
rx_buf, SPI_XFER_END);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
static void aic_spi_get_platdata(struct udevice *dev)
|
|
{
|
|
struct aic_spi_platdata *plat = dev_get_plat(dev);
|
|
struct dtd_artinchip_aic_spi *dtplat = &plat->dtplat;
|
|
struct aic_spi_priv *priv = dev_get_priv(dev);
|
|
|
|
plat->base = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
|
|
plat->max_hz = AIC_SPI_MAX_RATE;
|
|
}
|
|
#endif
|
|
|
|
static int aic_spi_probe(struct udevice *bus)
|
|
{
|
|
struct aic_spi_platdata *plat = dev_get_plat(bus);
|
|
struct aic_spi_priv *priv = dev_get_priv(bus);
|
|
int ret = 0;
|
|
|
|
dev_info(bus, "%s\n", __func__);
|
|
#if CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
aic_spi_get_platdata(bus);
|
|
#elif CONFIG_IS_ENABLED(OF_CONTROL) && defined(CONFIG_ARTINCHIP_DMA)
|
|
/* get dma channels */
|
|
ret = dma_get_by_name(bus, "tx", &priv->tx_dma);
|
|
if (ret) {
|
|
dev_err(bus, "get dma tx failed.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = dma_get_by_name(bus, "rx", &priv->rx_dma);
|
|
if (ret) {
|
|
dev_err(bus, "get dma tx failed.\n");
|
|
return -EINVAL;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_CLK_ARTINCHIP)
|
|
ret = clk_get_by_index(bus, 0, &priv->clk);
|
|
if (ret < 0) {
|
|
dev_err(bus, "failed to get clock\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = reset_get_by_index(bus, 0, &priv->reset);
|
|
if (ret && ret != -ENOENT) {
|
|
dev_err(bus, "failed to get reset\n");
|
|
return ret;
|
|
}
|
|
#endif
|
|
priv->spi_data = plat->spi_data;
|
|
priv->base = plat->base;
|
|
priv->freq = plat->max_hz;
|
|
priv->rx_samp_dly = plat->rx_samp_dly;
|
|
priv->tx_samp_dly = plat->tx_samp_dly;
|
|
priv->tx_clk_dly = plat->tx_clk_dly;
|
|
|
|
ret = aic_spi_set_clock(bus, true);
|
|
if (ret) {
|
|
dev_err(bus, "failed to set clock\n");
|
|
return ret;
|
|
}
|
|
/* Set max DMA delay cycle */
|
|
writel(readl(SPI_REG_DCR(priv)) | 0x1F, SPI_REG_DCR(priv));
|
|
/* enable interrupt */
|
|
setbits_le32(SPI_REG_ICR(priv), 0x3fff);
|
|
|
|
dev_info(bus, "%s done.\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
static int aic_spi_parse_sample_phase(struct udevice *bus, u32 rx_sample_dly,
|
|
u32 tx_sample_dly, u32 tx_clk_dly)
|
|
{
|
|
struct aic_spi_platdata *plat = dev_get_plat(bus);
|
|
|
|
if (((rx_sample_dly % 90) == 0) && rx_sample_dly <= 1080)
|
|
plat->rx_samp_dly = rx_sample_dly / 90;
|
|
else
|
|
plat->rx_samp_dly = RX_SAMP_DLY_AUTOP;
|
|
|
|
if (((tx_clk_dly % 90) == 0) && tx_clk_dly <= 360)
|
|
plat->tx_clk_dly = tx_clk_dly / 90;
|
|
else
|
|
plat->tx_clk_dly = 0;
|
|
|
|
if (((tx_sample_dly % 180) == 0) && tx_sample_dly <= 180)
|
|
plat->tx_samp_dly = tx_sample_dly / 180;
|
|
else
|
|
plat->tx_samp_dly = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aic_spi_ofdata_to_platdata(struct udevice *bus)
|
|
{
|
|
struct aic_spi_platdata *plat = dev_get_plat(bus);
|
|
|
|
plat->rx_samp_dly = RX_SAMP_DLY_AUTO;
|
|
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
|
|
int node = dev_of_offset(bus);
|
|
const char *dly = NULL;
|
|
u32 rx_sample_dly, tx_sample_dly, tx_clk_dly;
|
|
|
|
plat->base = (void *)devfdt_get_addr(bus);
|
|
plat->spi_data = (struct aic_spi_data *)dev_get_driver_data(bus);
|
|
plat->max_hz = fdtdec_get_int(gd->fdt_blob, node, "spi-max-frequency",
|
|
AIC_SPI_DEFAULT_RATE);
|
|
if (plat->max_hz > AIC_SPI_MAX_RATE)
|
|
plat->max_hz = AIC_SPI_MAX_RATE;
|
|
|
|
if (!(plat->spi_data->has_pahse_ctl)) {
|
|
dly = fdt_getprop(gd->fdt_blob, node, "aic,rx-samp-dly", NULL);
|
|
if (dly && !strcmp(dly, "none"))
|
|
plat->rx_samp_dly = RX_SAMP_DLY_NONE;
|
|
if (dly && !strcmp(dly, "half"))
|
|
plat->rx_samp_dly = RX_SAMP_DLY_HALF_CYCLE;
|
|
if (dly && !strcmp(dly, "one"))
|
|
plat->rx_samp_dly = RX_SAMP_DLY_ONE_CYCLE;
|
|
} else {
|
|
rx_sample_dly = fdtdec_get_int(gd->fdt_blob, node, "aic,rx-samp-dly", 0);
|
|
tx_sample_dly = fdtdec_get_int(gd->fdt_blob, node, "aic,tx-data-dly", 0);
|
|
tx_clk_dly = fdtdec_get_int(gd->fdt_blob, node, "aic,tx-clk-dly", 0);
|
|
|
|
aic_spi_parse_sample_phase(bus, rx_sample_dly, tx_sample_dly, tx_clk_dly);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct aic_spi_data aic_spi_data_v1 = {
|
|
.fifo_depth = 64,
|
|
.has_soft_reset = true,
|
|
.has_burst_ctl = true,
|
|
.has_pahse_ctl = false,
|
|
};
|
|
|
|
static const struct aic_spi_data aic_spi_data_v2 = {
|
|
.fifo_depth = 64,
|
|
.has_soft_reset = true,
|
|
.has_burst_ctl = true,
|
|
.has_pahse_ctl = true,
|
|
};
|
|
|
|
static const struct spi_controller_mem_ops aic_spi_mem_ops = {
|
|
.supports_op = aic_spi_mem_supports_op,
|
|
.exec_op = aic_spi_mem_exec_op,
|
|
};
|
|
|
|
static const struct dm_spi_ops aic_spi_ops = {
|
|
.claim_bus = aic_spi_claim_bus,
|
|
.release_bus = aic_spi_release_bus,
|
|
.xfer = aic_spi_xfer,
|
|
.set_speed = aic_spi_set_speed,
|
|
.set_mode = aic_spi_set_mode,
|
|
.mem_ops = &aic_spi_mem_ops,
|
|
};
|
|
|
|
static const struct udevice_id aic_spi_ids[] = {
|
|
{
|
|
.compatible = "artinchip,aic-spi-v1.0",
|
|
.data = (ulong)&aic_spi_data_v1,
|
|
},
|
|
{
|
|
.compatible = "artinchip,aic-spi-v2.0",
|
|
.data = (ulong)&aic_spi_data_v2,
|
|
},
|
|
{}
|
|
};
|
|
|
|
U_BOOT_DRIVER(aic_spi) = {
|
|
.name = "artinchip_aic_spi",
|
|
.id = UCLASS_SPI,
|
|
.of_match = aic_spi_ids,
|
|
.ops = &aic_spi_ops,
|
|
.of_to_plat = aic_spi_ofdata_to_platdata,
|
|
.plat_auto = sizeof(struct aic_spi_platdata),
|
|
.priv_auto = sizeof(struct aic_spi_priv),
|
|
.probe = aic_spi_probe,
|
|
};
|