sdk-hwV1.3/lichee/brandy-2.0/spl/drivers/spi.c

1087 lines
29 KiB
C

/*
* sunxi SPI driver for uboot.
*
* Copyright (C) 2018
* 2013.5.7 Mintow <duanmintao@allwinnertech.com>
* 2018.11.7 wangwei <wangwei@allwinnertech.com>
*/
#include <common.h>
#include <asm/io.h>
#include <arch/gpio.h>
#include <arch/clock.h>
#include <arch/spinor.h>
#include <arch/dma.h>
#include "spi-sunxi.h"
#include <private_boot0.h>
#ifdef CFG_SPI_USE_DMA
static sunxi_dma_set *spi_rx_dma;
static uint spi_rx_dma_hd;
__attribute__((section(".data")))
sunxi_dma_set g_spi_rx_dma;
#endif
#define CONFIG_SF_DEFAULT_BUS 0
#define SUNXI_SPI_MAX_TIMEOUT 1000000
#define SUNXI_SPI_PORT_OFFSET 0x1000
#define SUNXI_SPI_DEFAULT_CLK (50000000)
/* For debug */
#define SPI_DEBUG 0
#if SPI_DEBUG
#define SPI_EXIT() printf("%s()%d - %s\n", __func__, __LINE__, "Exit")
#define SPI_ENTER() printf("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
#define SPI_DBG(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SPI_INF(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SPI_ERR(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
#else
#define SPI_EXIT()
#define SPI_ENTER()
#define SPI_DBG(fmt, arg...)
#define SPI_INF(fmt, arg...)
#define SPI_ERR(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
#endif
#define SUNXI_SPI_OK 0
#define SUNXI_SPI_FAIL -1
extern void sunxi_dma_reg_func(void *p);
static int spi_clk = SUNXI_SPI_DEFAULT_CLK;
extern boot_spinor_info_t *spinor_info;
#if SPI_DEBUG
int snprintf(char *buf, size_t size, const char *fmt, ...);
static void spi_print_info(void __iomem *base_addr)
{
char buf[1024] = {0};
snprintf(buf, sizeof(buf)-1,
"base_addr = 0x%x, the SPI control register:\n"
"[VER] 0x%02x = 0x%08x, [GCR] 0x%02x = 0x%08x, [TCR] 0x%02x = 0x%08x\n"
"[ICR] 0x%02x = 0x%08x, [ISR] 0x%02x = 0x%08x, [FCR] 0x%02x = 0x%08x\n"
"[FSR] 0x%02x = 0x%08x, [WCR] 0x%02x = 0x%08x, [CCR] 0x%02x = 0x%08x\n"
"[DCR] 0x%02x = 0x%08x, [BCR] 0x%02x = 0x%08x, [TCR] 0x%02x = 0x%08x\n"
"[BCC] 0x%02x = 0x%08x, [DMA] 0x%02x = 0x%08x",
base_addr,
SPI_VER_REG, readl(base_addr + SPI_VER_REG),
SPI_GC_REG, readl(base_addr + SPI_GC_REG),
SPI_TC_REG, readl(base_addr + SPI_TC_REG),
SPI_INT_CTL_REG, readl(base_addr + SPI_INT_CTL_REG),
SPI_INT_STA_REG, readl(base_addr + SPI_INT_STA_REG),
SPI_FIFO_CTL_REG, readl(base_addr + SPI_FIFO_CTL_REG),
SPI_FIFO_STA_REG, readl(base_addr + SPI_FIFO_STA_REG),
SPI_WAIT_CNT_REG, readl(base_addr + SPI_WAIT_CNT_REG),
SPI_CLK_CTL_REG, readl(base_addr + SPI_CLK_CTL_REG),
SPI_SDC_REG, readl(base_addr + SPI_SDC_REG),
SPI_BURST_CNT_REG, readl(base_addr + SPI_BURST_CNT_REG),
SPI_TRANSMIT_CNT_REG, readl(base_addr + SPI_TRANSMIT_CNT_REG),
SPI_BCC_REG, readl(base_addr + SPI_BCC_REG),
SPI_DMA_CTL_REG, readl(base_addr + SPI_DMA_CTL_REG));
printf("%s\n", buf);
}
#endif
static u32 clock_get_pll6(void)
{
return 600;
}
/* config chip select */
static s32 spi_set_cs(u32 chipselect, void __iomem *base_addr)
{
int ret;
u32 reg_val = readl(base_addr + SPI_TC_REG);
if (chipselect < 4) {
reg_val &= ~SPI_TC_SS_MASK;/* SS-chip select, clear two bits */
reg_val |= chipselect << SPI_TC_SS_BIT_POS;/* set chip select */
writel(reg_val, base_addr + SPI_TC_REG);
ret = SUNXI_SPI_OK;
} else {
SPI_ERR("Chip Select set fail! cs = %d\n", chipselect);
ret = SUNXI_SPI_FAIL;
}
return ret;
}
static void spi_set_sdm(void __iomem *base_addr, unsigned int smod)
{
unsigned int rval = readl(base_addr + SPI_TC_REG) & (~SPI_TC_SDM);
if (smod)
rval |= SPI_TC_SDM;
writel(rval, base_addr + SPI_TC_REG);
}
static void spi_set_sdc(void __iomem *base_addr, unsigned int sample)
{
unsigned int rval = readl(base_addr + SPI_TC_REG) & (~SPI_TC_SDC);
if (sample)
rval |= SPI_TC_SDC;
writel(rval, base_addr + SPI_TC_REG);
}
static void spi_set_sdc1(void __iomem *base_addr, unsigned int sample)
{
unsigned int rval = readl(base_addr + SPI_TC_REG) & (~SPI_TC_SDC1);
if (sample)
rval |= SPI_TC_SDC1;
writel(rval, base_addr + SPI_TC_REG);
}
void spi_set_sample_mode(void __iomem *base_addr, unsigned int mode)
{
unsigned int sample_mode[7] = {
DELAY_NORMAL_SAMPLE, DELAY_0_5_CYCLE_SAMPLE,
DELAY_1_CYCLE_SAMPLE, DELAY_1_5_CYCLE_SAMPLE,
DELAY_2_CYCLE_SAMPLE, DELAY_2_5_CYCLE_SAMPLE,
DELAY_3_CYCLE_SAMPLE
};
spi_set_sdm(base_addr, (sample_mode[mode] >> 8) & 0xf);
spi_set_sdc(base_addr, (sample_mode[mode] >> 4) & 0xf);
spi_set_sdc1(base_addr, sample_mode[mode] & 0xf);
}
void spi_set_sample_delay(void __iomem *base_addr,
unsigned int sample_delay)
{
unsigned int rval = readl(base_addr + SPI_SDC_REG)&(~(0x3f << 0));
rval |= sample_delay;
writel(rval, base_addr + SPI_SDC_REG);
mdelay(1);
}
void spi_samp_dl_sw_status(void __iomem *base_addr, unsigned int status)
{
unsigned int rval = readl(base_addr + SPI_SDC_REG);
if (status)
rval |= SPI_SAMP_DL_SW_EN;
else
rval &= ~SPI_SAMP_DL_SW_EN;
writel(rval, base_addr + SPI_SDC_REG);
}
void spi_samp_mode(void __iomem *base_addr, unsigned int status)
{
unsigned int rval = readl(base_addr + SPI_GC_REG);
if (status)
rval |= SPI_SAMP_MODE_EN;
else
rval &= ~SPI_SAMP_MODE_EN;
writel(rval, base_addr + SPI_GC_REG);
}
/* config spi */
static void spi_config_tc(u32 master, u32 config, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
if (config || !spinor_info || spinor_info->sample_delay ==
SAMP_MODE_DL_DEFAULT) {
if (spi_clk >= 60000000) {
reg_val &= ~(SPI_TC_SDC | SPI_TC_SDM);
reg_val |= SPI_TC_SDC;
} else if (spi_clk <= 24000000) {
reg_val &= ~(SPI_TC_SDC | SPI_TC_SDM);
reg_val |= SPI_TC_SDM;
} else
reg_val &= ~(SPI_TC_SDC | SPI_TC_SDM);
} else {
spi_set_sample_mode(base_addr, spinor_info->sample_mode);
spi_set_sample_delay(base_addr, spinor_info->sample_delay);
reg_val = readl(base_addr + SPI_TC_REG);
}
reg_val |= SPI_TC_DHB | SPI_TC_SS_LEVEL | SPI_TC_SPOL;
writel(reg_val, base_addr + SPI_TC_REG);
#if 0
/*1. POL */
if (config & SPI_POL_ACTIVE_)
reg_val |= SPI_TC_POL;/*default POL = 1 */
else
reg_val &= ~SPI_TC_POL;
/*2. PHA */
if (config & SPI_PHA_ACTIVE_)
reg_val |= SPI_TC_PHA;/*default PHA = 1 */
else
reg_val &= ~SPI_TC_PHA;
/*3. SSPOL,chip select signal polarity */
if (config & SPI_CS_HIGH_ACTIVE_)
reg_val &= ~SPI_TC_SPOL;
else
reg_val |= SPI_TC_SPOL; /*default SSPOL = 1,Low level effect */
/*4. LMTF--LSB/MSB transfer first select */
if (config & SPI_LSB_FIRST_ACTIVE_)
reg_val |= SPI_TC_FBS;
else
reg_val &= ~SPI_TC_FBS;/*default LMTF =0, MSB first */
/*master mode: set DDB,DHB,SMC,SSCTL*/
if (master == 1) {
/*5. dummy burst type */
if (config & SPI_DUMMY_ONE_ACTIVE_)
reg_val |= SPI_TC_DDB;
else
reg_val &= ~SPI_TC_DDB;/*default DDB =0, ZERO */
/*6.discard hash burst-DHB */
if (config & SPI_RECEIVE_ALL_ACTIVE_)
reg_val &= ~SPI_TC_DHB;
else
reg_val |= SPI_TC_DHB;/*default DHB =1, discard unused burst */
/*7. set SMC = 1 , SSCTL = 0 ,TPE = 1 */
reg_val &= ~SPI_TC_SSCTL;
} else {
/* tips for slave mode config */
SPI_INF("slave mode configurate control register.\n");
}
#endif
}
/* set spi clock */
static void spi_set_clk(u32 spi_clk, u32 ahb_clk, void __iomem *base_addr, u32 cdr)
{
u32 reg_val = 0;
u32 div_clk = 0;
SPI_DBG("set spi clock %d, mclk %d\n", spi_clk, ahb_clk);
reg_val = readl(base_addr + SPI_CLK_CTL_REG);
/* CDR2 */
if (cdr) {
div_clk = ahb_clk / (spi_clk * 2) - 1;
reg_val &= ~SPI_CLK_CTL_CDR2;
reg_val |= (div_clk | SPI_CLK_CTL_DRS);
SPI_DBG("CDR2 - n = %d\n", div_clk);
} else { /* CDR1 */
while (ahb_clk > spi_clk) {
div_clk++;
ahb_clk >>= 1;
}
reg_val &= ~(SPI_CLK_CTL_CDR1 | SPI_CLK_CTL_DRS);
reg_val |= (div_clk << 8);
SPI_DBG("CDR1 - n = %d\n", div_clk);
}
writel(reg_val, base_addr + SPI_CLK_CTL_REG);
}
/* start spi transfer */
static void spi_start_xfer(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
reg_val |= SPI_TC_XCH;
writel(reg_val, base_addr + SPI_TC_REG);
}
/* enable spi bus */
static void spi_enable_bus(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_EN;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* disbale spi bus */
static void spi_disable_bus(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val &= ~SPI_GC_EN;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* set master mode */
static void spi_set_master(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_MODE;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* enable transmit pause */
static void spi_enable_tp(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_TP_EN;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* soft reset spi controller */
static void spi_soft_reset(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_SRST;
writel(reg_val, base_addr + SPI_GC_REG);
}
#if 0
/* enable irq type */
static void spi_enable_irq(u32 bitmap, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_INT_CTL_REG);
bitmap &= SPI_INTEN_MASK;
reg_val |= bitmap;
writel(reg_val, base_addr + SPI_INT_CTL_REG);
}
#endif
/* disable irq type */
static void spi_disable_irq(u32 bitmap, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_INT_CTL_REG);
bitmap &= SPI_INTEN_MASK;
reg_val &= ~bitmap;
writel(reg_val, base_addr + SPI_INT_CTL_REG);
}
/* query irq pending */
static u32 spi_qry_irq_pending(void __iomem *base_addr)
{
return (SPI_INT_STA_MASK & readl(base_addr + SPI_INT_STA_REG));
}
/* clear irq pending */
static void spi_clr_irq_pending(u32 pending_bit, void __iomem *base_addr)
{
pending_bit &= SPI_INT_STA_MASK;
writel(pending_bit, base_addr + SPI_INT_STA_REG);
}
/* query txfifo bytes */
static u32 spi_query_txfifo(void __iomem *base_addr)
{
u32 reg_val = (SPI_FIFO_STA_TX_CNT & readl(base_addr + SPI_FIFO_STA_REG));
reg_val >>= SPI_TXCNT_BIT_POS;
return reg_val;
}
/* query rxfifo bytes */
static u32 spi_query_rxfifo(void __iomem *base_addr)
{
u32 reg_val = (SPI_FIFO_STA_RX_CNT & readl(base_addr + SPI_FIFO_STA_REG));
reg_val >>= SPI_RXCNT_BIT_POS;
return reg_val;
}
/* reset fifo */
static void spi_reset_fifo(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_FIFO_CTL_REG);
reg_val |= (SPI_FIFO_CTL_RX_RST|SPI_FIFO_CTL_TX_RST);
/* Set the trigger level of RxFIFO/TxFIFO. */
reg_val &= ~(SPI_FIFO_CTL_RX_LEVEL|SPI_FIFO_CTL_TX_LEVEL);
reg_val |= (0x20<<16) | 0x20;
writel(reg_val, base_addr + SPI_FIFO_CTL_REG);
}
/* set transfer total length BC, transfer length TC and single transmit length STC */
static void spi_set_bc_tc_stc(u32 tx_len, u32 rx_len, u32 stc_len, u32 dummy_cnt, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_BURST_CNT_REG);
reg_val &= ~SPI_BC_CNT_MASK;
reg_val |= (SPI_BC_CNT_MASK & (tx_len + rx_len + dummy_cnt));
writel(reg_val, base_addr + SPI_BURST_CNT_REG);
reg_val = readl(base_addr + SPI_TRANSMIT_CNT_REG);
reg_val &= ~SPI_TC_CNT_MASK;
reg_val |= (SPI_TC_CNT_MASK & tx_len);
writel(reg_val, base_addr + SPI_TRANSMIT_CNT_REG);
reg_val = readl(base_addr + SPI_BCC_REG);
reg_val &= ~SPI_BCC_STC_MASK;
reg_val |= (SPI_BCC_STC_MASK & stc_len);
reg_val &= ~(0xf << 24);
reg_val |= (dummy_cnt << 24);
writel(reg_val, base_addr + SPI_BCC_REG);
}
/* set ss control */
static void spi_ss_owner(void __iomem *base_addr, u32 on_off)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
on_off &= 0x1;
if (on_off)
reg_val |= SPI_TC_SS_OWNER;
else
reg_val &= ~SPI_TC_SS_OWNER;
writel(reg_val, base_addr + SPI_TC_REG);
}
/* set ss level */
static void spi_ss_level(void __iomem *base_addr, u32 hi_lo)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
hi_lo &= 0x1;
if (hi_lo)
reg_val |= SPI_TC_SS_LEVEL;
else
reg_val &= ~SPI_TC_SS_LEVEL;
writel(reg_val, base_addr + SPI_TC_REG);
}
#if 1
static void spi_disable_dual(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val &= ~SPI_BCC_DUAL_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static void spi_enable_dual(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val &= ~SPI_BCC_QUAD_MODE;
reg_val |= SPI_BCC_DUAL_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static void spi_disable_quad(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val &= ~SPI_BCC_QUAD_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static void spi_enable_quad(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val |= SPI_BCC_QUAD_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static int sunxi_spi_mode_check(void __iomem *base_addr, u32 tcnt, u32 rcnt, u8 cmd)
{
/* single mode transmit counter*/
unsigned int stc = 0;
if (SPINOR_OP_READ_1_1_4 == cmd || SPINOR_OP_READ4_1_1_4 == cmd) {
/*tcnt is cmd len, use single mode to transmit*/
/*rcnt is the len of recv data, use quad mode to transmit*/
stc = tcnt;
spi_enable_quad(base_addr);
spi_set_bc_tc_stc(tcnt, rcnt, stc, 0, base_addr);
} else if (SPINOR_OP_READ_1_1_2 == cmd || SPINOR_OP_READ4_1_1_2 == cmd) {
/*tcnt is cmd len, use single mode to transmit*/
/*rcnt is the len of recv data, use dual mode to transmit*/
stc = tcnt;
spi_enable_dual(base_addr);
spi_set_bc_tc_stc(tcnt, rcnt, stc, 0, base_addr);
} else {
/*tcnt is the len of cmd, rcnt is the len of recv data. use single mode to transmit*/
stc = tcnt+rcnt;
spi_disable_dual(base_addr);
spi_disable_quad(base_addr);
spi_set_bc_tc_stc(tcnt, rcnt, stc, 0, base_addr);
}
return 0;
}
#endif
#define SUNXI_GPIO_POSITION(port, port_num) (32 * (port - 1) + port_num)
static int sunxi_spi_gpio_init(void)
{
#ifndef CFG_SUNXI_SBOOT
int i = 0;
#if defined(CONFIG_ARCH_SUN8IW11)
int pin_num = 4;
#else
int pin_num = 6;
#endif
if (BT0_head.prvt_head.storage_gpio[i].port) {
boot_set_gpio(
(normal_gpio_cfg *)BT0_head.prvt_head.storage_gpio, pin_num, 1);
} else {
#endif
#if defined(CONFIG_ARCH_SUN50IW3)
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUN50I_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(5), SUN50I_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN50I_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN50I_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_pull(SUNXI_GPC(5), 1);
#elif defined(CONFIG_ARCH_SUN8IW11)
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUNXI_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(23), SUNXI_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUNXI_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(1), SUNXI_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_pull(SUNXI_GPC(23), 1);
#elif defined(CONFIG_ARCH_SUN8IW16)
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUN50I_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(1), SUN50I_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN50I_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN50I_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(10), SUN50I_GPC_SPI0); /*spi0_cs1*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(13), SUN50I_GPC_SPI0); /*spi0_wp*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(14), SUN50I_GPC_SPI0); /*spi0_hold*/
sunxi_gpio_set_pull(SUNXI_GPC(1), 1);
sunxi_gpio_set_pull(SUNXI_GPC(10), 1);
sunxi_gpio_set_pull(SUNXI_GPC(13), 1);
sunxi_gpio_set_pull(SUNXI_GPC(14), 1);
#elif defined(CONFIG_ARCH_SUN8IW18) || defined(CONFIG_ARCH_SUN50IW9)
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUN50I_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN50I_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN50I_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(4), SUN50I_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(7), SUN50I_GPC_SPI0); /*spi0_cs1*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(15), SUN50I_GPC_SPI0); /*spi0_wp*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(16), SUN50I_GPC_SPI0); /*spi0_hold*/
sunxi_gpio_set_pull(SUNXI_GPC(3), 1);
sunxi_gpio_set_pull(SUNXI_GPC(7), 1);
sunxi_gpio_set_pull(SUNXI_GPC(15), 1);
sunxi_gpio_set_pull(SUNXI_GPC(16), 1);
#elif defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN8IW21)
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUN50I_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(1), SUN50I_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN50I_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN50I_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(4), SUN50I_GPC_SPI0); /*spi0_wp*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(5), SUN50I_GPC_SPI0); /*spi0_hold*/
sunxi_gpio_set_pull(SUNXI_GPC(1), 1);
sunxi_gpio_set_pull(SUNXI_GPC(4), 1);
sunxi_gpio_set_pull(SUNXI_GPC(5), 1);
#elif defined(CONFIG_ARCH_SUN50IW11)
sunxi_gpio_set_cfgpin(SUNXI_GPC(4), SUN50I_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUN50I_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN50I_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(1), SUN50I_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN50I_GPC_SPI0); /*spi0_wp*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(5), SUN50I_GPC_SPI0); /*spi0_hold*/
sunxi_gpio_set_pull(SUNXI_GPC(0), 1);
sunxi_gpio_set_pull(SUNXI_GPC(2), 1);
sunxi_gpio_set_pull(SUNXI_GPC(5), 1);
#elif defined(CONFIG_ARCH_SUN50IW12)
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUN50I_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN50I_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN50I_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(4), SUN50I_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(15), SUN50I_GPC_SPI0); /*spi0_wp*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(16), SUN50I_GPC_SPI0); /*spi0_hold*/
sunxi_gpio_set_pull(SUNXI_GPC(3), 1);
sunxi_gpio_set_pull(SUNXI_GPC(15), 1);
sunxi_gpio_set_pull(SUNXI_GPC(16), 1);
#elif defined(CONFIG_ARCH_SUN8IW20) || defined(CONFIG_ARCH_SUN20IW1P1)
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN20I_GPC_SPI0); /*spi0_sclk */
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN20I_GPC_SPI0); /*spi0_cs0*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(4), SUN20I_GPC_SPI0); /*spi0_mosi*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(5), SUN20I_GPC_SPI0); /*spi0_miso*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(6), SUN20I_GPC_SPI0); /*spi0_wp*/
sunxi_gpio_set_cfgpin(SUNXI_GPC(7), SUN20I_GPC_SPI0); /*spi0_hold*/
sunxi_gpio_set_pull(SUNXI_GPC(3), 1);
sunxi_gpio_set_pull(SUNXI_GPC(6), 1);
sunxi_gpio_set_pull(SUNXI_GPC(7), 1);
#else
#error "spi pinctrl not available for this architecture"
#endif
#ifndef CFG_SUNXI_SBOOT
}
#endif
return 0;
}
static int sunxi_spi_clk_init( u32 bus, u32 mod_clk)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
u32 mclk_base = (((phys_addr_t)&ccm->spi0_clk_cfg) & 0xffffffff) + bus * 0x4;
u32 rval;
__maybe_unused u32 source_clk = 0;
__maybe_unused u32 m, n, div, factor_m;
void __iomem *spi_base = sunxi_get_iobase(mclk_base);
/* SCLK = src/M/N */
/* N: 00:1 01:2 10:4 11:8 */
/* M: factor_m + 1 */
#ifdef FPGA_PLATFORM
n = 0;
m = 1;
div = 1;
factor_m = m - 1;
rval = (1U << 31) | (0x0 << 24) | (n << 8) | factor_m;
source_clk = 24000000;
#else
source_clk = clock_get_pll6() * 1000000;/*pll6, fix me*/
SPI_INF("source_clk: %d Hz\n", source_clk);
div = (source_clk + mod_clk - 1) / mod_clk;
div = div == 0 ? 1 : div;
if (div > 128) {
m = 1;
n = 0;
return -1;
} else if (div > 64) {
n = 3;
m = div >> 3;
} else if (div > 32) {
n = 2;
m = div >> 2;
} else if (div > 16) {
n = 1;
m = div >> 1;
} else {
n = 0;
m = div;
}
factor_m = m - 1;
#if defined(CONFIG_ARCH_SUN8IW11)
rval = (1U << 31) | (0x1 << 24) | (n << 16) | factor_m;
#else
rval = (1U << 31) | (0x1 << 24) | (n << 8) | factor_m;
#endif
/**sclk_freq = source_clk / (1 << n) / m; */
#endif
SPI_INF("source_clk : %d Hz, div =%d, n= %d, m=%d\n", source_clk, div, n, m);
writel(rval, spi_base);
#if defined(CONFIG_ARCH_SUN8IW11)
/* spi reset */
writel(readl(&ccm->ahb_reset0_cfg) & ~(1 << 20), &ccm->ahb_reset0_cfg);
writel(readl(&ccm->ahb_reset0_cfg) | (1 << 20), &ccm->ahb_reset0_cfg);
/* spi gating */
writel(readl(&ccm->ahb_gate0) | (1 << 20), &ccm->ahb_gate0);
#else
/* spi reset */
setbits_le32(&ccm->spi_gate_reset, (0<<RESET_SHIFT));
setbits_le32(&ccm->spi_gate_reset, (1<<RESET_SHIFT));
/* spi gating */
setbits_le32(&ccm->spi_gate_reset, (1<<GATING_SHIFT));
#endif
return 0;
}
static int sunxi_get_spic_clk(int bus)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
u32 mclk_base = (((phys_addr_t)&ccm->spi0_clk_cfg) & 0xffffffff) + bus * 0x4;
u32 reg_val = 0;
u32 src = 0, clk = 0, sclk_freq = 0;
u32 n, m;
void __iomem *spi_base = sunxi_get_iobase(mclk_base);
reg_val = readl(spi_base);
src = (reg_val >> 24)&0x7;
n = (reg_val >> 8)&0x3;
m = ((reg_val >> 0)&0xf) + 1;
switch(src) {
case 0:
clk = 24000000;
break;
case 1:
clk = clock_get_pll6() * 1000000;
break;
default:
clk = 0;
break;
}
sclk_freq = clk / (1 << n) / m;
SPI_INF("sclk_freq= %d Hz,reg_val: %x , n=%d, m=%d\n", sclk_freq, reg_val, n, m);
return sclk_freq;
}
static int sunxi_spi_clk_exit(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
#if defined(CONFIG_ARCH_SUN8IW11)
/* clr spi gating */
writel(readl(&ccm->ahb_gate0) & ~(1 << 20), &ccm->ahb_gate0);
/* clr spi reset */
writel(readl(&ccm->ahb_reset0_cfg) & ~(1 << 20), &ccm->ahb_reset0_cfg);
#else
/* spi gating */
clrbits_le32(&ccm->spi_gate_reset, 1<<GATING_SHIFT);
/* spi reset */
clrbits_le32(&ccm->spi_gate_reset, 1<<RESET_SHIFT);
#endif
return 0;
}
static int sunxi_spi_cpu_writel(void *base_addr, const unsigned char *buf, unsigned int len)
{
#ifndef CONFIG_DMA_ENGINE
unsigned char time;
#endif
unsigned int tx_len = len; /* number of bytes receieved */
unsigned char *tx_buf = (unsigned char *)buf;
unsigned int poll_time = 0x7ffffff;
#if SPI_DEBUG
printf("spi tx: %d bytes\n", len);
/*sunxi_dump(tx_buf, len);*/
#endif
for (; tx_len > 0; --tx_len) {
writeb(*tx_buf++, base_addr + SPI_TXDATA_REG);
if (spi_query_txfifo(base_addr) >= MAX_FIFU)
for (time = 2; 0 < time; --time)
;
}
while (spi_query_txfifo(base_addr) && (--poll_time > 0))
;
if (poll_time <= 0) {
SPI_ERR("cpu transfer data time out!\n");
return -1;
}
return 0;
}
static int sunxi_spi_cpu_readl(void *base_addr, unsigned char *buf, unsigned int len)
{
unsigned int rx_len = len; /* number of bytes sent */
unsigned char *rx_buf = buf;
unsigned int poll_time = 0x7ffffff;
while (rx_len && poll_time ) {
/* rxFIFO counter */
if (spi_query_rxfifo(base_addr) && (--poll_time > 0)) {
*rx_buf++ = readb(base_addr + SPI_RXDATA_REG);
--rx_len;
}
}
if (poll_time <= 0) {
SPI_ERR("cpu receive data time out!\n");
return -1;
}
#if SPI_DEBUG
printf("spi rx: %d bytes\n" , len);
/*sunxi_dump(buf, len);*/
#endif
return 0;
}
int spi_claim_bus(void *base_addr)
{
uint sclk_freq = 0;
SPI_ENTER();
sclk_freq = sunxi_get_spic_clk(0);
if(!sclk_freq)
return -1;
spi_soft_reset(base_addr);
/* 1. enable the spi module */
spi_enable_bus(base_addr);
/* 2. set the default chip select */
spi_set_cs(0, base_addr);
/* 3. master: set spi module clock;
* 4. set the default frequency 10MHz
*/
spi_set_master(base_addr);
spi_set_clk(spi_clk, sclk_freq, base_addr, 0);
/* 5. master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0.
* 6. set bit width-default: 8 bits
*/
spi_config_tc(1, 1, base_addr);
spi_ss_level(base_addr, 1);
spi_enable_tp(base_addr);
/* 7. spi controller sends ss signal automatically*/
spi_ss_owner(base_addr, 0);
/* 8. reset fifo */
spi_reset_fifo(base_addr);
return 0;
}
void spi_release_bus(void* base_addr)
{
SPI_ENTER();
/* disable the spi controller */
spi_disable_bus(base_addr);
/* disable module clock */
sunxi_spi_clk_exit();
/*release gpio*/
}
#ifdef CFG_SPI_USE_DMA
static int spi_dma_recv_start(void *spi_addr, uint spi_no, unchar *pbuf, uint byte_cnt)
{
flush_dcache_range((unsigned long)pbuf, (unsigned long)pbuf + byte_cnt);
sunxi_dma_start(spi_rx_dma_hd, (phys_addr_t)spi_addr + SPI_RXDATA_REG, (phys_addr_t)pbuf, byte_cnt);
return 0;
}
static int spi_wait_dma_recv_over(uint spi_no)
{
return sunxi_dma_querystatus(spi_rx_dma_hd);
}
static int spi_dma_cfg(u32 spi_no)
{
sunxi_dma_init();
spi_rx_dma = &g_spi_rx_dma;
spi_rx_dma_hd = sunxi_dma_request(DMAC_DMATYPE_NORMAL);
if (spi_rx_dma_hd == 0) {
printf("spi request dma failed\n");
return -1;
}
/* config spi rx dma */
spi_rx_dma->loop_mode = 0;
spi_rx_dma->wait_cyc = 0x8;
/* spi_rx_dma->data_block_size = 1 * DMAC_CFG_SRC_DATA_WIDTH_8BIT/8;*/
spi_rx_dma->data_block_size = 1 * 32 / 8;
spi_rx_dma->channal_cfg.src_drq_type = DMAC_CFG_TYPE_SPI0; /* SPI0 */
spi_rx_dma->channal_cfg.src_addr_mode = DMAC_CFG_SRC_ADDR_TYPE_IO_MODE;
spi_rx_dma->channal_cfg.src_burst_length = DMAC_CFG_SRC_8_BURST;
spi_rx_dma->channal_cfg.src_data_width = DMAC_CFG_SRC_DATA_WIDTH_32BIT;
spi_rx_dma->channal_cfg.dst_drq_type = DMAC_CFG_TYPE_DRAM; /* DRAM */
spi_rx_dma->channal_cfg.dst_addr_mode = DMAC_CFG_DEST_ADDR_TYPE_LINEAR_MODE;
spi_rx_dma->channal_cfg.dst_burst_length = DMAC_CFG_DEST_8_BURST;
spi_rx_dma->channal_cfg.dst_data_width = DMAC_CFG_DEST_DATA_WIDTH_32BIT;
return 0;
}
static int sunxi_spi_dma_readl(void *base_addr, unsigned char *buf, unsigned int len)
{
unsigned char *rx_buf = buf;
unsigned int rcnt = len;
ulong ctime = 0;
int ret = 0;
/*#ifdef SUNXI_DMA_SECURITY
if((get_boot_work_mode() != WORK_MODE_BOOT)
&& (sunxi_get_securemode() != SUNXI_NORMAL_MODE))
return sunxi_spi_cpu_readl(sspi, buf, len);
#endif*/
memset((void *)rx_buf, 0, len);
writel((readl(base_addr + SPI_FIFO_CTL_REG) | SPI_FIFO_CTL_RX_DRQEN), base_addr + SPI_FIFO_CTL_REG);
spi_dma_recv_start(base_addr, 0, rx_buf, rcnt);
/* wait DMA finish */
ctime = timer_get_us();
while (1) {
if (spi_wait_dma_recv_over(0) == 0) {
ret = 0;
invalidate_dcache_range((unsigned long)rx_buf, (unsigned long)rx_buf + rcnt);
break;
}
if (timer_get_us() - ctime > 0x4C4B40) {
printf("rx wait_dma_recv_over fail\n");
ret = -1;
break;
}
}
return ret;
}
static int sunxi_spi_dma_disable(void *base_addr)
{
u32 fcr;
sunxi_dma_reg_func(NULL);
/* disable dma req */
fcr = readl(base_addr + SPI_FIFO_CTL_REG);
fcr &= ~(SPI_FIFO_CTL_TX_DRQEN | SPI_FIFO_CTL_RX_DRQEN);
writel(fcr, base_addr + SPI_FIFO_CTL_REG);
return 0;
}
int spi_dma_init(void)
{
if (spi_dma_cfg(CONFIG_SF_DEFAULT_BUS)) {
printf("spi dma cfg error!\n");
return -1;
}
sunxi_dma_install_int(spi_rx_dma_hd, NULL);
sunxi_dma_enable_int(spi_rx_dma_hd);
sunxi_dma_setting(spi_rx_dma_hd, (void *)spi_rx_dma);
return 0;
}
#endif
int set_spi_clk(void)
{
/* clock */
if (spinor_info->frequency) {
spi_clk = spinor_info->frequency;
if (sunxi_spi_clk_init(0, spi_clk))
return -1;
/* spi hardware init*/
if (spi_claim_bus((void *)SUNXI_SPI0_BASE))
return -1;
printf("set spi freq:%d\n", spinor_info->frequency);
spi_config_tc(1, 0, (void *)SUNXI_SPI0_BASE);
}
return 0;
}
int spi_init(void)
{
SPI_ENTER();
#ifdef CFG_SPI_USE_DMA
if (spi_dma_init())
return -1;
#endif
/* gpio */
sunxi_spi_gpio_init();
/* clock */
if (sunxi_spi_clk_init(0, SUNXI_SPI_DEFAULT_CLK))
return -1;
/* spi hardware init*/
if(spi_claim_bus((void*)SUNXI_SPI0_BASE))
return -1;
return 0;
}
void spi_exit(void)
{
spi_release_bus((void*)SUNXI_SPI0_BASE);
}
int spi_xfer( unsigned int tx_len, const void *dout, unsigned int rx_len, void *din)
{
int timeout = 0xfffff;
void __iomem *base_addr = (void*)SUNXI_SPI0_BASE;
unsigned int tcnt = 0, rcnt = 0;
u8 cmd = *(u8*)dout;
SPI_ENTER();
/* No data */
if (!din && !dout)
return 0;
rcnt = rx_len;
tcnt = tx_len;
spi_disable_irq(0xffffffff, base_addr);
spi_clr_irq_pending(0xffffffff, base_addr);
/*spi_set_bc_tc_stc(tcnt, rcnt, stc, 0, base_addr);*/
sunxi_spi_mode_check(base_addr, tcnt, rcnt, cmd);
//spi_config_tc(1, 0, base_addr);
spi_ss_level(base_addr, 1);
spi_start_xfer(base_addr);
if (tcnt) {
if (sunxi_spi_cpu_writel(base_addr, dout, tcnt))
return -1;
}
/* recv data */
if (rcnt) {
#ifdef CFG_SPI_USE_DMA
if (rcnt <= 64) {
if (sunxi_spi_cpu_readl(base_addr, din, rcnt))
return -1;
} else {
if (sunxi_spi_dma_readl(base_addr, din, rcnt))
return -1;
}
#else
if (sunxi_spi_cpu_readl(base_addr, din, rcnt))
return -1;
#endif
}
/* check int status error */
if (spi_qry_irq_pending(base_addr) & SPI_INT_STA_ERR) {
printf("int stauts error");
return -1;
}
/* check tx/rx finish */
timeout = 0xfffff;
/* wait transfer complete */
while (!(spi_qry_irq_pending(base_addr)&SPI_INT_STA_TC)) {
timeout--;
if (!timeout) {
printf("SPI_ISR time_out \n");
return -1;
}
}
#ifdef CFG_SPI_USE_DMA
sunxi_spi_dma_disable(base_addr);
#endif
/* check SPI_EXCHANGE when SPI_MBC is 0 */
if (readl(base_addr + SPI_BURST_CNT_REG) == 0) {
if (readl(base_addr + SPI_TC_REG) & SPI_TC_XCH) {
printf("XCH Control Error!!\n");
return -1;
}
} else {
printf("SPI_MBC Error!\n");
return -1;
}
spi_clr_irq_pending(0xffffffff, base_addr);
return 0;
}