// SPDX-License-Identifier: GPL-2.0+ /* * sunxi SPI driver for uboot. * * Copyright (C) 2018 * 2013.5.7 Mintow * 2018.11.7 wangwei */ #include #include #include #include #include #include #include #include #include #ifdef CONFIG_SUNXI_DMA #include #endif #include #include #include "spi-sunxi.h" #include #include #include #include #include #include "../mtd/spi/sf_internal.h" #include #include #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<spi_gate_reset, 1<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; }