Alexey Brodkin | a7069dd | 2014-02-04 12:56:19 +0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved. |
| 3 | * |
| 4 | * SPDX-License-Identifier: GPL-2.0+ |
| 5 | */ |
| 6 | |
| 7 | #include <bouncebuf.h> |
| 8 | #include <common.h> |
| 9 | #include <malloc.h> |
| 10 | #include <nand.h> |
| 11 | #include <asm/io.h> |
| 12 | |
| 13 | #define BUS_WIDTH 8 /* AXI data bus width in bytes */ |
| 14 | |
| 15 | /* DMA buffer descriptor bits & masks */ |
| 16 | #define BD_STAT_OWN (1 << 31) |
| 17 | #define BD_STAT_BD_FIRST (1 << 3) |
| 18 | #define BD_STAT_BD_LAST (1 << 2) |
| 19 | #define BD_SIZES_BUFFER1_MASK 0xfff |
| 20 | |
| 21 | #define BD_STAT_BD_COMPLETE (BD_STAT_BD_FIRST | BD_STAT_BD_LAST) |
| 22 | |
| 23 | /* Controller command flags */ |
| 24 | #define B_WFR (1 << 19) /* 1b - Wait for ready */ |
| 25 | #define B_LC (1 << 18) /* 1b - Last cycle */ |
| 26 | #define B_IWC (1 << 13) /* 1b - Interrupt when complete */ |
| 27 | |
| 28 | /* NAND cycle types */ |
| 29 | #define B_CT_ADDRESS (0x0 << 16) /* Address operation */ |
| 30 | #define B_CT_COMMAND (0x1 << 16) /* Command operation */ |
| 31 | #define B_CT_WRITE (0x2 << 16) /* Write operation */ |
| 32 | #define B_CT_READ (0x3 << 16) /* Write operation */ |
| 33 | |
| 34 | enum nand_isr_t { |
| 35 | NAND_ISR_DATAREQUIRED = 0, |
| 36 | NAND_ISR_TXUNDERFLOW, |
| 37 | NAND_ISR_TXOVERFLOW, |
| 38 | NAND_ISR_DATAAVAILABLE, |
| 39 | NAND_ISR_RXUNDERFLOW, |
| 40 | NAND_ISR_RXOVERFLOW, |
| 41 | NAND_ISR_TXDMACOMPLETE, |
| 42 | NAND_ISR_RXDMACOMPLETE, |
| 43 | NAND_ISR_DESCRIPTORUNAVAILABLE, |
| 44 | NAND_ISR_CMDDONE, |
| 45 | NAND_ISR_CMDAVAILABLE, |
| 46 | NAND_ISR_CMDERROR, |
| 47 | NAND_ISR_DATATRANSFEROVER, |
| 48 | NAND_ISR_NONE |
| 49 | }; |
| 50 | |
| 51 | enum nand_regs_t { |
| 52 | AC_FIFO = 0, /* address and command fifo */ |
| 53 | IDMAC_BDADDR = 0x18, /* idmac descriptor list base address */ |
| 54 | INT_STATUS = 0x118, /* interrupt status register */ |
| 55 | INT_CLR_STATUS = 0x120, /* interrupt clear status register */ |
| 56 | }; |
| 57 | |
| 58 | struct nand_bd { |
| 59 | uint32_t status; /* DES0 */ |
| 60 | uint32_t sizes; /* DES1 */ |
| 61 | uint32_t buffer_ptr0; /* DES2 */ |
| 62 | uint32_t buffer_ptr1; /* DES3 */ |
| 63 | }; |
| 64 | |
Vasili Galka | 2fea4f5 | 2014-08-26 17:50:29 +0300 | [diff] [blame] | 65 | #define NAND_REG_WRITE(r, v) \ |
| 66 | writel(v, (volatile void __iomem *)(CONFIG_SYS_NAND_BASE + r)) |
| 67 | #define NAND_REG_READ(r) \ |
| 68 | readl((const volatile void __iomem *)(CONFIG_SYS_NAND_BASE + r)) |
Alexey Brodkin | a7069dd | 2014-02-04 12:56:19 +0400 | [diff] [blame] | 69 | |
| 70 | static struct nand_bd *bd; /* DMA buffer descriptors */ |
| 71 | |
| 72 | /** |
| 73 | * axs101_nand_write_buf - write buffer to chip |
| 74 | * @mtd: MTD device structure |
| 75 | * @buf: data buffer |
| 76 | * @len: number of bytes to write |
| 77 | */ |
| 78 | static uint32_t nand_flag_is_set(uint32_t flag) |
| 79 | { |
| 80 | uint32_t reg = NAND_REG_READ(INT_STATUS); |
| 81 | |
| 82 | if (reg & (1 << NAND_ISR_CMDERROR)) |
| 83 | return 0; |
| 84 | |
| 85 | if (reg & (1 << flag)) { |
| 86 | NAND_REG_WRITE(INT_CLR_STATUS, 1 << flag); |
| 87 | return 1; |
| 88 | } |
| 89 | |
| 90 | return 0; |
| 91 | } |
| 92 | |
| 93 | /** |
| 94 | * axs101_nand_write_buf - write buffer to chip |
| 95 | * @mtd: MTD device structure |
| 96 | * @buf: data buffer |
| 97 | * @len: number of bytes to write |
| 98 | */ |
| 99 | static void axs101_nand_write_buf(struct mtd_info *mtd, const u_char *buf, |
| 100 | int len) |
| 101 | { |
| 102 | struct bounce_buffer bbstate; |
| 103 | |
| 104 | bounce_buffer_start(&bbstate, (void *)buf, len, GEN_BB_READ); |
| 105 | |
| 106 | /* Setup buffer descriptor */ |
| 107 | writel(BD_STAT_OWN | BD_STAT_BD_COMPLETE, &bd->status); |
| 108 | writel(ALIGN(len, BUS_WIDTH) & BD_SIZES_BUFFER1_MASK, &bd->sizes); |
| 109 | writel(bbstate.bounce_buffer, &bd->buffer_ptr0); |
| 110 | writel(0, &bd->buffer_ptr1); |
| 111 | |
Alexey Brodkin | 17b0da8 | 2014-03-21 16:57:47 +0400 | [diff] [blame] | 112 | /* Flush modified buffer descriptor */ |
| 113 | flush_dcache_range((unsigned long)bd, |
| 114 | (unsigned long)bd + sizeof(struct nand_bd)); |
| 115 | |
Alexey Brodkin | a7069dd | 2014-02-04 12:56:19 +0400 | [diff] [blame] | 116 | /* Issue "write" command */ |
| 117 | NAND_REG_WRITE(AC_FIFO, B_CT_WRITE | B_WFR | B_IWC | B_LC | (len-1)); |
| 118 | |
| 119 | /* Wait for NAND command and DMA to complete */ |
| 120 | while (!nand_flag_is_set(NAND_ISR_CMDDONE)) |
| 121 | ; |
| 122 | while (!nand_flag_is_set(NAND_ISR_TXDMACOMPLETE)) |
| 123 | ; |
| 124 | |
| 125 | bounce_buffer_stop(&bbstate); |
| 126 | } |
| 127 | |
| 128 | /** |
| 129 | * axs101_nand_read_buf - read chip data into buffer |
| 130 | * @mtd: MTD device structure |
| 131 | * @buf: buffer to store data |
| 132 | * @len: number of bytes to read |
| 133 | */ |
| 134 | static void axs101_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) |
| 135 | { |
| 136 | struct bounce_buffer bbstate; |
| 137 | |
| 138 | bounce_buffer_start(&bbstate, buf, len, GEN_BB_WRITE); |
| 139 | |
| 140 | /* Setup buffer descriptor */ |
| 141 | writel(BD_STAT_OWN | BD_STAT_BD_COMPLETE, &bd->status); |
| 142 | writel(ALIGN(len, BUS_WIDTH) & BD_SIZES_BUFFER1_MASK, &bd->sizes); |
| 143 | writel(bbstate.bounce_buffer, &bd->buffer_ptr0); |
| 144 | writel(0, &bd->buffer_ptr1); |
| 145 | |
Alexey Brodkin | 17b0da8 | 2014-03-21 16:57:47 +0400 | [diff] [blame] | 146 | /* Flush modified buffer descriptor */ |
| 147 | flush_dcache_range((unsigned long)bd, |
| 148 | (unsigned long)bd + sizeof(struct nand_bd)); |
| 149 | |
Alexey Brodkin | a7069dd | 2014-02-04 12:56:19 +0400 | [diff] [blame] | 150 | /* Issue "read" command */ |
| 151 | NAND_REG_WRITE(AC_FIFO, B_CT_READ | B_WFR | B_IWC | B_LC | (len - 1)); |
| 152 | |
| 153 | /* Wait for NAND command and DMA to complete */ |
| 154 | while (!nand_flag_is_set(NAND_ISR_CMDDONE)) |
| 155 | ; |
| 156 | while (!nand_flag_is_set(NAND_ISR_RXDMACOMPLETE)) |
| 157 | ; |
| 158 | |
| 159 | bounce_buffer_stop(&bbstate); |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * axs101_nand_read_byte - read one byte from the chip |
| 164 | * @mtd: MTD device structure |
| 165 | */ |
| 166 | static u_char axs101_nand_read_byte(struct mtd_info *mtd) |
| 167 | { |
| 168 | u8 byte; |
| 169 | |
| 170 | axs101_nand_read_buf(mtd, (uchar *)&byte, sizeof(byte)); |
| 171 | return byte; |
| 172 | } |
| 173 | |
| 174 | /** |
| 175 | * axs101_nand_read_word - read one word from the chip |
| 176 | * @mtd: MTD device structure |
| 177 | */ |
| 178 | static u16 axs101_nand_read_word(struct mtd_info *mtd) |
| 179 | { |
| 180 | u16 word; |
| 181 | |
| 182 | axs101_nand_read_buf(mtd, (uchar *)&word, sizeof(word)); |
| 183 | return word; |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * axs101_nand_hwcontrol - NAND control functions wrapper. |
| 188 | * @mtd: MTD device structure |
| 189 | * @cmd: Command |
| 190 | */ |
| 191 | static void axs101_nand_hwcontrol(struct mtd_info *mtdinfo, int cmd, |
| 192 | unsigned int ctrl) |
| 193 | { |
| 194 | if (cmd == NAND_CMD_NONE) |
| 195 | return; |
| 196 | |
| 197 | cmd = cmd & 0xff; |
| 198 | |
| 199 | switch (ctrl & (NAND_ALE | NAND_CLE)) { |
| 200 | /* Address */ |
| 201 | case NAND_ALE: |
| 202 | cmd |= B_CT_ADDRESS; |
| 203 | break; |
| 204 | |
| 205 | /* Command */ |
| 206 | case NAND_CLE: |
| 207 | cmd |= B_CT_COMMAND | B_WFR; |
| 208 | |
| 209 | break; |
| 210 | |
| 211 | default: |
| 212 | debug("%s: unknown ctrl %#x\n", __func__, ctrl); |
| 213 | } |
| 214 | |
| 215 | NAND_REG_WRITE(AC_FIFO, cmd | B_LC); |
| 216 | while (!nand_flag_is_set(NAND_ISR_CMDDONE)) |
| 217 | ; |
| 218 | } |
| 219 | |
| 220 | int board_nand_init(struct nand_chip *nand) |
| 221 | { |
| 222 | bd = (struct nand_bd *)memalign(ARCH_DMA_MINALIGN, |
| 223 | sizeof(struct nand_bd)); |
| 224 | |
| 225 | /* Set buffer descriptor address in IDMAC */ |
| 226 | NAND_REG_WRITE(IDMAC_BDADDR, bd); |
| 227 | |
| 228 | nand->ecc.mode = NAND_ECC_SOFT; |
| 229 | nand->cmd_ctrl = axs101_nand_hwcontrol; |
| 230 | nand->read_byte = axs101_nand_read_byte; |
| 231 | nand->read_word = axs101_nand_read_word; |
| 232 | nand->write_buf = axs101_nand_write_buf; |
| 233 | nand->read_buf = axs101_nand_read_buf; |
| 234 | |
| 235 | return 0; |
| 236 | } |