sdk-hwV1.3/lichee/brandy-2.0/u-boot-2018/drivers/spi/sunxi_spi.c

1708 lines
44 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0+
/*
* 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 <dm.h>
#include <malloc.h>
#include <memalign.h>
#include <spi.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/sys_proto.h>
#ifdef CONFIG_SUNXI_DMA
#include <asm/arch/dma.h>
#endif
#include <asm/arch/gpio.h>
#include <sunxi_board.h>
#include "spi-sunxi.h"
#include <sys_config.h>
#include <fdt_support.h>
#include <linux/mtd/spi-nor.h>
#include <private_boot0.h>
#include <private_toc.h>
#include "../mtd/spi/sf_internal.h"
#include <linux/sizes.h>
#include <boot_param.h>
#ifdef CONFIG_SPI_USE_DMA
static sunxi_dma_set *spi_tx_dma;
static sunxi_dma_set *spi_rx_dma;
static uint spi_tx_dma_hd;
static uint spi_rx_dma_hd;
#endif
#define SUNXI_SPI_MAX_TIMEOUT 1000000
#define SUNXI_SPI_PORT_OFFSET 0x1000
#define SUNXI_SPI_DEFAULT_CLK (40000000)
/* 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() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Exit")
#define SPI_ENTER() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
#define SPI_DBG(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SPI_INF(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SPI_ERR(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
#endif
#define SUNXI_SPI_OK 0
#define SUNXI_SPI_FAIL -1
static int sunxi_get_spic_clk(unsigned int bus);
struct sunxi_spi_slave *g_sspi[SPI_MAX_BUS];
struct sunxi_spi_slave *get_sspi(unsigned int bus)
{
struct sunxi_spi_slave *sspi;
if (bus < 0 || bus >= SPI_MAX_BUS)
sspi = NULL;
else
sspi = g_sspi[bus];
return sspi;
}
#if SPI_DEBUG
static void spi_print_info(struct sunxi_spi_slave *sspi)
{
void __iomem *base_addr = (void __iomem *)(unsigned long)sspi->base_addr;
char buf[1024] = {0};
snprintf(buf, sizeof(buf)-1,
"sspi->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",
sspi->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 inline struct sunxi_spi_slave *to_sunxi_slave(struct spi_slave *slave)
{
return container_of(slave, struct sunxi_spi_slave, slave);
}
static s32 sunxi_spi_gpio_request(unsigned int bus)
{
int ret = 0;
char spi_name[10];
sprintf(spi_name, "spi%d", bus);
ret = fdt_set_all_pin(spi_name, "pinctrl-0");
if (ret <= 0) {
SPI_INF("set pin of spi%d fail %d\n", bus, ret);
return -1;
}
return 0;
}
static s32 sunxi_get_spi_mode(unsigned int bus)
{
int nodeoffset = 0;
int ret = 0;
u32 rval = 0;
u32 mode = 0;
char spi_board[20];
sprintf(spi_board, "spi%d/spi_board%d", bus, bus);
nodeoffset = fdt_path_offset(working_fdt, spi_board);
if (nodeoffset < 0) {
SPI_INF("get spi%d para fail\n", bus);
return -1;
}
ret = fdt_getprop_u32(working_fdt, nodeoffset, "spi-rx-bus-width", (uint32_t *)(&rval));
if (ret < 0) {
SPI_INF("get spi-rx-bus-width fail %d\n", ret);
return -2;
}
if (rval == 1) {
mode |= SPI_RX_SLOW;
} else if (rval == 2) {
mode |= SPI_RX_DUAL;
} else if (rval == 4) {
mode |= SPI_RX_QUAD;
}
ret = fdt_getprop_u32(working_fdt, nodeoffset, "spi-tx-bus-width", (uint32_t *)(&rval));
if (ret < 0) {
SPI_INF("get spi-tx-bus-width fail %d\n", ret);
return -3;
}
if (rval == 1) {
mode |= SPI_TX_BYTE;
} else if (rval == 2) {
mode |= SPI_TX_DUAL;
} else if (rval == 4) {
mode |= SPI_TX_QUAD;
}
SPI_INF("get spi-bus-width 0x%x\n", mode);
return mode;
}
/* 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);
}
static 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);
}
static 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);
}
/* config spi */
static void spi_config_tc(struct sunxi_spi_slave *sspi, u32 master,
u32 config, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
uint sclk_freq = 0;
sclk_freq = sunxi_get_spic_clk(sspi->bus);
if (sspi->right_sample_delay == SAMP_MODE_DL_DEFAULT) {
if (sclk_freq >= 60000000)
reg_val |= SPI_TC_SDC;
else if (sclk_freq <= 24000000)
reg_val |= SPI_TC_SDM;
else
reg_val &= ~(SPI_TC_SDM | SPI_TC_SDC);
} else {
spi_set_sample_mode(base_addr, sspi->right_sample_mode);
spi_set_sample_delay(base_addr, sspi->right_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
* cdr1: spi_sclk =source_clk/(2^cdr1_M)
* cdr2: spi_sclk =source_clk/(2*(cdr2_N+1))
* spi_clk : the spi final clk.
* ahb_clk: source_clk - the clk from ccmu config, spic clk src is osc24M or peri0_1X.
*/
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);
}
/* set dhb, 1: discard unused spi burst; 0: receiving all spi burst */
static void spi_set_all_burst_received(void __iomem *base_addr, u32 on_off)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
if (on_off)
reg_val |= SPI_TC_DHB;
else
reg_val &= ~SPI_TC_DHB;
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);
}
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);
}
static 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);
}
static int sunxi_spi_mode_check(void __iomem *base_addr, u32 tcnt, u32 rcnt, u32 cmdcnt, u8 cmd)
{
spi_set_all_burst_received(base_addr, 1);
if (SPINOR_OP_READ_1_1_4 == cmd || SPINOR_OP_READ_1_1_4_4B == cmd ||
SPINOR_OP_PP_1_1_4 == cmd || SPINOR_OP_PP_1_1_4_4B == cmd) {
/*tcnt is cmd len, use single mode to transmit*/
/*rcnt is the len of recv data, use quad mode to transmit*/
spi_disable_dual(base_addr);
spi_enable_quad(base_addr);
spi_set_bc_tc_stc(cmdcnt+tcnt, rcnt, cmdcnt, 0, base_addr);
} else if (SPINOR_OP_READ_1_1_2 == cmd || SPINOR_OP_READ_1_1_2_4B == cmd) {
/*tcnt is cmd len, use single mode to transmit*/
/*rcnt is the len of recv data, use dual mode to transmit*/
spi_disable_quad(base_addr);
spi_enable_dual(base_addr);
spi_set_bc_tc_stc(cmdcnt+tcnt, rcnt, cmdcnt, 0, base_addr);
} else {
/*tcnt is the len of cmd, rcnt is the len of recv data. use single mode to transmit*/
spi_disable_dual(base_addr);
spi_disable_quad(base_addr);
if (!cmdcnt && tcnt && rcnt) {
/* full duplex */
spi_set_all_burst_received(base_addr, 0);
spi_set_bc_tc_stc(tcnt, 0, tcnt, 0, base_addr);
} else {
/* half duplex transmit */
spi_set_bc_tc_stc(cmdcnt+tcnt, rcnt, cmdcnt+tcnt, 0, base_addr);
}
}
return 0;
}
#endif
/* check the valid of cs id */
static int sunxi_spi_check_cs(unsigned int bus, unsigned int cs)
{
return SUNXI_SPI_OK;
}
static int sunxi_spi_gpio_init(unsigned int bus)
{
#if 0
#if defined(CONFIG_MACH_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_MACH_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);
#elif defined(CONFIG_MACH_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_MACH_SUN8IW18)
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(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);
#elif defined(CONFIG_MACH_SUN8IW19)
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);
#else
sunxi_spi_gpio_request(bus);
#endif
#else
sunxi_spi_gpio_request(bus);
#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;
unsigned long mclk_base = (unsigned long)&ccm->spi0_clk_cfg + bus*0x4;
u32 source_clk = 0;
u32 rval;
u32 m, n, div, factor_m;
#ifndef CONFIG_MACH_SUN8IW11
u8 reset_shift;
u8 gating_shift;
#endif
/* SCLK = src/M/N */
/* N: 00:1 01:2 10:4 11:8 */
/* M: factor_m + 1 */
#ifdef FPGA_PLATFORM
div = 1;
n = 0;
m = div;
factor_m = m - 1;
rval = (1U << 31) | (n << 8) | factor_m;
source_clk = 24000000;
#else
#if defined(CONFIG_MACH_SUN8IW21) || defined(CONFIG_MACH_SUN55IW3)
source_clk = 300000000;
#else
source_clk = clock_get_pll6() * 1000000;
#endif
SPI_DBG("source_clk: %d Hz, mod_clk: %d Hz\n", source_clk, mod_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_MACH_SUN8IW11)
rval = (1U << 31) | (0x1 << 24) | (n << 16) | factor_m;
#else
rval = (1U << 31) | (0x1 << 24) | (n << 8) | factor_m;
#endif
#endif
writel(rval, (volatile void __iomem *)mclk_base);
#if defined(CONFIG_MACH_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
reset_shift = RESET_SHIFT + bus;
gating_shift = GATING_SHIFT + bus;
/* 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));
/*sclk_freq = source_clk / (1 << n) / m*/
#endif
SPI_DBG("src: %d Hz, spic:%d Hz, n=%d, m=%d\n",source_clk, source_clk/ (1 << n) / m, n, m);
return 0;
}
static int sunxi_get_spic_clk(unsigned int bus)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
unsigned long mclk_base = (unsigned long)&ccm->spi0_clk_cfg + bus*0x4;
u32 reg_val = 0;
u32 src = 0, clk = 0, sclk_freq = 0;
u32 n, m;
reg_val = readl((volatile void __iomem *)mclk_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:
#if defined(CONFIG_MACH_SUN8IW21) || defined(CONFIG_MACH_SUN55IW3)
clk = 300000000;
#else
clk = clock_get_pll6() * 1000000;
#endif
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_MACH_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(struct sunxi_spi_slave *sspi, const unsigned char *buf, unsigned int len)
{
void __iomem *base_addr = (void __iomem *)(unsigned long)sspi->base_addr;
#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(struct sunxi_spi_slave *sspi, unsigned char *buf, unsigned int len)
{
void __iomem *base_addr = (void __iomem *)(unsigned long)sspi->base_addr;
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)) {
*rx_buf++ = readb(base_addr + SPI_RXDATA_REG);
--rx_len;
} else {
--poll_time;
}
}
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;
}
#ifdef CONFIG_SPI_USE_DMA
static int spi_dma_recv_start(void *spi_addr, uint spi_no, uchar *pbuf, uint byte_cnt)
{
flush_cache((ulong)pbuf, byte_cnt);
sunxi_dma_start(spi_rx_dma_hd, (ulong)spi_addr + SPI_RXDATA_REG, (ulong)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_send_start(void *spi_addr, uint spi_no, uchar *pbuf, uint byte_cnt)
{
flush_cache((ulong)pbuf, byte_cnt);
sunxi_dma_start(spi_tx_dma_hd, (ulong)pbuf, (ulong)spi_addr + SPI_TXDATA_REG, byte_cnt);
return 0;
}
static int spi_wait_dma_send_over(uint spi_no)
{
return sunxi_dma_querystatus(spi_tx_dma_hd);
}
static int spi_dma_cfg(u32 spi_no)
{
spi_rx_dma = malloc(sizeof(sunxi_dma_set));
spi_tx_dma = malloc(sizeof(sunxi_dma_set));
if (!(spi_rx_dma) || !(spi_tx_dma)) {
printf("no enough memory to malloc \n");
return -1;
}
memset(spi_tx_dma, 0, sizeof(sunxi_dma_set));
memset(spi_rx_dma, 0, sizeof(sunxi_dma_set));
spi_rx_dma_hd = sunxi_dma_request(DMAC_DMATYPE_NORMAL);
spi_tx_dma_hd = sunxi_dma_request(DMAC_DMATYPE_NORMAL);
if ((spi_tx_dma_hd == 0) || (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_1_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_1_BURST;
spi_rx_dma->channal_cfg.dst_data_width = DMAC_CFG_DEST_DATA_WIDTH_32BIT;
spi_tx_dma->loop_mode = 0;
spi_tx_dma->wait_cyc = 0x8;
/* spi_tx_dma->data_block_size = 1 * DMAC_CFG_SRC_DATA_WIDTH_8BIT/8; */
spi_tx_dma->data_block_size = 1 * 32 / 8;
spi_tx_dma->channal_cfg.src_drq_type = DMAC_CFG_TYPE_DRAM;
spi_tx_dma->channal_cfg.src_addr_mode = DMAC_CFG_SRC_ADDR_TYPE_LINEAR_MODE;
spi_tx_dma->channal_cfg.src_burst_length = DMAC_CFG_SRC_1_BURST;
spi_tx_dma->channal_cfg.src_data_width = DMAC_CFG_SRC_DATA_WIDTH_32BIT;
spi_tx_dma->channal_cfg.dst_drq_type = DMAC_CFG_TYPE_SPI0; /* SPI0 */
spi_tx_dma->channal_cfg.dst_addr_mode = DMAC_CFG_DEST_ADDR_TYPE_IO_MODE;
spi_tx_dma->channal_cfg.dst_burst_length = DMAC_CFG_DEST_1_BURST;
spi_tx_dma->channal_cfg.dst_data_width = DMAC_CFG_DEST_DATA_WIDTH_32BIT;
//spi_tx_dma->wait_cyc = 0x10;
return 0;
}
static int sunxi_spi_dma_writel(struct sunxi_spi_slave *sspi, const unsigned char *buf, unsigned int len)
{
void __iomem *base_addr = (void *)(ulong)sspi->base_addr;
unsigned int tcnt = len;
unsigned char *tx_buf = (unsigned char *)buf;
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_writel(sspi, buf, len);
#endif
writel((readl(base_addr + SPI_FIFO_CTL_REG) | SPI_FIFO_CTL_TX_DRQEN), base_addr + SPI_FIFO_CTL_REG);
spi_dma_send_start(base_addr, 0, tx_buf, tcnt);
/* wait DMA finish */
ctime = get_timer(0);
while (1) {
if (spi_wait_dma_send_over(0) == 0) {
ret = 0;
break;
}
if (get_timer(ctime) > 1000) {
printf("tx wait_dma_send_over fail\n");
ret = -1;
break;
}
}
return ret;
}
static int sunxi_spi_dma_readl(struct sunxi_spi_slave *sspi, unsigned char *buf, unsigned int len)
{
void __iomem *base_addr = (void *)(ulong)sspi->base_addr;
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
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 = get_timer(0);
while (1) {
if (spi_wait_dma_recv_over(0) == 0) {
ret = 0;
invalidate_dcache_range((ulong)rx_buf, (ulong)rx_buf + rcnt);
break;
}
if (get_timer(ctime) > 5000) {
printf("rx wait_dma_recv_over fail\n");
ret = -1;
break;
}
}
return ret;
}
static int sunxi_spi_dma_disable(struct sunxi_spi_slave *sspi)
{
void __iomem *base_addr = (void *)(ulong)sspi->base_addr;
u32 fcr;
/* 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;
}
static void sunxi_dma_isr(void *p_arg)
{
/* printf("dma int occur\n"); */
}
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, sunxi_dma_isr, NULL);
sunxi_dma_install_int(spi_tx_dma_hd, sunxi_dma_isr, NULL);
sunxi_dma_enable_int(spi_rx_dma_hd);
sunxi_dma_enable_int(spi_tx_dma_hd);
sunxi_dma_setting(spi_rx_dma_hd, (void *)spi_rx_dma);
sunxi_dma_setting(spi_tx_dma_hd, (void *)spi_tx_dma);
return 0;
}
#endif
int spi_init(void)
{
SPI_ENTER();
#ifdef CONFIG_SPI_USE_DMA
return spi_dma_init();
#endif
return 0;
}
int spi_gain_config_clk(int bus)
{
int ret = 0, nodeoffset = 0;
u32 freq = 0;
char spi_board[20];
sprintf(spi_board, "spi%d", bus);
nodeoffset = fdt_path_offset(working_fdt, spi_board);
if (nodeoffset < 0) {
SPI_INF("get spi%d para fail\n", bus);
} else {
ret = fdt_getprop_u32(working_fdt, nodeoffset,
"clock-frequency", (uint32_t *)(&freq));
if (ret < 0) {
SPI_INF("get clock-frequency fail %d\n", ret);
freq = CONFIG_SF_DEFAULT_SPEED;
}
}
SPI_INF("spi get clock-frequency:%d\n", freq);
return freq;
}
int spi_init_all(void)
{
SPI_ENTER();
#if defined(CONFIG_SUNXI_SPI)
int bus_num;
char *spi_status;
int nodeoffset = 0, ret = 0;
char spi_name[10];
struct spi_slave *bus;
for (bus_num = 1; bus_num < SPI_MAX_BUS; bus_num++) {
sprintf(spi_name, "spi%d", bus_num);
nodeoffset = fdt_path_offset(working_fdt, spi_name);
if (nodeoffset < 0) {
SPI_INF("get spi%d para fail\n", bus_num);
continue;
} else {
ret = fdt_getprop_string(working_fdt, nodeoffset,
"status", &spi_status);
if (ret < 0 || (strcmp(spi_status, "okay") != 0)) {
SPI_INF("spi%d status not okay\n", ret);
continue;
}
}
bus = spi_setup_slave(bus_num, CONFIG_SF_DEFAULT_CS,
spi_gain_config_clk(bus_num), CONFIG_SF_DEFAULT_MODE);
if (!bus) {
SPI_INF("setup spi%d\n", bus_num);
continue;
}
ret = spi_claim_bus(bus);
if (ret) {
debug("SF: Failed to claim SPI bus: %d\n", ret);
return ret;
}
}
#endif
return 0;
}
void spi_init_clk(struct spi_slave *slave)
{
struct sunxi_spi_slave *sunxi_slave = to_sunxi_slave(slave);
int ret = 0, nodeoffset = 0;
u32 rval = 0;
char spi_board[20];
sprintf(spi_board, "spi%d/spi_board%d", sunxi_slave->bus, sunxi_slave->bus);
nodeoffset = fdt_path_offset(working_fdt, spi_board);
if (nodeoffset < 0) {
SPI_INF("get spi0 para fail\n");
} else {
ret = fdt_getprop_u32(working_fdt, nodeoffset,
"spi-max-frequency", (uint32_t *)(&rval));
if (ret < 0) {
SPI_INF("get spi-max-frequency fail %d\n", ret);
} else
sunxi_slave->max_hz = rval;
}
pr_force("spi sunxi_slave->max_hz:%d \n", sunxi_slave->max_hz);
/* clock */
if (sunxi_spi_clk_init(sunxi_slave->bus, sunxi_slave->max_hz))
pr_err("spi clk init error\n");
spi_config_tc(sunxi_slave, 1, SPI_MODE_3,
(void __iomem *)(unsigned long)sunxi_slave->base_addr);
return;
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
if (sunxi_spi_check_cs(bus, cs) == SUNXI_SPI_OK)
return 1;
else
return 0;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct sunxi_spi_slave *sunxi_slave;
int ret = 0;
SPI_ENTER();
if (!spi_cs_is_valid(bus, cs)) {
printf("sunxi_spi: invalid bus %d / chip select %d\n", bus, cs);
return NULL;
}
sunxi_slave = spi_alloc_slave(struct sunxi_spi_slave, bus, cs);
if (!sunxi_slave)
return NULL;
sunxi_slave->bus = bus;
sunxi_slave->max_hz = max_hz;
sunxi_slave->mode = mode;
sunxi_slave->cdr = 0;
sunxi_slave->base_addr = SUNXI_SPI0_BASE + bus*SUNXI_SPI_PORT_OFFSET;
sunxi_slave->right_sample_delay = SAMP_MODE_DL_DEFAULT;
sunxi_slave->right_sample_mode = SAMP_MODE_DL_DEFAULT;
sunxi_slave->rx_dtr_en = 0;
sunxi_slave->tx_dtr_en = 0;
sunxi_slave->slave.mode = mode;
g_sspi[sunxi_slave->bus] = sunxi_slave;
/* gpio */
sunxi_spi_gpio_init(sunxi_slave->bus);
/*rx/tx bus width*/
ret = sunxi_get_spi_mode(sunxi_slave->bus);
if (ret > 0) {
sunxi_slave->mode = ret;
sunxi_slave->slave.mode = ret;
}
/* clock */
if (sunxi_spi_clk_init(sunxi_slave->bus, sunxi_slave->max_hz)) {
free(sunxi_slave);
return NULL;
}
spi_config_tc(sunxi_slave, 1, SPI_MODE_3,
(void __iomem *)(unsigned long)sunxi_slave->base_addr);
return &sunxi_slave->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct sunxi_spi_slave *sunxi_slave = to_sunxi_slave(slave);
SPI_ENTER();
free(sunxi_slave);
/* disable module clock */
sunxi_spi_clk_exit();
/*release gpio*/
}
int spi_claim_bus(struct spi_slave *slave)
{
struct sunxi_spi_slave *sspi = to_sunxi_slave(slave);
void __iomem *base_addr = (void __iomem *)(unsigned long)sspi->base_addr;
uint sclk_freq = 0;
SPI_ENTER();
sclk_freq = sunxi_get_spic_clk(sspi->bus);
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(sspi->max_hz, sclk_freq, base_addr, sspi->cdr);
/* 5. master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0.
* 6. set bit width-default: 8 bits
*/
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(struct spi_slave *slave)
{
struct sunxi_spi_slave *sspi = to_sunxi_slave(slave);
SPI_ENTER();
/* disable the spi controller */
spi_disable_bus((void __iomem *)(unsigned long)sspi->base_addr);
}
void sunxi_update_right_delay_para(struct mtd_info *mtd)
{
struct spi_nor *nor = mtd->priv;
struct spi_slave *slave = nor->spi;
struct sunxi_spi_slave *sspi = to_sunxi_slave(slave);
void __iomem *base_addr = (void *)(ulong)sspi->base_addr;
unsigned int sample_delay;
unsigned int start_ok = 0, end_ok = 0, len_ok = 0, mode_ok = 0;
unsigned int start_backup = 0, end_backup = 0, len_backup = 0;
unsigned int mode = 0, startry_mode = 0, endtry_mode = 6, block = 0;
u8 erase_opcode = nor->erase_opcode;
uint32_t erasesize = mtd->erasesize;
if (mtd->size > SZ_16M)
nor->erase_opcode = SPINOR_OP_BE_32K_4B;
else
nor->erase_opcode = SPINOR_OP_BE_32K;
mtd->erasesize = 32 * 1024;
size_t retlen;
u_char *cache_source;
u_char *cache_target;
u_char *cache_boot0;
int len = nor->page_size;
struct erase_info instr;
instr.addr = block * mtd->erasesize;
instr.len = (endtry_mode - startry_mode + 1) * mtd->erasesize;
//sspi->msglevel &= ~sspi_MSG_EN;
cache_boot0 = malloc_align(instr.len, 64);
mtd->_read(mtd, instr.addr, instr.len, &retlen, cache_boot0);
mtd->_erase(mtd, &instr);
/* re-initialize from device tree */
spi_init_clk(slave);
cache_source = malloc_align(len, 64);
cache_target = malloc_align(len, 64);
memset(cache_source, 0xa5, len);
spi_samp_mode(base_addr, 1);
spi_samp_dl_sw_status(base_addr, 1);
for (mode = startry_mode; mode <= endtry_mode; mode++) {
spi_set_sample_mode(base_addr, mode);
for (sample_delay = 0; sample_delay < 64; sample_delay++) {
spi_set_sample_delay(base_addr, sample_delay);
mtd->_write(mtd, block * mtd->erasesize +
sample_delay * nor->page_size,
len, &retlen, cache_source);
}
for (sample_delay = 0; sample_delay < 64; sample_delay++) {
spi_set_sample_delay(base_addr, sample_delay);
memset(cache_target, 0, len);
mtd->_read(mtd, block * mtd->erasesize +
sample_delay * nor->page_size,
len, &retlen, cache_target);
if (strncmp((char *)cache_source, (char *)cache_target,
len) == 0) {
pr_debug("mode:%d delat:%d [OK]\n",
mode, sample_delay);
if (!len_backup) {
start_backup = sample_delay;
end_backup = sample_delay;
} else
end_backup = sample_delay;
len_backup++;
} else {
pr_debug("mode:%d delay:%d [ERROR]\n",
mode, sample_delay);
if (!start_backup)
continue;
else {
if (len_backup > len_ok) {
len_ok = len_backup;
start_ok = start_backup;
end_ok = end_backup;
mode_ok = mode;
}
len_backup = 0;
start_backup = 0;
end_backup = 0;
}
}
}
if (len_backup > len_ok) {
len_ok = len_backup;
start_ok = start_backup;
end_ok = end_backup;
mode_ok = mode;
}
len_backup = 0;
start_backup = 0;
end_backup = 0;
block++;
}
if (!len_ok) {
spi_samp_mode(base_addr, 0);
spi_samp_dl_sw_status(base_addr, 0);
if ((sspi->max_hz / 1000 / 1000) >= 60)
sspi->right_sample_mode = 2;
else if ((sspi->max_hz / 1000 / 1000) <= 24)
sspi->right_sample_mode = 0;
else
sspi->right_sample_mode = 1;
} else {
sspi->right_sample_delay = (start_ok + end_ok) / 2;
sspi->right_sample_mode = mode_ok;
spi_set_sample_delay(base_addr, sspi->right_sample_delay);
}
pr_info("Sample mode:%d start:%d end:%d right_sample_delay:0x%x\n",
mode_ok, start_ok, end_ok,
sspi->right_sample_delay);
spi_set_sample_mode(base_addr, sspi->right_sample_mode);
mtd->_write(mtd, instr.addr, instr.len, &retlen, cache_boot0);
//sspi->msglevel |= sspi_MSG_EN;
nor->erase_opcode = erase_opcode;
mtd->erasesize = erasesize;
free_align(cache_source);
free_align(cache_target);
free_align(cache_boot0);
return ;
}
#if defined(CONFIG_SPI_SAMP_DL_EN) && defined(CONFIG_SUNXI_SPINOR)
void boot_try_delay_param(struct mtd_info *mtd, boot_spinor_info_t *boot_info)
{
struct spi_nor *nor = mtd->priv;
struct spi_slave *slave = nor->spi;
struct sunxi_spi_slave *sspi = to_sunxi_slave(slave);
void __iomem *base_addr = (void *)(ulong)sspi->base_addr;
unsigned int sample_delay;
unsigned int start_ok = 0, end_ok = 0, len_ok = 0, mode_ok = 0;
unsigned int start_backup = 0, end_backup = 0, len_backup = 0;
unsigned int mode = 0, startry_mode = 0, endtry_mode = 6;
size_t retlen, len = 512;
boot0_file_head_t *boot0_head;
boot0_head = malloc_align(len, 64);
/* re-initialize from device tree */
spi_init_clk(slave);
spi_samp_mode(base_addr, 1);
spi_samp_dl_sw_status(base_addr, 1);
for (mode = startry_mode; mode <= endtry_mode; mode++) {
spi_set_sample_mode(base_addr, mode);
for (sample_delay = 0; sample_delay < 64; sample_delay++) {
spi_set_sample_delay(base_addr, sample_delay);
memset(boot0_head, 0, len);
mtd->_read(mtd, 0, len, &retlen, (u_char *)boot0_head);
if (strncmp((char *)boot0_head->boot_head.magic,
(char *)BOOT0_MAGIC,
sizeof(boot0_head->boot_head.magic)) == 0) {
pr_debug("mode:%d delat:%d [OK]\n",
mode, sample_delay);
if (!len_backup) {
start_backup = sample_delay;
end_backup = sample_delay;
} else
end_backup = sample_delay;
len_backup++;
} else {
pr_debug("mode:%d delay:%d [ERROR]\n",
mode, sample_delay);
if (!start_backup)
continue;
else {
if (len_backup > len_ok) {
len_ok = len_backup;
start_ok = start_backup;
end_ok = end_backup;
mode_ok = mode;
}
len_backup = 0;
start_backup = 0;
end_backup = 0;
}
}
}
if (len_backup > len_ok) {
len_ok = len_backup;
start_ok = start_backup;
end_ok = end_backup;
mode_ok = mode;
}
len_backup = 0;
start_backup = 0;
end_backup = 0;
}
if (!len_ok) {
spi_samp_mode(base_addr, 0);
spi_samp_dl_sw_status(base_addr, 0);
if ((sspi->max_hz / 1000 / 1000) >= 60)
sspi->right_sample_mode = 2;
else if ((sspi->max_hz / 1000 / 1000) <= 24)
sspi->right_sample_mode = 0;
else
sspi->right_sample_mode = 1;
} else {
sspi->right_sample_delay = (start_ok + end_ok) / 2;
sspi->right_sample_mode = mode_ok;
spi_set_sample_delay(base_addr, sspi->right_sample_delay);
}
pr_info("Sample mode:%d start:%d end:%d right_sample_delay:0x%x\n",
mode_ok, start_ok, end_ok,
sspi->right_sample_delay);
spi_set_sample_mode(base_addr, sspi->right_sample_mode);
boot_info->sample_delay = sspi->right_sample_delay;
boot_info->sample_mode = sspi->right_sample_mode;
free_align(boot0_head);
return;
}
extern int update_boot_param(struct spi_nor *flash);
int sunxi_set_right_delay_para(struct mtd_info *mtd)
{
struct spi_nor *nor = mtd->priv;
struct spi_slave *slave = nor->spi;
struct sunxi_spi_slave *sspi = to_sunxi_slave(slave);
void __iomem *base_addr = (void *)(ulong)sspi->base_addr;
boot_spinor_info_t *boot_info = NULL;
size_t retlen;
struct sunxi_boot_param_region *boot_param = NULL;
boot_param = malloc_align(BOOT_PARAM_SIZE, 64);
mtd->_read(mtd, sunxi_flashmap_offset(FLASHMAP_SPI_NOR, BOOT_PARAM) << 9,
sunxi_flashmap_size(FLASHMAP_SPI_NOR, BOOT_PARAM) << 9, &retlen, (u_char *)boot_param);
boot_info = (boot_spinor_info_t *)boot_param->spiflash_info;
if (strncmp((const char *)boot_param->header.magic,
(const char *)BOOT_PARAM_MAGIC,
sizeof(boot_param->header.magic)) ||
strncmp((const char *)boot_info->magic,
(const char *)SPINOR_BOOT_PARAM_MAGIC,
sizeof(boot_info->magic))) {
boot_try_delay_param(mtd, boot_info);
if (update_boot_param(nor))
printf("update boot param error\n");
}
if (boot_info->sample_delay == SAMP_MODE_DL_DEFAULT) {
boot_try_delay_param(mtd, boot_info);
if (update_boot_param(nor))
printf("update boot param error\n");
}
pr_force("spi sample_mode:%x sample_delay:%x\n",
boot_info->sample_mode, boot_info->sample_delay);
spi_samp_mode(base_addr, 1);
spi_samp_dl_sw_status(base_addr, 1);
spi_set_sample_mode(base_addr, boot_info->sample_mode);
spi_set_sample_delay(base_addr, boot_info->sample_delay);
sspi->right_sample_delay = boot_info->sample_delay;
sspi->right_sample_mode = boot_info->sample_mode;
spi_init_clk(slave);
free_align(boot_param);
return 0;
}
#endif
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct sunxi_spi_slave *sspi = to_sunxi_slave(slave);
unsigned int len = bitlen / 8;
int timeout = 0xfffff;
void __iomem *base_addr = (void __iomem *)(unsigned long)sspi->base_addr;
unsigned int tcnt = 0, rcnt = 0;
static char cmd[16] = {0};
static int cmd_len = 0;
SPI_ENTER();
/* No data */
if (!din && !dout)
return 0;
if( (flags&SPI_XFER_BEGIN) && (flags&SPI_XFER_END) ) {
SPI_INF("xfer begin&end flag\n");
memcpy(cmd, dout, len);
cmd_len = len;
rcnt = 0;
tcnt = 0;
/*stc = cmd_len; only cmd, no data*/
}
else if(flags & SPI_XFER_BEGIN) {
SPI_INF("xfer only begin flag\n");
memcpy(cmd, dout, len);
cmd_len = len;
return 0;
} else if(flags & SPI_XFER_END){
SPI_INF("xfer only end flag\n");
tcnt = (dout ? len : 0); /*write cmd*/
rcnt = (din ? len : 0); /*read cmd*/
/* stc = cmd_len + len; */
}
spi_disable_irq(0xffffffff, base_addr);
spi_clr_irq_pending(0xffffffff, base_addr);
//spi_set_bc_tc_stc(tcnt+cmd_len, rcnt, stc, 0, base_addr);
sunxi_spi_mode_check(base_addr, tcnt, rcnt, cmd_len, cmd[0]);
//spi_config_tc(sspi, 1, SPI_MODE_3, base_addr);
spi_ss_level(base_addr, 1);
spi_start_xfer(base_addr);
/*send cmd*/
if(cmd_len) {
if(sunxi_spi_cpu_writel(sspi, (const void *)cmd, cmd_len))
return -1;
cmd_len = 0;
}
/* send data */
if (tcnt) {
#ifdef CONFIG_SPI_USE_DMA
if (tcnt <= 64) {
if (sunxi_spi_cpu_writel(sspi, dout, tcnt))
return -1;
} else {
if (sunxi_spi_dma_writel(sspi, dout, tcnt))
return -1;
}
#else
if (sunxi_spi_cpu_writel(sspi, dout, tcnt))
return -1;
#endif
}
/* recv data */
if (rcnt) {
#ifdef CONFIG_SPI_USE_DMA
if (rcnt <= 64) {
if (sunxi_spi_cpu_readl(sspi, din, rcnt))
return -1;
} else {
if (sunxi_spi_dma_readl(sspi, din, rcnt))
return -1;
}
#else
if (sunxi_spi_cpu_readl(sspi, 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 CONFIG_SPI_USE_DMA
sunxi_spi_dma_disable(sspi);
#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;
}