742 lines
19 KiB
C
742 lines
19 KiB
C
/*
|
|
* (C) Copyright 2022-2025
|
|
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
|
|
*
|
|
* lujianliang <lujianliang@allwinnertech.com>
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
#include <arch/clock.h>
|
|
#include <arch/gpio.h>
|
|
#include <arch/spinor.h>
|
|
#include "spif-sunxi.h"
|
|
#include <private_boot0.h>
|
|
#include <cache_align.h>
|
|
|
|
#define SUNXI_SPIF_DEFAULT_CLK (50000000)
|
|
|
|
/* For debug */
|
|
#define SPIF_DEBUG 0
|
|
|
|
#if SPIF_DEBUG
|
|
#define SPIF_EXIT() printf("%s()%d - %s\n", __func__, __LINE__, "Exit")
|
|
#define SPIF_ENTER() printf("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
|
|
#define SPIF_DBG(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
#define SPIF_INF(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
#define SPIF_ERR(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
|
|
#else
|
|
#define SPIF_EXIT()
|
|
#define SPIF_ENTER()
|
|
#define SPIF_DBG(fmt, arg...)
|
|
#define SPIF_INF(fmt, arg...)
|
|
#define SPIF_ERR(fmt, arg...) printf("%s()%d - "fmt, __func__, __LINE__, ##arg)
|
|
#endif
|
|
#define SUNXI_SPIF_OK 0
|
|
#define SUNXI_SPIF_FAIL -1
|
|
|
|
extern boot_spinor_info_t *spinor_info;
|
|
__attribute__((section(".data"))) __aligned(64)
|
|
struct sunxi_spif_slave g_sspif;
|
|
|
|
struct sunxi_spif_slave *get_sspif(void)
|
|
{
|
|
return &g_sspif;
|
|
}
|
|
|
|
#if SPIF_DEBUG
|
|
int snprintf(char *buf, size_t size, const char *fmt, ...);
|
|
static void spif_print_info(void __iomem *base_addr)
|
|
{
|
|
char buf[1024] = {0};
|
|
snprintf(buf, sizeof(buf)-1,
|
|
"base_addr = 0x%x, the SPIF control register:\n"
|
|
"[VER] 0x%02x = 0x%08x, [GC] 0x%02x = 0x%08x, [GCA] 0x%02x = 0x%08x\n"
|
|
"[TCR] 0x%02x = 0x%08x, [TDS] 0x%02x = 0x%08x, [INT] 0x%02x = 0x%08x\n"
|
|
"[STA] 0x%02x = 0x%08x, [CSD] 0x%02x = 0x%08x, [PHC] 0x%02x = 0x%08x\n"
|
|
"[TCF] 0x%02x = 0x%08x, [TCS] 0x%02x = 0x%08x, [TNM] 0x%02x = 0x%08x\n"
|
|
"[PSR] 0x%02x = 0x%08x, [PSA] 0x%02x = 0x%08x, [PEA] 0x%02x = 0x%08x\n"
|
|
"[PMA] 0x%02x = 0x%08x, [DMA] 0x%02x = 0x%08x, [DSC] 0x%02x = 0x%08x\n"
|
|
"[DFT] 0x%02x = 0x%08x, [CFT] 0x%02x = 0x%08x, [CFS] 0x%02x = 0x%08x\n"
|
|
"[BAT] 0x%02x = 0x%08x, [BAC] 0x%02x = 0x%08x, [TB] 0x%02x = 0x%08x\n"
|
|
"[RB] 0x%02x = 0x%08x\n",
|
|
base_addr,
|
|
SPIF_VER_REG, readl(base_addr + SPIF_VER_REG),
|
|
SPIF_GC_REG, readl(base_addr + SPIF_GC_REG),
|
|
SPIF_GCA_REG, readl(base_addr + SPIF_GCA_REG),
|
|
|
|
SPIF_TC_REG, readl(base_addr + SPIF_TC_REG),
|
|
SPIF_TDS_REG, readl(base_addr + SPIF_TDS_REG),
|
|
SPIF_INT_EN_REG, readl(base_addr + SPIF_INT_EN_REG),
|
|
|
|
SPIF_INT_STA_REG, readl(base_addr + SPIF_INT_STA_REG),
|
|
SPIF_CSD_REG, readl(base_addr + SPIF_CSD_REG),
|
|
SPIF_PHC_REG, readl(base_addr + SPIF_PHC_REG),
|
|
|
|
SPIF_TCF_REG, readl(base_addr + SPIF_TCF_REG),
|
|
SPIF_TCS_REG, readl(base_addr + SPIF_TCS_REG),
|
|
SPIF_TNM_REG, readl(base_addr + SPIF_TNM_REG),
|
|
|
|
SPIF_PS_REG, readl(base_addr + SPIF_PS_REG),
|
|
SPIF_PSA_REG, readl(base_addr + SPIF_PSA_REG),
|
|
SPIF_PEA_REG, readl(base_addr + SPIF_PEA_REG),
|
|
|
|
SPIF_PMA_REG, readl(base_addr + SPIF_PMA_REG),
|
|
SPIF_DMA_CTL_REG, readl(base_addr + SPIF_DMA_CTL_REG),
|
|
SPIF_DSC_REG, readl(base_addr + SPIF_DSC_REG),
|
|
|
|
SPIF_DFT_REG, readl(base_addr + SPIF_DFT_REG),
|
|
SPIF_CFT_REG, readl(base_addr + SPIF_CFT_REG),
|
|
SPIF_CFS_REG, readl(base_addr + SPIF_CFS_REG),
|
|
|
|
SPIF_BAT_REG, readl(base_addr + SPIF_BAT_REG),
|
|
SPIF_BAC_REG, readl(base_addr + SPIF_BAC_REG),
|
|
SPIF_TB_REG, readl(base_addr + SPIF_TB_REG),
|
|
|
|
SPIF_RB_REG, readl(base_addr + SPIF_RB_REG));
|
|
printf("%s\n\n", buf);
|
|
}
|
|
|
|
void spif_print_descriptor(struct spif_descriptor_op *spif_op)
|
|
{
|
|
char buf[512] = {0};
|
|
snprintf(buf, sizeof(buf)-1,
|
|
"hburst_rw_flag : 0x%x\n"
|
|
"block_data_len : 0x%x\n"
|
|
"data_addr : 0x%x\n"
|
|
"next_des_addr : 0x%x\n"
|
|
"trans_phase : 0x%x\n"
|
|
"flash_addr : 0x%x\n"
|
|
"cmd_mode_buswidth : 0x%x\n"
|
|
"addr_dummy_data_count : 0x%x\n",
|
|
spif_op->hburst_rw_flag,
|
|
spif_op->block_data_len,
|
|
spif_op->data_addr,
|
|
spif_op->next_des_addr,
|
|
spif_op->trans_phase,
|
|
spif_op->flash_addr,
|
|
spif_op->cmd_mode_buswidth,
|
|
spif_op->addr_dummy_data_count);
|
|
printf("%s", buf);
|
|
printf("spif_op addr [%x]...\n\n", (u32)spif_op);
|
|
}
|
|
|
|
#endif
|
|
|
|
static s32 sunxi_spif_gpio_request(void)
|
|
{
|
|
/*#ifndef CFG_SUNXI_SBOOT
|
|
int i = 0;
|
|
int pin_num = 6;
|
|
|
|
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_SUN8IW21)
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(0), SUN8I_GPC_SPIF); /*spif_sclk */
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(1), SUN8I_GPC_SPIF); /*spif_cs0*/
|
|
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(2), SUN8I_GPC_SPIF); /*spif_mosi*/
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUN8I_GPC_SPIF); /*spif_miso*/
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(4), SUN8I_GPC_SPIF); /*spif_wp*/
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(5), SUN8I_GPC_SPIF); /*spif_hold*/
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(6), SUN8I_GPC_SPIF); /*spif_io4*/
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(7), SUN8I_GPC_SPIF); /*spif_io5*/
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(8), SUN8I_GPC_SPIF); /*spif_io6*/
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPC(9), SUN8I_GPC_SPIF); /*spif_io7*/
|
|
|
|
sunxi_gpio_set_pull(SUNXI_GPC(1), 1);
|
|
sunxi_gpio_set_pull(SUNXI_GPC(4), 1);
|
|
sunxi_gpio_set_pull(SUNXI_GPC(5), 1);
|
|
#else
|
|
#error "spi pinctrl not available for this architecture"
|
|
#endif
|
|
/*#ifndef CFG_SUNXI_SBOOT
|
|
}
|
|
#endif*/
|
|
return 0;
|
|
}
|
|
|
|
static void spif_big_little_endian(bool endian, void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
if (endian == LSB_FIRST)
|
|
reg_val |= (SPIF_GC_RX_CFG_FBS | SPIF_GC_TX_CFG_FBS);
|
|
else
|
|
reg_val &= ~(SPIF_GC_RX_CFG_FBS | SPIF_GC_TX_CFG_FBS);
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
static void spif_clean_mode_en(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
reg_val &= ~(SPIF_GC_NMODE_EN | SPIF_GC_PMODE_EN);
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
static void spif_wp_en(bool enable, void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
if (enable)
|
|
reg_val |= SPIF_GC_WP_EN;
|
|
else
|
|
reg_val &= ~SPIF_GC_WP_EN;
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
static void spif_hold_en(bool enable, void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
if (enable)
|
|
reg_val |= SPIF_GC_HOLD_EN;
|
|
else
|
|
reg_val &= ~SPIF_GC_HOLD_EN;
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
static void spif_set_cs_pol(bool pol, void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
if (pol)
|
|
reg_val |= SPIF_GC_CS_POL;
|
|
else
|
|
reg_val &= ~SPIF_GC_CS_POL;
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
/* spif config chip select */
|
|
static s32 spif_set_cs(u32 chipselect, void __iomem *base_addr)
|
|
{
|
|
int ret;
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
if (chipselect < 4) {
|
|
reg_val &= ~SPIF_GC_SS_MASK;/* SS-chip select, clear two bits */
|
|
reg_val |= chipselect << SPIF_GC_SS_BIT_POS;/* set chip select */
|
|
reg_val |= SPIF_GC_CS_POL;/* active low polarity */
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
ret = SUNXI_SPIF_OK;
|
|
} else {
|
|
SPIF_ERR("Chip Select set fail! cs = %d\n", chipselect);
|
|
ret = SUNXI_SPIF_FAIL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void spif_set_mode(u32 spi_mode, void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
reg_val &= ~SPIF_MASK;
|
|
reg_val |= spi_mode;
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
void spif_samp_dl_sw_rx_status(void __iomem *base_addr, unsigned int status)
|
|
{
|
|
unsigned int rval = readl(base_addr + SPIF_TC_REG);
|
|
|
|
if (status)
|
|
rval |= SPIF_ANALOG_DL_SW_RX_EN;
|
|
else
|
|
rval &= ~SPIF_ANALOG_DL_SW_RX_EN;
|
|
|
|
writel(rval, base_addr +SPIF_TC_REG);
|
|
}
|
|
|
|
void spif_samp_mode(void __iomem *base_addr, unsigned int status)
|
|
{
|
|
unsigned int rval = readl(base_addr + SPIF_TC_REG);
|
|
|
|
if (status)
|
|
rval |= SPIF_DIGITAL_ANALOG_EN;
|
|
else
|
|
rval &= ~SPIF_DIGITAL_ANALOG_EN;
|
|
|
|
writel(rval, base_addr + SPIF_TC_REG);
|
|
}
|
|
|
|
void spif_set_sample_mode(void __iomem *base_addr, unsigned int mode)
|
|
{
|
|
unsigned int rval = readl(base_addr + SPIF_TC_REG);
|
|
|
|
rval &= (~SPIF_DIGITAL_DELAY_MASK);
|
|
rval |= mode << SPIF_DIGITAL_DELAY;
|
|
writel(rval, base_addr + SPIF_TC_REG);
|
|
}
|
|
|
|
void spif_set_sample_delay(void __iomem *base_addr, unsigned int sample_delay)
|
|
{
|
|
unsigned int rval = readl(base_addr + SPIF_TC_REG);
|
|
|
|
rval &= (~SPIF_ANALOG_DELAY_MASK);
|
|
rval |= sample_delay << SPIF_ANALOG_DELAY;
|
|
writel(rval, base_addr + SPIF_TC_REG);
|
|
mdelay(1);
|
|
}
|
|
|
|
static void spif_config_tc(void __iomem *base_addr)
|
|
{
|
|
if (spinor_info->sample_mode != SAMP_MODE_DL_DEFAULT) {
|
|
spif_samp_mode(base_addr, 1);
|
|
spif_samp_dl_sw_rx_status(base_addr, 1);
|
|
spif_set_sample_mode(base_addr, spinor_info->sample_mode);
|
|
spif_set_sample_delay(base_addr, spinor_info->sample_delay);
|
|
}
|
|
}
|
|
|
|
static void spif_set_dqs(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_DFT_REG);
|
|
|
|
reg_val |= SPIF_DFT_DQS;
|
|
writel(reg_val, base_addr + SPIF_DFT_REG);
|
|
}
|
|
|
|
static void spif_set_cdc(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_CFT_REG);
|
|
|
|
reg_val = SPIF_CFT_CDC;
|
|
writel(reg_val, base_addr + SPIF_CFT_REG);
|
|
}
|
|
|
|
static void spif_set_csd(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_CSD_REG);
|
|
|
|
reg_val |= SPIF_CSD_DEF;
|
|
writel(reg_val, base_addr + SPIF_CSD_REG);
|
|
}
|
|
|
|
/* soft reset spif controller */
|
|
static void spif_soft_reset_fifo(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GCA_REG);
|
|
|
|
reg_val |= SPIF_GCA_SRST;
|
|
writel(reg_val, base_addr + SPIF_GCA_REG);
|
|
}
|
|
|
|
static void spif_reset_fifo(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GCA_REG);
|
|
|
|
reg_val |= SPIF_FIFO_SRST;
|
|
writel(reg_val, base_addr + SPIF_GCA_REG);
|
|
}
|
|
|
|
static void spif_set_trans_mode(u8 mode, void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
if (mode)
|
|
reg_val |= SPIF_GC_CFG_MODE;
|
|
else
|
|
reg_val &= ~SPIF_GC_CFG_MODE;
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
/* set first descriptor start addr */
|
|
static void spif_set_des_start_addr(struct spif_descriptor_op *spif_op,
|
|
void __iomem *base_addr)
|
|
{
|
|
flush_dcache_range((ulong)spif_op,
|
|
ALIGN(sizeof(struct spif_descriptor_op),
|
|
CONFIG_SYS_CACHELINE_SIZE));
|
|
writel((u32)spif_op, base_addr + SPIF_DSC_REG);
|
|
}
|
|
|
|
/* set descriptor len */
|
|
static void spif_set_des_len(int len, void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_DMA_CTL_REG);
|
|
|
|
reg_val |= len;
|
|
writel(reg_val, base_addr + SPIF_DMA_CTL_REG);
|
|
}
|
|
|
|
/* DMA start Signal */
|
|
static void spif_dma_start_signal(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_DMA_CTL_REG);
|
|
|
|
reg_val |= CFG_DMA_START;
|
|
writel(reg_val, base_addr + SPIF_DMA_CTL_REG);
|
|
}
|
|
|
|
static void spif_trans_type_enable(u32 type_phase, void __iomem *base_addr)
|
|
{
|
|
writel(type_phase, base_addr + SPIF_PHC_REG);
|
|
}
|
|
|
|
static void spif_set_flash_addr(u32 flash_addr, void __iomem *base_addr)
|
|
{
|
|
writel(flash_addr, base_addr + SPIF_TCF_REG);
|
|
}
|
|
|
|
static void spif_set_buswidth(u32 cmd_mode_buswidth, void __iomem *base_addr)
|
|
{
|
|
writel(cmd_mode_buswidth, base_addr + SPIF_TCS_REG);
|
|
}
|
|
|
|
static void spif_set_data_count(u32 addr_dummy_data_count, void __iomem *base_addr)
|
|
{
|
|
writel(addr_dummy_data_count, base_addr + SPIF_TNM_REG);
|
|
}
|
|
|
|
static void spif_cpu_start_transfer(void __iomem *base_addr)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
reg_val |= SPIF_GC_NMODE_EN;
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
static void spif_set_output_clk(void __iomem *base_addr, u32 status)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_TC_REG);
|
|
|
|
if (status)
|
|
reg_val |= SPIF_CLK_SCKOUT_SRC_SEL;
|
|
else
|
|
reg_val &= ~SPIF_CLK_SCKOUT_SRC_SEL;
|
|
writel(reg_val, base_addr + SPIF_TC_REG);
|
|
}
|
|
|
|
static void spif_set_dtr(void __iomem *base_addr, u32 status)
|
|
{
|
|
u32 reg_val = readl(base_addr + SPIF_GC_REG);
|
|
|
|
if (status)
|
|
reg_val |= SPIF_GC_DTR_EN;
|
|
else
|
|
reg_val &= ~SPIF_GC_DTR_EN;
|
|
writel(reg_val, base_addr + SPIF_GC_REG);
|
|
}
|
|
|
|
static int sunxi_spif_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->spif_clk_cfg + bus * 0x4;
|
|
u32 source_clk = 0;
|
|
u32 rval = 0;
|
|
u32 m, n, div;
|
|
|
|
/* SCLK = src/M/N */
|
|
/* N: 00:1 01:2 10:4 11:8 */
|
|
#ifdef FPGA_PLATFORM
|
|
n = 0;
|
|
m = 1;
|
|
rval = CCM_SPIF_CTRL_ENABLE | CCM_SPIF_CTRL_N(n) | CCM_SPIF_CTRL_M(m);;
|
|
source_clk = 24000000;
|
|
#else
|
|
|
|
source_clk = GET_SPIF_CLK_SOURECS(CCM_SPIF_CTRL_PERI);
|
|
SPIF_INF("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;
|
|
}
|
|
|
|
rval = CCM_SPIF_CTRL_ENABLE | CCM_SPIF_CTRL_PERI |
|
|
CCM_SPIF_CTRL_N(n) | CCM_SPIF_CTRL_M(m);
|
|
|
|
#endif
|
|
writel(rval, (volatile void __iomem *)mclk_base);
|
|
|
|
/* spif reset */
|
|
setbits_le32(&ccm->spi_gate_reset, (0<<SPIF_RESET_SHIFT));
|
|
setbits_le32(&ccm->spi_gate_reset, (1<<SPIF_RESET_SHIFT));
|
|
|
|
/* spif gating */
|
|
setbits_le32(&ccm->spi_gate_reset, (1<<SPIF_GATING_SHIFT));
|
|
|
|
SPIF_INF("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_spif_clk(int bus)
|
|
{
|
|
struct sunxi_ccm_reg *const ccm =
|
|
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
unsigned long mclk_base =
|
|
(unsigned long)&ccm->spif_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:
|
|
clk = GET_SPIF_CLK_SOURECS(CCM_SPIF_CTRL_PERI400M);
|
|
break;
|
|
case 2:
|
|
clk = GET_SPIF_CLK_SOURECS(CCM_SPIF_CTRL_PERI300M);
|
|
break;
|
|
default:
|
|
clk = 0;
|
|
break;
|
|
}
|
|
sclk_freq = clk / (1 << n) / m;
|
|
SPIF_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_spif_clk_exit(void)
|
|
{
|
|
struct sunxi_ccm_reg *const ccm =
|
|
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
|
|
|
/* spif gating */
|
|
clrbits_le32(&ccm->spi_gate_reset, 1<<SPIF_GATING_SHIFT);
|
|
|
|
/* spif reset */
|
|
clrbits_le32(&ccm->spi_gate_reset, 1<<SPIF_RESET_SHIFT);
|
|
return 0;
|
|
}
|
|
|
|
int set_spif_clk(void)
|
|
{
|
|
/* clock */
|
|
if (spinor_info->frequency) {
|
|
if (sunxi_spif_clk_init(0, spinor_info->frequency))
|
|
return -1;
|
|
printf("set spif freq:%d\n", spinor_info->frequency);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void spif_dtr_enable(struct spif_descriptor_op *spif_op)
|
|
{
|
|
struct sunxi_spif_slave *sspif = get_sspif();
|
|
void __iomem *base_addr = (void *)SUNXI_SPIF_BASE;
|
|
unsigned int bus = 0;
|
|
unsigned int clk = sspif->max_hz;
|
|
unsigned int dtr_double_clk = clk * 2;
|
|
static int double_clk_flag;
|
|
|
|
if (!sspif->rx_dtr_en && !sspif->tx_dtr_en)
|
|
return;
|
|
|
|
if ((spif_op->cmd_mode_buswidth >> SPIF_ADDR_TRANS_POS) & 0x3) {
|
|
if ((spif_op->trans_phase & SPIF_RX_TRANS_EN) &&
|
|
sspif->rx_dtr_en) {
|
|
spif_set_output_clk(base_addr, 1);
|
|
spif_set_dtr(base_addr, 1);
|
|
if (!double_clk_flag) {
|
|
sunxi_spif_clk_init(bus, dtr_double_clk);
|
|
double_clk_flag = 1;
|
|
}
|
|
} else if (spif_op->trans_phase & SPIF_TX_TRANS_EN &&
|
|
sspif->tx_dtr_en) {
|
|
spif_set_output_clk(base_addr, 1);
|
|
spif_set_dtr(base_addr, 1);
|
|
if (!dtr_double_clk) {
|
|
sunxi_spif_clk_init(bus, dtr_double_clk);
|
|
double_clk_flag = 1;
|
|
}
|
|
}
|
|
} else {
|
|
spif_set_output_clk(base_addr, 0);
|
|
spif_set_dtr(base_addr, 0);
|
|
if (double_clk_flag) {
|
|
sunxi_spif_clk_init(bus, clk);
|
|
double_clk_flag = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int spif_claim_bus(void *base_addr)
|
|
{
|
|
SPIF_ENTER();
|
|
|
|
/* 1. reset all tie logic & fifo */
|
|
spif_soft_reset_fifo(base_addr);
|
|
spif_clean_mode_en(base_addr);
|
|
|
|
/* 2. interface first transmit bit select */
|
|
spif_big_little_endian(MSB_FIRST, base_addr);
|
|
|
|
/* 3. disable wp & hold */
|
|
spif_wp_en(0, base_addr);
|
|
spif_hold_en(0, base_addr);
|
|
|
|
/* 4. disable DTR */
|
|
spif_set_output_clk(base_addr, 0);
|
|
spif_set_dtr(base_addr, 0);
|
|
|
|
/* 5. set the default chip select */
|
|
spif_set_cs(0, base_addr);
|
|
spif_set_cs_pol(1, base_addr);
|
|
|
|
/* 6. set spi CPOL and CPHA */
|
|
spif_set_mode(SPIF_MODE0, base_addr);
|
|
|
|
/* 7. set reg defauld count */
|
|
spif_set_dqs(base_addr);
|
|
spif_set_cdc(base_addr);
|
|
spif_set_csd(base_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int spif_init(void)
|
|
{
|
|
SPIF_ENTER();
|
|
/* gpio */
|
|
sunxi_spif_gpio_request();
|
|
|
|
/* clock */
|
|
if (sunxi_spif_clk_init(0, SUNXI_SPIF_DEFAULT_CLK))
|
|
return -1;
|
|
|
|
if (spif_claim_bus((void *)SUNXI_SPIF_BASE))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
void spif_exit(void)
|
|
{
|
|
/* disable module clock */
|
|
sunxi_spif_clk_exit();
|
|
}
|
|
|
|
static void spif_ctr_recover(void)
|
|
{
|
|
spif_exit();
|
|
spif_init();
|
|
spif_config_tc((void *)SUNXI_SPIF_BASE);
|
|
}
|
|
|
|
int spif_xfer(struct spif_descriptor_op *spif_op, unsigned int data_len)
|
|
{
|
|
int timeout = 0xfffffff;
|
|
void __iomem *base_addr = (void *)SUNXI_SPIF_BASE;
|
|
uint desc_count = ((data_len + SPIF_MAX_TRANS_NUM - 1) / SPIF_MAX_TRANS_NUM) + 1;
|
|
uint desc_size = desc_count * sizeof(struct spif_descriptor_op);
|
|
|
|
spif_reset_fifo(base_addr);
|
|
spif_dtr_enable(spif_op);
|
|
if ((spif_op->block_data_len & DMA_DATA_LEN) == 0) {
|
|
spif_set_trans_mode(SPIF_GC_CPU_MODE, base_addr);
|
|
|
|
spif_trans_type_enable(spif_op->trans_phase, base_addr);
|
|
|
|
spif_set_flash_addr(spif_op->flash_addr, base_addr);
|
|
|
|
spif_set_buswidth(spif_op->cmd_mode_buswidth, base_addr);
|
|
|
|
spif_set_data_count(spif_op->addr_dummy_data_count, base_addr);
|
|
|
|
spif_cpu_start_transfer(base_addr);
|
|
|
|
while ((readl(base_addr + SPIF_GC_REG) & SPIF_GC_NMODE_EN)) {
|
|
timeout--;
|
|
if (!timeout) {
|
|
spif_ctr_recover();
|
|
printf("SPIF DMA transfer time_out\n");
|
|
return -1;
|
|
}
|
|
}
|
|
// spif_print_info(base_addr);
|
|
} else {
|
|
#if 0 //CFG_SPI_USE_DMA
|
|
spif_set_trans_mode(SPIF_GC_CPU_MODE, base_addr);
|
|
|
|
spif_trans_type_enable(spif_op->trans_phase, base_addr);
|
|
|
|
spif_set_flash_addr(spif_op->flash_addr, base_addr);
|
|
|
|
spif_set_buswidth(spif_op->cmd_mode_buswidth, base_addr);
|
|
|
|
spif_set_data_count(spif_op->addr_dummy_data_count, base_addr);
|
|
#else
|
|
spif_set_trans_mode(SPIF_GC_DMA_MODE, base_addr);
|
|
|
|
#endif
|
|
/* flush data addr */
|
|
flush_dcache_range((u32)spif_op->data_addr,
|
|
data_len <= CONFIG_SYS_CACHELINE_SIZE ?
|
|
CONFIG_SYS_CACHELINE_SIZE : data_len);
|
|
flush_dcache_range((u32)spif_op, desc_size);
|
|
|
|
spif_set_des_start_addr(spif_op, base_addr);
|
|
|
|
spif_set_des_len(DMA_DESCRIPTOR_LEN, base_addr);
|
|
|
|
spif_dma_start_signal(base_addr);
|
|
|
|
//spif_print_descriptor(spif_op);
|
|
/*
|
|
* The SPIF move data through DMA, and DMA and CPU modes
|
|
* differ only between actively configuring registers and
|
|
* configuring registers through the DMA descriptor
|
|
*/
|
|
#if 0 //CFG_SPI_USE_DMA
|
|
spif_cpu_start_transfer(base_addr);
|
|
#endif
|
|
|
|
/* waiting DMA finish */
|
|
while (!(readl(base_addr + SPIF_INT_STA_REG) &
|
|
DMA_TRANS_DONE_INT)) {
|
|
timeout--;
|
|
if (!timeout) {
|
|
spif_ctr_recover();
|
|
printf("SPIF DMA transfer time_out\n");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//spif_print_info(base_addr);
|
|
|
|
invalidate_dcache_range((u32)spif_op->data_addr,
|
|
(u32)spif_op->data_addr +
|
|
(data_len <= CONFIG_SYS_CACHELINE_SIZE ?
|
|
CONFIG_SYS_CACHELINE_SIZE : data_len));
|
|
writel(DMA_TRANS_DONE_INT, base_addr + SPIF_INT_STA_REG);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|