|
@@ -23,50 +23,43 @@
|
|
|
#include <linux/mutex.h>
|
|
|
#include <linux/mtd/mtd.h>
|
|
|
#include <linux/module.h>
|
|
|
+#include <linux/slab.h>
|
|
|
|
|
|
#include "denali.h"
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
-/*
|
|
|
- * We define a module parameter that allows the user to override
|
|
|
- * the hardware and decide what timing mode should be used.
|
|
|
- */
|
|
|
-#define NAND_DEFAULT_TIMINGS -1
|
|
|
+#define DENALI_NAND_NAME "denali-nand"
|
|
|
|
|
|
-static int onfi_timing_mode = NAND_DEFAULT_TIMINGS;
|
|
|
-module_param(onfi_timing_mode, int, S_IRUGO);
|
|
|
-MODULE_PARM_DESC(onfi_timing_mode,
|
|
|
- "Overrides default ONFI setting. -1 indicates use default timings");
|
|
|
+/* Host Data/Command Interface */
|
|
|
+#define DENALI_HOST_ADDR 0x00
|
|
|
+#define DENALI_HOST_DATA 0x10
|
|
|
|
|
|
-#define DENALI_NAND_NAME "denali-nand"
|
|
|
+#define DENALI_MAP00 (0 << 26) /* direct access to buffer */
|
|
|
+#define DENALI_MAP01 (1 << 26) /* read/write pages in PIO */
|
|
|
+#define DENALI_MAP10 (2 << 26) /* high-level control plane */
|
|
|
+#define DENALI_MAP11 (3 << 26) /* direct controller access */
|
|
|
|
|
|
-/*
|
|
|
- * We define a macro here that combines all interrupts this driver uses into
|
|
|
- * a single constant value, for convenience.
|
|
|
- */
|
|
|
-#define DENALI_IRQ_ALL (INTR__DMA_CMD_COMP | \
|
|
|
- INTR__ECC_TRANSACTION_DONE | \
|
|
|
- INTR__ECC_ERR | \
|
|
|
- INTR__PROGRAM_FAIL | \
|
|
|
- INTR__LOAD_COMP | \
|
|
|
- INTR__PROGRAM_COMP | \
|
|
|
- INTR__TIME_OUT | \
|
|
|
- INTR__ERASE_FAIL | \
|
|
|
- INTR__RST_COMP | \
|
|
|
- INTR__ERASE_COMP)
|
|
|
+/* MAP11 access cycle type */
|
|
|
+#define DENALI_MAP11_CMD ((DENALI_MAP11) | 0) /* command cycle */
|
|
|
+#define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1) /* address cycle */
|
|
|
+#define DENALI_MAP11_DATA ((DENALI_MAP11) | 2) /* data cycle */
|
|
|
|
|
|
-/*
|
|
|
- * indicates whether or not the internal value for the flash bank is
|
|
|
- * valid or not
|
|
|
- */
|
|
|
-#define CHIP_SELECT_INVALID -1
|
|
|
+/* MAP10 commands */
|
|
|
+#define DENALI_ERASE 0x01
|
|
|
+
|
|
|
+#define DENALI_BANK(denali) ((denali)->active_bank << 24)
|
|
|
+
|
|
|
+#define DENALI_INVALID_BANK -1
|
|
|
+#define DENALI_NR_BANKS 4
|
|
|
|
|
|
/*
|
|
|
- * This macro divides two integers and rounds fractional values up
|
|
|
- * to the nearest integer value.
|
|
|
+ * The bus interface clock, clk_x, is phase aligned with the core clock. The
|
|
|
+ * clk_x is an integral multiple N of the core clk. The value N is configured
|
|
|
+ * at IP delivery time, and its available value is 4, 5, or 6. We need to align
|
|
|
+ * to the largest value to make it work with any possible configuration.
|
|
|
*/
|
|
|
-#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
|
|
|
+#define DENALI_CLK_X_MULT 6
|
|
|
|
|
|
/*
|
|
|
* this macro allows us to convert from an MTD structure to our own
|
|
@@ -77,339 +70,11 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
|
|
|
return container_of(mtd_to_nand(mtd), struct denali_nand_info, nand);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * These constants are defined by the driver to enable common driver
|
|
|
- * configuration options.
|
|
|
- */
|
|
|
-#define SPARE_ACCESS 0x41
|
|
|
-#define MAIN_ACCESS 0x42
|
|
|
-#define MAIN_SPARE_ACCESS 0x43
|
|
|
-
|
|
|
-#define DENALI_READ 0
|
|
|
-#define DENALI_WRITE 0x100
|
|
|
-
|
|
|
-/*
|
|
|
- * this is a helper macro that allows us to
|
|
|
- * format the bank into the proper bits for the controller
|
|
|
- */
|
|
|
-#define BANK(x) ((x) << 24)
|
|
|
-
|
|
|
-/* forward declarations */
|
|
|
-static void clear_interrupts(struct denali_nand_info *denali);
|
|
|
-static uint32_t wait_for_irq(struct denali_nand_info *denali,
|
|
|
- uint32_t irq_mask);
|
|
|
-static void denali_irq_enable(struct denali_nand_info *denali,
|
|
|
- uint32_t int_mask);
|
|
|
-static uint32_t read_interrupt_status(struct denali_nand_info *denali);
|
|
|
-
|
|
|
-/*
|
|
|
- * Certain operations for the denali NAND controller use an indexed mode to
|
|
|
- * read/write data. The operation is performed by writing the address value
|
|
|
- * of the command to the device memory followed by the data. This function
|
|
|
- * abstracts this common operation.
|
|
|
- */
|
|
|
-static void index_addr(struct denali_nand_info *denali,
|
|
|
- uint32_t address, uint32_t data)
|
|
|
-{
|
|
|
- iowrite32(address, denali->flash_mem);
|
|
|
- iowrite32(data, denali->flash_mem + 0x10);
|
|
|
-}
|
|
|
-
|
|
|
-/* Perform an indexed read of the device */
|
|
|
-static void index_addr_read_data(struct denali_nand_info *denali,
|
|
|
- uint32_t address, uint32_t *pdata)
|
|
|
-{
|
|
|
- iowrite32(address, denali->flash_mem);
|
|
|
- *pdata = ioread32(denali->flash_mem + 0x10);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * We need to buffer some data for some of the NAND core routines.
|
|
|
- * The operations manage buffering that data.
|
|
|
- */
|
|
|
-static void reset_buf(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- denali->buf.head = denali->buf.tail = 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
|
|
|
-{
|
|
|
- denali->buf.buf[denali->buf.tail++] = byte;
|
|
|
-}
|
|
|
-
|
|
|
-/* reads the status of the device */
|
|
|
-static void read_status(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- uint32_t cmd;
|
|
|
-
|
|
|
- /* initialize the data buffer to store status */
|
|
|
- reset_buf(denali);
|
|
|
-
|
|
|
- cmd = ioread32(denali->flash_reg + WRITE_PROTECT);
|
|
|
- if (cmd)
|
|
|
- write_byte_to_buf(denali, NAND_STATUS_WP);
|
|
|
- else
|
|
|
- write_byte_to_buf(denali, 0);
|
|
|
-}
|
|
|
-
|
|
|
-/* resets a specific device connected to the core */
|
|
|
-static void reset_bank(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- uint32_t irq_status;
|
|
|
- uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
|
|
|
-
|
|
|
- clear_interrupts(denali);
|
|
|
-
|
|
|
- iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
|
|
|
-
|
|
|
- irq_status = wait_for_irq(denali, irq_mask);
|
|
|
-
|
|
|
- if (irq_status & INTR__TIME_OUT)
|
|
|
- dev_err(denali->dev, "reset bank failed.\n");
|
|
|
-}
|
|
|
-
|
|
|
-/* Reset the flash controller */
|
|
|
-static uint16_t denali_nand_reset(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- for (i = 0; i < denali->max_banks; i++)
|
|
|
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
|
|
|
- denali->flash_reg + INTR_STATUS(i));
|
|
|
-
|
|
|
- for (i = 0; i < denali->max_banks; i++) {
|
|
|
- iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
|
|
|
- while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
|
|
|
- (INTR__RST_COMP | INTR__TIME_OUT)))
|
|
|
- cpu_relax();
|
|
|
- if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
|
|
|
- INTR__TIME_OUT)
|
|
|
- dev_dbg(denali->dev,
|
|
|
- "NAND Reset operation timed out on bank %d\n", i);
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < denali->max_banks; i++)
|
|
|
- iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
|
|
|
- denali->flash_reg + INTR_STATUS(i));
|
|
|
-
|
|
|
- return PASS;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * this routine calculates the ONFI timing values for a given mode and
|
|
|
- * programs the clocking register accordingly. The mode is determined by
|
|
|
- * the get_onfi_nand_para routine.
|
|
|
- */
|
|
|
-static void nand_onfi_timing_set(struct denali_nand_info *denali,
|
|
|
- uint16_t mode)
|
|
|
-{
|
|
|
- uint16_t Trea[6] = {40, 30, 25, 20, 20, 16};
|
|
|
- uint16_t Trp[6] = {50, 25, 17, 15, 12, 10};
|
|
|
- uint16_t Treh[6] = {30, 15, 15, 10, 10, 7};
|
|
|
- uint16_t Trc[6] = {100, 50, 35, 30, 25, 20};
|
|
|
- uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15};
|
|
|
- uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5};
|
|
|
- uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25};
|
|
|
- uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70};
|
|
|
- uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100};
|
|
|
- uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100};
|
|
|
- uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60};
|
|
|
- uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15};
|
|
|
-
|
|
|
- uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid;
|
|
|
- uint16_t dv_window = 0;
|
|
|
- uint16_t en_lo, en_hi;
|
|
|
- uint16_t acc_clks;
|
|
|
- uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt;
|
|
|
-
|
|
|
- en_lo = CEIL_DIV(Trp[mode], CLK_X);
|
|
|
- en_hi = CEIL_DIV(Treh[mode], CLK_X);
|
|
|
-#if ONFI_BLOOM_TIME
|
|
|
- if ((en_hi * CLK_X) < (Treh[mode] + 2))
|
|
|
- en_hi++;
|
|
|
-#endif
|
|
|
-
|
|
|
- if ((en_lo + en_hi) * CLK_X < Trc[mode])
|
|
|
- en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X);
|
|
|
-
|
|
|
- if ((en_lo + en_hi) < CLK_MULTI)
|
|
|
- en_lo += CLK_MULTI - en_lo - en_hi;
|
|
|
-
|
|
|
- while (dv_window < 8) {
|
|
|
- data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode];
|
|
|
-
|
|
|
- data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode];
|
|
|
-
|
|
|
- data_invalid = data_invalid_rhoh < data_invalid_rloh ?
|
|
|
- data_invalid_rhoh : data_invalid_rloh;
|
|
|
-
|
|
|
- dv_window = data_invalid - Trea[mode];
|
|
|
-
|
|
|
- if (dv_window < 8)
|
|
|
- en_lo++;
|
|
|
- }
|
|
|
-
|
|
|
- acc_clks = CEIL_DIV(Trea[mode], CLK_X);
|
|
|
-
|
|
|
- while (acc_clks * CLK_X - Trea[mode] < 3)
|
|
|
- acc_clks++;
|
|
|
-
|
|
|
- if (data_invalid - acc_clks * CLK_X < 2)
|
|
|
- dev_warn(denali->dev, "%s, Line %d: Warning!\n",
|
|
|
- __FILE__, __LINE__);
|
|
|
-
|
|
|
- addr_2_data = CEIL_DIV(Tadl[mode], CLK_X);
|
|
|
- re_2_we = CEIL_DIV(Trhw[mode], CLK_X);
|
|
|
- re_2_re = CEIL_DIV(Trhz[mode], CLK_X);
|
|
|
- we_2_re = CEIL_DIV(Twhr[mode], CLK_X);
|
|
|
- cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X);
|
|
|
- if (cs_cnt == 0)
|
|
|
- cs_cnt = 1;
|
|
|
-
|
|
|
- if (Tcea[mode]) {
|
|
|
- while (cs_cnt * CLK_X + Trea[mode] < Tcea[mode])
|
|
|
- cs_cnt++;
|
|
|
- }
|
|
|
-
|
|
|
-#if MODE5_WORKAROUND
|
|
|
- if (mode == 5)
|
|
|
- acc_clks = 5;
|
|
|
-#endif
|
|
|
-
|
|
|
- /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */
|
|
|
- if (ioread32(denali->flash_reg + MANUFACTURER_ID) == 0 &&
|
|
|
- ioread32(denali->flash_reg + DEVICE_ID) == 0x88)
|
|
|
- acc_clks = 6;
|
|
|
-
|
|
|
- iowrite32(acc_clks, denali->flash_reg + ACC_CLKS);
|
|
|
- iowrite32(re_2_we, denali->flash_reg + RE_2_WE);
|
|
|
- iowrite32(re_2_re, denali->flash_reg + RE_2_RE);
|
|
|
- iowrite32(we_2_re, denali->flash_reg + WE_2_RE);
|
|
|
- iowrite32(addr_2_data, denali->flash_reg + ADDR_2_DATA);
|
|
|
- iowrite32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT);
|
|
|
- iowrite32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT);
|
|
|
- iowrite32(cs_cnt, denali->flash_reg + CS_SETUP_CNT);
|
|
|
-}
|
|
|
-
|
|
|
-/* queries the NAND device to see what ONFI modes it supports. */
|
|
|
-static uint16_t get_onfi_nand_para(struct denali_nand_info *denali)
|
|
|
+static void denali_host_write(struct denali_nand_info *denali,
|
|
|
+ uint32_t addr, uint32_t data)
|
|
|
{
|
|
|
- int i;
|
|
|
-
|
|
|
- /*
|
|
|
- * we needn't to do a reset here because driver has already
|
|
|
- * reset all the banks before
|
|
|
- */
|
|
|
- if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
|
|
|
- ONFI_TIMING_MODE__VALUE))
|
|
|
- return FAIL;
|
|
|
-
|
|
|
- for (i = 5; i > 0; i--) {
|
|
|
- if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) &
|
|
|
- (0x01 << i))
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- nand_onfi_timing_set(denali, i);
|
|
|
-
|
|
|
- /*
|
|
|
- * By now, all the ONFI devices we know support the page cache
|
|
|
- * rw feature. So here we enable the pipeline_rw_ahead feature
|
|
|
- */
|
|
|
- /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */
|
|
|
- /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */
|
|
|
-
|
|
|
- return PASS;
|
|
|
-}
|
|
|
-
|
|
|
-static void get_samsung_nand_para(struct denali_nand_info *denali,
|
|
|
- uint8_t device_id)
|
|
|
-{
|
|
|
- if (device_id == 0xd3) { /* Samsung K9WAG08U1A */
|
|
|
- /* Set timing register values according to datasheet */
|
|
|
- iowrite32(5, denali->flash_reg + ACC_CLKS);
|
|
|
- iowrite32(20, denali->flash_reg + RE_2_WE);
|
|
|
- iowrite32(12, denali->flash_reg + WE_2_RE);
|
|
|
- iowrite32(14, denali->flash_reg + ADDR_2_DATA);
|
|
|
- iowrite32(3, denali->flash_reg + RDWR_EN_LO_CNT);
|
|
|
- iowrite32(2, denali->flash_reg + RDWR_EN_HI_CNT);
|
|
|
- iowrite32(2, denali->flash_reg + CS_SETUP_CNT);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static void get_toshiba_nand_para(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- /*
|
|
|
- * Workaround to fix a controller bug which reports a wrong
|
|
|
- * spare area size for some kind of Toshiba NAND device
|
|
|
- */
|
|
|
- if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
|
|
|
- (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
|
|
|
- iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
|
|
|
-}
|
|
|
-
|
|
|
-static void get_hynix_nand_para(struct denali_nand_info *denali,
|
|
|
- uint8_t device_id)
|
|
|
-{
|
|
|
- switch (device_id) {
|
|
|
- case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
|
|
|
- case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
|
|
|
- iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
|
|
|
- iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
|
|
|
- iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
|
|
|
- iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
|
|
|
- break;
|
|
|
- default:
|
|
|
- dev_warn(denali->dev,
|
|
|
- "Unknown Hynix NAND (Device ID: 0x%x).\n"
|
|
|
- "Will use default parameter values instead.\n",
|
|
|
- device_id);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * determines how many NAND chips are connected to the controller. Note for
|
|
|
- * Intel CE4100 devices we don't support more than one device.
|
|
|
- */
|
|
|
-static void find_valid_banks(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- uint32_t id[denali->max_banks];
|
|
|
- int i;
|
|
|
-
|
|
|
- denali->total_used_banks = 1;
|
|
|
- for (i = 0; i < denali->max_banks; i++) {
|
|
|
- index_addr(denali, MODE_11 | (i << 24) | 0, 0x90);
|
|
|
- index_addr(denali, MODE_11 | (i << 24) | 1, 0);
|
|
|
- index_addr_read_data(denali, MODE_11 | (i << 24) | 2, &id[i]);
|
|
|
-
|
|
|
- dev_dbg(denali->dev,
|
|
|
- "Return 1st ID for bank[%d]: %x\n", i, id[i]);
|
|
|
-
|
|
|
- if (i == 0) {
|
|
|
- if (!(id[i] & 0x0ff))
|
|
|
- break; /* WTF? */
|
|
|
- } else {
|
|
|
- if ((id[i] & 0x0ff) == (id[0] & 0x0ff))
|
|
|
- denali->total_used_banks++;
|
|
|
- else
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (denali->platform == INTEL_CE4100) {
|
|
|
- /*
|
|
|
- * Platform limitations of the CE4100 device limit
|
|
|
- * users to a single chip solution for NAND.
|
|
|
- * Multichip support is not enabled.
|
|
|
- */
|
|
|
- if (denali->total_used_banks != 1) {
|
|
|
- dev_err(denali->dev,
|
|
|
- "Sorry, Intel CE4100 only supports a single NAND device.\n");
|
|
|
- BUG();
|
|
|
- }
|
|
|
- }
|
|
|
- dev_dbg(denali->dev,
|
|
|
- "denali->total_used_banks: %d\n", denali->total_used_banks);
|
|
|
+ iowrite32(addr, denali->host + DENALI_HOST_ADDR);
|
|
|
+ iowrite32(data, denali->host + DENALI_HOST_DATA);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -418,7 +83,7 @@ static void find_valid_banks(struct denali_nand_info *denali)
|
|
|
*/
|
|
|
static void detect_max_banks(struct denali_nand_info *denali)
|
|
|
{
|
|
|
- uint32_t features = ioread32(denali->flash_reg + FEATURES);
|
|
|
+ uint32_t features = ioread32(denali->reg + FEATURES);
|
|
|
|
|
|
denali->max_banks = 1 << (features & FEATURES__N_BANKS);
|
|
|
|
|
@@ -427,227 +92,120 @@ static void detect_max_banks(struct denali_nand_info *denali)
|
|
|
denali->max_banks <<= 1;
|
|
|
}
|
|
|
|
|
|
-static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
|
|
|
+static void denali_enable_irq(struct denali_nand_info *denali)
|
|
|
{
|
|
|
- uint16_t status = PASS;
|
|
|
- uint32_t id_bytes[8], addr;
|
|
|
- uint8_t maf_id, device_id;
|
|
|
int i;
|
|
|
|
|
|
- /*
|
|
|
- * Use read id method to get device ID and other params.
|
|
|
- * For some NAND chips, controller can't report the correct
|
|
|
- * device ID by reading from DEVICE_ID register
|
|
|
- */
|
|
|
- addr = MODE_11 | BANK(denali->flash_bank);
|
|
|
- index_addr(denali, addr | 0, 0x90);
|
|
|
- index_addr(denali, addr | 1, 0);
|
|
|
- for (i = 0; i < 8; i++)
|
|
|
- index_addr_read_data(denali, addr | 2, &id_bytes[i]);
|
|
|
- maf_id = id_bytes[0];
|
|
|
- device_id = id_bytes[1];
|
|
|
-
|
|
|
- if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) &
|
|
|
- ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */
|
|
|
- if (FAIL == get_onfi_nand_para(denali))
|
|
|
- return FAIL;
|
|
|
- } else if (maf_id == 0xEC) { /* Samsung NAND */
|
|
|
- get_samsung_nand_para(denali, device_id);
|
|
|
- } else if (maf_id == 0x98) { /* Toshiba NAND */
|
|
|
- get_toshiba_nand_para(denali);
|
|
|
- } else if (maf_id == 0xAD) { /* Hynix NAND */
|
|
|
- get_hynix_nand_para(denali, device_id);
|
|
|
- }
|
|
|
-
|
|
|
- dev_info(denali->dev,
|
|
|
- "Dump timing register values:\n"
|
|
|
- "acc_clks: %d, re_2_we: %d, re_2_re: %d\n"
|
|
|
- "we_2_re: %d, addr_2_data: %d, rdwr_en_lo_cnt: %d\n"
|
|
|
- "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n",
|
|
|
- ioread32(denali->flash_reg + ACC_CLKS),
|
|
|
- ioread32(denali->flash_reg + RE_2_WE),
|
|
|
- ioread32(denali->flash_reg + RE_2_RE),
|
|
|
- ioread32(denali->flash_reg + WE_2_RE),
|
|
|
- ioread32(denali->flash_reg + ADDR_2_DATA),
|
|
|
- ioread32(denali->flash_reg + RDWR_EN_LO_CNT),
|
|
|
- ioread32(denali->flash_reg + RDWR_EN_HI_CNT),
|
|
|
- ioread32(denali->flash_reg + CS_SETUP_CNT));
|
|
|
-
|
|
|
- find_valid_banks(denali);
|
|
|
-
|
|
|
- /*
|
|
|
- * If the user specified to override the default timings
|
|
|
- * with a specific ONFI mode, we apply those changes here.
|
|
|
- */
|
|
|
- if (onfi_timing_mode != NAND_DEFAULT_TIMINGS)
|
|
|
- nand_onfi_timing_set(denali, onfi_timing_mode);
|
|
|
-
|
|
|
- return status;
|
|
|
+ for (i = 0; i < DENALI_NR_BANKS; i++)
|
|
|
+ iowrite32(U32_MAX, denali->reg + INTR_EN(i));
|
|
|
+ iowrite32(GLOBAL_INT_EN_FLAG, denali->reg + GLOBAL_INT_ENABLE);
|
|
|
}
|
|
|
|
|
|
-static void denali_set_intr_modes(struct denali_nand_info *denali,
|
|
|
- uint16_t INT_ENABLE)
|
|
|
+static void denali_disable_irq(struct denali_nand_info *denali)
|
|
|
{
|
|
|
- if (INT_ENABLE)
|
|
|
- iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
|
|
|
- else
|
|
|
- iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * validation function to verify that the controlling software is making
|
|
|
- * a valid request
|
|
|
- */
|
|
|
-static inline bool is_flash_bank_valid(int flash_bank)
|
|
|
-{
|
|
|
- return flash_bank >= 0 && flash_bank < 4;
|
|
|
-}
|
|
|
-
|
|
|
-static void denali_irq_init(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- uint32_t int_mask;
|
|
|
int i;
|
|
|
|
|
|
- /* Disable global interrupts */
|
|
|
- denali_set_intr_modes(denali, false);
|
|
|
-
|
|
|
- int_mask = DENALI_IRQ_ALL;
|
|
|
-
|
|
|
- /* Clear all status bits */
|
|
|
- for (i = 0; i < denali->max_banks; ++i)
|
|
|
- iowrite32(0xFFFF, denali->flash_reg + INTR_STATUS(i));
|
|
|
-
|
|
|
- denali_irq_enable(denali, int_mask);
|
|
|
+ for (i = 0; i < DENALI_NR_BANKS; i++)
|
|
|
+ iowrite32(0, denali->reg + INTR_EN(i));
|
|
|
+ iowrite32(0, denali->reg + GLOBAL_INT_ENABLE);
|
|
|
}
|
|
|
|
|
|
-static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali)
|
|
|
+static void denali_clear_irq(struct denali_nand_info *denali,
|
|
|
+ int bank, uint32_t irq_status)
|
|
|
{
|
|
|
- denali_set_intr_modes(denali, false);
|
|
|
+ /* write one to clear bits */
|
|
|
+ iowrite32(irq_status, denali->reg + INTR_STATUS(bank));
|
|
|
}
|
|
|
|
|
|
-static void denali_irq_enable(struct denali_nand_info *denali,
|
|
|
- uint32_t int_mask)
|
|
|
+static void denali_clear_irq_all(struct denali_nand_info *denali)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < denali->max_banks; ++i)
|
|
|
- iowrite32(int_mask, denali->flash_reg + INTR_EN(i));
|
|
|
+ for (i = 0; i < DENALI_NR_BANKS; i++)
|
|
|
+ denali_clear_irq(denali, i, U32_MAX);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * This function only returns when an interrupt that this driver cares about
|
|
|
- * occurs. This is to reduce the overhead of servicing interrupts
|
|
|
- */
|
|
|
-static inline uint32_t denali_irq_detected(struct denali_nand_info *denali)
|
|
|
+static irqreturn_t denali_isr(int irq, void *dev_id)
|
|
|
{
|
|
|
- return read_interrupt_status(denali) & DENALI_IRQ_ALL;
|
|
|
-}
|
|
|
+ struct denali_nand_info *denali = dev_id;
|
|
|
+ irqreturn_t ret = IRQ_NONE;
|
|
|
+ uint32_t irq_status;
|
|
|
+ int i;
|
|
|
|
|
|
-/* Interrupts are cleared by writing a 1 to the appropriate status bit */
|
|
|
-static inline void clear_interrupt(struct denali_nand_info *denali,
|
|
|
- uint32_t irq_mask)
|
|
|
-{
|
|
|
- uint32_t intr_status_reg;
|
|
|
+ spin_lock(&denali->irq_lock);
|
|
|
|
|
|
- intr_status_reg = INTR_STATUS(denali->flash_bank);
|
|
|
+ for (i = 0; i < DENALI_NR_BANKS; i++) {
|
|
|
+ irq_status = ioread32(denali->reg + INTR_STATUS(i));
|
|
|
+ if (irq_status)
|
|
|
+ ret = IRQ_HANDLED;
|
|
|
|
|
|
- iowrite32(irq_mask, denali->flash_reg + intr_status_reg);
|
|
|
-}
|
|
|
+ denali_clear_irq(denali, i, irq_status);
|
|
|
|
|
|
-static void clear_interrupts(struct denali_nand_info *denali)
|
|
|
-{
|
|
|
- uint32_t status;
|
|
|
+ if (i != denali->active_bank)
|
|
|
+ continue;
|
|
|
|
|
|
- spin_lock_irq(&denali->irq_lock);
|
|
|
+ denali->irq_status |= irq_status;
|
|
|
|
|
|
- status = read_interrupt_status(denali);
|
|
|
- clear_interrupt(denali, status);
|
|
|
+ if (denali->irq_status & denali->irq_mask)
|
|
|
+ complete(&denali->complete);
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_unlock(&denali->irq_lock);
|
|
|
|
|
|
- denali->irq_status = 0x0;
|
|
|
- spin_unlock_irq(&denali->irq_lock);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-static uint32_t read_interrupt_status(struct denali_nand_info *denali)
|
|
|
+static void denali_reset_irq(struct denali_nand_info *denali)
|
|
|
{
|
|
|
- uint32_t intr_status_reg;
|
|
|
-
|
|
|
- intr_status_reg = INTR_STATUS(denali->flash_bank);
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
- return ioread32(denali->flash_reg + intr_status_reg);
|
|
|
+ spin_lock_irqsave(&denali->irq_lock, flags);
|
|
|
+ denali->irq_status = 0;
|
|
|
+ denali->irq_mask = 0;
|
|
|
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * This is the interrupt service routine. It handles all interrupts
|
|
|
- * sent to this device. Note that on CE4100, this is a shared interrupt.
|
|
|
- */
|
|
|
-static irqreturn_t denali_isr(int irq, void *dev_id)
|
|
|
+static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
|
|
|
+ uint32_t irq_mask)
|
|
|
{
|
|
|
- struct denali_nand_info *denali = dev_id;
|
|
|
+ unsigned long time_left, flags;
|
|
|
uint32_t irq_status;
|
|
|
- irqreturn_t result = IRQ_NONE;
|
|
|
|
|
|
- spin_lock(&denali->irq_lock);
|
|
|
+ spin_lock_irqsave(&denali->irq_lock, flags);
|
|
|
|
|
|
- /* check to see if a valid NAND chip has been selected. */
|
|
|
- if (is_flash_bank_valid(denali->flash_bank)) {
|
|
|
- /*
|
|
|
- * check to see if controller generated the interrupt,
|
|
|
- * since this is a shared interrupt
|
|
|
- */
|
|
|
- irq_status = denali_irq_detected(denali);
|
|
|
- if (irq_status != 0) {
|
|
|
- /* handle interrupt */
|
|
|
- /* first acknowledge it */
|
|
|
- clear_interrupt(denali, irq_status);
|
|
|
- /*
|
|
|
- * store the status in the device context for someone
|
|
|
- * to read
|
|
|
- */
|
|
|
- denali->irq_status |= irq_status;
|
|
|
- /* notify anyone who cares that it happened */
|
|
|
- complete(&denali->complete);
|
|
|
- /* tell the OS that we've handled this */
|
|
|
- result = IRQ_HANDLED;
|
|
|
- }
|
|
|
+ irq_status = denali->irq_status;
|
|
|
+
|
|
|
+ if (irq_mask & irq_status) {
|
|
|
+ /* return immediately if the IRQ has already happened. */
|
|
|
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
|
|
|
+ return irq_status;
|
|
|
}
|
|
|
- spin_unlock(&denali->irq_lock);
|
|
|
- return result;
|
|
|
-}
|
|
|
|
|
|
-static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
|
|
|
-{
|
|
|
- unsigned long comp_res;
|
|
|
- uint32_t intr_status;
|
|
|
- unsigned long timeout = msecs_to_jiffies(1000);
|
|
|
+ denali->irq_mask = irq_mask;
|
|
|
+ reinit_completion(&denali->complete);
|
|
|
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
|
|
|
|
|
|
- do {
|
|
|
- comp_res =
|
|
|
- wait_for_completion_timeout(&denali->complete, timeout);
|
|
|
- spin_lock_irq(&denali->irq_lock);
|
|
|
- intr_status = denali->irq_status;
|
|
|
-
|
|
|
- if (intr_status & irq_mask) {
|
|
|
- denali->irq_status &= ~irq_mask;
|
|
|
- spin_unlock_irq(&denali->irq_lock);
|
|
|
- /* our interrupt was detected */
|
|
|
- break;
|
|
|
- }
|
|
|
+ time_left = wait_for_completion_timeout(&denali->complete,
|
|
|
+ msecs_to_jiffies(1000));
|
|
|
+ if (!time_left) {
|
|
|
+ dev_err(denali->dev, "timeout while waiting for irq 0x%x\n",
|
|
|
+ denali->irq_mask);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- * these are not the interrupts you are looking for -
|
|
|
- * need to wait again
|
|
|
- */
|
|
|
- spin_unlock_irq(&denali->irq_lock);
|
|
|
- } while (comp_res != 0);
|
|
|
+ return denali->irq_status;
|
|
|
+}
|
|
|
+
|
|
|
+static uint32_t denali_check_irq(struct denali_nand_info *denali)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ uint32_t irq_status;
|
|
|
|
|
|
- if (comp_res == 0) {
|
|
|
- /* timeout */
|
|
|
- pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n",
|
|
|
- intr_status, irq_mask);
|
|
|
+ spin_lock_irqsave(&denali->irq_lock, flags);
|
|
|
+ irq_status = denali->irq_status;
|
|
|
+ spin_unlock_irqrestore(&denali->irq_lock, flags);
|
|
|
|
|
|
- intr_status = 0;
|
|
|
- }
|
|
|
- return intr_status;
|
|
|
+ return irq_status;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -664,153 +222,111 @@ static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en,
|
|
|
transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0;
|
|
|
|
|
|
/* Enable spare area/ECC per user's request. */
|
|
|
- iowrite32(ecc_en_flag, denali->flash_reg + ECC_ENABLE);
|
|
|
- iowrite32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG);
|
|
|
+ iowrite32(ecc_en_flag, denali->reg + ECC_ENABLE);
|
|
|
+ iowrite32(transfer_spare_flag, denali->reg + TRANSFER_SPARE_REG);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * sends a pipeline command operation to the controller. See the Denali NAND
|
|
|
- * controller's user guide for more information (section 4.2.3.6).
|
|
|
- */
|
|
|
-static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
|
|
|
- bool ecc_en, bool transfer_spare,
|
|
|
- int access_type, int op)
|
|
|
+static void denali_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
|
{
|
|
|
- int status = PASS;
|
|
|
- uint32_t addr, cmd;
|
|
|
-
|
|
|
- setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ int i;
|
|
|
|
|
|
- clear_interrupts(denali);
|
|
|
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
|
|
|
+ denali->host + DENALI_HOST_ADDR);
|
|
|
|
|
|
- addr = BANK(denali->flash_bank) | denali->page;
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
+ buf[i] = ioread32(denali->host + DENALI_HOST_DATA);
|
|
|
+}
|
|
|
|
|
|
- if (op == DENALI_WRITE && access_type != SPARE_ACCESS) {
|
|
|
- cmd = MODE_01 | addr;
|
|
|
- iowrite32(cmd, denali->flash_mem);
|
|
|
- } else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) {
|
|
|
- /* read spare area */
|
|
|
- cmd = MODE_10 | addr;
|
|
|
- index_addr(denali, cmd, access_type);
|
|
|
+static void denali_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
|
|
+{
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ int i;
|
|
|
|
|
|
- cmd = MODE_01 | addr;
|
|
|
- iowrite32(cmd, denali->flash_mem);
|
|
|
- } else if (op == DENALI_READ) {
|
|
|
- /* setup page read request for access type */
|
|
|
- cmd = MODE_10 | addr;
|
|
|
- index_addr(denali, cmd, access_type);
|
|
|
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
|
|
|
+ denali->host + DENALI_HOST_ADDR);
|
|
|
|
|
|
- cmd = MODE_01 | addr;
|
|
|
- iowrite32(cmd, denali->flash_mem);
|
|
|
- }
|
|
|
- return status;
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
+ iowrite32(buf[i], denali->host + DENALI_HOST_DATA);
|
|
|
}
|
|
|
|
|
|
-/* helper function that simply writes a buffer to the flash */
|
|
|
-static int write_data_to_flash_mem(struct denali_nand_info *denali,
|
|
|
- const uint8_t *buf, int len)
|
|
|
+static void denali_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
|
|
|
{
|
|
|
- uint32_t *buf32;
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ uint16_t *buf16 = (uint16_t *)buf;
|
|
|
int i;
|
|
|
|
|
|
- /*
|
|
|
- * verify that the len is a multiple of 4.
|
|
|
- * see comment in read_data_from_flash_mem()
|
|
|
- */
|
|
|
- BUG_ON((len % 4) != 0);
|
|
|
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
|
|
|
+ denali->host + DENALI_HOST_ADDR);
|
|
|
|
|
|
- /* write the data to the flash memory */
|
|
|
- buf32 = (uint32_t *)buf;
|
|
|
- for (i = 0; i < len / 4; i++)
|
|
|
- iowrite32(*buf32++, denali->flash_mem + 0x10);
|
|
|
- return i * 4; /* intent is to return the number of bytes read */
|
|
|
+ for (i = 0; i < len / 2; i++)
|
|
|
+ buf16[i] = ioread32(denali->host + DENALI_HOST_DATA);
|
|
|
}
|
|
|
|
|
|
-/* helper function that simply reads a buffer from the flash */
|
|
|
-static int read_data_from_flash_mem(struct denali_nand_info *denali,
|
|
|
- uint8_t *buf, int len)
|
|
|
+static void denali_write_buf16(struct mtd_info *mtd, const uint8_t *buf,
|
|
|
+ int len)
|
|
|
{
|
|
|
- uint32_t *buf32;
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ const uint16_t *buf16 = (const uint16_t *)buf;
|
|
|
int i;
|
|
|
|
|
|
- /*
|
|
|
- * we assume that len will be a multiple of 4, if not it would be nice
|
|
|
- * to know about it ASAP rather than have random failures...
|
|
|
- * This assumption is based on the fact that this function is designed
|
|
|
- * to be used to read flash pages, which are typically multiples of 4.
|
|
|
- */
|
|
|
- BUG_ON((len % 4) != 0);
|
|
|
+ iowrite32(DENALI_MAP11_DATA | DENALI_BANK(denali),
|
|
|
+ denali->host + DENALI_HOST_ADDR);
|
|
|
|
|
|
- /* transfer the data from the flash */
|
|
|
- buf32 = (uint32_t *)buf;
|
|
|
- for (i = 0; i < len / 4; i++)
|
|
|
- *buf32++ = ioread32(denali->flash_mem + 0x10);
|
|
|
- return i * 4; /* intent is to return the number of bytes read */
|
|
|
+ for (i = 0; i < len / 2; i++)
|
|
|
+ iowrite32(buf16[i], denali->host + DENALI_HOST_DATA);
|
|
|
}
|
|
|
|
|
|
-/* writes OOB data to the device */
|
|
|
-static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
|
|
|
+static uint8_t denali_read_byte(struct mtd_info *mtd)
|
|
|
{
|
|
|
- struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- uint32_t irq_status;
|
|
|
- uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
|
|
|
- int status = 0;
|
|
|
+ uint8_t byte;
|
|
|
|
|
|
- denali->page = page;
|
|
|
+ denali_read_buf(mtd, &byte, 1);
|
|
|
|
|
|
- if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS,
|
|
|
- DENALI_WRITE) == PASS) {
|
|
|
- write_data_to_flash_mem(denali, buf, mtd->oobsize);
|
|
|
+ return byte;
|
|
|
+}
|
|
|
|
|
|
- /* wait for operation to complete */
|
|
|
- irq_status = wait_for_irq(denali, irq_mask);
|
|
|
+static void denali_write_byte(struct mtd_info *mtd, uint8_t byte)
|
|
|
+{
|
|
|
+ denali_write_buf(mtd, &byte, 1);
|
|
|
+}
|
|
|
|
|
|
- if (irq_status == 0) {
|
|
|
- dev_err(denali->dev, "OOB write failed\n");
|
|
|
- status = -EIO;
|
|
|
- }
|
|
|
- } else {
|
|
|
- dev_err(denali->dev, "unable to send pipeline command\n");
|
|
|
- status = -EIO;
|
|
|
- }
|
|
|
- return status;
|
|
|
+static uint16_t denali_read_word(struct mtd_info *mtd)
|
|
|
+{
|
|
|
+ uint16_t word;
|
|
|
+
|
|
|
+ denali_read_buf16(mtd, (uint8_t *)&word, 2);
|
|
|
+
|
|
|
+ return word;
|
|
|
}
|
|
|
|
|
|
-/* reads OOB data from the device */
|
|
|
-static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
|
|
|
+static void denali_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- uint32_t irq_mask = INTR__LOAD_COMP;
|
|
|
- uint32_t irq_status, addr, cmd;
|
|
|
+ uint32_t type;
|
|
|
|
|
|
- denali->page = page;
|
|
|
+ if (ctrl & NAND_CLE)
|
|
|
+ type = DENALI_MAP11_CMD;
|
|
|
+ else if (ctrl & NAND_ALE)
|
|
|
+ type = DENALI_MAP11_ADDR;
|
|
|
+ else
|
|
|
+ return;
|
|
|
|
|
|
- if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
|
|
|
- DENALI_READ) == PASS) {
|
|
|
- read_data_from_flash_mem(denali, buf, mtd->oobsize);
|
|
|
+ /*
|
|
|
+ * Some commands are followed by chip->dev_ready or chip->waitfunc.
|
|
|
+ * irq_status must be cleared here to catch the R/B# interrupt later.
|
|
|
+ */
|
|
|
+ if (ctrl & NAND_CTRL_CHANGE)
|
|
|
+ denali_reset_irq(denali);
|
|
|
|
|
|
- /*
|
|
|
- * wait for command to be accepted
|
|
|
- * can always use status0 bit as the
|
|
|
- * mask is identical for each bank.
|
|
|
- */
|
|
|
- irq_status = wait_for_irq(denali, irq_mask);
|
|
|
+ denali_host_write(denali, DENALI_BANK(denali) | type, dat);
|
|
|
+}
|
|
|
|
|
|
- if (irq_status == 0)
|
|
|
- dev_err(denali->dev, "page on OOB timeout %d\n",
|
|
|
- denali->page);
|
|
|
+static int denali_dev_ready(struct mtd_info *mtd)
|
|
|
+{
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
|
|
|
- /*
|
|
|
- * We set the device back to MAIN_ACCESS here as I observed
|
|
|
- * instability with the controller if you do a block erase
|
|
|
- * and the last transaction was a SPARE_ACCESS. Block erase
|
|
|
- * is reliable (according to the MTD test infrastructure)
|
|
|
- * if you are in MAIN_ACCESS.
|
|
|
- */
|
|
|
- addr = BANK(denali->flash_bank) | denali->page;
|
|
|
- cmd = MODE_10 | addr;
|
|
|
- index_addr(denali, cmd, MAIN_ACCESS);
|
|
|
- }
|
|
|
+ return !!(denali_check_irq(denali) & INTR__INT_ACT);
|
|
|
}
|
|
|
|
|
|
static int denali_check_erased_page(struct mtd_info *mtd,
|
|
@@ -856,11 +372,11 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
|
|
|
unsigned long *uncor_ecc_flags)
|
|
|
{
|
|
|
struct nand_chip *chip = mtd_to_nand(mtd);
|
|
|
- int bank = denali->flash_bank;
|
|
|
+ int bank = denali->active_bank;
|
|
|
uint32_t ecc_cor;
|
|
|
unsigned int max_bitflips;
|
|
|
|
|
|
- ecc_cor = ioread32(denali->flash_reg + ECC_COR_INFO(bank));
|
|
|
+ ecc_cor = ioread32(denali->reg + ECC_COR_INFO(bank));
|
|
|
ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
|
|
|
|
|
|
if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
|
|
@@ -886,8 +402,6 @@ static int denali_hw_ecc_fixup(struct mtd_info *mtd,
|
|
|
return max_bitflips;
|
|
|
}
|
|
|
|
|
|
-#define ECC_SECTOR_SIZE 512
|
|
|
-
|
|
|
#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
|
|
|
#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET))
|
|
|
#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
|
|
@@ -899,22 +413,23 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
|
|
|
struct denali_nand_info *denali,
|
|
|
unsigned long *uncor_ecc_flags, uint8_t *buf)
|
|
|
{
|
|
|
+ unsigned int ecc_size = denali->nand.ecc.size;
|
|
|
unsigned int bitflips = 0;
|
|
|
unsigned int max_bitflips = 0;
|
|
|
uint32_t err_addr, err_cor_info;
|
|
|
unsigned int err_byte, err_sector, err_device;
|
|
|
uint8_t err_cor_value;
|
|
|
unsigned int prev_sector = 0;
|
|
|
+ uint32_t irq_status;
|
|
|
|
|
|
- /* read the ECC errors. we'll ignore them for now */
|
|
|
- denali_set_intr_modes(denali, false);
|
|
|
+ denali_reset_irq(denali);
|
|
|
|
|
|
do {
|
|
|
- err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
|
|
|
+ err_addr = ioread32(denali->reg + ECC_ERROR_ADDRESS);
|
|
|
err_sector = ECC_SECTOR(err_addr);
|
|
|
err_byte = ECC_BYTE(err_addr);
|
|
|
|
|
|
- err_cor_info = ioread32(denali->flash_reg + ERR_CORRECTION_INFO);
|
|
|
+ err_cor_info = ioread32(denali->reg + ERR_CORRECTION_INFO);
|
|
|
err_cor_value = ECC_CORRECTION_VALUE(err_cor_info);
|
|
|
err_device = ECC_ERR_DEVICE(err_cor_info);
|
|
|
|
|
@@ -928,9 +443,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
|
|
|
* an erased sector.
|
|
|
*/
|
|
|
*uncor_ecc_flags |= BIT(err_sector);
|
|
|
- } else if (err_byte < ECC_SECTOR_SIZE) {
|
|
|
+ } else if (err_byte < ecc_size) {
|
|
|
/*
|
|
|
- * If err_byte is larger than ECC_SECTOR_SIZE, means error
|
|
|
+ * If err_byte is larger than ecc_size, means error
|
|
|
* happened in OOB, so we ignore it. It's no need for
|
|
|
* us to correct it err_device is represented the NAND
|
|
|
* error bits are happened in if there are more than
|
|
@@ -939,8 +454,8 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
|
|
|
int offset;
|
|
|
unsigned int flips_in_byte;
|
|
|
|
|
|
- offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
|
|
|
- denali->devnum + err_device;
|
|
|
+ offset = (err_sector * ecc_size + err_byte) *
|
|
|
+ denali->devs_per_cs + err_device;
|
|
|
|
|
|
/* correct the ECC error */
|
|
|
flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
|
|
@@ -959,10 +474,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
|
|
|
* ECC_TRANSACTION_DONE interrupt, so here just wait for
|
|
|
* a while for this interrupt
|
|
|
*/
|
|
|
- while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
|
|
|
- cpu_relax();
|
|
|
- clear_interrupts(denali);
|
|
|
- denali_set_intr_modes(denali, true);
|
|
|
+ irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
|
|
|
+ if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
|
|
|
+ return -EIO;
|
|
|
|
|
|
return max_bitflips;
|
|
|
}
|
|
@@ -970,17 +484,17 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
|
|
|
/* programs the controller to either enable/disable DMA transfers */
|
|
|
static void denali_enable_dma(struct denali_nand_info *denali, bool en)
|
|
|
{
|
|
|
- iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->flash_reg + DMA_ENABLE);
|
|
|
- ioread32(denali->flash_reg + DMA_ENABLE);
|
|
|
+ iowrite32(en ? DMA_ENABLE__FLAG : 0, denali->reg + DMA_ENABLE);
|
|
|
+ ioread32(denali->reg + DMA_ENABLE);
|
|
|
}
|
|
|
|
|
|
-static void denali_setup_dma64(struct denali_nand_info *denali, int op)
|
|
|
+static void denali_setup_dma64(struct denali_nand_info *denali,
|
|
|
+ dma_addr_t dma_addr, int page, int write)
|
|
|
{
|
|
|
uint32_t mode;
|
|
|
const int page_count = 1;
|
|
|
- uint64_t addr = denali->buf.dma_buf;
|
|
|
|
|
|
- mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
|
|
|
+ mode = DENALI_MAP10 | DENALI_BANK(denali) | page;
|
|
|
|
|
|
/* DMA is a three step process */
|
|
|
|
|
@@ -988,191 +502,354 @@ static void denali_setup_dma64(struct denali_nand_info *denali, int op)
|
|
|
* 1. setup transfer type, interrupt when complete,
|
|
|
* burst len = 64 bytes, the number of pages
|
|
|
*/
|
|
|
- index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
|
|
|
+ denali_host_write(denali, mode,
|
|
|
+ 0x01002000 | (64 << 16) | (write << 8) | page_count);
|
|
|
|
|
|
/* 2. set memory low address */
|
|
|
- index_addr(denali, mode, addr);
|
|
|
+ denali_host_write(denali, mode, dma_addr);
|
|
|
|
|
|
/* 3. set memory high address */
|
|
|
- index_addr(denali, mode, addr >> 32);
|
|
|
+ denali_host_write(denali, mode, (uint64_t)dma_addr >> 32);
|
|
|
}
|
|
|
|
|
|
-static void denali_setup_dma32(struct denali_nand_info *denali, int op)
|
|
|
+static void denali_setup_dma32(struct denali_nand_info *denali,
|
|
|
+ dma_addr_t dma_addr, int page, int write)
|
|
|
{
|
|
|
uint32_t mode;
|
|
|
const int page_count = 1;
|
|
|
- uint32_t addr = denali->buf.dma_buf;
|
|
|
|
|
|
- mode = MODE_10 | BANK(denali->flash_bank);
|
|
|
+ mode = DENALI_MAP10 | DENALI_BANK(denali);
|
|
|
|
|
|
/* DMA is a four step process */
|
|
|
|
|
|
/* 1. setup transfer type and # of pages */
|
|
|
- index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
|
|
|
+ denali_host_write(denali, mode | page,
|
|
|
+ 0x2000 | (write << 8) | page_count);
|
|
|
|
|
|
/* 2. set memory high address bits 23:8 */
|
|
|
- index_addr(denali, mode | ((addr >> 16) << 8), 0x2200);
|
|
|
+ denali_host_write(denali, mode | ((dma_addr >> 16) << 8), 0x2200);
|
|
|
|
|
|
/* 3. set memory low address bits 23:8 */
|
|
|
- index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300);
|
|
|
+ denali_host_write(denali, mode | ((dma_addr & 0xffff) << 8), 0x2300);
|
|
|
|
|
|
/* 4. interrupt when complete, burst len = 64 bytes */
|
|
|
- index_addr(denali, mode | 0x14000, 0x2400);
|
|
|
+ denali_host_write(denali, mode | 0x14000, 0x2400);
|
|
|
}
|
|
|
|
|
|
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
|
|
|
+static void denali_setup_dma(struct denali_nand_info *denali,
|
|
|
+ dma_addr_t dma_addr, int page, int write)
|
|
|
{
|
|
|
if (denali->caps & DENALI_CAP_DMA_64BIT)
|
|
|
- denali_setup_dma64(denali, op);
|
|
|
+ denali_setup_dma64(denali, dma_addr, page, write);
|
|
|
else
|
|
|
- denali_setup_dma32(denali, op);
|
|
|
+ denali_setup_dma32(denali, dma_addr, page, write);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * writes a page. user specifies type, and this function handles the
|
|
|
- * configuration details.
|
|
|
- */
|
|
|
-static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
- const uint8_t *buf, bool raw_xfer)
|
|
|
+static int denali_pio_read(struct denali_nand_info *denali, void *buf,
|
|
|
+ size_t size, int page, int raw)
|
|
|
{
|
|
|
- struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- dma_addr_t addr = denali->buf.dma_buf;
|
|
|
- size_t size = mtd->writesize + mtd->oobsize;
|
|
|
+ uint32_t addr = DENALI_BANK(denali) | page;
|
|
|
+ uint32_t *buf32 = (uint32_t *)buf;
|
|
|
+ uint32_t irq_status, ecc_err_mask;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
|
|
|
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
|
|
|
+ else
|
|
|
+ ecc_err_mask = INTR__ECC_ERR;
|
|
|
+
|
|
|
+ denali_reset_irq(denali);
|
|
|
+
|
|
|
+ iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR);
|
|
|
+ for (i = 0; i < size / 4; i++)
|
|
|
+ *buf32++ = ioread32(denali->host + DENALI_HOST_DATA);
|
|
|
+
|
|
|
+ irq_status = denali_wait_for_irq(denali, INTR__PAGE_XFER_INC);
|
|
|
+ if (!(irq_status & INTR__PAGE_XFER_INC))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ if (irq_status & INTR__ERASED_PAGE)
|
|
|
+ memset(buf, 0xff, size);
|
|
|
+
|
|
|
+ return irq_status & ecc_err_mask ? -EBADMSG : 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int denali_pio_write(struct denali_nand_info *denali,
|
|
|
+ const void *buf, size_t size, int page, int raw)
|
|
|
+{
|
|
|
+ uint32_t addr = DENALI_BANK(denali) | page;
|
|
|
+ const uint32_t *buf32 = (uint32_t *)buf;
|
|
|
uint32_t irq_status;
|
|
|
- uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
|
|
|
+ int i;
|
|
|
|
|
|
- /*
|
|
|
- * if it is a raw xfer, we want to disable ecc and send the spare area.
|
|
|
- * !raw_xfer - enable ecc
|
|
|
- * raw_xfer - transfer spare
|
|
|
- */
|
|
|
- setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
|
|
|
+ denali_reset_irq(denali);
|
|
|
|
|
|
- /* copy buffer into DMA buffer */
|
|
|
- memcpy(denali->buf.buf, buf, mtd->writesize);
|
|
|
+ iowrite32(DENALI_MAP01 | addr, denali->host + DENALI_HOST_ADDR);
|
|
|
+ for (i = 0; i < size / 4; i++)
|
|
|
+ iowrite32(*buf32++, denali->host + DENALI_HOST_DATA);
|
|
|
|
|
|
- if (raw_xfer) {
|
|
|
- /* transfer the data to the spare area */
|
|
|
- memcpy(denali->buf.buf + mtd->writesize,
|
|
|
- chip->oob_poi,
|
|
|
- mtd->oobsize);
|
|
|
+ irq_status = denali_wait_for_irq(denali,
|
|
|
+ INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL);
|
|
|
+ if (!(irq_status & INTR__PROGRAM_COMP))
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,
|
|
|
+ size_t size, int page, int raw, int write)
|
|
|
+{
|
|
|
+ if (write)
|
|
|
+ return denali_pio_write(denali, buf, size, page, raw);
|
|
|
+ else
|
|
|
+ return denali_pio_read(denali, buf, size, page, raw);
|
|
|
+}
|
|
|
+
|
|
|
+static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,
|
|
|
+ size_t size, int page, int raw, int write)
|
|
|
+{
|
|
|
+ dma_addr_t dma_addr;
|
|
|
+ uint32_t irq_mask, irq_status, ecc_err_mask;
|
|
|
+ enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ dma_addr = dma_map_single(denali->dev, buf, size, dir);
|
|
|
+ if (dma_mapping_error(denali->dev, dma_addr)) {
|
|
|
+ dev_dbg(denali->dev, "Failed to DMA-map buffer. Trying PIO.\n");
|
|
|
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
|
|
|
}
|
|
|
|
|
|
- dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
|
|
|
+ if (write) {
|
|
|
+ /*
|
|
|
+ * INTR__PROGRAM_COMP is never asserted for the DMA transfer.
|
|
|
+ * We can use INTR__DMA_CMD_COMP instead. This flag is asserted
|
|
|
+ * when the page program is completed.
|
|
|
+ */
|
|
|
+ irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
|
|
|
+ ecc_err_mask = 0;
|
|
|
+ } else if (denali->caps & DENALI_CAP_HW_ECC_FIXUP) {
|
|
|
+ irq_mask = INTR__DMA_CMD_COMP;
|
|
|
+ ecc_err_mask = INTR__ECC_UNCOR_ERR;
|
|
|
+ } else {
|
|
|
+ irq_mask = INTR__DMA_CMD_COMP;
|
|
|
+ ecc_err_mask = INTR__ECC_ERR;
|
|
|
+ }
|
|
|
|
|
|
- clear_interrupts(denali);
|
|
|
denali_enable_dma(denali, true);
|
|
|
|
|
|
- denali_setup_dma(denali, DENALI_WRITE);
|
|
|
+ denali_reset_irq(denali);
|
|
|
+ denali_setup_dma(denali, dma_addr, page, write);
|
|
|
|
|
|
/* wait for operation to complete */
|
|
|
- irq_status = wait_for_irq(denali, irq_mask);
|
|
|
-
|
|
|
- if (irq_status == 0) {
|
|
|
- dev_err(denali->dev, "timeout on write_page (type = %d)\n",
|
|
|
- raw_xfer);
|
|
|
- denali->status = NAND_STATUS_FAIL;
|
|
|
- }
|
|
|
+ irq_status = denali_wait_for_irq(denali, irq_mask);
|
|
|
+ if (!(irq_status & INTR__DMA_CMD_COMP))
|
|
|
+ ret = -EIO;
|
|
|
+ else if (irq_status & ecc_err_mask)
|
|
|
+ ret = -EBADMSG;
|
|
|
|
|
|
denali_enable_dma(denali, false);
|
|
|
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
|
|
|
+ dma_unmap_single(denali->dev, dma_addr, size, dir);
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ if (irq_status & INTR__ERASED_PAGE)
|
|
|
+ memset(buf, 0xff, size);
|
|
|
|
|
|
-/* NAND core entry points */
|
|
|
+ return ret;
|
|
|
+}
|
|
|
|
|
|
-/*
|
|
|
- * this is the callback that the NAND core calls to write a page. Since
|
|
|
- * writing a page with ECC or without is similar, all the work is done
|
|
|
- * by write_page above.
|
|
|
- */
|
|
|
-static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
- const uint8_t *buf, int oob_required, int page)
|
|
|
+static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
|
|
|
+ size_t size, int page, int raw, int write)
|
|
|
{
|
|
|
- /*
|
|
|
- * for regular page writes, we let HW handle all the ECC
|
|
|
- * data written to the device.
|
|
|
- */
|
|
|
- return write_page(mtd, chip, buf, false);
|
|
|
+ setup_ecc_for_xfer(denali, !raw, raw);
|
|
|
+
|
|
|
+ if (denali->dma_avail)
|
|
|
+ return denali_dma_xfer(denali, buf, size, page, raw, write);
|
|
|
+ else
|
|
|
+ return denali_pio_xfer(denali, buf, size, page, raw, write);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * This is the callback that the NAND core calls to write a page without ECC.
|
|
|
- * raw access is similar to ECC page writes, so all the work is done in the
|
|
|
- * write_page() function above.
|
|
|
- */
|
|
|
-static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
- const uint8_t *buf, int oob_required,
|
|
|
- int page)
|
|
|
+static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ int page, int write)
|
|
|
{
|
|
|
- /*
|
|
|
- * for raw page writes, we want to disable ECC and simply write
|
|
|
- * whatever data is in the buffer.
|
|
|
- */
|
|
|
- return write_page(mtd, chip, buf, true);
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ unsigned int start_cmd = write ? NAND_CMD_SEQIN : NAND_CMD_READ0;
|
|
|
+ unsigned int rnd_cmd = write ? NAND_CMD_RNDIN : NAND_CMD_RNDOUT;
|
|
|
+ int writesize = mtd->writesize;
|
|
|
+ int oobsize = mtd->oobsize;
|
|
|
+ uint8_t *bufpoi = chip->oob_poi;
|
|
|
+ int ecc_steps = chip->ecc.steps;
|
|
|
+ int ecc_size = chip->ecc.size;
|
|
|
+ int ecc_bytes = chip->ecc.bytes;
|
|
|
+ int oob_skip = denali->oob_skip_bytes;
|
|
|
+ size_t size = writesize + oobsize;
|
|
|
+ int i, pos, len;
|
|
|
+
|
|
|
+ /* BBM at the beginning of the OOB area */
|
|
|
+ chip->cmdfunc(mtd, start_cmd, writesize, page);
|
|
|
+ if (write)
|
|
|
+ chip->write_buf(mtd, bufpoi, oob_skip);
|
|
|
+ else
|
|
|
+ chip->read_buf(mtd, bufpoi, oob_skip);
|
|
|
+ bufpoi += oob_skip;
|
|
|
+
|
|
|
+ /* OOB ECC */
|
|
|
+ for (i = 0; i < ecc_steps; i++) {
|
|
|
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
|
|
|
+ len = ecc_bytes;
|
|
|
+
|
|
|
+ if (pos >= writesize)
|
|
|
+ pos += oob_skip;
|
|
|
+ else if (pos + len > writesize)
|
|
|
+ len = writesize - pos;
|
|
|
+
|
|
|
+ chip->cmdfunc(mtd, rnd_cmd, pos, -1);
|
|
|
+ if (write)
|
|
|
+ chip->write_buf(mtd, bufpoi, len);
|
|
|
+ else
|
|
|
+ chip->read_buf(mtd, bufpoi, len);
|
|
|
+ bufpoi += len;
|
|
|
+ if (len < ecc_bytes) {
|
|
|
+ len = ecc_bytes - len;
|
|
|
+ chip->cmdfunc(mtd, rnd_cmd, writesize + oob_skip, -1);
|
|
|
+ if (write)
|
|
|
+ chip->write_buf(mtd, bufpoi, len);
|
|
|
+ else
|
|
|
+ chip->read_buf(mtd, bufpoi, len);
|
|
|
+ bufpoi += len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* OOB free */
|
|
|
+ len = oobsize - (bufpoi - chip->oob_poi);
|
|
|
+ chip->cmdfunc(mtd, rnd_cmd, size - len, -1);
|
|
|
+ if (write)
|
|
|
+ chip->write_buf(mtd, bufpoi, len);
|
|
|
+ else
|
|
|
+ chip->read_buf(mtd, bufpoi, len);
|
|
|
}
|
|
|
|
|
|
-static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
- int page)
|
|
|
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ uint8_t *buf, int oob_required, int page)
|
|
|
{
|
|
|
- return write_oob_data(mtd, chip->oob_poi, page);
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ int writesize = mtd->writesize;
|
|
|
+ int oobsize = mtd->oobsize;
|
|
|
+ int ecc_steps = chip->ecc.steps;
|
|
|
+ int ecc_size = chip->ecc.size;
|
|
|
+ int ecc_bytes = chip->ecc.bytes;
|
|
|
+ void *dma_buf = denali->buf;
|
|
|
+ int oob_skip = denali->oob_skip_bytes;
|
|
|
+ size_t size = writesize + oobsize;
|
|
|
+ int ret, i, pos, len;
|
|
|
+
|
|
|
+ ret = denali_data_xfer(denali, dma_buf, size, page, 1, 0);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* Arrange the buffer for syndrome payload/ecc layout */
|
|
|
+ if (buf) {
|
|
|
+ for (i = 0; i < ecc_steps; i++) {
|
|
|
+ pos = i * (ecc_size + ecc_bytes);
|
|
|
+ len = ecc_size;
|
|
|
+
|
|
|
+ if (pos >= writesize)
|
|
|
+ pos += oob_skip;
|
|
|
+ else if (pos + len > writesize)
|
|
|
+ len = writesize - pos;
|
|
|
+
|
|
|
+ memcpy(buf, dma_buf + pos, len);
|
|
|
+ buf += len;
|
|
|
+ if (len < ecc_size) {
|
|
|
+ len = ecc_size - len;
|
|
|
+ memcpy(buf, dma_buf + writesize + oob_skip,
|
|
|
+ len);
|
|
|
+ buf += len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (oob_required) {
|
|
|
+ uint8_t *oob = chip->oob_poi;
|
|
|
+
|
|
|
+ /* BBM at the beginning of the OOB area */
|
|
|
+ memcpy(oob, dma_buf + writesize, oob_skip);
|
|
|
+ oob += oob_skip;
|
|
|
+
|
|
|
+ /* OOB ECC */
|
|
|
+ for (i = 0; i < ecc_steps; i++) {
|
|
|
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
|
|
|
+ len = ecc_bytes;
|
|
|
+
|
|
|
+ if (pos >= writesize)
|
|
|
+ pos += oob_skip;
|
|
|
+ else if (pos + len > writesize)
|
|
|
+ len = writesize - pos;
|
|
|
+
|
|
|
+ memcpy(oob, dma_buf + pos, len);
|
|
|
+ oob += len;
|
|
|
+ if (len < ecc_bytes) {
|
|
|
+ len = ecc_bytes - len;
|
|
|
+ memcpy(oob, dma_buf + writesize + oob_skip,
|
|
|
+ len);
|
|
|
+ oob += len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* OOB free */
|
|
|
+ len = oobsize - (oob - chip->oob_poi);
|
|
|
+ memcpy(oob, dma_buf + size - len, len);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
int page)
|
|
|
{
|
|
|
- read_oob_data(mtd, chip->oob_poi, page);
|
|
|
+ denali_oob_xfer(mtd, chip, page, 0);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
- uint8_t *buf, int oob_required, int page)
|
|
|
+static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ int page)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- dma_addr_t addr = denali->buf.dma_buf;
|
|
|
- size_t size = mtd->writesize + mtd->oobsize;
|
|
|
- uint32_t irq_status;
|
|
|
- uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
|
|
|
- INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
|
|
|
- INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
|
|
|
- unsigned long uncor_ecc_flags = 0;
|
|
|
- int stat = 0;
|
|
|
+ int status;
|
|
|
|
|
|
- if (page != denali->page) {
|
|
|
- dev_err(denali->dev,
|
|
|
- "IN %s: page %d is not equal to denali->page %d",
|
|
|
- __func__, page, denali->page);
|
|
|
- BUG();
|
|
|
- }
|
|
|
+ denali_reset_irq(denali);
|
|
|
|
|
|
- setup_ecc_for_xfer(denali, true, false);
|
|
|
+ denali_oob_xfer(mtd, chip, page, 1);
|
|
|
|
|
|
- denali_enable_dma(denali, true);
|
|
|
- dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
|
|
+ status = chip->waitfunc(mtd, chip);
|
|
|
|
|
|
- clear_interrupts(denali);
|
|
|
- denali_setup_dma(denali, DENALI_READ);
|
|
|
-
|
|
|
- /* wait for operation to complete */
|
|
|
- irq_status = wait_for_irq(denali, irq_mask);
|
|
|
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
|
|
|
+}
|
|
|
|
|
|
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
|
|
|
+static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ uint8_t *buf, int oob_required, int page)
|
|
|
+{
|
|
|
+ struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ unsigned long uncor_ecc_flags = 0;
|
|
|
+ int stat = 0;
|
|
|
+ int ret;
|
|
|
|
|
|
- memcpy(buf, denali->buf.buf, mtd->writesize);
|
|
|
+ ret = denali_data_xfer(denali, buf, mtd->writesize, page, 0, 0);
|
|
|
+ if (ret && ret != -EBADMSG)
|
|
|
+ return ret;
|
|
|
|
|
|
if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
|
|
|
stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
|
|
|
- else if (irq_status & INTR__ECC_ERR)
|
|
|
+ else if (ret == -EBADMSG)
|
|
|
stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
|
|
|
- denali_enable_dma(denali, false);
|
|
|
|
|
|
if (stat < 0)
|
|
|
return stat;
|
|
|
|
|
|
if (uncor_ecc_flags) {
|
|
|
- read_oob_data(mtd, chip->oob_poi, denali->page);
|
|
|
+ ret = denali_read_oob(mtd, chip, page);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
stat = denali_check_erased_page(mtd, chip, buf,
|
|
|
uncor_ecc_flags, stat);
|
|
@@ -1181,137 +858,266 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
return stat;
|
|
|
}
|
|
|
|
|
|
-static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
- uint8_t *buf, int oob_required, int page)
|
|
|
+static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ const uint8_t *buf, int oob_required, int page)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- dma_addr_t addr = denali->buf.dma_buf;
|
|
|
- size_t size = mtd->writesize + mtd->oobsize;
|
|
|
- uint32_t irq_mask = INTR__DMA_CMD_COMP;
|
|
|
-
|
|
|
- if (page != denali->page) {
|
|
|
- dev_err(denali->dev,
|
|
|
- "IN %s: page %d is not equal to denali->page %d",
|
|
|
- __func__, page, denali->page);
|
|
|
- BUG();
|
|
|
- }
|
|
|
-
|
|
|
- setup_ecc_for_xfer(denali, false, true);
|
|
|
- denali_enable_dma(denali, true);
|
|
|
-
|
|
|
- dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
|
|
|
-
|
|
|
- clear_interrupts(denali);
|
|
|
- denali_setup_dma(denali, DENALI_READ);
|
|
|
-
|
|
|
- /* wait for operation to complete */
|
|
|
- wait_for_irq(denali, irq_mask);
|
|
|
+ int writesize = mtd->writesize;
|
|
|
+ int oobsize = mtd->oobsize;
|
|
|
+ int ecc_steps = chip->ecc.steps;
|
|
|
+ int ecc_size = chip->ecc.size;
|
|
|
+ int ecc_bytes = chip->ecc.bytes;
|
|
|
+ void *dma_buf = denali->buf;
|
|
|
+ int oob_skip = denali->oob_skip_bytes;
|
|
|
+ size_t size = writesize + oobsize;
|
|
|
+ int i, pos, len;
|
|
|
|
|
|
- dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
|
|
|
+ /*
|
|
|
+ * Fill the buffer with 0xff first except the full page transfer.
|
|
|
+ * This simplifies the logic.
|
|
|
+ */
|
|
|
+ if (!buf || !oob_required)
|
|
|
+ memset(dma_buf, 0xff, size);
|
|
|
+
|
|
|
+ /* Arrange the buffer for syndrome payload/ecc layout */
|
|
|
+ if (buf) {
|
|
|
+ for (i = 0; i < ecc_steps; i++) {
|
|
|
+ pos = i * (ecc_size + ecc_bytes);
|
|
|
+ len = ecc_size;
|
|
|
+
|
|
|
+ if (pos >= writesize)
|
|
|
+ pos += oob_skip;
|
|
|
+ else if (pos + len > writesize)
|
|
|
+ len = writesize - pos;
|
|
|
+
|
|
|
+ memcpy(dma_buf + pos, buf, len);
|
|
|
+ buf += len;
|
|
|
+ if (len < ecc_size) {
|
|
|
+ len = ecc_size - len;
|
|
|
+ memcpy(dma_buf + writesize + oob_skip, buf,
|
|
|
+ len);
|
|
|
+ buf += len;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- denali_enable_dma(denali, false);
|
|
|
+ if (oob_required) {
|
|
|
+ const uint8_t *oob = chip->oob_poi;
|
|
|
+
|
|
|
+ /* BBM at the beginning of the OOB area */
|
|
|
+ memcpy(dma_buf + writesize, oob, oob_skip);
|
|
|
+ oob += oob_skip;
|
|
|
+
|
|
|
+ /* OOB ECC */
|
|
|
+ for (i = 0; i < ecc_steps; i++) {
|
|
|
+ pos = ecc_size + i * (ecc_size + ecc_bytes);
|
|
|
+ len = ecc_bytes;
|
|
|
+
|
|
|
+ if (pos >= writesize)
|
|
|
+ pos += oob_skip;
|
|
|
+ else if (pos + len > writesize)
|
|
|
+ len = writesize - pos;
|
|
|
+
|
|
|
+ memcpy(dma_buf + pos, oob, len);
|
|
|
+ oob += len;
|
|
|
+ if (len < ecc_bytes) {
|
|
|
+ len = ecc_bytes - len;
|
|
|
+ memcpy(dma_buf + writesize + oob_skip, oob,
|
|
|
+ len);
|
|
|
+ oob += len;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- memcpy(buf, denali->buf.buf, mtd->writesize);
|
|
|
- memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize);
|
|
|
+ /* OOB free */
|
|
|
+ len = oobsize - (oob - chip->oob_poi);
|
|
|
+ memcpy(dma_buf + size - len, oob, len);
|
|
|
+ }
|
|
|
|
|
|
- return 0;
|
|
|
+ return denali_data_xfer(denali, dma_buf, size, page, 1, 1);
|
|
|
}
|
|
|
|
|
|
-static uint8_t denali_read_byte(struct mtd_info *mtd)
|
|
|
+static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ const uint8_t *buf, int oob_required, int page)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- uint8_t result = 0xff;
|
|
|
-
|
|
|
- if (denali->buf.head < denali->buf.tail)
|
|
|
- result = denali->buf.buf[denali->buf.head++];
|
|
|
|
|
|
- return result;
|
|
|
+ return denali_data_xfer(denali, (void *)buf, mtd->writesize,
|
|
|
+ page, 0, 1);
|
|
|
}
|
|
|
|
|
|
static void denali_select_chip(struct mtd_info *mtd, int chip)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
|
|
|
- spin_lock_irq(&denali->irq_lock);
|
|
|
- denali->flash_bank = chip;
|
|
|
- spin_unlock_irq(&denali->irq_lock);
|
|
|
+ denali->active_bank = chip;
|
|
|
}
|
|
|
|
|
|
static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- int status = denali->status;
|
|
|
+ uint32_t irq_status;
|
|
|
|
|
|
- denali->status = 0;
|
|
|
+ /* R/B# pin transitioned from low to high? */
|
|
|
+ irq_status = denali_wait_for_irq(denali, INTR__INT_ACT);
|
|
|
|
|
|
- return status;
|
|
|
+ return irq_status & INTR__INT_ACT ? 0 : NAND_STATUS_FAIL;
|
|
|
}
|
|
|
|
|
|
static int denali_erase(struct mtd_info *mtd, int page)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
+ uint32_t irq_status;
|
|
|
|
|
|
- uint32_t cmd, irq_status;
|
|
|
-
|
|
|
- clear_interrupts(denali);
|
|
|
+ denali_reset_irq(denali);
|
|
|
|
|
|
- /* setup page read request for access type */
|
|
|
- cmd = MODE_10 | BANK(denali->flash_bank) | page;
|
|
|
- index_addr(denali, cmd, 0x1);
|
|
|
+ denali_host_write(denali, DENALI_MAP10 | DENALI_BANK(denali) | page,
|
|
|
+ DENALI_ERASE);
|
|
|
|
|
|
/* wait for erase to complete or failure to occur */
|
|
|
- irq_status = wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL);
|
|
|
+ irq_status = denali_wait_for_irq(denali,
|
|
|
+ INTR__ERASE_COMP | INTR__ERASE_FAIL);
|
|
|
|
|
|
- return irq_status & INTR__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
|
|
|
+ return irq_status & INTR__ERASE_COMP ? 0 : NAND_STATUS_FAIL;
|
|
|
}
|
|
|
|
|
|
-static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
|
|
|
- int page)
|
|
|
+#define DIV_ROUND_DOWN_ULL(ll, d) \
|
|
|
+ ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
|
|
|
+
|
|
|
+static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
|
|
|
+ const struct nand_data_interface *conf)
|
|
|
{
|
|
|
struct denali_nand_info *denali = mtd_to_denali(mtd);
|
|
|
- uint32_t addr, id;
|
|
|
+ const struct nand_sdr_timings *timings;
|
|
|
+ unsigned long t_clk;
|
|
|
+ int acc_clks, re_2_we, re_2_re, we_2_re, addr_2_data;
|
|
|
+ int rdwr_en_lo, rdwr_en_hi, rdwr_en_lo_hi, cs_setup;
|
|
|
+ int addr_2_data_mask;
|
|
|
+ uint32_t tmp;
|
|
|
+
|
|
|
+ timings = nand_get_sdr_timings(conf);
|
|
|
+ if (IS_ERR(timings))
|
|
|
+ return PTR_ERR(timings);
|
|
|
+
|
|
|
+ /* clk_x period in picoseconds */
|
|
|
+ t_clk = DIV_ROUND_DOWN_ULL(1000000000000ULL, denali->clk_x_rate);
|
|
|
+ if (!t_clk)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /* tREA -> ACC_CLKS */
|
|
|
+ acc_clks = DIV_ROUND_UP(timings->tREA_max, t_clk);
|
|
|
+ acc_clks = min_t(int, acc_clks, ACC_CLKS__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + ACC_CLKS);
|
|
|
+ tmp &= ~ACC_CLKS__VALUE;
|
|
|
+ tmp |= acc_clks;
|
|
|
+ iowrite32(tmp, denali->reg + ACC_CLKS);
|
|
|
+
|
|
|
+ /* tRWH -> RE_2_WE */
|
|
|
+ re_2_we = DIV_ROUND_UP(timings->tRHW_min, t_clk);
|
|
|
+ re_2_we = min_t(int, re_2_we, RE_2_WE__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + RE_2_WE);
|
|
|
+ tmp &= ~RE_2_WE__VALUE;
|
|
|
+ tmp |= re_2_we;
|
|
|
+ iowrite32(tmp, denali->reg + RE_2_WE);
|
|
|
+
|
|
|
+ /* tRHZ -> RE_2_RE */
|
|
|
+ re_2_re = DIV_ROUND_UP(timings->tRHZ_max, t_clk);
|
|
|
+ re_2_re = min_t(int, re_2_re, RE_2_RE__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + RE_2_RE);
|
|
|
+ tmp &= ~RE_2_RE__VALUE;
|
|
|
+ tmp |= re_2_re;
|
|
|
+ iowrite32(tmp, denali->reg + RE_2_RE);
|
|
|
+
|
|
|
+ /* tWHR -> WE_2_RE */
|
|
|
+ we_2_re = DIV_ROUND_UP(timings->tWHR_min, t_clk);
|
|
|
+ we_2_re = min_t(int, we_2_re, TWHR2_AND_WE_2_RE__WE_2_RE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + TWHR2_AND_WE_2_RE);
|
|
|
+ tmp &= ~TWHR2_AND_WE_2_RE__WE_2_RE;
|
|
|
+ tmp |= we_2_re;
|
|
|
+ iowrite32(tmp, denali->reg + TWHR2_AND_WE_2_RE);
|
|
|
+
|
|
|
+ /* tADL -> ADDR_2_DATA */
|
|
|
+
|
|
|
+ /* for older versions, ADDR_2_DATA is only 6 bit wide */
|
|
|
+ addr_2_data_mask = TCWAW_AND_ADDR_2_DATA__ADDR_2_DATA;
|
|
|
+ if (denali->revision < 0x0501)
|
|
|
+ addr_2_data_mask >>= 1;
|
|
|
+
|
|
|
+ addr_2_data = DIV_ROUND_UP(timings->tADL_min, t_clk);
|
|
|
+ addr_2_data = min_t(int, addr_2_data, addr_2_data_mask);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + TCWAW_AND_ADDR_2_DATA);
|
|
|
+ tmp &= ~addr_2_data_mask;
|
|
|
+ tmp |= addr_2_data;
|
|
|
+ iowrite32(tmp, denali->reg + TCWAW_AND_ADDR_2_DATA);
|
|
|
+
|
|
|
+ /* tREH, tWH -> RDWR_EN_HI_CNT */
|
|
|
+ rdwr_en_hi = DIV_ROUND_UP(max(timings->tREH_min, timings->tWH_min),
|
|
|
+ t_clk);
|
|
|
+ rdwr_en_hi = min_t(int, rdwr_en_hi, RDWR_EN_HI_CNT__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + RDWR_EN_HI_CNT);
|
|
|
+ tmp &= ~RDWR_EN_HI_CNT__VALUE;
|
|
|
+ tmp |= rdwr_en_hi;
|
|
|
+ iowrite32(tmp, denali->reg + RDWR_EN_HI_CNT);
|
|
|
+
|
|
|
+ /* tRP, tWP -> RDWR_EN_LO_CNT */
|
|
|
+ rdwr_en_lo = DIV_ROUND_UP(max(timings->tRP_min, timings->tWP_min),
|
|
|
+ t_clk);
|
|
|
+ rdwr_en_lo_hi = DIV_ROUND_UP(max(timings->tRC_min, timings->tWC_min),
|
|
|
+ t_clk);
|
|
|
+ rdwr_en_lo_hi = max(rdwr_en_lo_hi, DENALI_CLK_X_MULT);
|
|
|
+ rdwr_en_lo = max(rdwr_en_lo, rdwr_en_lo_hi - rdwr_en_hi);
|
|
|
+ rdwr_en_lo = min_t(int, rdwr_en_lo, RDWR_EN_LO_CNT__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + RDWR_EN_LO_CNT);
|
|
|
+ tmp &= ~RDWR_EN_LO_CNT__VALUE;
|
|
|
+ tmp |= rdwr_en_lo;
|
|
|
+ iowrite32(tmp, denali->reg + RDWR_EN_LO_CNT);
|
|
|
+
|
|
|
+ /* tCS, tCEA -> CS_SETUP_CNT */
|
|
|
+ cs_setup = max3((int)DIV_ROUND_UP(timings->tCS_min, t_clk) - rdwr_en_lo,
|
|
|
+ (int)DIV_ROUND_UP(timings->tCEA_max, t_clk) - acc_clks,
|
|
|
+ 0);
|
|
|
+ cs_setup = min_t(int, cs_setup, CS_SETUP_CNT__VALUE);
|
|
|
+
|
|
|
+ tmp = ioread32(denali->reg + CS_SETUP_CNT);
|
|
|
+ tmp &= ~CS_SETUP_CNT__VALUE;
|
|
|
+ tmp |= cs_setup;
|
|
|
+ iowrite32(tmp, denali->reg + CS_SETUP_CNT);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void denali_reset_banks(struct denali_nand_info *denali)
|
|
|
+{
|
|
|
+ u32 irq_status;
|
|
|
int i;
|
|
|
|
|
|
- switch (cmd) {
|
|
|
- case NAND_CMD_PAGEPROG:
|
|
|
- break;
|
|
|
- case NAND_CMD_STATUS:
|
|
|
- read_status(denali);
|
|
|
- break;
|
|
|
- case NAND_CMD_READID:
|
|
|
- case NAND_CMD_PARAM:
|
|
|
- reset_buf(denali);
|
|
|
- /*
|
|
|
- * sometimes ManufactureId read from register is not right
|
|
|
- * e.g. some of Micron MT29F32G08QAA MLC NAND chips
|
|
|
- * So here we send READID cmd to NAND insteand
|
|
|
- */
|
|
|
- addr = MODE_11 | BANK(denali->flash_bank);
|
|
|
- index_addr(denali, addr | 0, 0x90);
|
|
|
- index_addr(denali, addr | 1, col);
|
|
|
- for (i = 0; i < 8; i++) {
|
|
|
- index_addr_read_data(denali, addr | 2, &id);
|
|
|
- write_byte_to_buf(denali, id);
|
|
|
- }
|
|
|
- break;
|
|
|
- case NAND_CMD_READ0:
|
|
|
- case NAND_CMD_SEQIN:
|
|
|
- denali->page = page;
|
|
|
- break;
|
|
|
- case NAND_CMD_RESET:
|
|
|
- reset_bank(denali);
|
|
|
- break;
|
|
|
- case NAND_CMD_READOOB:
|
|
|
- /* TODO: Read OOB data */
|
|
|
- break;
|
|
|
- default:
|
|
|
- pr_err(": unsupported command received 0x%x\n", cmd);
|
|
|
- break;
|
|
|
+ for (i = 0; i < denali->max_banks; i++) {
|
|
|
+ denali->active_bank = i;
|
|
|
+
|
|
|
+ denali_reset_irq(denali);
|
|
|
+
|
|
|
+ iowrite32(DEVICE_RESET__BANK(i),
|
|
|
+ denali->reg + DEVICE_RESET);
|
|
|
+
|
|
|
+ irq_status = denali_wait_for_irq(denali,
|
|
|
+ INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
|
|
|
+ if (!(irq_status & INTR__INT_ACT))
|
|
|
+ break;
|
|
|
}
|
|
|
+
|
|
|
+ dev_dbg(denali->dev, "%d chips connected\n", i);
|
|
|
+ denali->max_banks = i;
|
|
|
}
|
|
|
-/* end NAND core entry points */
|
|
|
|
|
|
-/* Initialization code to bring the device up to a known good state */
|
|
|
static void denali_hw_init(struct denali_nand_info *denali)
|
|
|
{
|
|
|
/*
|
|
@@ -1319,8 +1125,7 @@ static void denali_hw_init(struct denali_nand_info *denali)
|
|
|
* override it.
|
|
|
*/
|
|
|
if (!denali->revision)
|
|
|
- denali->revision =
|
|
|
- swab16(ioread32(denali->flash_reg + REVISION));
|
|
|
+ denali->revision = swab16(ioread32(denali->reg + REVISION));
|
|
|
|
|
|
/*
|
|
|
* tell driver how many bit controller will skip before
|
|
@@ -1328,30 +1133,51 @@ static void denali_hw_init(struct denali_nand_info *denali)
|
|
|
* set by firmware. So we read this value out.
|
|
|
* if this value is 0, just let it be.
|
|
|
*/
|
|
|
- denali->bbtskipbytes = ioread32(denali->flash_reg +
|
|
|
- SPARE_AREA_SKIP_BYTES);
|
|
|
+ denali->oob_skip_bytes = ioread32(denali->reg + SPARE_AREA_SKIP_BYTES);
|
|
|
detect_max_banks(denali);
|
|
|
- denali_nand_reset(denali);
|
|
|
- iowrite32(0x0F, denali->flash_reg + RB_PIN_ENABLED);
|
|
|
- iowrite32(CHIP_EN_DONT_CARE__FLAG,
|
|
|
- denali->flash_reg + CHIP_ENABLE_DONT_CARE);
|
|
|
+ iowrite32(0x0F, denali->reg + RB_PIN_ENABLED);
|
|
|
+ iowrite32(CHIP_EN_DONT_CARE__FLAG, denali->reg + CHIP_ENABLE_DONT_CARE);
|
|
|
|
|
|
- iowrite32(0xffff, denali->flash_reg + SPARE_AREA_MARKER);
|
|
|
+ iowrite32(0xffff, denali->reg + SPARE_AREA_MARKER);
|
|
|
|
|
|
/* Should set value for these registers when init */
|
|
|
- iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
|
|
|
- iowrite32(1, denali->flash_reg + ECC_ENABLE);
|
|
|
- denali_nand_timing_set(denali);
|
|
|
- denali_irq_init(denali);
|
|
|
+ iowrite32(0, denali->reg + TWO_ROW_ADDR_CYCLES);
|
|
|
+ iowrite32(1, denali->reg + ECC_ENABLE);
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Althogh controller spec said SLC ECC is forceb to be 4bit,
|
|
|
- * but denali controller in MRST only support 15bit and 8bit ECC
|
|
|
- * correction
|
|
|
- */
|
|
|
-#define ECC_8BITS 14
|
|
|
-#define ECC_15BITS 26
|
|
|
+int denali_calc_ecc_bytes(int step_size, int strength)
|
|
|
+{
|
|
|
+ /* BCH code. Denali requires ecc.bytes to be multiple of 2 */
|
|
|
+ return DIV_ROUND_UP(strength * fls(step_size * 8), 16) * 2;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(denali_calc_ecc_bytes);
|
|
|
+
|
|
|
+static int denali_ecc_setup(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
+ struct denali_nand_info *denali)
|
|
|
+{
|
|
|
+ int oobavail = mtd->oobsize - denali->oob_skip_bytes;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If .size and .strength are already set (usually by DT),
|
|
|
+ * check if they are supported by this controller.
|
|
|
+ */
|
|
|
+ if (chip->ecc.size && chip->ecc.strength)
|
|
|
+ return nand_check_ecc_caps(chip, denali->ecc_caps, oobavail);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We want .size and .strength closest to the chip's requirement
|
|
|
+ * unless NAND_ECC_MAXIMIZE is requested.
|
|
|
+ */
|
|
|
+ if (!(chip->ecc.options & NAND_ECC_MAXIMIZE)) {
|
|
|
+ ret = nand_match_ecc_req(chip, denali->ecc_caps, oobavail);
|
|
|
+ if (!ret)
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Max ECC strength is the last thing we can do */
|
|
|
+ return nand_maximize_ecc(chip, denali->ecc_caps, oobavail);
|
|
|
+}
|
|
|
|
|
|
static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
|
|
|
struct mtd_oob_region *oobregion)
|
|
@@ -1362,7 +1188,7 @@ static int denali_ooblayout_ecc(struct mtd_info *mtd, int section,
|
|
|
if (section)
|
|
|
return -ERANGE;
|
|
|
|
|
|
- oobregion->offset = denali->bbtskipbytes;
|
|
|
+ oobregion->offset = denali->oob_skip_bytes;
|
|
|
oobregion->length = chip->ecc.total;
|
|
|
|
|
|
return 0;
|
|
@@ -1377,7 +1203,7 @@ static int denali_ooblayout_free(struct mtd_info *mtd, int section,
|
|
|
if (section)
|
|
|
return -ERANGE;
|
|
|
|
|
|
- oobregion->offset = chip->ecc.total + denali->bbtskipbytes;
|
|
|
+ oobregion->offset = chip->ecc.total + denali->oob_skip_bytes;
|
|
|
oobregion->length = mtd->oobsize - oobregion->offset;
|
|
|
|
|
|
return 0;
|
|
@@ -1388,29 +1214,6 @@ static const struct mtd_ooblayout_ops denali_ooblayout_ops = {
|
|
|
.free = denali_ooblayout_free,
|
|
|
};
|
|
|
|
|
|
-static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
|
|
|
-static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
|
|
|
-
|
|
|
-static struct nand_bbt_descr bbt_main_descr = {
|
|
|
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
|
|
|
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
|
|
|
- .offs = 8,
|
|
|
- .len = 4,
|
|
|
- .veroffs = 12,
|
|
|
- .maxblocks = 4,
|
|
|
- .pattern = bbt_pattern,
|
|
|
-};
|
|
|
-
|
|
|
-static struct nand_bbt_descr bbt_mirror_descr = {
|
|
|
- .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
|
|
|
- | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
|
|
|
- .offs = 8,
|
|
|
- .len = 4,
|
|
|
- .veroffs = 12,
|
|
|
- .maxblocks = 4,
|
|
|
- .pattern = mirror_pattern,
|
|
|
-};
|
|
|
-
|
|
|
/* initialize driver data structures */
|
|
|
static void denali_drv_init(struct denali_nand_info *denali)
|
|
|
{
|
|
@@ -1425,12 +1228,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
|
|
|
* element that might be access shared data (interrupt status)
|
|
|
*/
|
|
|
spin_lock_init(&denali->irq_lock);
|
|
|
-
|
|
|
- /* indicate that MTD has not selected a valid bank yet */
|
|
|
- denali->flash_bank = CHIP_SELECT_INVALID;
|
|
|
-
|
|
|
- /* initialize our irq_status variable to indicate no interrupts */
|
|
|
- denali->irq_status = 0;
|
|
|
}
|
|
|
|
|
|
static int denali_multidev_fixup(struct denali_nand_info *denali)
|
|
@@ -1445,23 +1242,23 @@ static int denali_multidev_fixup(struct denali_nand_info *denali)
|
|
|
* In this case, the core framework knows nothing about this fact,
|
|
|
* so we should tell it the _logical_ pagesize and anything necessary.
|
|
|
*/
|
|
|
- denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
|
|
|
+ denali->devs_per_cs = ioread32(denali->reg + DEVICES_CONNECTED);
|
|
|
|
|
|
/*
|
|
|
* On some SoCs, DEVICES_CONNECTED is not auto-detected.
|
|
|
* For those, DEVICES_CONNECTED is left to 0. Set 1 if it is the case.
|
|
|
*/
|
|
|
- if (denali->devnum == 0) {
|
|
|
- denali->devnum = 1;
|
|
|
- iowrite32(1, denali->flash_reg + DEVICES_CONNECTED);
|
|
|
+ if (denali->devs_per_cs == 0) {
|
|
|
+ denali->devs_per_cs = 1;
|
|
|
+ iowrite32(1, denali->reg + DEVICES_CONNECTED);
|
|
|
}
|
|
|
|
|
|
- if (denali->devnum == 1)
|
|
|
+ if (denali->devs_per_cs == 1)
|
|
|
return 0;
|
|
|
|
|
|
- if (denali->devnum != 2) {
|
|
|
+ if (denali->devs_per_cs != 2) {
|
|
|
dev_err(denali->dev, "unsupported number of devices %d\n",
|
|
|
- denali->devnum);
|
|
|
+ denali->devs_per_cs);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
@@ -1479,7 +1276,7 @@ static int denali_multidev_fixup(struct denali_nand_info *denali)
|
|
|
chip->ecc.size <<= 1;
|
|
|
chip->ecc.bytes <<= 1;
|
|
|
chip->ecc.strength <<= 1;
|
|
|
- denali->bbtskipbytes <<= 1;
|
|
|
+ denali->oob_skip_bytes <<= 1;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -1490,27 +1287,12 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
struct mtd_info *mtd = nand_to_mtd(chip);
|
|
|
int ret;
|
|
|
|
|
|
- if (denali->platform == INTEL_CE4100) {
|
|
|
- /*
|
|
|
- * Due to a silicon limitation, we can only support
|
|
|
- * ONFI timing mode 1 and below.
|
|
|
- */
|
|
|
- if (onfi_timing_mode < -1 || onfi_timing_mode > 1) {
|
|
|
- pr_err("Intel CE4100 only supports ONFI timing mode 1 or below\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* allocate a temporary buffer for nand_scan_ident() */
|
|
|
- denali->buf.buf = devm_kzalloc(denali->dev, PAGE_SIZE,
|
|
|
- GFP_DMA | GFP_KERNEL);
|
|
|
- if (!denali->buf.buf)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
mtd->dev.parent = denali->dev;
|
|
|
denali_hw_init(denali);
|
|
|
denali_drv_init(denali);
|
|
|
|
|
|
+ denali_clear_irq_all(denali);
|
|
|
+
|
|
|
/* Request IRQ after all the hardware initialization is finished */
|
|
|
ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
|
|
|
IRQF_SHARED, DENALI_NAND_NAME, denali);
|
|
@@ -1519,8 +1301,11 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- /* now that our ISR is registered, we can enable interrupts */
|
|
|
- denali_set_intr_modes(denali, true);
|
|
|
+ denali_enable_irq(denali);
|
|
|
+ denali_reset_banks(denali);
|
|
|
+
|
|
|
+ denali->active_bank = DENALI_INVALID_BANK;
|
|
|
+
|
|
|
nand_set_flash_node(chip, denali->dev->of_node);
|
|
|
/* Fallback to the default name if DT did not give "label" property */
|
|
|
if (!mtd->name)
|
|
@@ -1528,10 +1313,17 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
|
|
|
/* register the driver with the NAND core subsystem */
|
|
|
chip->select_chip = denali_select_chip;
|
|
|
- chip->cmdfunc = denali_cmdfunc;
|
|
|
chip->read_byte = denali_read_byte;
|
|
|
+ chip->write_byte = denali_write_byte;
|
|
|
+ chip->read_word = denali_read_word;
|
|
|
+ chip->cmd_ctrl = denali_cmd_ctrl;
|
|
|
+ chip->dev_ready = denali_dev_ready;
|
|
|
chip->waitfunc = denali_waitfunc;
|
|
|
|
|
|
+ /* clk rate info is needed for setup_data_interface */
|
|
|
+ if (denali->clk_x_rate)
|
|
|
+ chip->setup_data_interface = denali_setup_data_interface;
|
|
|
+
|
|
|
/*
|
|
|
* scan for NAND devices attached to the controller
|
|
|
* this is the first stage in a two step process to register
|
|
@@ -1539,33 +1331,25 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
*/
|
|
|
ret = nand_scan_ident(mtd, denali->max_banks, NULL);
|
|
|
if (ret)
|
|
|
- goto failed_req_irq;
|
|
|
-
|
|
|
- /* allocate the right size buffer now */
|
|
|
- devm_kfree(denali->dev, denali->buf.buf);
|
|
|
- denali->buf.buf = devm_kzalloc(denali->dev,
|
|
|
- mtd->writesize + mtd->oobsize,
|
|
|
- GFP_KERNEL);
|
|
|
- if (!denali->buf.buf) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto failed_req_irq;
|
|
|
- }
|
|
|
+ goto disable_irq;
|
|
|
|
|
|
- ret = dma_set_mask(denali->dev,
|
|
|
- DMA_BIT_MASK(denali->caps & DENALI_CAP_DMA_64BIT ?
|
|
|
- 64 : 32));
|
|
|
- if (ret) {
|
|
|
- dev_err(denali->dev, "No usable DMA configuration\n");
|
|
|
- goto failed_req_irq;
|
|
|
+ if (ioread32(denali->reg + FEATURES) & FEATURES__DMA)
|
|
|
+ denali->dma_avail = 1;
|
|
|
+
|
|
|
+ if (denali->dma_avail) {
|
|
|
+ int dma_bit = denali->caps & DENALI_CAP_DMA_64BIT ? 64 : 32;
|
|
|
+
|
|
|
+ ret = dma_set_mask(denali->dev, DMA_BIT_MASK(dma_bit));
|
|
|
+ if (ret) {
|
|
|
+ dev_info(denali->dev,
|
|
|
+ "Failed to set DMA mask. Disabling DMA.\n");
|
|
|
+ denali->dma_avail = 0;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
|
|
|
- mtd->writesize + mtd->oobsize,
|
|
|
- DMA_BIDIRECTIONAL);
|
|
|
- if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
|
|
|
- dev_err(denali->dev, "Failed to map DMA buffer\n");
|
|
|
- ret = -EIO;
|
|
|
- goto failed_req_irq;
|
|
|
+ if (denali->dma_avail) {
|
|
|
+ chip->options |= NAND_USE_BOUNCE_BUFFER;
|
|
|
+ chip->buf_align = 16;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1574,46 +1358,49 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
* bad block management.
|
|
|
*/
|
|
|
|
|
|
- /* Bad block management */
|
|
|
- chip->bbt_td = &bbt_main_descr;
|
|
|
- chip->bbt_md = &bbt_mirror_descr;
|
|
|
-
|
|
|
- /* skip the scan for now until we have OOB read and write support */
|
|
|
chip->bbt_options |= NAND_BBT_USE_FLASH;
|
|
|
- chip->options |= NAND_SKIP_BBTSCAN;
|
|
|
+ chip->bbt_options |= NAND_BBT_NO_OOB;
|
|
|
+
|
|
|
chip->ecc.mode = NAND_ECC_HW_SYNDROME;
|
|
|
|
|
|
/* no subpage writes on denali */
|
|
|
chip->options |= NAND_NO_SUBPAGE_WRITE;
|
|
|
|
|
|
- /*
|
|
|
- * Denali Controller only support 15bit and 8bit ECC in MRST,
|
|
|
- * so just let controller do 15bit ECC for MLC and 8bit ECC for
|
|
|
- * SLC if possible.
|
|
|
- * */
|
|
|
- if (!nand_is_slc(chip) &&
|
|
|
- (mtd->oobsize > (denali->bbtskipbytes +
|
|
|
- ECC_15BITS * (mtd->writesize /
|
|
|
- ECC_SECTOR_SIZE)))) {
|
|
|
- /* if MLC OOB size is large enough, use 15bit ECC*/
|
|
|
- chip->ecc.strength = 15;
|
|
|
- chip->ecc.bytes = ECC_15BITS;
|
|
|
- iowrite32(15, denali->flash_reg + ECC_CORRECTION);
|
|
|
- } else if (mtd->oobsize < (denali->bbtskipbytes +
|
|
|
- ECC_8BITS * (mtd->writesize /
|
|
|
- ECC_SECTOR_SIZE))) {
|
|
|
- pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
|
|
|
- goto failed_req_irq;
|
|
|
- } else {
|
|
|
- chip->ecc.strength = 8;
|
|
|
- chip->ecc.bytes = ECC_8BITS;
|
|
|
- iowrite32(8, denali->flash_reg + ECC_CORRECTION);
|
|
|
+ ret = denali_ecc_setup(mtd, chip, denali);
|
|
|
+ if (ret) {
|
|
|
+ dev_err(denali->dev, "Failed to setup ECC settings.\n");
|
|
|
+ goto disable_irq;
|
|
|
}
|
|
|
|
|
|
+ dev_dbg(denali->dev,
|
|
|
+ "chosen ECC settings: step=%d, strength=%d, bytes=%d\n",
|
|
|
+ chip->ecc.size, chip->ecc.strength, chip->ecc.bytes);
|
|
|
+
|
|
|
+ iowrite32(MAKE_ECC_CORRECTION(chip->ecc.strength, 1),
|
|
|
+ denali->reg + ECC_CORRECTION);
|
|
|
+ iowrite32(mtd->erasesize / mtd->writesize,
|
|
|
+ denali->reg + PAGES_PER_BLOCK);
|
|
|
+ iowrite32(chip->options & NAND_BUSWIDTH_16 ? 1 : 0,
|
|
|
+ denali->reg + DEVICE_WIDTH);
|
|
|
+ iowrite32(mtd->writesize, denali->reg + DEVICE_MAIN_AREA_SIZE);
|
|
|
+ iowrite32(mtd->oobsize, denali->reg + DEVICE_SPARE_AREA_SIZE);
|
|
|
+
|
|
|
+ iowrite32(chip->ecc.size, denali->reg + CFG_DATA_BLOCK_SIZE);
|
|
|
+ iowrite32(chip->ecc.size, denali->reg + CFG_LAST_DATA_BLOCK_SIZE);
|
|
|
+ /* chip->ecc.steps is set by nand_scan_tail(); not available here */
|
|
|
+ iowrite32(mtd->writesize / chip->ecc.size,
|
|
|
+ denali->reg + CFG_NUM_DATA_BLOCKS);
|
|
|
+
|
|
|
mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
|
|
|
|
|
|
- /* override the default read operations */
|
|
|
- chip->ecc.size = ECC_SECTOR_SIZE;
|
|
|
+ if (chip->options & NAND_BUSWIDTH_16) {
|
|
|
+ chip->read_buf = denali_read_buf16;
|
|
|
+ chip->write_buf = denali_write_buf16;
|
|
|
+ } else {
|
|
|
+ chip->read_buf = denali_read_buf;
|
|
|
+ chip->write_buf = denali_write_buf;
|
|
|
+ }
|
|
|
+ chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
|
|
|
chip->ecc.read_page = denali_read_page;
|
|
|
chip->ecc.read_page_raw = denali_read_page_raw;
|
|
|
chip->ecc.write_page = denali_write_page;
|
|
@@ -1624,21 +1411,34 @@ int denali_init(struct denali_nand_info *denali)
|
|
|
|
|
|
ret = denali_multidev_fixup(denali);
|
|
|
if (ret)
|
|
|
- goto failed_req_irq;
|
|
|
+ goto disable_irq;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * This buffer is DMA-mapped by denali_{read,write}_page_raw. Do not
|
|
|
+ * use devm_kmalloc() because the memory allocated by devm_ does not
|
|
|
+ * guarantee DMA-safe alignment.
|
|
|
+ */
|
|
|
+ denali->buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
|
|
|
+ if (!denali->buf) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto disable_irq;
|
|
|
+ }
|
|
|
|
|
|
ret = nand_scan_tail(mtd);
|
|
|
if (ret)
|
|
|
- goto failed_req_irq;
|
|
|
+ goto free_buf;
|
|
|
|
|
|
ret = mtd_device_register(mtd, NULL, 0);
|
|
|
if (ret) {
|
|
|
dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
|
|
|
- goto failed_req_irq;
|
|
|
+ goto free_buf;
|
|
|
}
|
|
|
return 0;
|
|
|
|
|
|
-failed_req_irq:
|
|
|
- denali_irq_cleanup(denali->irq, denali);
|
|
|
+free_buf:
|
|
|
+ kfree(denali->buf);
|
|
|
+disable_irq:
|
|
|
+ denali_disable_irq(denali);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
@@ -1648,16 +1448,9 @@ EXPORT_SYMBOL(denali_init);
|
|
|
void denali_remove(struct denali_nand_info *denali)
|
|
|
{
|
|
|
struct mtd_info *mtd = nand_to_mtd(&denali->nand);
|
|
|
- /*
|
|
|
- * Pre-compute DMA buffer size to avoid any problems in case
|
|
|
- * nand_release() ever changes in a way that mtd->writesize and
|
|
|
- * mtd->oobsize are not reliable after this call.
|
|
|
- */
|
|
|
- int bufsize = mtd->writesize + mtd->oobsize;
|
|
|
|
|
|
nand_release(mtd);
|
|
|
- denali_irq_cleanup(denali->irq, denali);
|
|
|
- dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
|
|
|
- DMA_BIDIRECTIONAL);
|
|
|
+ kfree(denali->buf);
|
|
|
+ denali_disable_irq(denali);
|
|
|
}
|
|
|
EXPORT_SYMBOL(denali_remove);
|