Commit c19e31d0 authored by Masahiro Yamada's avatar Masahiro Yamada Committed by Boris Brezillon

mtd: nand: denali: rework interrupt handling

Simplify the interrupt handling and fix issues:

- The register field view of INTR_EN / INTR_STATUS is different
  among IP versions.  The global macro DENALI_IRQ_ALL is hard-coded
  for Intel platforms.  The interrupt mask should be determined at
  run-time depending on the running platform.

- wait_for_irq() loops do {} while() until interested flags are
  asserted.  The logic can be simplified.

- The spin_lock() guard seems too complex (and suspicious in a race
  condition if wait_for_completion_timeout() bails out by timeout).

- denali->complete is reused again and again, but reinit_completion()
  is missing.  Add it.

Re-work the code to make it more robust and easier to handle.

While we are here, also rename the jump label "failed_req_irq" to
more appropriate "disable_irq".
Signed-off-by: default avatarMasahiro Yamada <yamada.masahiro@socionext.com>
Signed-off-by: default avatarBoris Brezillon <boris.brezillon@free-electrons.com>
parent 1bb88666
...@@ -30,27 +30,14 @@ MODULE_LICENSE("GPL"); ...@@ -30,27 +30,14 @@ MODULE_LICENSE("GPL");
#define DENALI_NAND_NAME "denali-nand" #define DENALI_NAND_NAME "denali-nand"
/*
* 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)
/* /*
* indicates whether or not the internal value for the flash bank is * indicates whether or not the internal value for the flash bank is
* valid or not * valid or not
*/ */
#define CHIP_SELECT_INVALID -1 #define CHIP_SELECT_INVALID -1
#define DENALI_NR_BANKS 4
/* /*
* The bus interface clock, clk_x, is phase aligned with the core clock. The * 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 * clk_x is an integral multiple N of the core clk. The value N is configured
...@@ -85,14 +72,6 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd) ...@@ -85,14 +72,6 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
*/ */
#define BANK(x) ((x) << 24) #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 * Certain operations for the denali NAND controller use an indexed mode to
* read/write data. The operation is performed by writing the address value * read/write data. The operation is performed by writing the address value
...@@ -143,22 +122,6 @@ static void read_status(struct denali_nand_info *denali) ...@@ -143,22 +122,6 @@ static void read_status(struct denali_nand_info *denali)
write_byte_to_buf(denali, 0); 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 */ /* Reset the flash controller */
static uint16_t denali_nand_reset(struct denali_nand_info *denali) static uint16_t denali_nand_reset(struct denali_nand_info *denali)
{ {
...@@ -201,169 +164,124 @@ static void detect_max_banks(struct denali_nand_info *denali) ...@@ -201,169 +164,124 @@ static void detect_max_banks(struct denali_nand_info *denali)
denali->max_banks <<= 1; denali->max_banks <<= 1;
} }
static void denali_set_intr_modes(struct denali_nand_info *denali, static void denali_enable_irq(struct denali_nand_info *denali)
uint16_t INT_ENABLE)
{ {
if (INT_ENABLE) int i;
iowrite32(1, denali->flash_reg + GLOBAL_INT_ENABLE);
else
iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
}
/* for (i = 0; i < DENALI_NR_BANKS; i++)
* validation function to verify that the controlling software is making iowrite32(U32_MAX, denali->flash_reg + INTR_EN(i));
* a valid request iowrite32(GLOBAL_INT_EN_FLAG, denali->flash_reg + GLOBAL_INT_ENABLE);
*/
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) static void denali_disable_irq(struct denali_nand_info *denali)
{ {
uint32_t int_mask;
int i; int i;
/* Disable global interrupts */ for (i = 0; i < DENALI_NR_BANKS; i++)
denali_set_intr_modes(denali, false); iowrite32(0, denali->flash_reg + INTR_EN(i));
iowrite32(0, denali->flash_reg + GLOBAL_INT_ENABLE);
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);
} }
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->flash_reg + INTR_STATUS(bank));
} }
static void denali_irq_enable(struct denali_nand_info *denali, static void denali_clear_irq_all(struct denali_nand_info *denali)
uint32_t int_mask)
{ {
int i; int i;
for (i = 0; i < denali->max_banks; ++i) for (i = 0; i < DENALI_NR_BANKS; i++)
iowrite32(int_mask, denali->flash_reg + INTR_EN(i)); denali_clear_irq(denali, i, U32_MAX);
} }
/* static irqreturn_t denali_isr(int irq, void *dev_id)
* 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)
{ {
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 */ spin_lock(&denali->irq_lock);
static inline void clear_interrupt(struct denali_nand_info *denali,
uint32_t irq_mask)
{
uint32_t intr_status_reg;
intr_status_reg = INTR_STATUS(denali->flash_bank); for (i = 0; i < DENALI_NR_BANKS; i++) {
irq_status = ioread32(denali->flash_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) if (i != denali->flash_bank)
{ continue;
uint32_t status;
spin_lock_irq(&denali->irq_lock); denali->irq_status |= irq_status;
if (denali->irq_status & denali->irq_mask)
complete(&denali->complete);
}
status = read_interrupt_status(denali); spin_unlock(&denali->irq_lock);
clear_interrupt(denali, status);
denali->irq_status = 0x0; return ret;
spin_unlock_irq(&denali->irq_lock);
} }
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; unsigned long flags;
intr_status_reg = INTR_STATUS(denali->flash_bank); spin_lock_irqsave(&denali->irq_lock, flags);
denali->irq_status = 0;
return ioread32(denali->flash_reg + intr_status_reg); denali->irq_mask = 0;
spin_unlock_irqrestore(&denali->irq_lock, flags);
} }
/* static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
* This is the interrupt service routine. It handles all interrupts uint32_t irq_mask)
* sent to this device. Note that on CE4100, this is a shared interrupt.
*/
static irqreturn_t denali_isr(int irq, void *dev_id)
{ {
struct denali_nand_info *denali = dev_id; unsigned long time_left, flags;
uint32_t irq_status; 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. */ irq_status = denali->irq_status;
if (is_flash_bank_valid(denali->flash_bank)) {
/* if (irq_mask & irq_status) {
* check to see if controller generated the interrupt, /* return immediately if the IRQ has already happened. */
* since this is a shared interrupt spin_unlock_irqrestore(&denali->irq_lock, flags);
*/ return irq_status;
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;
} }
denali->irq_mask = irq_mask;
reinit_completion(&denali->complete);
spin_unlock_irqrestore(&denali->irq_lock, flags);
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;
} }
spin_unlock(&denali->irq_lock);
return result; return denali->irq_status;
} }
static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) /* resets a specific device connected to the core */
static void reset_bank(struct denali_nand_info *denali)
{ {
unsigned long comp_res; uint32_t irq_status;
uint32_t intr_status;
unsigned long timeout = msecs_to_jiffies(1000);
do { denali_reset_irq(denali);
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;
}
/* iowrite32(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
* these are not the interrupts you are looking for -
* need to wait again
*/
spin_unlock_irq(&denali->irq_lock);
} while (comp_res != 0);
if (comp_res == 0) { irq_status = denali_wait_for_irq(denali,
/* timeout */ INTR__RST_COMP | INTR__TIME_OUT);
pr_err("timeout occurred, status = 0x%x, mask = 0x%x\n",
intr_status, irq_mask);
intr_status = 0; if (!(irq_status & INTR__RST_COMP))
} dev_err(denali->dev, "reset bank failed.\n");
return intr_status;
} }
/* /*
...@@ -397,7 +315,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali, ...@@ -397,7 +315,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
setup_ecc_for_xfer(denali, ecc_en, transfer_spare); setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
clear_interrupts(denali); denali_reset_irq(denali);
addr = BANK(denali->flash_bank) | denali->page; addr = BANK(denali->flash_bank) | denali->page;
...@@ -479,9 +397,9 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) ...@@ -479,9 +397,9 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
write_data_to_flash_mem(denali, buf, mtd->oobsize); write_data_to_flash_mem(denali, buf, mtd->oobsize);
/* wait for operation to complete */ /* wait for operation to complete */
irq_status = wait_for_irq(denali, irq_mask); irq_status = denali_wait_for_irq(denali, irq_mask);
if (irq_status == 0) { if (!(irq_status & INTR__PROGRAM_COMP)) {
dev_err(denali->dev, "OOB write failed\n"); dev_err(denali->dev, "OOB write failed\n");
status = -EIO; status = -EIO;
} }
...@@ -510,9 +428,9 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) ...@@ -510,9 +428,9 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
* can always use status0 bit as the * can always use status0 bit as the
* mask is identical for each bank. * mask is identical for each bank.
*/ */
irq_status = wait_for_irq(denali, irq_mask); irq_status = denali_wait_for_irq(denali, irq_mask);
if (irq_status == 0) if (!(irq_status & INTR__LOAD_COMP))
dev_err(denali->dev, "page on OOB timeout %d\n", dev_err(denali->dev, "page on OOB timeout %d\n",
denali->page); denali->page);
...@@ -620,9 +538,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd, ...@@ -620,9 +538,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
unsigned int err_byte, err_sector, err_device; unsigned int err_byte, err_sector, err_device;
uint8_t err_cor_value; uint8_t err_cor_value;
unsigned int prev_sector = 0; unsigned int prev_sector = 0;
uint32_t irq_status;
/* read the ECC errors. we'll ignore them for now */ denali_reset_irq(denali);
denali_set_intr_modes(denali, false);
do { do {
err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS); err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
...@@ -674,10 +592,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd, ...@@ -674,10 +592,9 @@ static int denali_sw_ecc_fixup(struct mtd_info *mtd,
* ECC_TRANSACTION_DONE interrupt, so here just wait for * ECC_TRANSACTION_DONE interrupt, so here just wait for
* a while for this interrupt * a while for this interrupt
*/ */
while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE)) irq_status = denali_wait_for_irq(denali, INTR__ECC_TRANSACTION_DONE);
cpu_relax(); if (!(irq_status & INTR__ECC_TRANSACTION_DONE))
clear_interrupts(denali); return -EIO;
denali_set_intr_modes(denali, true);
return max_bitflips; return max_bitflips;
} }
...@@ -778,15 +695,14 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -778,15 +695,14 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE); dma_sync_single_for_device(denali->dev, addr, size, DMA_TO_DEVICE);
clear_interrupts(denali); denali_reset_irq(denali);
denali_enable_dma(denali, true); denali_enable_dma(denali, true);
denali_setup_dma(denali, DENALI_WRITE); denali_setup_dma(denali, DENALI_WRITE);
/* wait for operation to complete */ /* wait for operation to complete */
irq_status = wait_for_irq(denali, irq_mask); irq_status = denali_wait_for_irq(denali, irq_mask);
if (!(irq_status & INTR__DMA_CMD_COMP)) {
if (irq_status == 0) {
dev_err(denali->dev, "timeout on write_page (type = %d)\n", dev_err(denali->dev, "timeout on write_page (type = %d)\n",
raw_xfer); raw_xfer);
ret = -EIO; ret = -EIO;
...@@ -865,11 +781,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -865,11 +781,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
denali_enable_dma(denali, true); denali_enable_dma(denali, true);
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE); dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
clear_interrupts(denali); denali_reset_irq(denali);
denali_setup_dma(denali, DENALI_READ); denali_setup_dma(denali, DENALI_READ);
/* wait for operation to complete */ /* wait for operation to complete */
irq_status = wait_for_irq(denali, irq_mask); irq_status = denali_wait_for_irq(denali, irq_mask);
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE); dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
...@@ -901,6 +817,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -901,6 +817,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
dma_addr_t addr = denali->buf.dma_buf; dma_addr_t addr = denali->buf.dma_buf;
size_t size = mtd->writesize + mtd->oobsize; size_t size = mtd->writesize + mtd->oobsize;
uint32_t irq_mask = INTR__DMA_CMD_COMP; uint32_t irq_mask = INTR__DMA_CMD_COMP;
uint32_t irq_status;
denali->page = page; denali->page = page;
...@@ -909,11 +826,13 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -909,11 +826,13 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE); dma_sync_single_for_device(denali->dev, addr, size, DMA_FROM_DEVICE);
clear_interrupts(denali); denali_reset_irq(denali);
denali_setup_dma(denali, DENALI_READ); denali_setup_dma(denali, DENALI_READ);
/* wait for operation to complete */ /* wait for operation to complete */
wait_for_irq(denali, irq_mask); irq_status = denali_wait_for_irq(denali, irq_mask);
if (irq_status & INTR__DMA_CMD_COMP)
return -ETIMEDOUT;
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE); dma_sync_single_for_cpu(denali->dev, addr, size, DMA_FROM_DEVICE);
...@@ -940,9 +859,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip) ...@@ -940,9 +859,7 @@ static void denali_select_chip(struct mtd_info *mtd, int chip)
{ {
struct denali_nand_info *denali = mtd_to_denali(mtd); struct denali_nand_info *denali = mtd_to_denali(mtd);
spin_lock_irq(&denali->irq_lock);
denali->flash_bank = chip; denali->flash_bank = chip;
spin_unlock_irq(&denali->irq_lock);
} }
static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
...@@ -953,19 +870,19 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) ...@@ -953,19 +870,19 @@ static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
static int denali_erase(struct mtd_info *mtd, int page) static int denali_erase(struct mtd_info *mtd, int page)
{ {
struct denali_nand_info *denali = mtd_to_denali(mtd); struct denali_nand_info *denali = mtd_to_denali(mtd);
uint32_t cmd, irq_status; uint32_t cmd, irq_status;
clear_interrupts(denali); denali_reset_irq(denali);
/* setup page read request for access type */ /* setup page read request for access type */
cmd = MODE_10 | BANK(denali->flash_bank) | page; cmd = MODE_10 | BANK(denali->flash_bank) | page;
index_addr(denali, cmd, 0x1); index_addr(denali, cmd, 0x1);
/* wait for erase to complete or failure to occur */ /* 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, static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
...@@ -1152,7 +1069,6 @@ static void denali_hw_init(struct denali_nand_info *denali) ...@@ -1152,7 +1069,6 @@ static void denali_hw_init(struct denali_nand_info *denali)
/* Should set value for these registers when init */ /* Should set value for these registers when init */
iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES); iowrite32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
iowrite32(1, denali->flash_reg + ECC_ENABLE); iowrite32(1, denali->flash_reg + ECC_ENABLE);
denali_irq_init(denali);
} }
int denali_calc_ecc_bytes(int step_size, int strength) int denali_calc_ecc_bytes(int step_size, int strength)
...@@ -1264,9 +1180,6 @@ static void denali_drv_init(struct denali_nand_info *denali) ...@@ -1264,9 +1180,6 @@ static void denali_drv_init(struct denali_nand_info *denali)
/* indicate that MTD has not selected a valid bank yet */ /* indicate that MTD has not selected a valid bank yet */
denali->flash_bank = CHIP_SELECT_INVALID; 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) static int denali_multidev_fixup(struct denali_nand_info *denali)
...@@ -1336,6 +1249,8 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1336,6 +1249,8 @@ int denali_init(struct denali_nand_info *denali)
denali_hw_init(denali); denali_hw_init(denali);
denali_drv_init(denali); denali_drv_init(denali);
denali_clear_irq_all(denali);
/* Request IRQ after all the hardware initialization is finished */ /* Request IRQ after all the hardware initialization is finished */
ret = devm_request_irq(denali->dev, denali->irq, denali_isr, ret = devm_request_irq(denali->dev, denali->irq, denali_isr,
IRQF_SHARED, DENALI_NAND_NAME, denali); IRQF_SHARED, DENALI_NAND_NAME, denali);
...@@ -1344,8 +1259,8 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1344,8 +1259,8 @@ int denali_init(struct denali_nand_info *denali)
return ret; return ret;
} }
/* now that our ISR is registered, we can enable interrupts */ denali_enable_irq(denali);
denali_set_intr_modes(denali, true);
nand_set_flash_node(chip, denali->dev->of_node); nand_set_flash_node(chip, denali->dev->of_node);
/* Fallback to the default name if DT did not give "label" property */ /* Fallback to the default name if DT did not give "label" property */
if (!mtd->name) if (!mtd->name)
...@@ -1370,7 +1285,7 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1370,7 +1285,7 @@ int denali_init(struct denali_nand_info *denali)
*/ */
ret = nand_scan_ident(mtd, denali->max_banks, NULL); ret = nand_scan_ident(mtd, denali->max_banks, NULL);
if (ret) if (ret)
goto failed_req_irq; goto disable_irq;
/* allocate the right size buffer now */ /* allocate the right size buffer now */
devm_kfree(denali->dev, denali->buf.buf); devm_kfree(denali->dev, denali->buf.buf);
...@@ -1379,7 +1294,7 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1379,7 +1294,7 @@ int denali_init(struct denali_nand_info *denali)
GFP_KERNEL); GFP_KERNEL);
if (!denali->buf.buf) { if (!denali->buf.buf) {
ret = -ENOMEM; ret = -ENOMEM;
goto failed_req_irq; goto disable_irq;
} }
ret = dma_set_mask(denali->dev, ret = dma_set_mask(denali->dev,
...@@ -1387,7 +1302,7 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1387,7 +1302,7 @@ int denali_init(struct denali_nand_info *denali)
64 : 32)); 64 : 32));
if (ret) { if (ret) {
dev_err(denali->dev, "No usable DMA configuration\n"); dev_err(denali->dev, "No usable DMA configuration\n");
goto failed_req_irq; goto disable_irq;
} }
denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf, denali->buf.dma_buf = dma_map_single(denali->dev, denali->buf.buf,
...@@ -1396,7 +1311,7 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1396,7 +1311,7 @@ int denali_init(struct denali_nand_info *denali)
if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) { if (dma_mapping_error(denali->dev, denali->buf.dma_buf)) {
dev_err(denali->dev, "Failed to map DMA buffer\n"); dev_err(denali->dev, "Failed to map DMA buffer\n");
ret = -EIO; ret = -EIO;
goto failed_req_irq; goto disable_irq;
} }
/* /*
...@@ -1420,7 +1335,7 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1420,7 +1335,7 @@ int denali_init(struct denali_nand_info *denali)
ret = denali_ecc_setup(mtd, chip, denali); ret = denali_ecc_setup(mtd, chip, denali);
if (ret) { if (ret) {
dev_err(denali->dev, "Failed to setup ECC settings.\n"); dev_err(denali->dev, "Failed to setup ECC settings.\n");
goto failed_req_irq; goto disable_irq;
} }
dev_dbg(denali->dev, dev_dbg(denali->dev,
...@@ -1454,21 +1369,21 @@ int denali_init(struct denali_nand_info *denali) ...@@ -1454,21 +1369,21 @@ int denali_init(struct denali_nand_info *denali)
ret = denali_multidev_fixup(denali); ret = denali_multidev_fixup(denali);
if (ret) if (ret)
goto failed_req_irq; goto disable_irq;
ret = nand_scan_tail(mtd); ret = nand_scan_tail(mtd);
if (ret) if (ret)
goto failed_req_irq; goto disable_irq;
ret = mtd_device_register(mtd, NULL, 0); ret = mtd_device_register(mtd, NULL, 0);
if (ret) { if (ret) {
dev_err(denali->dev, "Failed to register MTD: %d\n", ret); dev_err(denali->dev, "Failed to register MTD: %d\n", ret);
goto failed_req_irq; goto disable_irq;
} }
return 0; return 0;
failed_req_irq: disable_irq:
denali_irq_cleanup(denali->irq, denali); denali_disable_irq(denali);
return ret; return ret;
} }
...@@ -1486,7 +1401,7 @@ void denali_remove(struct denali_nand_info *denali) ...@@ -1486,7 +1401,7 @@ void denali_remove(struct denali_nand_info *denali)
int bufsize = mtd->writesize + mtd->oobsize; int bufsize = mtd->writesize + mtd->oobsize;
nand_release(mtd); nand_release(mtd);
denali_irq_cleanup(denali->irq, denali); denali_disable_irq(denali);
dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize, dma_unmap_single(denali->dev, denali->buf.dma_buf, bufsize,
DMA_BIDIRECTIONAL); DMA_BIDIRECTIONAL);
} }
......
...@@ -325,6 +325,7 @@ struct denali_nand_info { ...@@ -325,6 +325,7 @@ struct denali_nand_info {
/* elements used by ISR */ /* elements used by ISR */
struct completion complete; struct completion complete;
spinlock_t irq_lock; spinlock_t irq_lock;
uint32_t irq_mask;
uint32_t irq_status; uint32_t irq_status;
int irq; int irq;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment