// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2019 Amlogic, Inc. All rights reserved. */ #include "nfc.h" #include "page_info.h" struct nfc_clk_provider clk_provider[MAX_CLK_PROVIDER] = { {OSC_CLK_24MHZ, CLK_6MHZ, 1, 4, 0, 1}, {OSC_CLK_24MHZ, CLK_12MHZ, 1, 2, 0, 2}, {FIX_PLL_DIV2, CLK_20MHZ, 12, 4, 0, 3}, {FIX_PLL_DIV2, CLK_41MHZ, 6, 4, 0, 5}, {FIX_PLL_DIV2, CLK_83MHZ, 3, 4, 0, 7}, {FIX_PLL_DIV2P5, CLK_100MHZ_400, 2, 4, 0, 11}, {FIX_PLL_DIV2, CLK_100MHZ_500, 2, 5, 0, 11}, {FIX_PLL_DIV2, CLK_125MHZ, 2, 4, 0, 13}, {FIX_PLL_DIV2P5, CLK_133MHZ, 2, 3, 0, 13}, {FIX_PLL_DIV2, CLK_166MHZ_333, 3, 2, 0, 15}, {FIX_PLL_DIV3, CLK_166MHZ_666, 1, 4, 0, 15}, {FIX_PLL_DIV2P5, CLK_200MHZ_400, 2, 2, 0, 21}, {FIX_PLL_DIV2P5, CLK_200MHZ_800, 1, 4, 0, 21}, }; struct nfc_clk_provider *g_clk_info = &clk_provider[CLK_83MHZ]; void nfc_pinmux_init(enum NFC_STORAGE_TYPE storage_type) { unsigned int value; /* * Bit2 of PADCTRL_MISC_CTRL0: spi_nand_sel_pad * spi_nand_sel_pad == 1, the spi pinmux should select func4. * spi_nand_sel_pad == 0, the spi pinmux should select func1. but it * maybe have riskiness */ switch (storage_type) { case SPINAND_FLASH: case SPINOR_FLASH: /* d0-7 and cs pull up; clk pull down */ writel(readl(PADCTRL_GPIOB_PULL_EN) | 0x24FF, PADCTRL_GPIOB_PULL_UP); value = readl(PADCTRL_GPIOB_PULL_UP) & (~0x24FF); writel(value | 0x20FF, PADCTRL_GPIOB_PULL_UP); writel(0x44444444, PADCTRL_PIN_MUX_REG0); value = readl(PADCTRL_PIN_MUX_REG1) & (~0x00F00F00); writel(value | 0x00400400, PADCTRL_PIN_MUX_REG1); break; #ifdef CONFIG_BOOT_SPI_NAND case SLCNAND_FLASH: writel(readl(PADCTRL_GPIOB_PULL_EN) | 0xFFFF, PADCTRL_GPIOB_PULL_EN); writel(0xF500, PADCTRL_GPIOB_PULL_UP); writel(0x11111111, PADCTRL_PIN_MUX_REG0); writel(0x22222, PADCTRL_PIN_MUX_REG1); break; #endif default: break; } } void nfc_send_idle(uint32_t cycle) { writel(CE0 | IDLE | cycle, NAND_CMD); } void nfc_deselect_chip(uint32_t cycle) { writel(CEF | IDLE | cycle, NAND_CMD); } void nfc_select_mode(int spi_mode) { unsigned int value = 0; if (spi_mode) { value = readl(SPI_CFG); value |= (1 << 31); } writel(value, SPI_CFG); } unsigned int nfc_recalculate_n2m_command(uint32_t size, int no_cal) { uint32_t n2m_cmd, page_size; int ecc_size; page_size = page_info_get_page_size(); n2m_cmd = page_info_get_n2m_command(); if (no_cal) return n2m_cmd; if (size == page_size) return n2m_cmd; ecc_size = (GET_BCH_MODE(n2m_cmd) == 1) ? 512 : 1024; size = (size + ecc_size - 1) & (~(ecc_size - 1)); n2m_cmd &= ~0x3F; while (size >= ecc_size) { n2m_cmd++; size -= ecc_size; } return n2m_cmd; } int nfc_wait_command_fifo_done(uint32_t timeout, int repeat_rb) { unsigned int t0 = get_timer(0), value; while (get_timer(t0) < timeout) { value = readl(NAND_CMD); if (((value >> 22) & 0x1f) == 0) return 0; if (repeat_rb && ((value >> 27) & 0x01)) return ERR_NFC_TIMEOUT; } return ERR_NFC_TIMEOUT; } void nfc_raw_size_ext_convert(uint32_t size) { unsigned int raw_ext; raw_ext = readl(SPI_CFG); raw_ext &= ~(0xFFF << 18); raw_ext |= ((size >> NFC_RAW_CHUNK_SHIFT) << 18); writel(raw_ext, SPI_CFG); NFC_Print("SPI_CFG", readl(SPI_CFG)); } #define is_fixpll_locked() (1) #define IS_FEAT_EN_83MHZ_SPI() (0) #define IS_FEAT_EN_41MHZ_SPI() (1) #define IS_FEAT_EN_25MHZ_NAND() (1) #define otp_get_nfc_rxadj(x) ((*(x)) = 0) static struct nfc_clk_provider *nfc_get_clock_provider(int init_stage, enum NFC_STORAGE_TYPE storage_type) { struct nfc_clk_provider *clk_info; unsigned char frequency_idx, cs_deselect_time; uint32_t line_delay1, line_delay2; if (storage_type == SLCNAND_FLASH) { clk_info = &clk_provider[CLK_6MHZ]; if (is_fixpll_locked() && IS_FEAT_EN_25MHZ_NAND()) clk_info = &clk_provider[CLK_20MHZ]; goto _exit_get_clk; } cs_deselect_time = page_info_get_cs_deselect_time(); frequency_idx = page_info_get_frequency_index(); if (init_stage || frequency_idx == 0xFF) { if (!is_fixpll_locked()) clk_info = &clk_provider[CLK_12MHZ]; else if (IS_FEAT_EN_83MHZ_SPI()) clk_info = &clk_provider[CLK_83MHZ]; else if (IS_FEAT_EN_41MHZ_SPI()) clk_info = &clk_provider[CLK_41MHZ]; else clk_info = &clk_provider[CLK_20MHZ]; otp_get_nfc_rxadj(&clk_info->adj); } else { line_delay1 = page_info_get_line_delay1(); line_delay2 = page_info_get_line_delay2(); clk_info = &clk_provider[frequency_idx & 0x7F]; clk_info->adj = page_info_get_adj_index(); if (frequency_idx & 0x80) { clk_info->div1 = page_info_get_core_div(); clk_info->div2 = page_info_get_bus_cycle(); } writel(line_delay1, SD_EMMC_DLY1); writel(line_delay2, SD_EMMC_DLY2); } if (cs_deselect_time != 0xFF) { clk_info->cs_deselect_time = cs_deselect_time; NFC_Print("clk_info->cs_deselect_time", clk_info->cs_deselect_time); } _exit_get_clk: g_clk_info = clk_info; return clk_info; } void nfc_set_clock_and_timing(int init_stage, enum NFC_STORAGE_TYPE storage_type) { struct nfc_clk_provider *clk_info; unsigned char data_lanes, cmd_lanes, addr_lanes, mode; uint32_t value; setbits_le32(CLKCTRL_SYS_CLK_EN0_REG0, CTRL_NAND_CLK_GATE_ON_BIT); setbits_le32(CLKCTRL_NAND_CLK_CTRL, CTRL_SDEMMC_CLK_CTRL_ON_BIT); clk_info = nfc_get_clock_provider(init_stage, storage_type); NFC_Print("clock_index", clk_info->clk_rate); value = CLK_DEF_CORE_PHASE | CLK_ALWAYS_ON | CLK_SELECT_NAND; value |= clk_info->div1; if (clk_info->clk_src_type == FIX_PLL_DIV2) { value |= (1 << 6); writel(value, SD_EMMC_CLK); } else { value &= ~(3 << 6); writel(value, SD_EMMC_CLK); setbits_le32(CLKCTRL_NAND_CLK_CTRL, clk_info->clk_src_type << 9); } if (storage_type != SLCNAND_FLASH) { data_lanes = page_info_get_data_lanes_mode(); cmd_lanes = page_info_get_cmd_lanes_mode(); addr_lanes = page_info_get_addr_lanes_mode(); mode = page_info_get_work_mode(); writel(data_lanes | (cmd_lanes << 2) | ((clk_info->adj & 0x0F) << 4) | (3 << 12) | (mode << 14) | (addr_lanes << 16) | (1 << 31), SPI_CFG); writel((clk_info->div2 - 1) | (3 << 5) | (1 << 31), NAND_CFG); } else { writel(clk_info->div2 | (3 << 5) | (1 << 31), NAND_CFG); } NFC_Print("SPI_CFG", readl(SPI_CFG)); NFC_Print("NAND_CFG", readl(NAND_CFG)); NFC_Print("SD_EMMC_CLK", readl(SD_EMMC_CLK)); NFC_Print("CLKCTRL_NAND_CLK_CTRL", readl(CLKCTRL_NAND_CLK_CTRL)); } void nfc_set_data_bus_width(int bus_width) { uint32_t spi_cfg = readl(SPI_CFG); spi_cfg &= (~0x03); spi_cfg |= bus_width; writel(spi_cfg, SPI_CFG); NFC_Print("SPI_CFG", readl(SPI_CFG)); } uint32_t nfc_send_cmd_addr_and_wait(unsigned char cmd_bitmap, uint8_t cmd, uint8_t *addr, uint8_t addr_len, uint8_t *data, uint8_t data_len, unsigned short state_dummy_ctl) { uint8_t *buffer = data, dummy_cycles = 0, com_cmd; enum CMD_TYPE cmd_type; enum STATE_AND_DUMMY_CTL action; int i = 0, j = 0, ret; if (((cmd_bitmap & TYPE_ADDR) && !addr) || ((cmd_bitmap & TYPE_DATA_DRD) && !buffer) || ((cmd_bitmap & TYPE_DATA_DWR) && !buffer) || cmd_bitmap == 0) return ERR_NFC_WRONG_PARAM; action = (state_dummy_ctl >> STATE_AND_DUMMY_CTL_BIT_SHIFT); NFC_Print("cs_deselect_time", g_clk_info->cs_deselect_time); writel(CEF | IDLE | g_clk_info->cs_deselect_time, NAND_CMD); while (cmd_bitmap != 0 && i < 8) { cmd_type = CMD_BITMAP_TEST_AND_GET_BIT_VALUE(cmd_bitmap, i); CMD_BITMAP_CLEAR_BIT(cmd_bitmap, i); switch (cmd_type) { case TYPE_NONE: /* skip the zero */ break; case TYPE_CMD: NFC_Print("TYPE_CMD", CE0 | CLE | cmd); if (action & DONT_SEND_CEF) { com_cmd = ~cmd; NFC_Print("TYPE_CMD", CE0 | CLE | com_cmd); writel(CE0 | CLE | com_cmd, NAND_CMD); } writel(CE0 | CLE | cmd, NAND_CMD); break; case TYPE_ADDR: if (state_dummy_ctl & 0xFF) dummy_cycles = (state_dummy_ctl & 0xFF) - 1; NFC_Print("dummy_cycles", dummy_cycles); if (action & DUMMY_BEFORE_ADDRESS) writel(CE0 | DUMMY | dummy_cycles, NAND_CMD); addr += addr_len - 1; for (j = 0; j < addr_len; j++, addr--) { NFC_Print("TYPE_ADDR", CE0 | ALE | *addr); writel(CE0 | ALE | *addr, NAND_CMD); } if (action & DUMMY_AFTER_ADDRESS) writel(CE0 | DUMMY | dummy_cycles, NAND_CMD); break; case TYPE_DATA_DRD: NFC_Print("TYPE_DATA_DRD", i); for (j = 0; j < data_len; j++, buffer++) { writel(CE0 | DRD | 0, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); ret = nfc_wait_command_fifo_done(NFC_COMMAND_FIFIO_TIMEOUT, 0); if (ret) return ret; *buffer = readl(NAND_BUF); } return 0; case TYPE_DATA_DWR: NFC_Print("TYPE_DATA_DWR", i); for (j = 0; j < data_len; j++, buffer++) { writel(CE0 | IDLE | 10, NAND_CMD); writel(CE0 | DWR | *buffer, NAND_CMD); writel(CE0 | IDLE | 10, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); ret = nfc_wait_command_fifo_done(NFC_COMMAND_FIFIO_TIMEOUT, 0); if (ret) return ret; } return 0; default: break; } writel(CE0 | IDLE, NAND_CMD); i++; } return nfc_wait_command_fifo_done(NFC_COMMAND_FIFIO_TIMEOUT, 0); } void nfc_send_repeat_rb_command(void) { writel(IO6 | RB | 13, NAND_CMD); writel(CE0 | IDLE | 2, NAND_CMD); writel(CE0 | CLE | 0, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); } void nfc_set_dma_mem_and_info(uint32_t mem, unsigned long info) { uint32_t cmd; cmd = GENCMDDADDRL(NFC_CMD_ADL, mem); writel(cmd, NAND_CMD); cmd = GENCMDDADDRH(NFC_CMD_ADH, mem); writel(cmd, NAND_CMD); cmd = GENCMDIADDRL(NFC_CMD_AIL, info); writel(cmd, NAND_CMD); cmd = GENCMDIADDRH(NFC_CMD_AIH, info); writel(cmd, NAND_CMD); } int nfc_start_dma_and_wait_done(uint32_t n2m_cmd) { writel(n2m_cmd, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); writel(CE0 | IDLE, NAND_CMD); return nfc_wait_command_fifo_done(NFC_DATA_FIFIO_TIMEOUT, 0); } void nfc_set_user_byte(u8 *oob_buf, uint64_t *info_buf, int ecc_steps) { uint64_t *info; int i, count; for (i = 0, count = 0; i < ecc_steps; i++, count += 2) { info = &info_buf[i]; *info |= oob_buf[count]; *info |= oob_buf[count + 1] << 8; } } void nfc_get_user_byte(u8 *oob_buf, uint64_t *info_buf, int ecc_steps) { uint64_t *info; int i, count; for (i = 0, count = 0; i < ecc_steps; i++, count += 2) { info = &info_buf[i]; oob_buf[count] = *info; oob_buf[count + 1] = *info >> 8; } } int nfc_wait_data_and_ecc_engine_done(uint64_t *info_buf, unsigned char nsteps, int raw) { volatile uint64_t *info; int i, count = 0; if (nsteps < 1) return ERR_NFC_WRONG_PARAM; info = info_buf + nsteps - 1; while (count <= 10000) { invalidate_dcache_range((unsigned long)info_buf, (unsigned long)info_buf + sizeof(uint64_t) * 8); if (*info & ECC_COMPLETE) break; if (count == 10000) { writel(1 << 31, NAND_CMD); return ERR_DMA_TIMEOUT; } udelay(1); count++; } if (raw) return 0; for (i = 0, info = info_buf; i < nsteps; i++, info++) { if (ECC_ERR_CNT(*info) != ECC_UNCORRECTABLE) continue; if (ECC_ZERO_CNT(*info) < 0x0a) return 0; return ERROR_ECC; } return 0; }