| /* |
| * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. |
| * |
| * SPDX-License-Identifier: GPL-2.0+ |
| */ |
| |
| #include <bouncebuf.h> |
| #include <common.h> |
| #include <malloc.h> |
| #include <nand.h> |
| #include <asm/io.h> |
| |
| #define BUS_WIDTH 8 /* AXI data bus width in bytes */ |
| |
| /* DMA buffer descriptor bits & masks */ |
| #define BD_STAT_OWN (1 << 31) |
| #define BD_STAT_BD_FIRST (1 << 3) |
| #define BD_STAT_BD_LAST (1 << 2) |
| #define BD_SIZES_BUFFER1_MASK 0xfff |
| |
| #define BD_STAT_BD_COMPLETE (BD_STAT_BD_FIRST | BD_STAT_BD_LAST) |
| |
| /* Controller command flags */ |
| #define B_WFR (1 << 19) /* 1b - Wait for ready */ |
| #define B_LC (1 << 18) /* 1b - Last cycle */ |
| #define B_IWC (1 << 13) /* 1b - Interrupt when complete */ |
| |
| /* NAND cycle types */ |
| #define B_CT_ADDRESS (0x0 << 16) /* Address operation */ |
| #define B_CT_COMMAND (0x1 << 16) /* Command operation */ |
| #define B_CT_WRITE (0x2 << 16) /* Write operation */ |
| #define B_CT_READ (0x3 << 16) /* Write operation */ |
| |
| enum nand_isr_t { |
| NAND_ISR_DATAREQUIRED = 0, |
| NAND_ISR_TXUNDERFLOW, |
| NAND_ISR_TXOVERFLOW, |
| NAND_ISR_DATAAVAILABLE, |
| NAND_ISR_RXUNDERFLOW, |
| NAND_ISR_RXOVERFLOW, |
| NAND_ISR_TXDMACOMPLETE, |
| NAND_ISR_RXDMACOMPLETE, |
| NAND_ISR_DESCRIPTORUNAVAILABLE, |
| NAND_ISR_CMDDONE, |
| NAND_ISR_CMDAVAILABLE, |
| NAND_ISR_CMDERROR, |
| NAND_ISR_DATATRANSFEROVER, |
| NAND_ISR_NONE |
| }; |
| |
| enum nand_regs_t { |
| AC_FIFO = 0, /* address and command fifo */ |
| IDMAC_BDADDR = 0x18, /* idmac descriptor list base address */ |
| INT_STATUS = 0x118, /* interrupt status register */ |
| INT_CLR_STATUS = 0x120, /* interrupt clear status register */ |
| }; |
| |
| struct nand_bd { |
| uint32_t status; /* DES0 */ |
| uint32_t sizes; /* DES1 */ |
| uint32_t buffer_ptr0; /* DES2 */ |
| uint32_t buffer_ptr1; /* DES3 */ |
| }; |
| |
| #define NAND_REG_WRITE(r, v) writel(v, CONFIG_SYS_NAND_BASE + r) |
| #define NAND_REG_READ(r) readl(CONFIG_SYS_NAND_BASE + r) |
| |
| static struct nand_bd *bd; /* DMA buffer descriptors */ |
| |
| /** |
| * axs101_nand_write_buf - write buffer to chip |
| * @mtd: MTD device structure |
| * @buf: data buffer |
| * @len: number of bytes to write |
| */ |
| static uint32_t nand_flag_is_set(uint32_t flag) |
| { |
| uint32_t reg = NAND_REG_READ(INT_STATUS); |
| |
| if (reg & (1 << NAND_ISR_CMDERROR)) |
| return 0; |
| |
| if (reg & (1 << flag)) { |
| NAND_REG_WRITE(INT_CLR_STATUS, 1 << flag); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * axs101_nand_write_buf - write buffer to chip |
| * @mtd: MTD device structure |
| * @buf: data buffer |
| * @len: number of bytes to write |
| */ |
| static void axs101_nand_write_buf(struct mtd_info *mtd, const u_char *buf, |
| int len) |
| { |
| struct bounce_buffer bbstate; |
| |
| bounce_buffer_start(&bbstate, (void *)buf, len, GEN_BB_READ); |
| |
| /* Setup buffer descriptor */ |
| writel(BD_STAT_OWN | BD_STAT_BD_COMPLETE, &bd->status); |
| writel(ALIGN(len, BUS_WIDTH) & BD_SIZES_BUFFER1_MASK, &bd->sizes); |
| writel(bbstate.bounce_buffer, &bd->buffer_ptr0); |
| writel(0, &bd->buffer_ptr1); |
| |
| /* Flush modified buffer descriptor */ |
| flush_dcache_range((unsigned long)bd, |
| (unsigned long)bd + sizeof(struct nand_bd)); |
| |
| /* Issue "write" command */ |
| NAND_REG_WRITE(AC_FIFO, B_CT_WRITE | B_WFR | B_IWC | B_LC | (len-1)); |
| |
| /* Wait for NAND command and DMA to complete */ |
| while (!nand_flag_is_set(NAND_ISR_CMDDONE)) |
| ; |
| while (!nand_flag_is_set(NAND_ISR_TXDMACOMPLETE)) |
| ; |
| |
| bounce_buffer_stop(&bbstate); |
| } |
| |
| /** |
| * axs101_nand_read_buf - read chip data into buffer |
| * @mtd: MTD device structure |
| * @buf: buffer to store data |
| * @len: number of bytes to read |
| */ |
| static void axs101_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) |
| { |
| struct bounce_buffer bbstate; |
| |
| bounce_buffer_start(&bbstate, buf, len, GEN_BB_WRITE); |
| |
| /* Setup buffer descriptor */ |
| writel(BD_STAT_OWN | BD_STAT_BD_COMPLETE, &bd->status); |
| writel(ALIGN(len, BUS_WIDTH) & BD_SIZES_BUFFER1_MASK, &bd->sizes); |
| writel(bbstate.bounce_buffer, &bd->buffer_ptr0); |
| writel(0, &bd->buffer_ptr1); |
| |
| /* Flush modified buffer descriptor */ |
| flush_dcache_range((unsigned long)bd, |
| (unsigned long)bd + sizeof(struct nand_bd)); |
| |
| /* Issue "read" command */ |
| NAND_REG_WRITE(AC_FIFO, B_CT_READ | B_WFR | B_IWC | B_LC | (len - 1)); |
| |
| /* Wait for NAND command and DMA to complete */ |
| while (!nand_flag_is_set(NAND_ISR_CMDDONE)) |
| ; |
| while (!nand_flag_is_set(NAND_ISR_RXDMACOMPLETE)) |
| ; |
| |
| bounce_buffer_stop(&bbstate); |
| } |
| |
| /** |
| * axs101_nand_read_byte - read one byte from the chip |
| * @mtd: MTD device structure |
| */ |
| static u_char axs101_nand_read_byte(struct mtd_info *mtd) |
| { |
| u8 byte; |
| |
| axs101_nand_read_buf(mtd, (uchar *)&byte, sizeof(byte)); |
| return byte; |
| } |
| |
| /** |
| * axs101_nand_read_word - read one word from the chip |
| * @mtd: MTD device structure |
| */ |
| static u16 axs101_nand_read_word(struct mtd_info *mtd) |
| { |
| u16 word; |
| |
| axs101_nand_read_buf(mtd, (uchar *)&word, sizeof(word)); |
| return word; |
| } |
| |
| /** |
| * axs101_nand_hwcontrol - NAND control functions wrapper. |
| * @mtd: MTD device structure |
| * @cmd: Command |
| */ |
| static void axs101_nand_hwcontrol(struct mtd_info *mtdinfo, int cmd, |
| unsigned int ctrl) |
| { |
| if (cmd == NAND_CMD_NONE) |
| return; |
| |
| cmd = cmd & 0xff; |
| |
| switch (ctrl & (NAND_ALE | NAND_CLE)) { |
| /* Address */ |
| case NAND_ALE: |
| cmd |= B_CT_ADDRESS; |
| break; |
| |
| /* Command */ |
| case NAND_CLE: |
| cmd |= B_CT_COMMAND | B_WFR; |
| |
| break; |
| |
| default: |
| debug("%s: unknown ctrl %#x\n", __func__, ctrl); |
| } |
| |
| NAND_REG_WRITE(AC_FIFO, cmd | B_LC); |
| while (!nand_flag_is_set(NAND_ISR_CMDDONE)) |
| ; |
| } |
| |
| int board_nand_init(struct nand_chip *nand) |
| { |
| bd = (struct nand_bd *)memalign(ARCH_DMA_MINALIGN, |
| sizeof(struct nand_bd)); |
| |
| /* Set buffer descriptor address in IDMAC */ |
| NAND_REG_WRITE(IDMAC_BDADDR, bd); |
| |
| nand->ecc.mode = NAND_ECC_SOFT; |
| nand->cmd_ctrl = axs101_nand_hwcontrol; |
| nand->read_byte = axs101_nand_read_byte; |
| nand->read_word = axs101_nand_read_word; |
| nand->write_buf = axs101_nand_write_buf; |
| nand->read_buf = axs101_nand_read_buf; |
| |
| return 0; |
| } |