/* * sunxi SPI driver for uboot. * * Copyright (C) 2018 * 2013.5.7 Mintow * 2018.11.7 wangwei */ #include #include #include #include #include #include #include "spi-sunxi.h" #include #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<spi_gate_reset, (1<spi_gate_reset, (1<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<spi_gate_reset, 1< 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; }