diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 4cdb2af7bf44..21f146fe7ff0 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -50,6 +50,16 @@ config MTD_MS02NV say M here and read . The module will be called ms02-nv. +config MTD_MSM_NAND + tristate "MSM NAND Device Support" + depends on MTD && ARCH_MSM + select CRC16 + select BITREVERSE + select MTD_NAND_IDS + default y + help + Support for some NAND chips connected to the MSM NAND controller. + config MTD_DATAFLASH tristate "Support for AT45xxx DataFlash" depends on SPI_MASTER && EXPERIMENTAL diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index a4dd1d822b6c..204bae2668bc 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_MTD_SLRAM) += slram.o obj-$(CONFIG_MTD_PHRAM) += phram.o obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o +obj-$(CONFIG_MTD_MSM_NAND) += msm_nand.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o diff --git a/drivers/mtd/devices/msm_nand.c b/drivers/mtd/devices/msm_nand.c new file mode 100644 index 000000000000..bd36aea0317c --- /dev/null +++ b/drivers/mtd/devices/msm_nand.c @@ -0,0 +1,7138 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "msm_nand.h" + +unsigned long msm_nand_phys; +unsigned long msm_nandc01_phys; +unsigned long msm_nandc10_phys; +unsigned long msm_nandc11_phys; +unsigned long ebi2_register_base; +uint32_t dual_nand_ctlr_present; +uint32_t interleave_enable; +uint32_t enable_bch_ecc; + +#define MSM_NAND_DMA_BUFFER_SIZE SZ_8K +#define MSM_NAND_DMA_BUFFER_SLOTS \ + (MSM_NAND_DMA_BUFFER_SIZE / (sizeof(((atomic_t *)0)->counter) * 8)) + +#define MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER 0x88000800 +#define MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO 0x88040000 +#define MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER 0x0005045d +#define MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO 0x0005045d + +#define ONFI_IDENTIFIER_LENGTH 0x0004 +#define ONFI_PARAM_INFO_LENGTH 0x0200 +#define ONFI_PARAM_PAGE_LENGTH 0x0100 + +#define ONFI_PARAMETER_PAGE_SIGNATURE 0x49464E4F + +#define FLASH_READ_ONFI_IDENTIFIER_COMMAND 0x90 +#define FLASH_READ_ONFI_IDENTIFIER_ADDRESS 0x20 +#define FLASH_READ_ONFI_PARAMETERS_COMMAND 0xEC +#define FLASH_READ_ONFI_PARAMETERS_ADDRESS 0x00 + +#define VERBOSE 0 + +struct msm_nand_chip { + struct device *dev; + wait_queue_head_t wait_queue; + atomic_t dma_buffer_busy; + unsigned dma_channel; + uint8_t *dma_buffer; + dma_addr_t dma_addr; + unsigned CFG0, CFG1, CFG0_RAW, CFG1_RAW; + uint32_t ecc_buf_cfg; + uint32_t ecc_bch_cfg; + uint32_t ecc_parity_bytes; + unsigned cw_size; + unsigned int uncorrectable_bit_mask; + unsigned int num_err_mask; +}; + +#define CFG1_WIDE_FLASH (1U << 1) + +/* TODO: move datamover code out */ + +#define SRC_CRCI_NAND_CMD CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD) +#define DST_CRCI_NAND_CMD CMD_DST_CRCI(DMOV_NAND_CRCI_CMD) +#define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA) +#define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA) + +#define msm_virt_to_dma(chip, vaddr) \ + ((chip)->dma_addr + \ + ((uint8_t *)(vaddr) - (chip)->dma_buffer)) + +/** + * msm_nand_oob_64 - oob info for 2KB page + */ +static struct nand_ecclayout msm_nand_oob_64 = { + .eccbytes = 40, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + }, + .oobavail = 16, + .oobfree = { + {30, 16}, + } +}; + +/** + * msm_nand_oob_128 - oob info for 4KB page + */ +static struct nand_ecclayout msm_nand_oob_128 = { + .eccbytes = 80, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, + }, + .oobavail = 32, + .oobfree = { + {70, 32}, + } +}; + +/** + * msm_nand_oob_224 - oob info for 4KB page 8Bit interface + */ +static struct nand_ecclayout msm_nand_oob_224_x8 = { + .eccbytes = 104, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobavail = 32, + .oobfree = { + {91, 32}, + } +}; + +/** + * msm_nand_oob_224 - oob info for 4KB page 16Bit interface + */ +static struct nand_ecclayout msm_nand_oob_224_x16 = { + .eccbytes = 112, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + }, + .oobavail = 32, + .oobfree = { + {98, 32}, + } +}; + +/** + * msm_nand_oob_256 - oob info for 8KB page + */ +static struct nand_ecclayout msm_nand_oob_256 = { + .eccbytes = 160, + .eccpos = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 96, 97, 98 , 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, + 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, + 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, + 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, + }, + .oobavail = 64, + .oobfree = { + {151, 64}, + } +}; + +/** + * msm_onenand_oob_64 - oob info for large (2KB) page + */ +static struct nand_ecclayout msm_onenand_oob_64 = { + .eccbytes = 20, + .eccpos = { + 8, 9, 10, 11, 12, + 24, 25, 26, 27, 28, + 40, 41, 42, 43, 44, + 56, 57, 58, 59, 60, + }, + .oobavail = 20, + .oobfree = { + {2, 3}, {14, 2}, {18, 3}, {30, 2}, + {34, 3}, {46, 2}, {50, 3}, {62, 2} + } +}; + +static void *msm_nand_get_dma_buffer(struct msm_nand_chip *chip, size_t size) +{ + unsigned int bitmask, free_bitmask, old_bitmask; + unsigned int need_mask, current_need_mask; + int free_index; + + need_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; + bitmask = atomic_read(&chip->dma_buffer_busy); + free_bitmask = ~bitmask; + do { + free_index = __ffs(free_bitmask); + current_need_mask = need_mask << free_index; + + if (size + free_index * MSM_NAND_DMA_BUFFER_SLOTS >= + MSM_NAND_DMA_BUFFER_SIZE) + return NULL; + + if ((bitmask & current_need_mask) == 0) { + old_bitmask = + atomic_cmpxchg(&chip->dma_buffer_busy, + bitmask, + bitmask | current_need_mask); + if (old_bitmask == bitmask) + return chip->dma_buffer + + free_index * MSM_NAND_DMA_BUFFER_SLOTS; + free_bitmask = 0; /* force return */ + } + /* current free range was too small, clear all free bits */ + /* below the top busy bit within current_need_mask */ + free_bitmask &= + ~(~0U >> (32 - fls(bitmask & current_need_mask))); + } while (free_bitmask); + + return NULL; +} + +static void msm_nand_release_dma_buffer(struct msm_nand_chip *chip, + void *buffer, size_t size) +{ + int index; + unsigned int used_mask; + + used_mask = (1UL << DIV_ROUND_UP(size, MSM_NAND_DMA_BUFFER_SLOTS)) - 1; + index = ((uint8_t *)buffer - chip->dma_buffer) / + MSM_NAND_DMA_BUFFER_SLOTS; + atomic_sub(used_mask << index, &chip->dma_buffer_busy); + + wake_up(&chip->wait_queue); +} + + +unsigned flash_rd_reg(struct msm_nand_chip *chip, unsigned addr) +{ + struct { + dmov_s cmd; + unsigned cmdptr; + unsigned data; + } *dma_buffer; + unsigned rv; + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU; + dma_buffer->cmd.src = addr; + dma_buffer->cmd.dst = msm_virt_to_dma(chip, &dma_buffer->data); + dma_buffer->cmd.len = 4; + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dma_buffer->data = 0xeeeeeeee; + + mb(); + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + rv = dma_buffer->data; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + return rv; +} + +void flash_wr_reg(struct msm_nand_chip *chip, unsigned addr, unsigned val) +{ + struct { + dmov_s cmd; + unsigned cmdptr; + unsigned data; + } *dma_buffer; + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + dma_buffer->cmd.cmd = CMD_LC | CMD_OCB | CMD_OCU; + dma_buffer->cmd.src = msm_virt_to_dma(chip, &dma_buffer->data); + dma_buffer->cmd.dst = addr; + dma_buffer->cmd.len = 4; + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, &dma_buffer->cmd) >> 3) | CMD_PTR_LP; + dma_buffer->data = val; + + mb(); + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); +} + +static dma_addr_t +msm_nand_dma_map(struct device *dev, void *addr, size_t size, + enum dma_data_direction dir) +{ + struct page *page; + unsigned long offset = (unsigned long)addr & ~PAGE_MASK; + if (virt_addr_valid(addr)) + page = virt_to_page(addr); + else { + if (WARN_ON(size + offset > PAGE_SIZE)) + return ~0; + page = vmalloc_to_page(addr); + } + return dma_map_page(dev, page, offset, size, dir); +} + +uint32_t flash_read_id(struct msm_nand_chip *chip) +{ + struct { + dmov_s cmd[7]; + unsigned cmdptr; + unsigned data[7]; + } *dma_buffer; + uint32_t rv; + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + dma_buffer->data[0] = 0 | 4; + dma_buffer->data[1] = MSM_NAND_CMD_FETCH_ID; + dma_buffer->data[2] = 1; + dma_buffer->data[3] = 0xeeeeeeee; + dma_buffer->data[4] = 0xeeeeeeee; + dma_buffer->data[5] = flash_rd_reg(chip, MSM_NAND_SFLASHC_BURST_CFG); + dma_buffer->data[6] = 0x00000000; + BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->data) - 1); + + dma_buffer->cmd[0].cmd = 0 | CMD_OCB; + dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[6]); + dma_buffer->cmd[0].dst = MSM_NAND_SFLASHC_BURST_CFG; + dma_buffer->cmd[0].len = 4; + + dma_buffer->cmd[1].cmd = 0; + dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[0]); + dma_buffer->cmd[1].dst = MSM_NAND_FLASH_CHIP_SELECT; + dma_buffer->cmd[1].len = 4; + + dma_buffer->cmd[2].cmd = DST_CRCI_NAND_CMD; + dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[1]); + dma_buffer->cmd[2].dst = MSM_NAND_FLASH_CMD; + dma_buffer->cmd[2].len = 4; + + dma_buffer->cmd[3].cmd = 0; + dma_buffer->cmd[3].src = msm_virt_to_dma(chip, &dma_buffer->data[2]); + dma_buffer->cmd[3].dst = MSM_NAND_EXEC_CMD; + dma_buffer->cmd[3].len = 4; + + dma_buffer->cmd[4].cmd = SRC_CRCI_NAND_DATA; + dma_buffer->cmd[4].src = MSM_NAND_FLASH_STATUS; + dma_buffer->cmd[4].dst = msm_virt_to_dma(chip, &dma_buffer->data[3]); + dma_buffer->cmd[4].len = 4; + + dma_buffer->cmd[5].cmd = 0; + dma_buffer->cmd[5].src = MSM_NAND_READ_ID; + dma_buffer->cmd[5].dst = msm_virt_to_dma(chip, &dma_buffer->data[4]); + dma_buffer->cmd[5].len = 4; + + dma_buffer->cmd[6].cmd = CMD_OCU | CMD_LC; + dma_buffer->cmd[6].src = msm_virt_to_dma(chip, &dma_buffer->data[5]); + dma_buffer->cmd[6].dst = MSM_NAND_SFLASHC_BURST_CFG; + dma_buffer->cmd[6].len = 4; + + BUILD_BUG_ON(6 != ARRAY_SIZE(dma_buffer->cmd) - 1); + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3 + ) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + pr_info("status: %x\n", dma_buffer->data[3]); + pr_info("nandid: %x maker %02x device %02x\n", + dma_buffer->data[4], dma_buffer->data[4] & 0xff, + (dma_buffer->data[4] >> 8) & 0xff); + rv = dma_buffer->data[4]; + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + return rv; +} + +struct flash_identification { + uint32_t flash_id; + uint32_t density; + uint32_t widebus; + uint32_t pagesize; + uint32_t blksize; + uint32_t oobsize; + uint32_t ecc_correctability; +} supported_flash; + +uint16_t flash_onfi_crc_check(uint8_t *buffer, uint16_t count) +{ + int i; + uint16_t result; + + for (i = 0; i < count; i++) + buffer[i] = bitrev8(buffer[i]); + + result = bitrev16(crc16(bitrev16(0x4f4e), buffer, count)); + + for (i = 0; i < count; i++) + buffer[i] = bitrev8(buffer[i]); + + return result; +} + + +uint32_t flash_onfi_probe(struct msm_nand_chip *chip) +{ + struct onfi_param_page { + uint32_t parameter_page_signature; + uint16_t revision_number; + uint16_t features_supported; + uint16_t optional_commands_supported; + uint8_t reserved0[22]; + uint8_t device_manufacturer[12]; + uint8_t device_model[20]; + uint8_t jedec_manufacturer_id; + uint16_t date_code; + uint8_t reserved1[13]; + uint32_t number_of_data_bytes_per_page; + uint16_t number_of_spare_bytes_per_page; + uint32_t number_of_data_bytes_per_partial_page; + uint16_t number_of_spare_bytes_per_partial_page; + uint32_t number_of_pages_per_block; + uint32_t number_of_blocks_per_logical_unit; + uint8_t number_of_logical_units; + uint8_t number_of_address_cycles; + uint8_t number_of_bits_per_cell; + uint16_t maximum_bad_blocks_per_logical_unit; + uint16_t block_endurance; + uint8_t guaranteed_valid_begin_blocks; + uint16_t guaranteed_valid_begin_blocks_endurance; + uint8_t number_of_programs_per_page; + uint8_t partial_program_attributes; + uint8_t number_of_bits_ecc_correctability; + uint8_t number_of_interleaved_address_bits; + uint8_t interleaved_operation_attributes; + uint8_t reserved2[13]; + uint8_t io_pin_capacitance; + uint16_t timing_mode_support; + uint16_t program_cache_timing_mode_support; + uint16_t maximum_page_programming_time; + uint16_t maximum_block_erase_time; + uint16_t maximum_page_read_time; + uint16_t maximum_change_column_setup_time; + uint8_t reserved3[23]; + uint16_t vendor_specific_revision_number; + uint8_t vendor_specific[88]; + uint16_t integrity_crc; + + } __attribute__((__packed__)); + + struct onfi_param_page *onfi_param_page_ptr; + uint8_t *onfi_identifier_buf = NULL; + uint8_t *onfi_param_info_buf = NULL; + + struct { + dmov_s cmd[11]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t cfg0; + uint32_t cfg1; + uint32_t exec; + uint32_t flash_status; + uint32_t devcmd1_orig; + uint32_t devcmdvld_orig; + uint32_t devcmd1_mod; + uint32_t devcmdvld_mod; + uint32_t sflash_bcfg_orig; + uint32_t sflash_bcfg_mod; + } data; + } *dma_buffer; + dmov_s *cmd; + + unsigned page_address = 0; + int err = 0; + dma_addr_t dma_addr_param_info = 0; + dma_addr_t dma_addr_identifier = 0; + unsigned cmd_set_count = 2; + unsigned crc_chk_count = 0; + + if (msm_nand_data.nr_parts) { + page_address = ((msm_nand_data.parts[0]).offset << 6); + } else { + pr_err("flash_onfi_probe: " + "No partition info available\n"); + err = -EIO; + return err; + } + + wait_event(chip->wait_queue, (onfi_identifier_buf = + msm_nand_get_dma_buffer(chip, ONFI_IDENTIFIER_LENGTH))); + dma_addr_identifier = msm_virt_to_dma(chip, onfi_identifier_buf); + + wait_event(chip->wait_queue, (onfi_param_info_buf = + msm_nand_get_dma_buffer(chip, ONFI_PARAM_INFO_LENGTH))); + dma_addr_param_info = msm_virt_to_dma(chip, onfi_param_info_buf); + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + dma_buffer->data.sflash_bcfg_orig = flash_rd_reg + (chip, MSM_NAND_SFLASHC_BURST_CFG); + dma_buffer->data.devcmd1_orig = flash_rd_reg(chip, MSM_NAND_DEV_CMD1); + dma_buffer->data.devcmdvld_orig = flash_rd_reg(chip, + MSM_NAND_DEV_CMD_VLD); + + while (cmd_set_count-- > 0) { + cmd = dma_buffer->cmd; + + dma_buffer->data.devcmd1_mod = (dma_buffer->data.devcmd1_orig & + 0xFFFFFF00) | (cmd_set_count + ? FLASH_READ_ONFI_IDENTIFIER_COMMAND + : FLASH_READ_ONFI_PARAMETERS_COMMAND); + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.addr0 = (page_address << 16) | (cmd_set_count + ? FLASH_READ_ONFI_IDENTIFIER_ADDRESS + : FLASH_READ_ONFI_PARAMETERS_ADDRESS); + dma_buffer->data.addr1 = (page_address >> 16) & 0xFF; + dma_buffer->data.cfg0 = (cmd_set_count + ? MSM_NAND_CFG0_RAW_ONFI_IDENTIFIER + : MSM_NAND_CFG0_RAW_ONFI_PARAM_INFO); + dma_buffer->data.cfg1 = (cmd_set_count + ? MSM_NAND_CFG1_RAW_ONFI_IDENTIFIER + : MSM_NAND_CFG1_RAW_ONFI_PARAM_INFO); + dma_buffer->data.sflash_bcfg_mod = 0x00000000; + dma_buffer->data.devcmdvld_mod = (dma_buffer-> + data.devcmdvld_orig & 0xFFFFFFFE); + dma_buffer->data.exec = 1; + dma_buffer->data.flash_status = 0xeeeeeeee; + + /* Put the Nand ctlr in Async mode and disable SFlash ctlr */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sflash_bcfg_mod); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + /* Block on cmd ready, & write CMD,ADDR0,ADDR1,CHIPSEL regs */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = MSM_NAND_FLASH_CMD; + cmd->len = 12; + cmd++; + + /* Configure the CFG0 and CFG1 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = MSM_NAND_DEV0_CFG0; + cmd->len = 8; + cmd++; + + /* Configure the DEV_CMD_VLD register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.devcmdvld_mod); + cmd->dst = MSM_NAND_DEV_CMD_VLD; + cmd->len = 4; + cmd++; + + /* Configure the DEV_CMD1 register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.devcmd1_mod); + cmd->dst = MSM_NAND_DEV_CMD1; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.exec); + cmd->dst = MSM_NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the two status registers */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.flash_status); + cmd->len = 4; + cmd++; + + /* Read data block - valid only if status says success */ + cmd->cmd = 0; + cmd->src = MSM_NAND_FLASH_BUFFER; + cmd->dst = (cmd_set_count ? dma_addr_identifier : + dma_addr_param_info); + cmd->len = (cmd_set_count ? ONFI_IDENTIFIER_LENGTH : + ONFI_PARAM_INFO_LENGTH); + cmd++; + + /* Restore the DEV_CMD1 register */ + cmd->cmd = 0 ; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.devcmd1_orig); + cmd->dst = MSM_NAND_DEV_CMD1; + cmd->len = 4; + cmd++; + + /* Restore the DEV_CMD_VLD register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.devcmdvld_orig); + cmd->dst = MSM_NAND_DEV_CMD_VLD; + cmd->len = 4; + cmd++; + + /* Restore the SFLASH_BURST_CONFIG register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sflash_bcfg_orig); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + BUILD_BUG_ON(11 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) + >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + /* Check for errors, protection violations etc */ + if (dma_buffer->data.flash_status & 0x110) { + pr_info("MPU/OP error (0x%x) during " + "ONFI probe\n", + dma_buffer->data.flash_status); + err = -EIO; + break; + } + + if (cmd_set_count) { + onfi_param_page_ptr = (struct onfi_param_page *) + (&(onfi_identifier_buf[0])); + if (onfi_param_page_ptr->parameter_page_signature != + ONFI_PARAMETER_PAGE_SIGNATURE) { + pr_info("ONFI probe : Found a non" + "ONFI Compliant device \n"); + err = -EIO; + break; + } + } else { + for (crc_chk_count = 0; crc_chk_count < + ONFI_PARAM_INFO_LENGTH + / ONFI_PARAM_PAGE_LENGTH; + crc_chk_count++) { + onfi_param_page_ptr = + (struct onfi_param_page *) + (&(onfi_param_info_buf + [ONFI_PARAM_PAGE_LENGTH * + crc_chk_count])); + if (flash_onfi_crc_check( + (uint8_t *)onfi_param_page_ptr, + ONFI_PARAM_PAGE_LENGTH - 2) == + onfi_param_page_ptr->integrity_crc) { + break; + } + } + if (crc_chk_count >= ONFI_PARAM_INFO_LENGTH + / ONFI_PARAM_PAGE_LENGTH) { + pr_info("ONFI probe : CRC Check " + "failed on ONFI Parameter " + "data \n"); + err = -EIO; + break; + } else { + supported_flash.flash_id = + flash_read_id(chip); + supported_flash.widebus = + onfi_param_page_ptr-> + features_supported & 0x01; + supported_flash.pagesize = + onfi_param_page_ptr-> + number_of_data_bytes_per_page; + supported_flash.blksize = + onfi_param_page_ptr-> + number_of_pages_per_block * + supported_flash.pagesize; + supported_flash.oobsize = + onfi_param_page_ptr-> + number_of_spare_bytes_per_page; + supported_flash.density = + onfi_param_page_ptr-> + number_of_blocks_per_logical_unit + * supported_flash.blksize; + supported_flash.ecc_correctability = + onfi_param_page_ptr-> + number_of_bits_ecc_correctability; + + pr_info("ONFI probe : Found an ONFI " + "compliant device %s\n", + onfi_param_page_ptr->device_model); + + /* Temporary hack for MT29F4G08ABC device. + * Since the device is not properly adhering + * to ONFi specification it is reporting + * as 16 bit device though it is 8 bit device!!! + */ + if (!strncmp(onfi_param_page_ptr->device_model, + "MT29F4G08ABC", 12)) + supported_flash.widebus = 0; + } + } + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + msm_nand_release_dma_buffer(chip, onfi_param_info_buf, + ONFI_PARAM_INFO_LENGTH); + msm_nand_release_dma_buffer(chip, onfi_identifier_buf, + ONFI_IDENTIFIER_LENGTH); + + return err; +} + +static int msm_nand_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[8 * 5 + 2]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel; + uint32_t cfg0; + uint32_t cfg1; + uint32_t eccbchcfg; + uint32_t exec; + uint32_t ecccfg; + struct { + uint32_t flash_status; + uint32_t buffer_status; + } result[8]; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned n; + unsigned page = 0; + uint32_t oob_len; + uint32_t sectordatasize; + uint32_t sectoroobsize; + int err, pageerr, rawerr; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + uint32_t oob_col = 0; + unsigned page_count; + unsigned pages_read = 0; + unsigned start_sector = 0; + uint32_t ecc_errors; + uint32_t total_ecc_errors = 0; + unsigned cwperpage; +#if VERBOSE + pr_info("=================================================" + "================\n"); + pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" + "\noobbuf 0x%p ooblen 0x%x\n", + __func__, from, ops->mode, ops->datbuf, ops->len, + ops->oobbuf, ops->ooblen); +#endif + + if (mtd->writesize == 2048) + page = from >> 11; + + if (mtd->writesize == 4096) + page = from >> 12; + + oob_len = ops->ooblen; + cwperpage = (mtd->writesize >> 9); + + if (from & (mtd->writesize - 1)) { + pr_err("%s: unsupported from, 0x%llx\n", + __func__, from); + return -EINVAL; + } + if (ops->mode != MTD_OPS_RAW) { + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + /* when ops->datbuf is NULL, ops->len can be ooblen */ + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if (ops->datbuf != NULL && + (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len," + " %d for MTD_OPS_RAW\n", __func__, ops->len); + return -EINVAL; + } + } + + if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { + pr_err("%s: unsupported ops->ooboffs, %d\n", + __func__, ops->ooboffs); + return -EINVAL; + } + + if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB) + start_sector = cwperpage - 1; + + if (ops->oobbuf && !ops->datbuf) { + page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? + mtd->oobavail : mtd->oobsize); + if ((page_count == 0) && (ops->ooblen)) + page_count = 1; + } else if (ops->mode != MTD_OPS_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); + + if (ops->datbuf) { + data_dma_addr_curr = data_dma_addr = + msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("msm_nand_read_oob: failed to get dma addr " + "for %p\n", ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + memset(ops->oobbuf, 0xff, ops->ooblen); + oob_dma_addr_curr = oob_dma_addr = + msm_nand_dma_map(chip->dev, ops->oobbuf, + ops->ooblen, DMA_BIDIRECTIONAL); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("msm_nand_read_oob: failed to get dma addr " + "for %p\n", ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + oob_col = start_sector * chip->cw_size; + if (chip->CFG1 & CFG1_WIDE_FLASH) + oob_col >>= 1; + + err = 0; + while (page_count-- > 0) { + cmd = dma_buffer->cmd; + + /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + if (ops->mode != MTD_OPS_RAW) { + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; + dma_buffer->data.cfg0 = + (chip->CFG0 & ~(7U << 6)) + | (((cwperpage-1) - start_sector) << 6); + dma_buffer->data.cfg1 = chip->CFG1; + if (enable_bch_ecc) + dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; + } else { + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = (chip->CFG0_RAW + & ~(7U << 6)) | ((cwperpage-1) << 6); + dma_buffer->data.cfg1 = chip->CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + + dma_buffer->data.addr0 = (page << 16) | oob_col; + dma_buffer->data.addr1 = (page >> 16) & 0xff; + /* chipsel_0 + enable DM interface */ + dma_buffer->data.chipsel = 0 | 4; + + + /* GO bit for the EXEC register */ + dma_buffer->data.exec = 1; + + + BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.result)); + + for (n = start_sector; n < cwperpage; n++) { + /* flash + buffer status return words */ + dma_buffer->data.result[n].flash_status = 0xeeeeeeee; + dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; + + /* block on cmd ready, then + * write CMD / ADDR0 / ADDR1 / CHIPSEL + * regs in a burst + */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = MSM_NAND_FLASH_CMD; + if (n == start_sector) + cmd->len = 16; + else + cmd->len = 4; + cmd++; + + if (n == start_sector) { + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = MSM_NAND_DEV0_CFG0; + if (enable_bch_ecc) + cmd->len = 12; + else + cmd->len = 8; + cmd++; + + dma_buffer->data.ecccfg = chip->ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg); + cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; + cmd->len = 4; + cmd++; + } + + /* kick the execute register */ + cmd->cmd = 0; + cmd->src = + msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = MSM_NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* block on data ready, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.result[n]); + /* MSM_NAND_FLASH_STATUS + MSM_NAND_BUFFER_STATUS */ + cmd->len = 8; + cmd++; + + /* read data block + * (only valid if status says success) + */ + if (ops->datbuf) { + if (ops->mode != MTD_OPS_RAW) + sectordatasize = (n < (cwperpage - 1)) + ? 516 : (512 - ((cwperpage - 1) << 2)); + else + sectordatasize = chip->cw_size; + + cmd->cmd = 0; + cmd->src = MSM_NAND_FLASH_BUFFER; + cmd->dst = data_dma_addr_curr; + data_dma_addr_curr += sectordatasize; + cmd->len = sectordatasize; + cmd++; + } + + if (ops->oobbuf && (n == (cwperpage - 1) + || ops->mode != MTD_OPS_AUTO_OOB)) { + cmd->cmd = 0; + if (n == (cwperpage - 1)) { + cmd->src = MSM_NAND_FLASH_BUFFER + + (512 - ((cwperpage - 1) << 2)); + sectoroobsize = (cwperpage << 2); + if (ops->mode != MTD_OPS_AUTO_OOB) + sectoroobsize += + chip->ecc_parity_bytes; + } else { + cmd->src = MSM_NAND_FLASH_BUFFER + 516; + sectoroobsize = chip->ecc_parity_bytes; + } + + cmd->dst = oob_dma_addr_curr; + if (sectoroobsize < oob_len) + cmd->len = sectoroobsize; + else + cmd->len = oob_len; + oob_dma_addr_curr += cmd->len; + oob_len -= cmd->len; + if (cmd->len > 0) + cmd++; + } + } + + BUILD_BUG_ON(8 * 5 + 2 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) + | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + /* if any of the writes failed (0x10), or there + * was a protection violation (0x100), we lose + */ + pageerr = rawerr = 0; + for (n = start_sector; n < cwperpage; n++) { + if (dma_buffer->data.result[n].flash_status & 0x110) { + rawerr = -EIO; + break; + } + } + if (rawerr) { + if (ops->datbuf && ops->mode != MTD_OPS_RAW) { + uint8_t *datbuf = ops->datbuf + + pages_read * mtd->writesize; + + dma_sync_single_for_cpu(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + + for (n = 0; n < mtd->writesize; n++) { + /* empty blocks read 0x54 at + * these offsets + */ + if ((n % 516 == 3 || n % 516 == 175) + && datbuf[n] == 0x54) + datbuf[n] = 0xff; + if (datbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + + dma_sync_single_for_device(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + + } + if (ops->oobbuf) { + dma_sync_single_for_cpu(chip->dev, + oob_dma_addr_curr - (ops->ooblen - oob_len), + ops->ooblen - oob_len, DMA_BIDIRECTIONAL); + + for (n = 0; n < ops->ooblen; n++) { + if (ops->oobbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + + dma_sync_single_for_device(chip->dev, + oob_dma_addr_curr - (ops->ooblen - oob_len), + ops->ooblen - oob_len, DMA_BIDIRECTIONAL); + } + } + if (pageerr) { + for (n = start_sector; n < cwperpage; n++) { + if (dma_buffer->data.result[n].buffer_status & + chip->uncorrectable_bit_mask) { + /* not thread safe */ + mtd->ecc_stats.failed++; + pageerr = -EBADMSG; + break; + } + } + } + if (!rawerr) { /* check for corretable errors */ + for (n = start_sector; n < cwperpage; n++) { + ecc_errors = + (dma_buffer->data.result[n].buffer_status + & chip->num_err_mask); + if (ecc_errors) { + total_ecc_errors += ecc_errors; + /* not thread safe */ + mtd->ecc_stats.corrected += ecc_errors; + if (ecc_errors > 1) + pageerr = -EUCLEAN; + } + } + } + if (pageerr && (pageerr != -EUCLEAN || err == 0)) + err = pageerr; + +#if VERBOSE + if (rawerr && !pageerr) { + pr_err("msm_nand_read_oob %llx %x %x empty page\n", + (loff_t)page * mtd->writesize, ops->len, + ops->ooblen); + } else { + for (n = start_sector; n < cwperpage; n++) + pr_info("flash_status[%d] = %x,\ + buffr_status[%d] = %x\n", + n, dma_buffer->data.result[n].flash_status, + n, dma_buffer->data.result[n].buffer_status); + } +#endif + if (err && err != -EUCLEAN && err != -EBADMSG) + break; + pages_read++; + page++; + } + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (ops->oobbuf) { + dma_unmap_page(chip->dev, oob_dma_addr, + ops->ooblen, DMA_FROM_DEVICE); + } +err_dma_map_oobbuf_failed: + if (ops->datbuf) { + dma_unmap_page(chip->dev, data_dma_addr, + ops->len, DMA_BIDIRECTIONAL); + } + + if (ops->mode != MTD_OPS_RAW) + ops->retlen = mtd->writesize * pages_read; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * + pages_read; + ops->oobretlen = ops->ooblen - oob_len; + if (err) + pr_err("msm_nand_read_oob %llx %x %x failed %d, corrected %d\n", + from, ops->datbuf ? ops->len : 0, ops->ooblen, err, + total_ecc_errors); +#if VERBOSE + pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", + __func__, err, ops->retlen, ops->oobretlen); + + pr_info("===================================================" + "==============\n"); +#endif + return err; +} + +static int msm_nand_read_oob_dualnandc(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[16 * 6 + 20]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t nandc01_addr0; + uint32_t nandc10_addr0; + uint32_t nandc11_addr1; + uint32_t chipsel_cs0; + uint32_t chipsel_cs1; + uint32_t cfg0; + uint32_t cfg1; + uint32_t eccbchcfg; + uint32_t exec; + uint32_t ecccfg; + uint32_t ebi2_chip_select_cfg0; + uint32_t adm_mux_data_ack_req_nc01; + uint32_t adm_mux_cmd_ack_req_nc01; + uint32_t adm_mux_data_ack_req_nc10; + uint32_t adm_mux_cmd_ack_req_nc10; + uint32_t adm_default_mux; + uint32_t default_ebi2_chip_select_cfg0; + uint32_t nc10_flash_dev_cmd_vld; + uint32_t nc10_flash_dev_cmd1; + uint32_t nc10_flash_dev_cmd_vld_default; + uint32_t nc10_flash_dev_cmd1_default; + struct { + uint32_t flash_status; + uint32_t buffer_status; + } result[16]; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned n; + unsigned page = 0; + uint32_t oob_len; + uint32_t sectordatasize; + uint32_t sectoroobsize; + int err, pageerr, rawerr; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + uint32_t oob_col = 0; + unsigned page_count; + unsigned pages_read = 0; + unsigned start_sector = 0; + uint32_t ecc_errors; + uint32_t total_ecc_errors = 0; + unsigned cwperpage; + unsigned cw_offset = chip->cw_size; +#if VERBOSE + pr_info("=================================================" + "============\n"); + pr_info("%s:\nfrom 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" + "\noobbuf 0x%p ooblen 0x%x\n\n", + __func__, from, ops->mode, ops->datbuf, + ops->len, ops->oobbuf, ops->ooblen); +#endif + + if (mtd->writesize == 2048) + page = from >> 11; + + if (mtd->writesize == 4096) + page = from >> 12; + + if (interleave_enable) + page = (from >> 1) >> 12; + + oob_len = ops->ooblen; + cwperpage = (mtd->writesize >> 9); + + if (from & (mtd->writesize - 1)) { + pr_err("%s: unsupported from, 0x%llx\n", + __func__, from); + return -EINVAL; + } + if (ops->mode != MTD_OPS_RAW) { + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if (ops->datbuf != NULL && + (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len," + " %d for MTD_OPS_RAW\n", __func__, ops->len); + return -EINVAL; + } + } + + if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { + pr_err("%s: unsupported ops->ooboffs, %d\n", + __func__, ops->ooboffs); + return -EINVAL; + } + + if (ops->oobbuf && !ops->datbuf && ops->mode == MTD_OPS_AUTO_OOB) + start_sector = cwperpage - 1; + + if (ops->oobbuf && !ops->datbuf) { + page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? + mtd->oobavail : mtd->oobsize); + if ((page_count == 0) && (ops->ooblen)) + page_count = 1; + } else if (ops->mode != MTD_OPS_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); + + if (ops->datbuf) { + data_dma_addr_curr = data_dma_addr = + msm_nand_dma_map(chip->dev, ops->datbuf, ops->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("msm_nand_read_oob_dualnandc: " + "failed to get dma addr for %p\n", + ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + memset(ops->oobbuf, 0xff, ops->ooblen); + oob_dma_addr_curr = oob_dma_addr = + msm_nand_dma_map(chip->dev, ops->oobbuf, + ops->ooblen, DMA_BIDIRECTIONAL); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("msm_nand_read_oob_dualnandc: " + "failed to get dma addr for %p\n", + ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + oob_col = start_sector * chip->cw_size; + if (chip->CFG1 & CFG1_WIDE_FLASH) { + oob_col >>= 1; + cw_offset >>= 1; + } + + err = 0; + while (page_count-- > 0) { + cmd = dma_buffer->cmd; + + if (ops->mode != MTD_OPS_RAW) { + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ_ECC; + if (start_sector == (cwperpage - 1)) { + dma_buffer->data.cfg0 = (chip->CFG0 & + ~(7U << 6)); + } else { + dma_buffer->data.cfg0 = (chip->CFG0 & + ~(7U << 6)) + | (((cwperpage >> 1)-1) << 6); + } + dma_buffer->data.cfg1 = chip->CFG1; + if (enable_bch_ecc) + dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; + } else { + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = ((chip->CFG0_RAW & + ~(7U << 6)) | ((((cwperpage >> 1)-1) << 6))); + dma_buffer->data.cfg1 = chip->CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + + if (!interleave_enable) { + if (start_sector == (cwperpage - 1)) { + dma_buffer->data.nandc10_addr0 = + (page << 16) | oob_col; + dma_buffer->data.nc10_flash_dev_cmd_vld = 0xD; + dma_buffer->data.nc10_flash_dev_cmd1 = + 0xF00F3000; + } else { + dma_buffer->data.nandc01_addr0 = page << 16; + /* NC10 ADDR0 points to the next code word */ + dma_buffer->data.nandc10_addr0 = (page << 16) | + cw_offset; + dma_buffer->data.nc10_flash_dev_cmd_vld = 0x1D; + dma_buffer->data.nc10_flash_dev_cmd1 = + 0xF00FE005; + } + } else { + dma_buffer->data.nandc01_addr0 = + dma_buffer->data.nandc10_addr0 = + (page << 16) | oob_col; + } + /* ADDR1 */ + dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff; + + dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; + dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; + dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; + dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; + dma_buffer->data.adm_default_mux = 0x00000FC0; + dma_buffer->data.nc10_flash_dev_cmd_vld_default = 0x1D; + dma_buffer->data.nc10_flash_dev_cmd1_default = 0xF00F3000; + + dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; + dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; + + /* chipsel_0 + enable DM interface */ + dma_buffer->data.chipsel_cs0 = (1<<4) | 4; + /* chipsel_1 + enable DM interface */ + dma_buffer->data.chipsel_cs1 = (1<<4) | 5; + + /* GO bit for the EXEC register */ + dma_buffer->data.exec = 1; + + BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.result)); + + for (n = start_sector; n < cwperpage; n++) { + /* flash + buffer status return words */ + dma_buffer->data.result[n].flash_status = 0xeeeeeeee; + dma_buffer->data.result[n].buffer_status = 0xeeeeeeee; + + if (n == start_sector) { + if (!interleave_enable) { + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.nc10_flash_dev_cmd_vld); + cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nc10_flash_dev_cmd1); + cmd->dst = NC10(MSM_NAND_DEV_CMD1); + cmd->len = 4; + cmd++; + + /* NC01, NC10 --> ADDR1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc11_addr1); + cmd->dst = NC11(MSM_NAND_ADDR1); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NC11(MSM_NAND_DEV0_CFG0); + if (enable_bch_ecc) + cmd->len = 12; + else + cmd->len = 8; + cmd++; + } else { + /* enable CS0 & CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + + /* NC01, NC10 --> ADDR1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc11_addr1); + cmd->dst = NC11(MSM_NAND_ADDR1); + cmd->len = 4; + cmd++; + + /* Enable CS0 for NC01 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.chipsel_cs0); + cmd->dst = + NC01(MSM_NAND_FLASH_CHIP_SELECT); + cmd->len = 4; + cmd++; + + /* Enable CS1 for NC10 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.chipsel_cs1); + cmd->dst = + NC10(MSM_NAND_FLASH_CHIP_SELECT); + cmd->len = 4; + cmd++; + + /* config DEV0_CFG0 & CFG1 for CS0 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NC01(MSM_NAND_DEV0_CFG0); + cmd->len = 8; + cmd++; + + /* config DEV1_CFG0 & CFG1 for CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NC10(MSM_NAND_DEV1_CFG0); + cmd->len = 8; + cmd++; + } + + dma_buffer->data.ecccfg = chip->ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg); + cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG); + cmd->len = 4; + cmd++; + + /* if 'only' the last code word */ + if (n == cwperpage - 1) { + /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_cmd_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* CMD */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cmd); + cmd->dst = NC10(MSM_NAND_FLASH_CMD); + cmd->len = 4; + cmd++; + + /* NC10 --> ADDR0 ( 0x0 ) */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc10_addr0); + cmd->dst = NC10(MSM_NAND_ADDR0); + cmd->len = 4; + cmd++; + + /* kick the execute reg for NC10 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.exec); + cmd->dst = NC10(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + + /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_data_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* block on data ready from NC10, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC10(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.result[n]); + /* MSM_NAND_FLASH_STATUS + + * MSM_NAND_BUFFER_STATUS + */ + cmd->len = 8; + cmd++; + } else { + /* NC01 --> ADDR0 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc01_addr0); + cmd->dst = NC01(MSM_NAND_ADDR0); + cmd->len = 4; + cmd++; + + /* NC10 --> ADDR1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc10_addr0); + cmd->dst = NC10(MSM_NAND_ADDR0); + cmd->len = 4; + cmd++; + + /* MASK CMD ACK/REQ --> NC10 (0xF14)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_cmd_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* CMD */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cmd); + cmd->dst = NC01(MSM_NAND_FLASH_CMD); + cmd->len = 4; + cmd++; + + /* kick the execute register for NC01*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.exec); + cmd->dst = NC01(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + } + } + + /* read data block + * (only valid if status says success) + */ + if (ops->datbuf || (ops->oobbuf && + ops->mode != MTD_OPS_AUTO_OOB)) { + if (ops->mode != MTD_OPS_RAW) + sectordatasize = (n < (cwperpage - 1)) + ? 516 : (512 - ((cwperpage - 1) << 2)); + else + sectordatasize = chip->cw_size; + + if (n % 2 == 0) { + /* MASK DATA ACK/REQ --> NC10 (0xF28)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_data_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* block on data ready from NC01, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC01(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.result[n]); + /* MSM_NAND_FLASH_STATUS + + * MSM_NAND_BUFFER_STATUS + */ + cmd->len = 8; + cmd++; + + /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_cmd_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* CMD */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cmd); + cmd->dst = NC10(MSM_NAND_FLASH_CMD); + cmd->len = 4; + cmd++; + + /* kick the execute register for NC10 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.exec); + cmd->dst = NC10(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + + /* Read only when there is data + * buffer + */ + if (ops->datbuf) { + cmd->cmd = 0; + cmd->src = + NC01(MSM_NAND_FLASH_BUFFER); + cmd->dst = data_dma_addr_curr; + data_dma_addr_curr += + sectordatasize; + cmd->len = sectordatasize; + cmd++; + } + } else { + /* MASK DATA ACK/REQ --> + * NC01 (0xA3C) + */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_data_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* block on data ready from NC10 + * then read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = + NC10(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.result[n]); + /* MSM_NAND_FLASH_STATUS + + * MSM_NAND_BUFFER_STATUS + */ + cmd->len = 8; + cmd++; + if (n != cwperpage - 1) { + /* MASK CMD ACK/REQ --> + * NC10 (0xF14) + */ + cmd->cmd = 0; + cmd->src = + msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_cmd_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* CMD */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cmd); + cmd->dst = + NC01(MSM_NAND_FLASH_CMD); + cmd->len = 4; + cmd++; + + /* EXEC */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.exec); + cmd->dst = + NC01(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + } + + /* Read only when there is data + * buffer + */ + if (ops->datbuf) { + cmd->cmd = 0; + cmd->src = + NC10(MSM_NAND_FLASH_BUFFER); + cmd->dst = data_dma_addr_curr; + data_dma_addr_curr += + sectordatasize; + cmd->len = sectordatasize; + cmd++; + } + } + } + + if (ops->oobbuf && (n == (cwperpage - 1) + || ops->mode != MTD_OPS_AUTO_OOB)) { + cmd->cmd = 0; + if (n == (cwperpage - 1)) { + /* Use NC10 for reading the + * last codeword!!! + */ + cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + + (512 - ((cwperpage - 1) << 2)); + sectoroobsize = (cwperpage << 2); + if (ops->mode != MTD_OPS_AUTO_OOB) + sectoroobsize += + chip->ecc_parity_bytes; + } else { + if (n % 2 == 0) + cmd->src = + NC01(MSM_NAND_FLASH_BUFFER) + + 516; + else + cmd->src = + NC10(MSM_NAND_FLASH_BUFFER) + + 516; + sectoroobsize = chip->ecc_parity_bytes; + } + cmd->dst = oob_dma_addr_curr; + if (sectoroobsize < oob_len) + cmd->len = sectoroobsize; + else + cmd->len = oob_len; + oob_dma_addr_curr += cmd->len; + oob_len -= cmd->len; + if (cmd->len > 0) + cmd++; + } + } + /* ADM --> Default mux state (0xFC0) */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_default_mux); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + if (!interleave_enable) { + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nc10_flash_dev_cmd_vld_default); + cmd->dst = NC10(MSM_NAND_DEV_CMD_VLD); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nc10_flash_dev_cmd1_default); + cmd->dst = NC10(MSM_NAND_DEV_CMD1); + cmd->len = 4; + cmd++; + } else { + /* disable CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.default_ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + } + + BUILD_BUG_ON(16 * 6 + 20 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) + | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + /* if any of the writes failed (0x10), or there + * was a protection violation (0x100), we lose + */ + pageerr = rawerr = 0; + for (n = start_sector; n < cwperpage; n++) { + if (dma_buffer->data.result[n].flash_status & 0x110) { + rawerr = -EIO; + break; + } + } + if (rawerr) { + if (ops->datbuf && ops->mode != MTD_OPS_RAW) { + uint8_t *datbuf = ops->datbuf + + pages_read * mtd->writesize; + + dma_sync_single_for_cpu(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + + for (n = 0; n < mtd->writesize; n++) { + /* empty blocks read 0x54 at + * these offsets + */ + if ((n % 516 == 3 || n % 516 == 175) + && datbuf[n] == 0x54) + datbuf[n] = 0xff; + if (datbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + + dma_sync_single_for_device(chip->dev, + data_dma_addr_curr-mtd->writesize, + mtd->writesize, DMA_BIDIRECTIONAL); + + } + if (ops->oobbuf) { + dma_sync_single_for_cpu(chip->dev, + oob_dma_addr_curr - (ops->ooblen - oob_len), + ops->ooblen - oob_len, DMA_BIDIRECTIONAL); + + for (n = 0; n < ops->ooblen; n++) { + if (ops->oobbuf[n] != 0xff) { + pageerr = rawerr; + break; + } + } + + dma_sync_single_for_device(chip->dev, + oob_dma_addr_curr - (ops->ooblen - oob_len), + ops->ooblen - oob_len, DMA_BIDIRECTIONAL); + } + } + if (pageerr) { + for (n = start_sector; n < cwperpage; n++) { + if (dma_buffer->data.result[n].buffer_status + & chip->uncorrectable_bit_mask) { + /* not thread safe */ + mtd->ecc_stats.failed++; + pageerr = -EBADMSG; + break; + } + } + } + if (!rawerr) { /* check for corretable errors */ + for (n = start_sector; n < cwperpage; n++) { + ecc_errors = dma_buffer->data. + result[n].buffer_status + & chip->num_err_mask; + if (ecc_errors) { + total_ecc_errors += ecc_errors; + /* not thread safe */ + mtd->ecc_stats.corrected += ecc_errors; + if (ecc_errors > 1) + pageerr = -EUCLEAN; + } + } + } + if (pageerr && (pageerr != -EUCLEAN || err == 0)) + err = pageerr; + +#if VERBOSE + if (rawerr && !pageerr) { + pr_err("msm_nand_read_oob_dualnandc " + "%llx %x %x empty page\n", + (loff_t)page * mtd->writesize, ops->len, + ops->ooblen); + } else { + for (n = start_sector; n < cwperpage; n++) { + if (n%2) { + pr_info("NC10: flash_status[%d] = %x, " + "buffr_status[%d] = %x\n", + n, dma_buffer-> + data.result[n].flash_status, + n, dma_buffer-> + data.result[n].buffer_status); + } else { + pr_info("NC01: flash_status[%d] = %x, " + "buffr_status[%d] = %x\n", + n, dma_buffer-> + data.result[n].flash_status, + n, dma_buffer-> + data.result[n].buffer_status); + } + } + } +#endif + if (err && err != -EUCLEAN && err != -EBADMSG) + break; + pages_read++; + page++; + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (ops->oobbuf) { + dma_unmap_page(chip->dev, oob_dma_addr, + ops->ooblen, DMA_FROM_DEVICE); + } +err_dma_map_oobbuf_failed: + if (ops->datbuf) { + dma_unmap_page(chip->dev, data_dma_addr, + ops->len, DMA_BIDIRECTIONAL); + } + + if (ops->mode != MTD_OPS_RAW) + ops->retlen = mtd->writesize * pages_read; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * + pages_read; + ops->oobretlen = ops->ooblen - oob_len; + if (err) + pr_err("msm_nand_read_oob_dualnandc " + "%llx %x %x failed %d, corrected %d\n", + from, ops->datbuf ? ops->len : 0, ops->ooblen, err, + total_ecc_errors); +#if VERBOSE + pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", + __func__, err, ops->retlen, ops->oobretlen); + + pr_info("===================================================" + "==========\n"); +#endif + return err; +} + +static int +msm_nand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int ret; + struct mtd_oob_ops ops; + + /* printk("msm_nand_read %llx %x\n", from, len); */ + + ops.mode = MTD_OPS_PLACE_OOB; + ops.len = len; + ops.retlen = 0; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + if (!dual_nand_ctlr_present) + ret = msm_nand_read_oob(mtd, from, &ops); + else + ret = msm_nand_read_oob_dualnandc(mtd, from, &ops); + *retlen = ops.retlen; + return ret; +} + +static int +msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + struct { + dmov_s cmd[8 * 7 + 2]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel; + uint32_t cfg0; + uint32_t cfg1; + uint32_t eccbchcfg; + uint32_t exec; + uint32_t ecccfg; + uint32_t clrfstatus; + uint32_t clrrstatus; + uint32_t flash_status[8]; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned n; + unsigned page = 0; + uint32_t oob_len; + uint32_t sectordatawritesize; + int err = 0; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + unsigned page_count; + unsigned pages_written = 0; + unsigned cwperpage; +#if VERBOSE + pr_info("=================================================" + "================\n"); + pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" + "\noobbuf 0x%p ooblen 0x%x\n", + __func__, to, ops->mode, ops->datbuf, ops->len, + ops->oobbuf, ops->ooblen); +#endif + + if (mtd->writesize == 2048) + page = to >> 11; + + if (mtd->writesize == 4096) + page = to >> 12; + + oob_len = ops->ooblen; + cwperpage = (mtd->writesize >> 9); + + if (to & (mtd->writesize - 1)) { + pr_err("%s: unsupported to, 0x%llx\n", __func__, to); + return -EINVAL; + } + + if (ops->mode != MTD_OPS_RAW) { + if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) { + pr_err("%s: unsupported ops->mode,%d\n", + __func__, ops->mode); + return -EINVAL; + } + if ((ops->len % mtd->writesize) != 0) { + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len, " + "%d for MTD_OPS_RAW mode\n", + __func__, ops->len); + return -EINVAL; + } + } + + if (ops->datbuf == NULL) { + pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); + return -EINVAL; + } + if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { + pr_err("%s: unsupported ops->ooboffs, %d\n", + __func__, ops->ooboffs); + return -EINVAL; + } + + if (ops->datbuf) { + data_dma_addr_curr = data_dma_addr = + msm_nand_dma_map(chip->dev, ops->datbuf, + ops->len, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("msm_nand_write_oob: failed to get dma addr " + "for %p\n", ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + oob_dma_addr_curr = oob_dma_addr = + msm_nand_dma_map(chip->dev, ops->oobbuf, + ops->ooblen, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("msm_nand_write_oob: failed to get dma addr " + "for %p\n", ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + if (ops->mode != MTD_OPS_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); + + wait_event(chip->wait_queue, (dma_buffer = + msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); + + while (page_count-- > 0) { + cmd = dma_buffer->cmd; + + if (ops->mode != MTD_OPS_RAW) { + dma_buffer->data.cfg0 = chip->CFG0; + dma_buffer->data.cfg1 = chip->CFG1; + if (enable_bch_ecc) + dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; + } else { + dma_buffer->data.cfg0 = (chip->CFG0_RAW & + ~(7U << 6)) | ((cwperpage-1) << 6); + dma_buffer->data.cfg1 = chip->CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + + /* CMD / ADDR0 / ADDR1 / CHIPSEL program values */ + dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; + dma_buffer->data.addr0 = page << 16; + dma_buffer->data.addr1 = (page >> 16) & 0xff; + /* chipsel_0 + enable DM interface */ + dma_buffer->data.chipsel = 0 | 4; + + + /* GO bit for the EXEC register */ + dma_buffer->data.exec = 1; + dma_buffer->data.clrfstatus = 0x00000020; + dma_buffer->data.clrrstatus = 0x000000C0; + + BUILD_BUG_ON(8 != ARRAY_SIZE(dma_buffer->data.flash_status)); + + for (n = 0; n < cwperpage ; n++) { + /* status return words */ + dma_buffer->data.flash_status[n] = 0xeeeeeeee; + /* block on cmd ready, then + * write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst + */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = + msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = MSM_NAND_FLASH_CMD; + if (n == 0) + cmd->len = 16; + else + cmd->len = 4; + cmd++; + + if (n == 0) { + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = MSM_NAND_DEV0_CFG0; + if (enable_bch_ecc) + cmd->len = 12; + else + cmd->len = 8; + cmd++; + + dma_buffer->data.ecccfg = chip->ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg); + cmd->dst = MSM_NAND_EBI2_ECC_BUF_CFG; + cmd->len = 4; + cmd++; + } + + /* write data block */ + if (ops->mode != MTD_OPS_RAW) + sectordatawritesize = (n < (cwperpage - 1)) ? + 516 : (512 - ((cwperpage - 1) << 2)); + else + sectordatawritesize = chip->cw_size; + + cmd->cmd = 0; + cmd->src = data_dma_addr_curr; + data_dma_addr_curr += sectordatawritesize; + cmd->dst = MSM_NAND_FLASH_BUFFER; + cmd->len = sectordatawritesize; + cmd++; + + if (ops->oobbuf) { + if (n == (cwperpage - 1)) { + cmd->cmd = 0; + cmd->src = oob_dma_addr_curr; + cmd->dst = MSM_NAND_FLASH_BUFFER + + (512 - ((cwperpage - 1) << 2)); + if ((cwperpage << 2) < oob_len) + cmd->len = (cwperpage << 2); + else + cmd->len = oob_len; + oob_dma_addr_curr += cmd->len; + oob_len -= cmd->len; + if (cmd->len > 0) + cmd++; + } + if (ops->mode != MTD_OPS_AUTO_OOB) { + /* skip ecc bytes in oobbuf */ + if (oob_len < chip->ecc_parity_bytes) { + oob_dma_addr_curr += + chip->ecc_parity_bytes; + oob_len -= + chip->ecc_parity_bytes; + } else { + oob_dma_addr_curr += oob_len; + oob_len = 0; + } + } + } + + /* kick the execute register */ + cmd->cmd = 0; + cmd->src = + msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = MSM_NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* block on data ready, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.flash_status[n]); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.clrfstatus); + cmd->dst = MSM_NAND_FLASH_STATUS; + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.clrrstatus); + cmd->dst = MSM_NAND_READ_STATUS; + cmd->len = 4; + cmd++; + + } + + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + BUILD_BUG_ON(8 * 7 + 2 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | + CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( + msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + /* if any of the writes failed (0x10), or there was a + * protection violation (0x100), or the program success + * bit (0x80) is unset, we lose + */ + err = 0; + for (n = 0; n < cwperpage; n++) { + if (dma_buffer->data.flash_status[n] & 0x110) { + err = -EIO; + break; + } + if (!(dma_buffer->data.flash_status[n] & 0x80)) { + err = -EIO; + break; + } + } + +#if VERBOSE + for (n = 0; n < cwperpage; n++) + pr_info("write pg %d: flash_status[%d] = %x\n", page, + n, dma_buffer->data.flash_status[n]); + +#endif + if (err) + break; + pages_written++; + page++; + } + if (ops->mode != MTD_OPS_RAW) + ops->retlen = mtd->writesize * pages_written; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; + + ops->oobretlen = ops->ooblen - oob_len; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (ops->oobbuf) + dma_unmap_page(chip->dev, oob_dma_addr, + ops->ooblen, DMA_TO_DEVICE); +err_dma_map_oobbuf_failed: + if (ops->datbuf) + dma_unmap_page(chip->dev, data_dma_addr, ops->len, + DMA_TO_DEVICE); + if (err) + pr_err("msm_nand_write_oob %llx %x %x failed %d\n", + to, ops->len, ops->ooblen, err); + +#if VERBOSE + pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", + __func__, err, ops->retlen, ops->oobretlen); + + pr_info("===================================================" + "==============\n"); +#endif + return err; +} + +static int +msm_nand_write_oob_dualnandc(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + struct { + dmov_s cmd[16 * 6 + 18]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t nandc01_addr0; + uint32_t nandc10_addr0; + uint32_t nandc11_addr1; + uint32_t chipsel_cs0; + uint32_t chipsel_cs1; + uint32_t cfg0; + uint32_t cfg1; + uint32_t eccbchcfg; + uint32_t exec; + uint32_t ecccfg; + uint32_t cfg0_nc01; + uint32_t ebi2_chip_select_cfg0; + uint32_t adm_mux_data_ack_req_nc01; + uint32_t adm_mux_cmd_ack_req_nc01; + uint32_t adm_mux_data_ack_req_nc10; + uint32_t adm_mux_cmd_ack_req_nc10; + uint32_t adm_default_mux; + uint32_t default_ebi2_chip_select_cfg0; + uint32_t nc01_flash_dev_cmd_vld; + uint32_t nc10_flash_dev_cmd0; + uint32_t nc01_flash_dev_cmd_vld_default; + uint32_t nc10_flash_dev_cmd0_default; + uint32_t flash_status[16]; + uint32_t clrfstatus; + uint32_t clrrstatus; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned n; + unsigned page = 0; + uint32_t oob_len; + uint32_t sectordatawritesize; + int err = 0; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + unsigned page_count; + unsigned pages_written = 0; + unsigned cwperpage; + unsigned cw_offset = chip->cw_size; +#if VERBOSE + pr_info("=================================================" + "============\n"); + pr_info("%s:\nto 0x%llx mode %d\ndatbuf 0x%p datlen 0x%x" + "\noobbuf 0x%p ooblen 0x%x\n\n", + __func__, to, ops->mode, ops->datbuf, ops->len, + ops->oobbuf, ops->ooblen); +#endif + + if (mtd->writesize == 2048) + page = to >> 11; + + if (mtd->writesize == 4096) + page = to >> 12; + + if (interleave_enable) + page = (to >> 1) >> 12; + + oob_len = ops->ooblen; + cwperpage = (mtd->writesize >> 9); + + if (to & (mtd->writesize - 1)) { + pr_err("%s: unsupported to, 0x%llx\n", __func__, to); + return -EINVAL; + } + + if (ops->mode != MTD_OPS_RAW) { + if (ops->ooblen != 0 && ops->mode != MTD_OPS_AUTO_OOB) { + pr_err("%s: unsupported ops->mode,%d\n", + __func__, ops->mode); + return -EINVAL; + } + if ((ops->len % mtd->writesize) != 0) { + pr_err("%s: unsupported ops->len, %d\n", + __func__, ops->len); + return -EINVAL; + } + } else { + if ((ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len, " + "%d for MTD_OPS_RAW mode\n", + __func__, ops->len); + return -EINVAL; + } + } + + if (ops->datbuf == NULL) { + pr_err("%s: unsupported ops->datbuf == NULL\n", __func__); + return -EINVAL; + } + + if (ops->mode != MTD_OPS_RAW && ops->ooblen != 0 && ops->ooboffs != 0) { + pr_err("%s: unsupported ops->ooboffs, %d\n", + __func__, ops->ooboffs); + return -EINVAL; + } + + if (ops->datbuf) { + data_dma_addr_curr = data_dma_addr = + msm_nand_dma_map(chip->dev, ops->datbuf, + ops->len, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("msm_nand_write_oob_dualnandc:" + "failed to get dma addr " + "for %p\n", ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + oob_dma_addr_curr = oob_dma_addr = + msm_nand_dma_map(chip->dev, ops->oobbuf, + ops->ooblen, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("msm_nand_write_oob_dualnandc:" + "failed to get dma addr " + "for %p\n", ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + if (ops->mode != MTD_OPS_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); + + wait_event(chip->wait_queue, (dma_buffer = + msm_nand_get_dma_buffer(chip, sizeof(*dma_buffer)))); + + if (chip->CFG1 & CFG1_WIDE_FLASH) + cw_offset >>= 1; + + dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; + dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; + dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; + dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; + dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; + dma_buffer->data.adm_default_mux = 0x00000FC0; + dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; + dma_buffer->data.nc01_flash_dev_cmd_vld = 0x9; + dma_buffer->data.nc10_flash_dev_cmd0 = 0x1085D060; + dma_buffer->data.nc01_flash_dev_cmd_vld_default = 0x1D; + dma_buffer->data.nc10_flash_dev_cmd0_default = 0x1080D060; + dma_buffer->data.clrfstatus = 0x00000020; + dma_buffer->data.clrrstatus = 0x000000C0; + + while (page_count-- > 0) { + cmd = dma_buffer->cmd; + + if (ops->mode != MTD_OPS_RAW) { + dma_buffer->data.cfg0 = ((chip->CFG0 & ~(7U << 6)) + & ~(1 << 4)) | ((((cwperpage >> 1)-1)) << 6); + dma_buffer->data.cfg1 = chip->CFG1; + if (enable_bch_ecc) + dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; + } else { + dma_buffer->data.cfg0 = ((chip->CFG0_RAW & + ~(7U << 6)) & ~(1 << 4)) | (((cwperpage >> 1)-1) << 6); + dma_buffer->data.cfg1 = chip->CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + } + + /* Disables the automatic issuing of the read + * status command for first NAND controller. + */ + if (!interleave_enable) + dma_buffer->data.cfg0_nc01 = dma_buffer->data.cfg0 + | (1 << 4); + else + dma_buffer->data.cfg0 |= (1 << 4); + + dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE; + dma_buffer->data.chipsel_cs0 = (1<<4) | 4; + dma_buffer->data.chipsel_cs1 = (1<<4) | 5; + + /* GO bit for the EXEC register */ + dma_buffer->data.exec = 1; + + if (!interleave_enable) { + dma_buffer->data.nandc01_addr0 = (page << 16) | 0x0; + /* NC10 ADDR0 points to the next code word */ + dma_buffer->data.nandc10_addr0 = + (page << 16) | cw_offset; + } else { + dma_buffer->data.nandc01_addr0 = + dma_buffer->data.nandc10_addr0 = (page << 16) | 0x0; + } + /* ADDR1 */ + dma_buffer->data.nandc11_addr1 = (page >> 16) & 0xff; + + BUILD_BUG_ON(16 != ARRAY_SIZE(dma_buffer->data.flash_status)); + + for (n = 0; n < cwperpage; n++) { + /* status return words */ + dma_buffer->data.flash_status[n] = 0xeeeeeeee; + + if (n == 0) { + if (!interleave_enable) { + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.nc01_flash_dev_cmd_vld); + cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nc10_flash_dev_cmd0); + cmd->dst = NC10(MSM_NAND_DEV_CMD0); + cmd->len = 4; + cmd++; + + /* common settings for both NC01 & NC10 + * NC01, NC10 --> ADDR1 / CHIPSEL + */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc11_addr1); + cmd->dst = NC11(MSM_NAND_ADDR1); + cmd->len = 8; + cmd++; + + /* Disables the automatic issue of the + * read status command after the write + * operation. + */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0_nc01); + cmd->dst = NC01(MSM_NAND_DEV0_CFG0); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NC10(MSM_NAND_DEV0_CFG0); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg1); + cmd->dst = NC11(MSM_NAND_DEV0_CFG1); + if (enable_bch_ecc) + cmd->len = 8; + else + cmd->len = 4; + cmd++; + } else { + /* enable CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + + /* NC11 --> ADDR1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc11_addr1); + cmd->dst = NC11(MSM_NAND_ADDR1); + cmd->len = 4; + cmd++; + + /* Enable CS0 for NC01 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.chipsel_cs0); + cmd->dst = + NC01(MSM_NAND_FLASH_CHIP_SELECT); + cmd->len = 4; + cmd++; + + /* Enable CS1 for NC10 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.chipsel_cs1); + cmd->dst = + NC10(MSM_NAND_FLASH_CHIP_SELECT); + cmd->len = 4; + cmd++; + + /* config DEV0_CFG0 & CFG1 for CS0 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NC01(MSM_NAND_DEV0_CFG0); + cmd->len = 8; + cmd++; + + /* config DEV1_CFG0 & CFG1 for CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cfg0); + cmd->dst = NC10(MSM_NAND_DEV1_CFG0); + cmd->len = 8; + cmd++; + } + + dma_buffer->data.ecccfg = chip->ecc_buf_cfg; + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ecccfg); + cmd->dst = NC11(MSM_NAND_EBI2_ECC_BUF_CFG); + cmd->len = 4; + cmd++; + + /* NC01 --> ADDR0 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc01_addr0); + cmd->dst = NC01(MSM_NAND_ADDR0); + cmd->len = 4; + cmd++; + + /* NC10 --> ADDR0 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nandc10_addr0); + cmd->dst = NC10(MSM_NAND_ADDR0); + cmd->len = 4; + cmd++; + } + + if (n % 2 == 0) { + /* MASK CMD ACK/REQ --> NC10 (0xF14)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_cmd_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* CMD */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cmd); + cmd->dst = NC01(MSM_NAND_FLASH_CMD); + cmd->len = 4; + cmd++; + } else { + /* MASK CMD ACK/REQ --> NC01 (0x53C)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_cmd_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* CMD */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.cmd); + cmd->dst = NC10(MSM_NAND_FLASH_CMD); + cmd->len = 4; + cmd++; + } + + if (ops->mode != MTD_OPS_RAW) + sectordatawritesize = (n < (cwperpage - 1)) ? + 516 : (512 - ((cwperpage - 1) << 2)); + else + sectordatawritesize = chip->cw_size; + + cmd->cmd = 0; + cmd->src = data_dma_addr_curr; + data_dma_addr_curr += sectordatawritesize; + + if (n % 2 == 0) + cmd->dst = NC01(MSM_NAND_FLASH_BUFFER); + else + cmd->dst = NC10(MSM_NAND_FLASH_BUFFER); + cmd->len = sectordatawritesize; + cmd++; + + if (ops->oobbuf) { + if (n == (cwperpage - 1)) { + cmd->cmd = 0; + cmd->src = oob_dma_addr_curr; + cmd->dst = NC10(MSM_NAND_FLASH_BUFFER) + + (512 - ((cwperpage - 1) << 2)); + if ((cwperpage << 2) < oob_len) + cmd->len = (cwperpage << 2); + else + cmd->len = oob_len; + oob_dma_addr_curr += cmd->len; + oob_len -= cmd->len; + if (cmd->len > 0) + cmd++; + } + if (ops->mode != MTD_OPS_AUTO_OOB) { + /* skip ecc bytes in oobbuf */ + if (oob_len < chip->ecc_parity_bytes) { + oob_dma_addr_curr += + chip->ecc_parity_bytes; + oob_len -= + chip->ecc_parity_bytes; + } else { + oob_dma_addr_curr += oob_len; + oob_len = 0; + } + } + } + + if (n % 2 == 0) { + if (n != 0) { + /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer-> + data.adm_mux_data_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* block on data ready from NC10, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC10(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.flash_status[n-1]); + cmd->len = 4; + cmd++; + } + /* kick the NC01 execute register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.exec); + cmd->dst = NC01(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + } else { + /* MASK DATA ACK/REQ --> NC10 (0xF28)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_data_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* block on data ready from NC01, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC01(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.flash_status[n-1]); + cmd->len = 4; + cmd++; + + /* kick the execute register */ + cmd->cmd = 0; + cmd->src = + msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NC10(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + } + } + + /* MASK DATA ACK/REQ --> NC01 (0xA3C)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_data_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* we should process outstanding request */ + /* block on data ready, then + * read the status register + */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC10(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.flash_status[n-1]); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); + cmd->dst = NC11(MSM_NAND_FLASH_STATUS); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); + cmd->dst = NC11(MSM_NAND_READ_STATUS); + cmd->len = 4; + cmd++; + + /* MASK DATA ACK/REQ --> NC01 (0xFC0)*/ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_default_mux); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + if (!interleave_enable) { + /* setting to defalut values back */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nc01_flash_dev_cmd_vld_default); + cmd->dst = NC01(MSM_NAND_DEV_CMD_VLD); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.nc10_flash_dev_cmd0_default); + cmd->dst = NC10(MSM_NAND_DEV_CMD0); + cmd->len = 4; + cmd++; + } else { + /* disable CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.default_ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + } + + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + BUILD_BUG_ON(16 * 6 + 18 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmdptr = + ((msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP); + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR( + msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + /* if any of the writes failed (0x10), or there was a + * protection violation (0x100), or the program success + * bit (0x80) is unset, we lose + */ + err = 0; + for (n = 0; n < cwperpage; n++) { + if (dma_buffer->data.flash_status[n] & 0x110) { + err = -EIO; + break; + } + if (!(dma_buffer->data.flash_status[n] & 0x80)) { + err = -EIO; + break; + } + } + /* check for flash status busy for the last codeword */ + if (!interleave_enable) + if (!(dma_buffer->data.flash_status[cwperpage - 1] + & 0x20)) { + err = -EIO; + break; + } +#if VERBOSE + for (n = 0; n < cwperpage; n++) { + if (n%2) { + pr_info("NC10: write pg %d: flash_status[%d] = %x\n", + page, n, dma_buffer->data.flash_status[n]); + } else { + pr_info("NC01: write pg %d: flash_status[%d] = %x\n", + page, n, dma_buffer->data.flash_status[n]); + } + } +#endif + if (err) + break; + pages_written++; + page++; + } + if (ops->mode != MTD_OPS_RAW) + ops->retlen = mtd->writesize * pages_written; + else + ops->retlen = (mtd->writesize + mtd->oobsize) * pages_written; + + ops->oobretlen = ops->ooblen - oob_len; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (ops->oobbuf) + dma_unmap_page(chip->dev, oob_dma_addr, + ops->ooblen, DMA_TO_DEVICE); +err_dma_map_oobbuf_failed: + if (ops->datbuf) + dma_unmap_page(chip->dev, data_dma_addr, ops->len, + DMA_TO_DEVICE); + if (err) + pr_err("msm_nand_write_oob_dualnandc %llx %x %x failed %d\n", + to, ops->len, ops->ooblen, err); + +#if VERBOSE + pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", + __func__, err, ops->retlen, ops->oobretlen); + + pr_info("===================================================" + "==========\n"); +#endif + return err; +} + +static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int ret; + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.len = len; + ops.retlen = 0; + ops.ooblen = 0; + ops.datbuf = (uint8_t *)buf; + ops.oobbuf = NULL; + if (!dual_nand_ctlr_present) + ret = msm_nand_write_oob(mtd, to, &ops); + else + ret = msm_nand_write_oob_dualnandc(mtd, to, &ops); + *retlen = ops.retlen; + return ret; +} + +static int +msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int err; + struct msm_nand_chip *chip = mtd->priv; + struct { + dmov_s cmd[6]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel; + uint32_t cfg0; + uint32_t cfg1; + uint32_t exec; + uint32_t flash_status; + uint32_t clrfstatus; + uint32_t clrrstatus; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned page = 0; + + if (mtd->writesize == 2048) + page = instr->addr >> 11; + + if (mtd->writesize == 4096) + page = instr->addr >> 12; + + if (instr->addr & (mtd->erasesize - 1)) { + pr_err("%s: unsupported erase address, 0x%llx\n", + __func__, instr->addr); + return -EINVAL; + } + if (instr->len != mtd->erasesize) { + pr_err("%s: unsupported erase len, %lld\n", + __func__, instr->len); + return -EINVAL; + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + cmd = dma_buffer->cmd; + + dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE; + dma_buffer->data.addr0 = page; + dma_buffer->data.addr1 = 0; + dma_buffer->data.chipsel = 0 | 4; + dma_buffer->data.exec = 1; + dma_buffer->data.flash_status = 0xeeeeeeee; + dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ + dma_buffer->data.cfg1 = chip->CFG1; + dma_buffer->data.clrfstatus = 0x00000020; + dma_buffer->data.clrrstatus = 0x000000C0; + + cmd->cmd = DST_CRCI_NAND_CMD | CMD_OCB; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = MSM_NAND_FLASH_CMD; + cmd->len = 16; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); + cmd->dst = MSM_NAND_DEV0_CFG0; + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = MSM_NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); + cmd->dst = MSM_NAND_FLASH_STATUS; + cmd->len = 4; + cmd++; + + cmd->cmd = CMD_OCU | CMD_LC; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); + cmd->dst = MSM_NAND_READ_STATUS; + cmd->len = 4; + cmd++; + + BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd) - 1); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + /* we fail if there was an operation error, a mpu error, or the + * erase success bit was not set. + */ + + if (dma_buffer->data.flash_status & 0x110 || + !(dma_buffer->data.flash_status & 0x80)) + err = -EIO; + else + err = 0; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + if (err) { + pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr); + instr->fail_addr = instr->addr; + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + instr->fail_addr = 0xffffffff; + mtd_erase_callback(instr); + } + return err; +} + +static int +msm_nand_erase_dualnandc(struct mtd_info *mtd, struct erase_info *instr) +{ + int err; + struct msm_nand_chip *chip = mtd->priv; + struct { + dmov_s cmd[18]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel_cs0; + uint32_t chipsel_cs1; + uint32_t cfg0; + uint32_t cfg1; + uint32_t exec; + uint32_t ecccfg; + uint32_t ebi2_chip_select_cfg0; + uint32_t adm_mux_data_ack_req_nc01; + uint32_t adm_mux_cmd_ack_req_nc01; + uint32_t adm_mux_data_ack_req_nc10; + uint32_t adm_mux_cmd_ack_req_nc10; + uint32_t adm_default_mux; + uint32_t default_ebi2_chip_select_cfg0; + uint32_t nc01_flash_dev_cmd0; + uint32_t nc01_flash_dev_cmd0_default; + uint32_t flash_status[2]; + uint32_t clrfstatus; + uint32_t clrrstatus; + } data; + } *dma_buffer; + dmov_s *cmd; + unsigned page = 0; + + if (mtd->writesize == 2048) + page = instr->addr >> 11; + + if (mtd->writesize == 4096) + page = instr->addr >> 12; + + if (mtd->writesize == 8192) + page = (instr->addr >> 1) >> 12; + + if (instr->addr & (mtd->erasesize - 1)) { + pr_err("%s: unsupported erase address, 0x%llx\n", + __func__, instr->addr); + return -EINVAL; + } + if (instr->len != mtd->erasesize) { + pr_err("%s: unsupported erase len, %lld\n", + __func__, instr->len); + return -EINVAL; + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + cmd = dma_buffer->cmd; + + dma_buffer->data.cmd = MSM_NAND_CMD_BLOCK_ERASE; + dma_buffer->data.addr0 = page; + dma_buffer->data.addr1 = 0; + dma_buffer->data.chipsel_cs0 = (1<<4) | 4; + dma_buffer->data.chipsel_cs1 = (1<<4) | 5; + dma_buffer->data.exec = 1; + dma_buffer->data.flash_status[0] = 0xeeeeeeee; + dma_buffer->data.flash_status[1] = 0xeeeeeeee; + dma_buffer->data.cfg0 = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */ + dma_buffer->data.cfg1 = chip->CFG1; + dma_buffer->data.clrfstatus = 0x00000020; + dma_buffer->data.clrrstatus = 0x000000C0; + + dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; + dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; + dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; + dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; + dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; + dma_buffer->data.adm_default_mux = 0x00000FC0; + dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; + + /* enable CS1 */ + cmd->cmd = 0 | CMD_OCB; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + + /* erase CS0 block now !!! */ + /* 0xF14 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_cmd_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = NC01(MSM_NAND_FLASH_CMD); + cmd->len = 16; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); + cmd->dst = NC01(MSM_NAND_DEV0_CFG0); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NC01(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + + /* 0xF28 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_data_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC01(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[0]); + cmd->len = 4; + cmd++; + + /* erase CS1 block now !!! */ + /* 0x53C */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_cmd_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = NC10(MSM_NAND_FLASH_CMD); + cmd->len = 12; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1); + cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); + cmd->dst = NC10(MSM_NAND_DEV1_CFG0); + cmd->len = 8; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NC10(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + + /* 0xA3C */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_data_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC10(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.flash_status[1]); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrfstatus); + cmd->dst = NC11(MSM_NAND_FLASH_STATUS); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.clrrstatus); + cmd->dst = NC11(MSM_NAND_READ_STATUS); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_default_mux); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* disable CS1 */ + cmd->cmd = CMD_OCU | CMD_LC; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.default_ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + + BUILD_BUG_ON(17 != ARRAY_SIZE(dma_buffer->cmd) - 1); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + + dma_buffer->cmdptr = + (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd( + chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + /* we fail if there was an operation error, a mpu error, or the + * erase success bit was not set. + */ + + if (dma_buffer->data.flash_status[0] & 0x110 || + !(dma_buffer->data.flash_status[0] & 0x80) || + dma_buffer->data.flash_status[1] & 0x110 || + !(dma_buffer->data.flash_status[1] & 0x80)) + err = -EIO; + else + err = 0; + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + if (err) { + pr_err("%s: erase failed, 0x%llx\n", __func__, instr->addr); + instr->fail_addr = instr->addr; + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + instr->fail_addr = 0xffffffff; + mtd_erase_callback(instr); + } + return err; +} + +static int +msm_nand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct msm_nand_chip *chip = mtd->priv; + int ret; + struct { + dmov_s cmd[5]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel; + uint32_t cfg0; + uint32_t cfg1; + uint32_t eccbchcfg; + uint32_t exec; + uint32_t ecccfg; + struct { + uint32_t flash_status; + uint32_t buffer_status; + } result; + } data; + } *dma_buffer; + dmov_s *cmd; + uint8_t *buf; + unsigned page = 0; + unsigned cwperpage; + + if (mtd->writesize == 2048) + page = ofs >> 11; + + if (mtd->writesize == 4096) + page = ofs >> 12; + + cwperpage = (mtd->writesize >> 9); + + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + if (ofs & (mtd->erasesize - 1)) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer(chip , + sizeof(*dma_buffer) + 4))); + buf = (uint8_t *)dma_buffer + sizeof(*dma_buffer); + + /* Read 4 bytes starting from the bad block marker location + * in the last code word of the page + */ + + cmd = dma_buffer->cmd; + + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6); + dma_buffer->data.cfg1 = chip->CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + if (enable_bch_ecc) + dma_buffer->data.eccbchcfg = chip->ecc_bch_cfg; + + if (chip->CFG1 & CFG1_WIDE_FLASH) + dma_buffer->data.addr0 = (page << 16) | + ((chip->cw_size * (cwperpage-1)) >> 1); + else + dma_buffer->data.addr0 = (page << 16) | + (chip->cw_size * (cwperpage-1)); + + dma_buffer->data.addr1 = (page >> 16) & 0xff; + dma_buffer->data.chipsel = 0 | 4; + + dma_buffer->data.exec = 1; + + dma_buffer->data.result.flash_status = 0xeeeeeeee; + dma_buffer->data.result.buffer_status = 0xeeeeeeee; + + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = MSM_NAND_FLASH_CMD; + cmd->len = 16; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); + cmd->dst = MSM_NAND_DEV0_CFG0; + if (enable_bch_ecc) + cmd->len = 12; + else + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = MSM_NAND_EXEC_CMD; + cmd->len = 4; + cmd++; + + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_FLASH_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = MSM_NAND_FLASH_BUFFER + + (mtd->writesize - (chip->cw_size * (cwperpage-1))); + cmd->dst = msm_virt_to_dma(chip, buf); + cmd->len = 4; + cmd++; + + BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, + dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + ret = 0; + if (dma_buffer->data.result.flash_status & 0x110) + ret = -EIO; + + if (!ret) { + /* Check for bad block marker byte */ + if (chip->CFG1 & CFG1_WIDE_FLASH) { + if (buf[0] != 0xFF || buf[1] != 0xFF) + ret = 1; + } else { + if (buf[0] != 0xFF) + ret = 1; + } + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 4); + return ret; +} + +static int +msm_nand_block_isbad_dualnandc(struct mtd_info *mtd, loff_t ofs) +{ + struct msm_nand_chip *chip = mtd->priv; + int ret; + struct { + dmov_s cmd[18]; + unsigned cmdptr; + struct { + uint32_t cmd; + uint32_t addr0; + uint32_t addr1; + uint32_t chipsel_cs0; + uint32_t chipsel_cs1; + uint32_t cfg0; + uint32_t cfg1; + uint32_t exec; + uint32_t ecccfg; + uint32_t ebi2_chip_select_cfg0; + uint32_t adm_mux_data_ack_req_nc01; + uint32_t adm_mux_cmd_ack_req_nc01; + uint32_t adm_mux_data_ack_req_nc10; + uint32_t adm_mux_cmd_ack_req_nc10; + uint32_t adm_default_mux; + uint32_t default_ebi2_chip_select_cfg0; + struct { + uint32_t flash_status; + uint32_t buffer_status; + } result[2]; + } data; + } *dma_buffer; + dmov_s *cmd; + uint8_t *buf01; + uint8_t *buf10; + unsigned page = 0; + unsigned cwperpage; + + if (mtd->writesize == 2048) + page = ofs >> 11; + + if (mtd->writesize == 4096) + page = ofs >> 12; + + if (mtd->writesize == 8192) + page = (ofs >> 1) >> 12; + + cwperpage = ((mtd->writesize >> 1) >> 9); + + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + if (ofs & (mtd->erasesize - 1)) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; + } + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer(chip , + sizeof(*dma_buffer) + 8))); + buf01 = (uint8_t *)dma_buffer + sizeof(*dma_buffer); + buf10 = buf01 + 4; + + /* Read 4 bytes starting from the bad block marker location + * in the last code word of the page + */ + cmd = dma_buffer->cmd; + + dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ; + dma_buffer->data.cfg0 = chip->CFG0_RAW & ~(7U << 6); + dma_buffer->data.cfg1 = chip->CFG1_RAW | + (chip->CFG1 & CFG1_WIDE_FLASH); + + if (chip->CFG1 & CFG1_WIDE_FLASH) + dma_buffer->data.addr0 = (page << 16) | + ((528*(cwperpage-1)) >> 1); + else + dma_buffer->data.addr0 = (page << 16) | + (528*(cwperpage-1)); + + dma_buffer->data.addr1 = (page >> 16) & 0xff; + dma_buffer->data.chipsel_cs0 = (1<<4) | 4; + dma_buffer->data.chipsel_cs1 = (1<<4) | 5; + + dma_buffer->data.exec = 1; + + dma_buffer->data.result[0].flash_status = 0xeeeeeeee; + dma_buffer->data.result[0].buffer_status = 0xeeeeeeee; + dma_buffer->data.result[1].flash_status = 0xeeeeeeee; + dma_buffer->data.result[1].buffer_status = 0xeeeeeeee; + + dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805; + dma_buffer->data.adm_mux_data_ack_req_nc01 = 0x00000A3C; + dma_buffer->data.adm_mux_cmd_ack_req_nc01 = 0x0000053C; + dma_buffer->data.adm_mux_data_ack_req_nc10 = 0x00000F28; + dma_buffer->data.adm_mux_cmd_ack_req_nc10 = 0x00000F14; + dma_buffer->data.adm_default_mux = 0x00000FC0; + dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801; + + /* Reading last code word from NC01 */ + /* enable CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + + /* 0xF14 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_cmd_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = NC01(MSM_NAND_FLASH_CMD); + cmd->len = 16; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); + cmd->dst = NC01(MSM_NAND_DEV0_CFG0); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NC01(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + + /* 0xF28 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_data_ack_req_nc10); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC01(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[0]); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = NC01(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) - + (528*(cwperpage-1))); + cmd->dst = msm_virt_to_dma(chip, buf01); + cmd->len = 4; + cmd++; + + /* Reading last code word from NC10 */ + /* 0x53C */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_cmd_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = NC10(MSM_NAND_FLASH_CMD); + cmd->len = 12; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.chipsel_cs1); + cmd->dst = NC10(MSM_NAND_FLASH_CHIP_SELECT); + cmd->len = 4; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cfg0); + cmd->dst = NC10(MSM_NAND_DEV1_CFG0); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = NC10(MSM_NAND_EXEC_CMD); + cmd->len = 4; + cmd++; + + /* A3C */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_mux_data_ack_req_nc01); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = NC10(MSM_NAND_FLASH_STATUS); + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.result[1]); + cmd->len = 8; + cmd++; + + cmd->cmd = 0; + cmd->src = NC10(MSM_NAND_FLASH_BUFFER) + ((mtd->writesize >> 1) - + (528*(cwperpage-1))); + cmd->dst = msm_virt_to_dma(chip, buf10); + cmd->len = 4; + cmd++; + + /* FC0 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.adm_default_mux); + cmd->dst = EBI2_NAND_ADM_MUX; + cmd->len = 4; + cmd++; + + /* disble CS1 */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.ebi2_chip_select_cfg0); + cmd->dst = EBI2_CHIP_SELECT_CFG0; + cmd->len = 4; + cmd++; + + BUILD_BUG_ON(18 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, + dma_buffer->cmd) >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST | + DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr))); + mb(); + + ret = 0; + if ((dma_buffer->data.result[0].flash_status & 0x110) || + (dma_buffer->data.result[1].flash_status & 0x110)) + ret = -EIO; + + if (!ret) { + /* Check for bad block marker byte for NC01 & NC10 */ + if (chip->CFG1 & CFG1_WIDE_FLASH) { + if ((buf01[0] != 0xFF || buf01[1] != 0xFF) || + (buf10[0] != 0xFF || buf10[1] != 0xFF)) + ret = 1; + } else { + if (buf01[0] != 0xFF || buf10[0] != 0xFF) + ret = 1; + } + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer) + 8); + return ret; +} + +static int +msm_nand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + int ret; + uint8_t *buf; + + /* Check for invalid offset */ + if (ofs > mtd->size) + return -EINVAL; + if (ofs & (mtd->erasesize - 1)) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; + } + + /* + Write all 0s to the first page + This will set the BB marker to 0 + */ + buf = page_address(ZERO_PAGE()); + + ops.mode = MTD_OPS_RAW; + ops.len = mtd->writesize + mtd->oobsize; + ops.retlen = 0; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + if (!interleave_enable) + ret = msm_nand_write_oob(mtd, ofs, &ops); + else + ret = msm_nand_write_oob_dualnandc(mtd, ofs, &ops); + + return ret; +} + +/** + * msm_nand_suspend - [MTD Interface] Suspend the msm_nand flash + * @param mtd MTD device structure + */ +static int msm_nand_suspend(struct mtd_info *mtd) +{ + return 0; +} + +/** + * msm_nand_resume - [MTD Interface] Resume the msm_nand flash + * @param mtd MTD device structure + */ +static void msm_nand_resume(struct mtd_info *mtd) +{ +} + +struct onenand_information { + uint16_t manufacturer_id; + uint16_t device_id; + uint16_t version_id; + uint16_t data_buf_size; + uint16_t boot_buf_size; + uint16_t num_of_buffers; + uint16_t technology; +}; + +static struct onenand_information onenand_info; +static uint32_t nand_sfcmd_mode; + +uint32_t flash_onenand_probe(struct msm_nand_chip *chip) +{ + struct { + dmov_s cmd[7]; + unsigned cmdptr; + struct { + uint32_t bcfg; + uint32_t cmd; + uint32_t exec; + uint32_t status; + uint32_t addr0; + uint32_t addr1; + uint32_t addr2; + uint32_t addr3; + uint32_t addr4; + uint32_t addr5; + uint32_t addr6; + uint32_t data0; + uint32_t data1; + uint32_t data2; + uint32_t data3; + uint32_t data4; + uint32_t data5; + uint32_t data6; + } data; + } *dma_buffer; + dmov_s *cmd; + + int err = 0; + uint32_t initialsflashcmd = 0; + + initialsflashcmd = flash_rd_reg(chip, MSM_NAND_SFLASHC_CMD); + + if ((initialsflashcmd & 0x10) == 0x10) + nand_sfcmd_mode = MSM_NAND_SFCMD_ASYNC; + else + nand_sfcmd_mode = MSM_NAND_SFCMD_BURST; + + printk(KERN_INFO "SFLASHC Async Mode bit: %x \n", nand_sfcmd_mode); + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + cmd = dma_buffer->cmd; + + dma_buffer->data.bcfg = SFLASH_BCFG | + (nand_sfcmd_mode ? 0 : (1 << 24)); + dma_buffer->data.cmd = SFLASH_PREPCMD(7, 0, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGRD); + dma_buffer->data.exec = 1; + dma_buffer->data.status = CLEAN_DATA_32; + dma_buffer->data.addr0 = (ONENAND_DEVICE_ID << 16) | + (ONENAND_MANUFACTURER_ID); + dma_buffer->data.addr1 = (ONENAND_DATA_BUFFER_SIZE << 16) | + (ONENAND_VERSION_ID); + dma_buffer->data.addr2 = (ONENAND_AMOUNT_OF_BUFFERS << 16) | + (ONENAND_BOOT_BUFFER_SIZE); + dma_buffer->data.addr3 = (CLEAN_DATA_16 << 16) | + (ONENAND_TECHNOLOGY << 0); + dma_buffer->data.data0 = CLEAN_DATA_32; + dma_buffer->data.data1 = CLEAN_DATA_32; + dma_buffer->data.data2 = CLEAN_DATA_32; + dma_buffer->data.data3 = CLEAN_DATA_32; + + /* Enable and configure the SFlash controller */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.bcfg); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.cmd); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Configure the ADDR0 and ADDR1 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); + cmd->dst = MSM_NAND_ADDR0; + cmd->len = 8; + cmd++; + + /* Configure the ADDR2 and ADDR3 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); + cmd->dst = MSM_NAND_ADDR2; + cmd->len = 8; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.exec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the two status registers */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.status); + cmd->len = 4; + cmd++; + + /* Read data registers - valid only if status says success */ + cmd->cmd = 0; + cmd->src = MSM_NAND_GENP_REG0; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data0); + cmd->len = 16; + cmd++; + + BUILD_BUG_ON(7 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) + >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST + | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + /* Check for errors, protection violations etc */ + if (dma_buffer->data.status & 0x110) { + pr_info("%s: MPU/OP error" + "(0x%x) during Onenand probe\n", + __func__, dma_buffer->data.status); + err = -EIO; + } else { + + onenand_info.manufacturer_id = + (dma_buffer->data.data0 >> 0) & 0x0000FFFF; + onenand_info.device_id = + (dma_buffer->data.data0 >> 16) & 0x0000FFFF; + onenand_info.version_id = + (dma_buffer->data.data1 >> 0) & 0x0000FFFF; + onenand_info.data_buf_size = + (dma_buffer->data.data1 >> 16) & 0x0000FFFF; + onenand_info.boot_buf_size = + (dma_buffer->data.data2 >> 0) & 0x0000FFFF; + onenand_info.num_of_buffers = + (dma_buffer->data.data2 >> 16) & 0x0000FFFF; + onenand_info.technology = + (dma_buffer->data.data3 >> 0) & 0x0000FFFF; + + + pr_info("=======================================" + "==========================\n"); + + pr_info("%s: manufacturer_id = 0x%x\n" + , __func__, onenand_info.manufacturer_id); + pr_info("%s: device_id = 0x%x\n" + , __func__, onenand_info.device_id); + pr_info("%s: version_id = 0x%x\n" + , __func__, onenand_info.version_id); + pr_info("%s: data_buf_size = 0x%x\n" + , __func__, onenand_info.data_buf_size); + pr_info("%s: boot_buf_size = 0x%x\n" + , __func__, onenand_info.boot_buf_size); + pr_info("%s: num_of_buffers = 0x%x\n" + , __func__, onenand_info.num_of_buffers); + pr_info("%s: technology = 0x%x\n" + , __func__, onenand_info.technology); + + pr_info("=======================================" + "==========================\n"); + + if ((onenand_info.manufacturer_id != 0x00EC) + || ((onenand_info.device_id & 0x0040) != 0x0040) + || (onenand_info.data_buf_size != 0x0800) + || (onenand_info.boot_buf_size != 0x0200) + || (onenand_info.num_of_buffers != 0x0201) + || (onenand_info.technology != 0)) { + + pr_info("%s: Detected an unsupported device\n" + , __func__); + err = -EIO; + } + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + return err; +} + +int msm_onenand_read_oob(struct mtd_info *mtd, + loff_t from, struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[53]; + unsigned cmdptr; + struct { + uint32_t sfbcfg; + uint32_t sfcmd[9]; + uint32_t sfexec; + uint32_t sfstat[9]; + uint32_t addr0; + uint32_t addr1; + uint32_t addr2; + uint32_t addr3; + uint32_t addr4; + uint32_t addr5; + uint32_t addr6; + uint32_t data0; + uint32_t data1; + uint32_t data2; + uint32_t data3; + uint32_t data4; + uint32_t data5; + uint32_t data6; + uint32_t macro[5]; + } data; + } *dma_buffer; + dmov_s *cmd; + + int err = 0; + int i; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + + loff_t from_curr = 0; + unsigned page_count; + unsigned pages_read = 0; + + uint16_t onenand_startaddr1; + uint16_t onenand_startaddr8; + uint16_t onenand_startaddr2; + uint16_t onenand_startbuffer; + uint16_t onenand_sysconfig1; + uint16_t controller_status; + uint16_t interrupt_status; + uint16_t ecc_status; +#if VERBOSE + pr_info("=================================================" + "================\n"); + pr_info("%s: from 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x" + "\noobbuf 0x%p ooblen 0x%x\n", + __func__, from, ops->mode, ops->datbuf, ops->len, + ops->oobbuf, ops->ooblen); +#endif + if (!mtd) { + pr_err("%s: invalid mtd pointer, 0x%x\n", __func__, + (uint32_t)mtd); + return -EINVAL; + } + if (from & (mtd->writesize - 1)) { + pr_err("%s: unsupported from, 0x%llx\n", __func__, + from); + return -EINVAL; + } + + if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) && + (ops->mode != MTD_OPS_RAW)) { + pr_err("%s: unsupported ops->mode, %d\n", __func__, + ops->mode); + return -EINVAL; + } + + if (((ops->datbuf == NULL) || (ops->len == 0)) && + ((ops->oobbuf == NULL) || (ops->ooblen == 0))) { + pr_err("%s: incorrect ops fields - nothing to do\n", + __func__); + return -EINVAL; + } + + if ((ops->datbuf != NULL) && (ops->len == 0)) { + pr_err("%s: data buffer passed but length 0\n", + __func__); + return -EINVAL; + } + + if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) { + pr_err("%s: oob buffer passed but length 0\n", + __func__); + return -EINVAL; + } + + if (ops->mode != MTD_OPS_RAW) { + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + /* when ops->datbuf is NULL, ops->len can be ooblen */ + pr_err("%s: unsupported ops->len, %d\n", __func__, + ops->len); + return -EINVAL; + } + } else { + if (ops->datbuf != NULL && + (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len," + " %d for MTD_OPS_RAW\n", __func__, ops->len); + return -EINVAL; + } + } + + if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) { + pr_err("%s: unsupported operation, oobbuf pointer " + "passed in for RAW mode, %x\n", __func__, + (uint32_t)ops->oobbuf); + return -EINVAL; + } + + if (ops->oobbuf && !ops->datbuf) { + page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? + mtd->oobavail : mtd->oobsize); + if ((page_count == 0) && (ops->ooblen)) + page_count = 1; + } else if (ops->mode != MTD_OPS_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); + + if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) { + if (page_count * mtd->oobsize > ops->ooblen) { + pr_err("%s: unsupported ops->ooblen for " + "PLACE, %d\n", __func__, ops->ooblen); + return -EINVAL; + } + } + + if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) && + (ops->ooboffs != 0)) { + pr_err("%s: unsupported ops->ooboffs, %d\n", __func__, + ops->ooboffs); + return -EINVAL; + } + + if (ops->datbuf) { + memset(ops->datbuf, 0x55, ops->len); + data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, + ops->datbuf, ops->len, DMA_FROM_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("%s: failed to get dma addr for %p\n", + __func__, ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + memset(ops->oobbuf, 0x55, ops->ooblen); + oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, + ops->oobbuf, ops->ooblen, DMA_FROM_DEVICE); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("%s: failed to get dma addr for %p\n", + __func__, ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + from_curr = from; + + while (page_count-- > 0) { + + cmd = dma_buffer->cmd; + + if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) + && (from_curr >= (mtd->size>>1))) { /* DDP Device */ + onenand_startaddr1 = DEVICE_FLASHCORE_1 | + (((uint32_t)(from_curr-(mtd->size>>1)) + / mtd->erasesize)); + onenand_startaddr2 = DEVICE_BUFFERRAM_1; + } else { + onenand_startaddr1 = DEVICE_FLASHCORE_0 | + ((uint32_t)from_curr / mtd->erasesize) ; + onenand_startaddr2 = DEVICE_BUFFERRAM_0; + } + + onenand_startaddr8 = (((uint32_t)from_curr & + (mtd->erasesize - 1)) / mtd->writesize) << 2; + onenand_startbuffer = DATARAM0_0 << 8; + onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ? + ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) : + ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode); + + dma_buffer->data.sfbcfg = SFLASH_BCFG | + (nand_sfcmd_mode ? 0 : (1 << 24)); + dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_INTHI); + dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGRD); + dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATRD); + dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATRD); + dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATRD); + dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATRD); + dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(32, 0, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATRD); + dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(4, 10, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfexec = 1; + dma_buffer->data.sfstat[0] = CLEAN_DATA_32; + dma_buffer->data.sfstat[1] = CLEAN_DATA_32; + dma_buffer->data.sfstat[2] = CLEAN_DATA_32; + dma_buffer->data.sfstat[3] = CLEAN_DATA_32; + dma_buffer->data.sfstat[4] = CLEAN_DATA_32; + dma_buffer->data.sfstat[5] = CLEAN_DATA_32; + dma_buffer->data.sfstat[6] = CLEAN_DATA_32; + dma_buffer->data.sfstat[7] = CLEAN_DATA_32; + dma_buffer->data.sfstat[8] = CLEAN_DATA_32; + dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | + (ONENAND_START_ADDRESS_2); + dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | + (ONENAND_COMMAND); + dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | + (ONENAND_INTERRUPT_STATUS); + dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | + (onenand_sysconfig1); + dma_buffer->data.data1 = (onenand_startaddr8 << 16) | + (onenand_startaddr1); + dma_buffer->data.data2 = (onenand_startbuffer << 16) | + (onenand_startaddr2); + dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | + (ONENAND_CMDLOADSPARE); + dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | + (CLEAN_DATA_16); + dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | + (ONENAND_STARTADDR1_RES); + dma_buffer->data.macro[0] = 0x0200; + dma_buffer->data.macro[1] = 0x0300; + dma_buffer->data.macro[2] = 0x0400; + dma_buffer->data.macro[3] = 0x0500; + dma_buffer->data.macro[4] = 0x8010; + + /*************************************************************/ + /* Write necessary address registers in the onenand device */ + /*************************************************************/ + + /* Enable and configure the SFlash controller */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Write the ADDR0 and ADDR1 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); + cmd->dst = MSM_NAND_ADDR0; + cmd->len = 8; + cmd++; + + /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); + cmd->dst = MSM_NAND_ADDR2; + cmd->len = 16; + cmd++; + + /* Write the ADDR6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); + cmd->dst = MSM_NAND_ADDR6; + cmd->len = 4; + cmd++; + + /* Write the GENP0, GENP1, GENP2, GENP3 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); + cmd->dst = MSM_NAND_GENP_REG0; + cmd->len = 16; + cmd++; + + /* Write the FLASH_DEV_CMD4,5,6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->dst = MSM_NAND_DEV_CMD4; + cmd->len = 12; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Wait for the interrupt from the Onenand device controller */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Read necessary status registers from the onenand device */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); + cmd->len = 4; + cmd++; + + /* Read the GENP3 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_GENP_REG3; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); + cmd->len = 4; + cmd++; + + /* Read the DEVCMD4 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_DEV_CMD4; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Read the data ram area from the onenand buffer ram */ + /*************************************************************/ + + if (ops->datbuf) { + + dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | + (ONENAND_CMDLOAD); + + for (i = 0; i < 4; i++) { + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sfcmd[3+i]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Write the MACRO1 register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.macro[i]); + cmd->dst = MSM_NAND_MACRO1_REG; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data rdy, & read status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.sfstat[3+i]); + cmd->len = 4; + cmd++; + + /* Transfer nand ctlr buf contents to usr buf */ + cmd->cmd = 0; + cmd->src = MSM_NAND_FLASH_BUFFER; + cmd->dst = data_dma_addr_curr; + cmd->len = 512; + data_dma_addr_curr += 512; + cmd++; + } + } + + if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) { + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sfcmd[7]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Write the MACRO1 register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.macro[4]); + cmd->dst = MSM_NAND_MACRO1_REG; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.sfstat[7]); + cmd->len = 4; + cmd++; + + /* Transfer nand ctlr buffer contents into usr buf */ + if (ops->mode == MTD_OPS_AUTO_OOB) { + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) { + cmd->cmd = 0; + cmd->src = MSM_NAND_FLASH_BUFFER + + mtd->ecclayout->oobfree[i].offset; + cmd->dst = oob_dma_addr_curr; + cmd->len = + mtd->ecclayout->oobfree[i].length; + oob_dma_addr_curr += + mtd->ecclayout->oobfree[i].length; + cmd++; + } + } + if (ops->mode == MTD_OPS_PLACE_OOB) { + cmd->cmd = 0; + cmd->src = MSM_NAND_FLASH_BUFFER; + cmd->dst = oob_dma_addr_curr; + cmd->len = mtd->oobsize; + oob_dma_addr_curr += mtd->oobsize; + cmd++; + } + if (ops->mode == MTD_OPS_RAW) { + cmd->cmd = 0; + cmd->src = MSM_NAND_FLASH_BUFFER; + cmd->dst = data_dma_addr_curr; + cmd->len = mtd->oobsize; + data_dma_addr_curr += mtd->oobsize; + cmd++; + } + } + + /*************************************************************/ + /* Restore the necessary registers to proper values */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]); + cmd->len = 4; + cmd++; + + + BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) + >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + ecc_status = (dma_buffer->data.data3 >> 16) & + 0x0000FFFF; + interrupt_status = (dma_buffer->data.data4 >> 0) & + 0x0000FFFF; + controller_status = (dma_buffer->data.data4 >> 16) & + 0x0000FFFF; + +#if VERBOSE + pr_info("\n%s: sflash status %x %x %x %x %x %x %x" + "%x %x\n", __func__, + dma_buffer->data.sfstat[0], + dma_buffer->data.sfstat[1], + dma_buffer->data.sfstat[2], + dma_buffer->data.sfstat[3], + dma_buffer->data.sfstat[4], + dma_buffer->data.sfstat[5], + dma_buffer->data.sfstat[6], + dma_buffer->data.sfstat[7], + dma_buffer->data.sfstat[8]); + + pr_info("%s: controller_status = %x\n", __func__, + controller_status); + pr_info("%s: interrupt_status = %x\n", __func__, + interrupt_status); + pr_info("%s: ecc_status = %x\n", __func__, + ecc_status); +#endif + /* Check for errors, protection violations etc */ + if ((controller_status != 0) + || (dma_buffer->data.sfstat[0] & 0x110) + || (dma_buffer->data.sfstat[1] & 0x110) + || (dma_buffer->data.sfstat[2] & 0x110) + || (dma_buffer->data.sfstat[8] & 0x110) + || ((dma_buffer->data.sfstat[3] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[4] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[5] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[6] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[7] & 0x110) && + ((ops->oobbuf) + || (ops->mode == MTD_OPS_RAW)))) { + pr_info("%s: ECC/MPU/OP error\n", __func__); + err = -EIO; + } + + if (err) + break; + pages_read++; + from_curr += mtd->writesize; + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (ops->oobbuf) { + dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen, + DMA_FROM_DEVICE); + } +err_dma_map_oobbuf_failed: + if (ops->datbuf) { + dma_unmap_page(chip->dev, data_dma_addr, ops->len, + DMA_FROM_DEVICE); + } + + if (err) { + pr_err("%s: %llx %x %x failed\n", __func__, from_curr, + ops->datbuf ? ops->len : 0, ops->ooblen); + } else { + ops->retlen = ops->oobretlen = 0; + if (ops->datbuf != NULL) { + if (ops->mode != MTD_OPS_RAW) + ops->retlen = mtd->writesize * pages_read; + else + ops->retlen = (mtd->writesize + mtd->oobsize) + * pages_read; + } + if (ops->oobbuf != NULL) { + if (ops->mode == MTD_OPS_AUTO_OOB) + ops->oobretlen = mtd->oobavail * pages_read; + else + ops->oobretlen = mtd->oobsize * pages_read; + } + } + +#if VERBOSE + pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", + __func__, err, ops->retlen, ops->oobretlen); + + pr_info("===================================================" + "==============\n"); +#endif + return err; +} + +int msm_onenand_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + int ret; + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.datbuf = buf; + ops.len = len; + ops.retlen = 0; + ops.oobbuf = NULL; + ops.ooblen = 0; + ops.oobretlen = 0; + ret = msm_onenand_read_oob(mtd, from, &ops); + *retlen = ops.retlen; + + return ret; +} + +static int msm_onenand_write_oob(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[53]; + unsigned cmdptr; + struct { + uint32_t sfbcfg; + uint32_t sfcmd[10]; + uint32_t sfexec; + uint32_t sfstat[10]; + uint32_t addr0; + uint32_t addr1; + uint32_t addr2; + uint32_t addr3; + uint32_t addr4; + uint32_t addr5; + uint32_t addr6; + uint32_t data0; + uint32_t data1; + uint32_t data2; + uint32_t data3; + uint32_t data4; + uint32_t data5; + uint32_t data6; + uint32_t macro[5]; + } data; + } *dma_buffer; + dmov_s *cmd; + + int err = 0; + int i, j, k; + dma_addr_t data_dma_addr = 0; + dma_addr_t oob_dma_addr = 0; + dma_addr_t init_dma_addr = 0; + dma_addr_t data_dma_addr_curr = 0; + dma_addr_t oob_dma_addr_curr = 0; + uint8_t *init_spare_bytes; + + loff_t to_curr = 0; + unsigned page_count; + unsigned pages_written = 0; + + uint16_t onenand_startaddr1; + uint16_t onenand_startaddr8; + uint16_t onenand_startaddr2; + uint16_t onenand_startbuffer; + uint16_t onenand_sysconfig1; + + uint16_t controller_status; + uint16_t interrupt_status; + uint16_t ecc_status; + +#if VERBOSE + pr_info("=================================================" + "================\n"); + pr_info("%s: to 0x%llx mode %d \ndatbuf 0x%p datlen 0x%x" + "\noobbuf 0x%p ooblen 0x%x\n", + __func__, to, ops->mode, ops->datbuf, ops->len, + ops->oobbuf, ops->ooblen); +#endif + if (!mtd) { + pr_err("%s: invalid mtd pointer, 0x%x\n", __func__, + (uint32_t)mtd); + return -EINVAL; + } + if (to & (mtd->writesize - 1)) { + pr_err("%s: unsupported to, 0x%llx\n", __func__, to); + return -EINVAL; + } + + if ((ops->mode != MTD_OPS_PLACE_OOB) && (ops->mode != MTD_OPS_AUTO_OOB) && + (ops->mode != MTD_OPS_RAW)) { + pr_err("%s: unsupported ops->mode, %d\n", __func__, + ops->mode); + return -EINVAL; + } + + if (((ops->datbuf == NULL) || (ops->len == 0)) && + ((ops->oobbuf == NULL) || (ops->ooblen == 0))) { + pr_err("%s: incorrect ops fields - nothing to do\n", + __func__); + return -EINVAL; + } + + if ((ops->datbuf != NULL) && (ops->len == 0)) { + pr_err("%s: data buffer passed but length 0\n", + __func__); + return -EINVAL; + } + + if ((ops->oobbuf != NULL) && (ops->ooblen == 0)) { + pr_err("%s: oob buffer passed but length 0\n", + __func__); + return -EINVAL; + } + + if (ops->mode != MTD_OPS_RAW) { + if (ops->datbuf != NULL && (ops->len % mtd->writesize) != 0) { + /* when ops->datbuf is NULL, ops->len can be ooblen */ + pr_err("%s: unsupported ops->len, %d\n", __func__, + ops->len); + return -EINVAL; + } + } else { + if (ops->datbuf != NULL && + (ops->len % (mtd->writesize + mtd->oobsize)) != 0) { + pr_err("%s: unsupported ops->len," + " %d for MTD_OPS_RAW\n", __func__, ops->len); + return -EINVAL; + } + } + + if ((ops->mode == MTD_OPS_RAW) && (ops->oobbuf)) { + pr_err("%s: unsupported operation, oobbuf pointer " + "passed in for RAW mode, %x\n", __func__, + (uint32_t)ops->oobbuf); + return -EINVAL; + } + + if (ops->oobbuf && !ops->datbuf) { + page_count = ops->ooblen / ((ops->mode == MTD_OPS_AUTO_OOB) ? + mtd->oobavail : mtd->oobsize); + if ((page_count == 0) && (ops->ooblen)) + page_count = 1; + } else if (ops->mode != MTD_OPS_RAW) + page_count = ops->len / mtd->writesize; + else + page_count = ops->len / (mtd->writesize + mtd->oobsize); + + if ((ops->mode == MTD_OPS_AUTO_OOB) && (ops->oobbuf != NULL)) { + if (page_count > 1) { + pr_err("%s: unsupported ops->ooblen for" + "AUTO, %d\n", __func__, ops->ooblen); + return -EINVAL; + } + } + + if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->oobbuf != NULL)) { + if (page_count * mtd->oobsize > ops->ooblen) { + pr_err("%s: unsupported ops->ooblen for" + "PLACE, %d\n", __func__, ops->ooblen); + return -EINVAL; + } + } + + if ((ops->mode == MTD_OPS_PLACE_OOB) && (ops->ooblen != 0) && + (ops->ooboffs != 0)) { + pr_err("%s: unsupported ops->ooboffs, %d\n", + __func__, ops->ooboffs); + return -EINVAL; + } + + init_spare_bytes = kmalloc(64, GFP_KERNEL); + if (!init_spare_bytes) { + pr_err("%s: failed to alloc init_spare_bytes buffer\n", + __func__); + return -ENOMEM; + } + for (i = 0; i < 64; i++) + init_spare_bytes[i] = 0xFF; + + if ((ops->oobbuf) && (ops->mode == MTD_OPS_AUTO_OOB)) { + for (i = 0, k = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) + for (j = 0; j < mtd->ecclayout->oobfree[i].length; + j++) { + init_spare_bytes[j + + mtd->ecclayout->oobfree[i].offset] + = (ops->oobbuf)[k]; + k++; + } + } + + if (ops->datbuf) { + data_dma_addr_curr = data_dma_addr = msm_nand_dma_map(chip->dev, + ops->datbuf, ops->len, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, data_dma_addr)) { + pr_err("%s: failed to get dma addr for %p\n", + __func__, ops->datbuf); + return -EIO; + } + } + if (ops->oobbuf) { + oob_dma_addr_curr = oob_dma_addr = msm_nand_dma_map(chip->dev, + ops->oobbuf, ops->ooblen, DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, oob_dma_addr)) { + pr_err("%s: failed to get dma addr for %p\n", + __func__, ops->oobbuf); + err = -EIO; + goto err_dma_map_oobbuf_failed; + } + } + + init_dma_addr = msm_nand_dma_map(chip->dev, init_spare_bytes, 64, + DMA_TO_DEVICE); + if (dma_mapping_error(chip->dev, init_dma_addr)) { + pr_err("%s: failed to get dma addr for %p\n", + __func__, init_spare_bytes); + err = -EIO; + goto err_dma_map_initbuf_failed; + } + + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + to_curr = to; + + while (page_count-- > 0) { + cmd = dma_buffer->cmd; + + if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) + && (to_curr >= (mtd->size>>1))) { /* DDP Device */ + onenand_startaddr1 = DEVICE_FLASHCORE_1 | + (((uint32_t)(to_curr-(mtd->size>>1)) + / mtd->erasesize)); + onenand_startaddr2 = DEVICE_BUFFERRAM_1; + } else { + onenand_startaddr1 = DEVICE_FLASHCORE_0 | + ((uint32_t)to_curr / mtd->erasesize) ; + onenand_startaddr2 = DEVICE_BUFFERRAM_0; + } + + onenand_startaddr8 = (((uint32_t)to_curr & + (mtd->erasesize - 1)) / mtd->writesize) << 2; + onenand_startbuffer = DATARAM0_0 << 8; + onenand_sysconfig1 = (ops->mode == MTD_OPS_RAW) ? + ONENAND_SYSCFG1_ECCDIS(nand_sfcmd_mode) : + ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode); + + dma_buffer->data.sfbcfg = SFLASH_BCFG | + (nand_sfcmd_mode ? 0 : (1 << 24)); + dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(6, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATWR); + dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATWR); + dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATWR); + dma_buffer->data.sfcmd[4] = SFLASH_PREPCMD(256, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATWR); + dma_buffer->data.sfcmd[5] = SFLASH_PREPCMD(32, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_DATWR); + dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(1, 6, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(0, 0, 32, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_INTHI); + dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(3, 7, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGRD); + dma_buffer->data.sfcmd[9] = SFLASH_PREPCMD(4, 10, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfexec = 1; + dma_buffer->data.sfstat[0] = CLEAN_DATA_32; + dma_buffer->data.sfstat[1] = CLEAN_DATA_32; + dma_buffer->data.sfstat[2] = CLEAN_DATA_32; + dma_buffer->data.sfstat[3] = CLEAN_DATA_32; + dma_buffer->data.sfstat[4] = CLEAN_DATA_32; + dma_buffer->data.sfstat[5] = CLEAN_DATA_32; + dma_buffer->data.sfstat[6] = CLEAN_DATA_32; + dma_buffer->data.sfstat[7] = CLEAN_DATA_32; + dma_buffer->data.sfstat[8] = CLEAN_DATA_32; + dma_buffer->data.sfstat[9] = CLEAN_DATA_32; + dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | + (ONENAND_START_ADDRESS_2); + dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | + (ONENAND_COMMAND); + dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | + (ONENAND_INTERRUPT_STATUS); + dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | + (onenand_sysconfig1); + dma_buffer->data.data1 = (onenand_startaddr8 << 16) | + (onenand_startaddr1); + dma_buffer->data.data2 = (onenand_startbuffer << 16) | + (onenand_startaddr2); + dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | + (ONENAND_CMDPROGSPARE); + dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | + (CLEAN_DATA_16); + dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | + (ONENAND_STARTADDR1_RES); + dma_buffer->data.macro[0] = 0x0200; + dma_buffer->data.macro[1] = 0x0300; + dma_buffer->data.macro[2] = 0x0400; + dma_buffer->data.macro[3] = 0x0500; + dma_buffer->data.macro[4] = 0x8010; + + + /*************************************************************/ + /* Write necessary address registers in the onenand device */ + /*************************************************************/ + + /* Enable and configure the SFlash controller */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Write the ADDR0 and ADDR1 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); + cmd->dst = MSM_NAND_ADDR0; + cmd->len = 8; + cmd++; + + /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); + cmd->dst = MSM_NAND_ADDR2; + cmd->len = 16; + cmd++; + + /* Write the ADDR6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); + cmd->dst = MSM_NAND_ADDR6; + cmd->len = 4; + cmd++; + + /* Write the GENP0, GENP1, GENP2, GENP3 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); + cmd->dst = MSM_NAND_GENP_REG0; + cmd->len = 16; + cmd++; + + /* Write the FLASH_DEV_CMD4,5,6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->dst = MSM_NAND_DEV_CMD4; + cmd->len = 12; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Write the data ram area in the onenand buffer ram */ + /*************************************************************/ + + if (ops->datbuf) { + dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | + (ONENAND_CMDPROG); + + for (i = 0; i < 4; i++) { + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sfcmd[1+i]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Trnsfr usr buf contents to nand ctlr buf */ + cmd->cmd = 0; + cmd->src = data_dma_addr_curr; + cmd->dst = MSM_NAND_FLASH_BUFFER; + cmd->len = 512; + data_dma_addr_curr += 512; + cmd++; + + /* Write the MACRO1 register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.macro[i]); + cmd->dst = MSM_NAND_MACRO1_REG; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, + &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data rdy, & read status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, + &dma_buffer->data.sfstat[1+i]); + cmd->len = 4; + cmd++; + + } + } + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[5]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + if ((ops->oobbuf) || (ops->mode == MTD_OPS_RAW)) { + + /* Transfer user buf contents into nand ctlr buffer */ + if (ops->mode == MTD_OPS_AUTO_OOB) { + cmd->cmd = 0; + cmd->src = init_dma_addr; + cmd->dst = MSM_NAND_FLASH_BUFFER; + cmd->len = mtd->oobsize; + cmd++; + } + if (ops->mode == MTD_OPS_PLACE_OOB) { + cmd->cmd = 0; + cmd->src = oob_dma_addr_curr; + cmd->dst = MSM_NAND_FLASH_BUFFER; + cmd->len = mtd->oobsize; + oob_dma_addr_curr += mtd->oobsize; + cmd++; + } + if (ops->mode == MTD_OPS_RAW) { + cmd->cmd = 0; + cmd->src = data_dma_addr_curr; + cmd->dst = MSM_NAND_FLASH_BUFFER; + cmd->len = mtd->oobsize; + data_dma_addr_curr += mtd->oobsize; + cmd++; + } + } else { + cmd->cmd = 0; + cmd->src = init_dma_addr; + cmd->dst = MSM_NAND_FLASH_BUFFER; + cmd->len = mtd->oobsize; + cmd++; + } + + /* Write the MACRO1 register */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.macro[4]); + cmd->dst = MSM_NAND_MACRO1_REG; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[5]); + cmd->len = 4; + cmd++; + + /*********************************************************/ + /* Issuing write command */ + /*********************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[6]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[6]); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Wait for the interrupt from the Onenand device controller */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[7]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[7]); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Read necessary status registers from the onenand device */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[8]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[8]); + cmd->len = 4; + cmd++; + + /* Read the GENP3 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_GENP_REG3; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); + cmd->len = 4; + cmd++; + + /* Read the DEVCMD4 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_DEV_CMD4; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Restore the necessary registers to proper values */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[9]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[9]); + cmd->len = 4; + cmd++; + + + BUILD_BUG_ON(53 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) + >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; + interrupt_status = (dma_buffer->data.data4 >> 0)&0x0000FFFF; + controller_status = (dma_buffer->data.data4 >> 16)&0x0000FFFF; + +#if VERBOSE + pr_info("\n%s: sflash status %x %x %x %x %x %x %x" + " %x %x %x\n", __func__, + dma_buffer->data.sfstat[0], + dma_buffer->data.sfstat[1], + dma_buffer->data.sfstat[2], + dma_buffer->data.sfstat[3], + dma_buffer->data.sfstat[4], + dma_buffer->data.sfstat[5], + dma_buffer->data.sfstat[6], + dma_buffer->data.sfstat[7], + dma_buffer->data.sfstat[8], + dma_buffer->data.sfstat[9]); + + pr_info("%s: controller_status = %x\n", __func__, + controller_status); + pr_info("%s: interrupt_status = %x\n", __func__, + interrupt_status); + pr_info("%s: ecc_status = %x\n", __func__, + ecc_status); +#endif + /* Check for errors, protection violations etc */ + if ((controller_status != 0) + || (dma_buffer->data.sfstat[0] & 0x110) + || (dma_buffer->data.sfstat[6] & 0x110) + || (dma_buffer->data.sfstat[7] & 0x110) + || (dma_buffer->data.sfstat[8] & 0x110) + || (dma_buffer->data.sfstat[9] & 0x110) + || ((dma_buffer->data.sfstat[1] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[2] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[3] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[4] & 0x110) && + (ops->datbuf)) + || ((dma_buffer->data.sfstat[5] & 0x110) && + ((ops->oobbuf) + || (ops->mode == MTD_OPS_RAW)))) { + pr_info("%s: ECC/MPU/OP error\n", __func__); + err = -EIO; + } + + if (err) + break; + pages_written++; + to_curr += mtd->writesize; + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + dma_unmap_page(chip->dev, init_dma_addr, 64, DMA_TO_DEVICE); + +err_dma_map_initbuf_failed: + if (ops->oobbuf) { + dma_unmap_page(chip->dev, oob_dma_addr, ops->ooblen, + DMA_TO_DEVICE); + } +err_dma_map_oobbuf_failed: + if (ops->datbuf) { + dma_unmap_page(chip->dev, data_dma_addr, ops->len, + DMA_TO_DEVICE); + } + + if (err) { + pr_err("%s: %llx %x %x failed\n", __func__, to_curr, + ops->datbuf ? ops->len : 0, ops->ooblen); + } else { + ops->retlen = ops->oobretlen = 0; + if (ops->datbuf != NULL) { + if (ops->mode != MTD_OPS_RAW) + ops->retlen = mtd->writesize * pages_written; + else + ops->retlen = (mtd->writesize + mtd->oobsize) + * pages_written; + } + if (ops->oobbuf != NULL) { + if (ops->mode == MTD_OPS_AUTO_OOB) + ops->oobretlen = mtd->oobavail * pages_written; + else + ops->oobretlen = mtd->oobsize * pages_written; + } + } + +#if VERBOSE + pr_info("\n%s: ret %d, retlen %d oobretlen %d\n", + __func__, err, ops->retlen, ops->oobretlen); + + pr_info("=================================================" + "================\n"); +#endif + kfree(init_spare_bytes); + return err; +} + +static int msm_onenand_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int ret; + struct mtd_oob_ops ops; + + ops.mode = MTD_OPS_PLACE_OOB; + ops.datbuf = (uint8_t *)buf; + ops.len = len; + ops.retlen = 0; + ops.oobbuf = NULL; + ops.ooblen = 0; + ops.oobretlen = 0; + ret = msm_onenand_write_oob(mtd, to, &ops); + *retlen = ops.retlen; + + return ret; +} + +static int msm_onenand_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[20]; + unsigned cmdptr; + struct { + uint32_t sfbcfg; + uint32_t sfcmd[4]; + uint32_t sfexec; + uint32_t sfstat[4]; + uint32_t addr0; + uint32_t addr1; + uint32_t addr2; + uint32_t addr3; + uint32_t addr4; + uint32_t addr5; + uint32_t addr6; + uint32_t data0; + uint32_t data1; + uint32_t data2; + uint32_t data3; + uint32_t data4; + uint32_t data5; + uint32_t data6; + } data; + } *dma_buffer; + dmov_s *cmd; + + int err = 0; + + uint16_t onenand_startaddr1; + uint16_t onenand_startaddr8; + uint16_t onenand_startaddr2; + uint16_t onenand_startbuffer; + + uint16_t controller_status; + uint16_t interrupt_status; + uint16_t ecc_status; + + uint64_t temp; + +#if VERBOSE + pr_info("=================================================" + "================\n"); + pr_info("%s: addr 0x%llx len 0x%llx\n", + __func__, instr->addr, instr->len); +#endif + if (instr->addr & (mtd->erasesize - 1)) { + pr_err("%s: Unsupported erase address, 0x%llx\n", + __func__, instr->addr); + return -EINVAL; + } + if (instr->len != mtd->erasesize) { + pr_err("%s: Unsupported erase len, %lld\n", + __func__, instr->len); + return -EINVAL; + } + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + cmd = dma_buffer->cmd; + + temp = instr->addr; + + if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) + && (temp >= (mtd->size>>1))) { /* DDP Device */ + onenand_startaddr1 = DEVICE_FLASHCORE_1 | + (((uint32_t)(temp-(mtd->size>>1)) + / mtd->erasesize)); + onenand_startaddr2 = DEVICE_BUFFERRAM_1; + } else { + onenand_startaddr1 = DEVICE_FLASHCORE_0 | + ((uint32_t)temp / mtd->erasesize) ; + onenand_startaddr2 = DEVICE_BUFFERRAM_0; + } + + onenand_startaddr8 = 0x0000; + onenand_startbuffer = DATARAM0_0 << 8; + + dma_buffer->data.sfbcfg = SFLASH_BCFG | + (nand_sfcmd_mode ? 0 : (1 << 24)); + dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_INTHI); + dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGRD); + dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfexec = 1; + dma_buffer->data.sfstat[0] = CLEAN_DATA_32; + dma_buffer->data.sfstat[1] = CLEAN_DATA_32; + dma_buffer->data.sfstat[2] = CLEAN_DATA_32; + dma_buffer->data.sfstat[3] = CLEAN_DATA_32; + dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.addr2 = (ONENAND_START_BUFFER << 16) | + (ONENAND_START_ADDRESS_2); + dma_buffer->data.addr3 = (ONENAND_ECC_STATUS << 16) | + (ONENAND_COMMAND); + dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | + (ONENAND_INTERRUPT_STATUS); + dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data1 = (onenand_startaddr8 << 16) | + (onenand_startaddr1); + dma_buffer->data.data2 = (onenand_startbuffer << 16) | + (onenand_startaddr2); + dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | + (ONENAND_CMDERAS); + dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | + (CLEAN_DATA_16); + dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | + (ONENAND_STARTADDR1_RES); + + /***************************************************************/ + /* Write the necessary address registers in the onenand device */ + /***************************************************************/ + + /* Enable and configure the SFlash controller */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Write the ADDR0 and ADDR1 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); + cmd->dst = MSM_NAND_ADDR0; + cmd->len = 8; + cmd++; + + /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); + cmd->dst = MSM_NAND_ADDR2; + cmd->len = 16; + cmd++; + + /* Write the ADDR6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); + cmd->dst = MSM_NAND_ADDR6; + cmd->len = 4; + cmd++; + + /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); + cmd->dst = MSM_NAND_GENP_REG0; + cmd->len = 16; + cmd++; + + /* Write the FLASH_DEV_CMD4,5,6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->dst = MSM_NAND_DEV_CMD4; + cmd->len = 12; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); + cmd->len = 4; + cmd++; + + /***************************************************************/ + /* Wait for the interrupt from the Onenand device controller */ + /***************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); + cmd->len = 4; + cmd++; + + /***************************************************************/ + /* Read the necessary status registers from the onenand device */ + /***************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); + cmd->len = 4; + cmd++; + + /* Read the GENP3 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_GENP_REG3; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); + cmd->len = 4; + cmd++; + + /* Read the DEVCMD4 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_DEV_CMD4; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->len = 4; + cmd++; + + /***************************************************************/ + /* Restore the necessary registers to proper values */ + /***************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); + cmd->len = 4; + cmd++; + + + BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) + >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST + | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + ecc_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; + interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; + controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; + +#if VERBOSE + pr_info("\n%s: sflash status %x %x %x %x\n", __func__, + dma_buffer->data.sfstat[0], + dma_buffer->data.sfstat[1], + dma_buffer->data.sfstat[2], + dma_buffer->data.sfstat[3]); + + pr_info("%s: controller_status = %x\n", __func__, + controller_status); + pr_info("%s: interrupt_status = %x\n", __func__, + interrupt_status); + pr_info("%s: ecc_status = %x\n", __func__, + ecc_status); +#endif + /* Check for errors, protection violations etc */ + if ((controller_status != 0) + || (dma_buffer->data.sfstat[0] & 0x110) + || (dma_buffer->data.sfstat[1] & 0x110) + || (dma_buffer->data.sfstat[2] & 0x110) + || (dma_buffer->data.sfstat[3] & 0x110)) { + pr_err("%s: ECC/MPU/OP error\n", __func__); + err = -EIO; + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + + if (err) { + pr_err("%s: Erase failed, 0x%llx\n", __func__, + instr->addr); + instr->fail_addr = instr->addr; + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + instr->fail_addr = 0xffffffff; + mtd_erase_callback(instr); + } + +#if VERBOSE + pr_info("\n%s: ret %d\n", __func__, err); + pr_info("====================================================" + "=============\n"); +#endif + return err; +} + +static int msm_onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + int rval, i; + int ret = 0; + uint8_t *buffer; + uint8_t *oobptr; + + if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; + } + + buffer = kmalloc(2112, GFP_KERNEL|GFP_DMA); + if (buffer == 0) { + pr_err("%s: Could not kmalloc for buffer\n", + __func__); + return -ENOMEM; + } + + memset(buffer, 0x00, 2112); + oobptr = &(buffer[2048]); + + ops.mode = MTD_OPS_RAW; + ops.len = 2112; + ops.retlen = 0; + ops.ooblen = 0; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = buffer; + ops.oobbuf = NULL; + + for (i = 0; i < 2; i++) { + ofs = ofs + i*mtd->writesize; + rval = msm_onenand_read_oob(mtd, ofs, &ops); + if (rval) { + pr_err("%s: Error in reading bad blk info\n", + __func__); + ret = rval; + break; + } + if ((oobptr[0] != 0xFF) || (oobptr[1] != 0xFF) || + (oobptr[16] != 0xFF) || (oobptr[17] != 0xFF) || + (oobptr[32] != 0xFF) || (oobptr[33] != 0xFF) || + (oobptr[48] != 0xFF) || (oobptr[49] != 0xFF) + ) { + ret = 1; + break; + } + } + + kfree(buffer); + +#if VERBOSE + if (ret == 1) + pr_info("%s : Block containing 0x%x is bad\n", + __func__, (unsigned int)ofs); +#endif + return ret; +} + +static int msm_onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + int rval, i; + int ret = 0; + uint8_t *buffer; + + if ((ofs > mtd->size) || (ofs & (mtd->erasesize - 1))) { + pr_err("%s: unsupported block address, 0x%x\n", + __func__, (uint32_t)ofs); + return -EINVAL; + } + + buffer = page_address(ZERO_PAGE()); + + ops.mode = MTD_OPS_RAW; + ops.len = 2112; + ops.retlen = 0; + ops.ooblen = 0; + ops.oobretlen = 0; + ops.ooboffs = 0; + ops.datbuf = buffer; + ops.oobbuf = NULL; + + for (i = 0; i < 2; i++) { + ofs = ofs + i*mtd->writesize; + rval = msm_onenand_write_oob(mtd, ofs, &ops); + if (rval) { + pr_err("%s: Error in writing bad blk info\n", + __func__); + ret = rval; + break; + } + } + + return ret; +} + +static int msm_onenand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[20]; + unsigned cmdptr; + struct { + uint32_t sfbcfg; + uint32_t sfcmd[4]; + uint32_t sfexec; + uint32_t sfstat[4]; + uint32_t addr0; + uint32_t addr1; + uint32_t addr2; + uint32_t addr3; + uint32_t addr4; + uint32_t addr5; + uint32_t addr6; + uint32_t data0; + uint32_t data1; + uint32_t data2; + uint32_t data3; + uint32_t data4; + uint32_t data5; + uint32_t data6; + } data; + } *dma_buffer; + dmov_s *cmd; + + int err = 0; + + uint16_t onenand_startaddr1; + uint16_t onenand_startaddr8; + uint16_t onenand_startaddr2; + uint16_t onenand_startblock; + + uint16_t controller_status; + uint16_t interrupt_status; + uint16_t write_prot_status; + + uint64_t start_ofs; + +#if VERBOSE + pr_info("====================================================" + "=============\n"); + pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); +#endif + /* 'ofs' & 'len' should align to block size */ + if (ofs&(mtd->erasesize - 1)) { + pr_err("%s: Unsupported ofs address, 0x%llx\n", + __func__, ofs); + return -EINVAL; + } + + if (len&(mtd->erasesize - 1)) { + pr_err("%s: Unsupported len, %lld\n", + __func__, len); + return -EINVAL; + } + + if (ofs+len > mtd->size) { + pr_err("%s: Maximum chip size exceeded\n", __func__); + return -EINVAL; + } + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) { +#if VERBOSE + pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); +#endif + + cmd = dma_buffer->cmd; + if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) + && (ofs >= (mtd->size>>1))) { /* DDP Device */ + onenand_startaddr1 = DEVICE_FLASHCORE_1 | + (((uint32_t)(ofs - (mtd->size>>1)) + / mtd->erasesize)); + onenand_startaddr2 = DEVICE_BUFFERRAM_1; + onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1)) + / mtd->erasesize); + } else { + onenand_startaddr1 = DEVICE_FLASHCORE_0 | + ((uint32_t)ofs / mtd->erasesize) ; + onenand_startaddr2 = DEVICE_BUFFERRAM_0; + onenand_startblock = ((uint32_t)ofs + / mtd->erasesize); + } + + onenand_startaddr8 = 0x0000; + dma_buffer->data.sfbcfg = SFLASH_BCFG | + (nand_sfcmd_mode ? 0 : (1 << 24)); + dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_INTHI); + dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGRD); + dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfexec = 1; + dma_buffer->data.sfstat[0] = CLEAN_DATA_32; + dma_buffer->data.sfstat[1] = CLEAN_DATA_32; + dma_buffer->data.sfstat[2] = CLEAN_DATA_32; + dma_buffer->data.sfstat[3] = CLEAN_DATA_32; + dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) | + (ONENAND_START_ADDRESS_2); + dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) | + (ONENAND_COMMAND); + dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | + (ONENAND_INTERRUPT_STATUS); + dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data1 = (onenand_startaddr8 << 16) | + (onenand_startaddr1); + dma_buffer->data.data2 = (onenand_startblock << 16) | + (onenand_startaddr2); + dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | + (ONENAND_CMD_UNLOCK); + dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | + (CLEAN_DATA_16); + dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | + (ONENAND_STARTADDR1_RES); + + /*************************************************************/ + /* Write the necessary address reg in the onenand device */ + /*************************************************************/ + + /* Enable and configure the SFlash controller */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Write the ADDR0 and ADDR1 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); + cmd->dst = MSM_NAND_ADDR0; + cmd->len = 8; + cmd++; + + /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); + cmd->dst = MSM_NAND_ADDR2; + cmd->len = 16; + cmd++; + + /* Write the ADDR6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); + cmd->dst = MSM_NAND_ADDR6; + cmd->len = 4; + cmd++; + + /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); + cmd->dst = MSM_NAND_GENP_REG0; + cmd->len = 16; + cmd++; + + /* Write the FLASH_DEV_CMD4,5,6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->dst = MSM_NAND_DEV_CMD4; + cmd->len = 12; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Wait for the interrupt from the Onenand device controller */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); + cmd->len = 4; + cmd++; + + /*********************************************************/ + /* Read the necessary status reg from the onenand device */ + /*********************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); + cmd->len = 4; + cmd++; + + /* Read the GENP3 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_GENP_REG3; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); + cmd->len = 4; + cmd++; + + /* Read the DEVCMD4 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_DEV_CMD4; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->len = 4; + cmd++; + + /************************************************************/ + /* Restore the necessary registers to proper values */ + /************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); + cmd->len = 4; + cmd++; + + + BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) + >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; + interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; + controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; + +#if VERBOSE + pr_info("\n%s: sflash status %x %x %x %x\n", __func__, + dma_buffer->data.sfstat[0], + dma_buffer->data.sfstat[1], + dma_buffer->data.sfstat[2], + dma_buffer->data.sfstat[3]); + + pr_info("%s: controller_status = %x\n", __func__, + controller_status); + pr_info("%s: interrupt_status = %x\n", __func__, + interrupt_status); + pr_info("%s: write_prot_status = %x\n", __func__, + write_prot_status); +#endif + /* Check for errors, protection violations etc */ + if ((controller_status != 0) + || (dma_buffer->data.sfstat[0] & 0x110) + || (dma_buffer->data.sfstat[1] & 0x110) + || (dma_buffer->data.sfstat[2] & 0x110) + || (dma_buffer->data.sfstat[3] & 0x110)) { + pr_err("%s: ECC/MPU/OP error\n", __func__); + err = -EIO; + } + + if (!(write_prot_status & ONENAND_WP_US)) { + pr_err("%s: Unexpected status ofs = 0x%llx," + "wp_status = %x\n", + __func__, ofs, write_prot_status); + err = -EIO; + } + + if (err) + break; + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + +#if VERBOSE + pr_info("\n%s: ret %d\n", __func__, err); + pr_info("====================================================" + "=============\n"); +#endif + return err; +} + +static int msm_onenand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[20]; + unsigned cmdptr; + struct { + uint32_t sfbcfg; + uint32_t sfcmd[4]; + uint32_t sfexec; + uint32_t sfstat[4]; + uint32_t addr0; + uint32_t addr1; + uint32_t addr2; + uint32_t addr3; + uint32_t addr4; + uint32_t addr5; + uint32_t addr6; + uint32_t data0; + uint32_t data1; + uint32_t data2; + uint32_t data3; + uint32_t data4; + uint32_t data5; + uint32_t data6; + } data; + } *dma_buffer; + dmov_s *cmd; + + int err = 0; + + uint16_t onenand_startaddr1; + uint16_t onenand_startaddr8; + uint16_t onenand_startaddr2; + uint16_t onenand_startblock; + + uint16_t controller_status; + uint16_t interrupt_status; + uint16_t write_prot_status; + + uint64_t start_ofs; + +#if VERBOSE + pr_info("====================================================" + "=============\n"); + pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); +#endif + /* 'ofs' & 'len' should align to block size */ + if (ofs&(mtd->erasesize - 1)) { + pr_err("%s: Unsupported ofs address, 0x%llx\n", + __func__, ofs); + return -EINVAL; + } + + if (len&(mtd->erasesize - 1)) { + pr_err("%s: Unsupported len, %lld\n", + __func__, len); + return -EINVAL; + } + + if (ofs+len > mtd->size) { + pr_err("%s: Maximum chip size exceeded\n", __func__); + return -EINVAL; + } + + wait_event(chip->wait_queue, (dma_buffer = msm_nand_get_dma_buffer + (chip, sizeof(*dma_buffer)))); + + for (start_ofs = ofs; ofs < start_ofs+len; ofs = ofs+mtd->erasesize) { +#if VERBOSE + pr_info("%s: ofs 0x%llx len %lld\n", __func__, ofs, len); +#endif + + cmd = dma_buffer->cmd; + if ((onenand_info.device_id & ONENAND_DEVICE_IS_DDP) + && (ofs >= (mtd->size>>1))) { /* DDP Device */ + onenand_startaddr1 = DEVICE_FLASHCORE_1 | + (((uint32_t)(ofs - (mtd->size>>1)) + / mtd->erasesize)); + onenand_startaddr2 = DEVICE_BUFFERRAM_1; + onenand_startblock = ((uint32_t)(ofs - (mtd->size>>1)) + / mtd->erasesize); + } else { + onenand_startaddr1 = DEVICE_FLASHCORE_0 | + ((uint32_t)ofs / mtd->erasesize) ; + onenand_startaddr2 = DEVICE_BUFFERRAM_0; + onenand_startblock = ((uint32_t)ofs + / mtd->erasesize); + } + + onenand_startaddr8 = 0x0000; + dma_buffer->data.sfbcfg = SFLASH_BCFG | + (nand_sfcmd_mode ? 0 : (1 << 24)); + dma_buffer->data.sfcmd[0] = SFLASH_PREPCMD(7, 0, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfcmd[1] = SFLASH_PREPCMD(0, 0, 32, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_INTHI); + dma_buffer->data.sfcmd[2] = SFLASH_PREPCMD(3, 7, 0, + MSM_NAND_SFCMD_DATXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGRD); + dma_buffer->data.sfcmd[3] = SFLASH_PREPCMD(4, 10, 0, + MSM_NAND_SFCMD_CMDXS, + nand_sfcmd_mode, + MSM_NAND_SFCMD_REGWR); + dma_buffer->data.sfexec = 1; + dma_buffer->data.sfstat[0] = CLEAN_DATA_32; + dma_buffer->data.sfstat[1] = CLEAN_DATA_32; + dma_buffer->data.sfstat[2] = CLEAN_DATA_32; + dma_buffer->data.sfstat[3] = CLEAN_DATA_32; + dma_buffer->data.addr0 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr1 = (ONENAND_START_ADDRESS_8 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.addr2 = (ONENAND_START_BLOCK_ADDRESS << 16) | + (ONENAND_START_ADDRESS_2); + dma_buffer->data.addr3 = (ONENAND_WRITE_PROT_STATUS << 16) | + (ONENAND_COMMAND); + dma_buffer->data.addr4 = (ONENAND_CONTROLLER_STATUS << 16) | + (ONENAND_INTERRUPT_STATUS); + dma_buffer->data.addr5 = (ONENAND_INTERRUPT_STATUS << 16) | + (ONENAND_SYSTEM_CONFIG_1); + dma_buffer->data.addr6 = (ONENAND_START_ADDRESS_3 << 16) | + (ONENAND_START_ADDRESS_1); + dma_buffer->data.data0 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data1 = (onenand_startaddr8 << 16) | + (onenand_startaddr1); + dma_buffer->data.data2 = (onenand_startblock << 16) | + (onenand_startaddr2); + dma_buffer->data.data3 = (CLEAN_DATA_16 << 16) | + (ONENAND_CMD_LOCK); + dma_buffer->data.data4 = (CLEAN_DATA_16 << 16) | + (CLEAN_DATA_16); + dma_buffer->data.data5 = (ONENAND_CLRINTR << 16) | + (ONENAND_SYSCFG1_ECCENA(nand_sfcmd_mode)); + dma_buffer->data.data6 = (ONENAND_STARTADDR3_RES << 16) | + (ONENAND_STARTADDR1_RES); + + /*************************************************************/ + /* Write the necessary address reg in the onenand device */ + /*************************************************************/ + + /* Enable and configure the SFlash controller */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfbcfg); + cmd->dst = MSM_NAND_SFLASHC_BURST_CFG; + cmd->len = 4; + cmd++; + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[0]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Write the ADDR0 and ADDR1 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr0); + cmd->dst = MSM_NAND_ADDR0; + cmd->len = 8; + cmd++; + + /* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr2); + cmd->dst = MSM_NAND_ADDR2; + cmd->len = 16; + cmd++; + + /* Write the ADDR6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.addr6); + cmd->dst = MSM_NAND_ADDR6; + cmd->len = 4; + cmd++; + + /* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data0); + cmd->dst = MSM_NAND_GENP_REG0; + cmd->len = 16; + cmd++; + + /* Write the FLASH_DEV_CMD4,5,6 registers */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->dst = MSM_NAND_DEV_CMD4; + cmd->len = 12; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[0]); + cmd->len = 4; + cmd++; + + /*************************************************************/ + /* Wait for the interrupt from the Onenand device controller */ + /*************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[1]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[1]); + cmd->len = 4; + cmd++; + + /*********************************************************/ + /* Read the necessary status reg from the onenand device */ + /*********************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[2]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[2]); + cmd->len = 4; + cmd++; + + /* Read the GENP3 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_GENP_REG3; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data3); + cmd->len = 4; + cmd++; + + /* Read the DEVCMD4 register */ + cmd->cmd = 0; + cmd->src = MSM_NAND_DEV_CMD4; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.data4); + cmd->len = 4; + cmd++; + + /************************************************************/ + /* Restore the necessary registers to proper values */ + /************************************************************/ + + /* Block on cmd ready and write CMD register */ + cmd->cmd = DST_CRCI_NAND_CMD; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfcmd[3]); + cmd->dst = MSM_NAND_SFLASHC_CMD; + cmd->len = 4; + cmd++; + + /* Kick the execute command */ + cmd->cmd = 0; + cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.sfexec); + cmd->dst = MSM_NAND_SFLASHC_EXEC_CMD; + cmd->len = 4; + cmd++; + + /* Block on data ready, and read the status register */ + cmd->cmd = SRC_CRCI_NAND_DATA; + cmd->src = MSM_NAND_SFLASHC_STATUS; + cmd->dst = msm_virt_to_dma(chip, &dma_buffer->data.sfstat[3]); + cmd->len = 4; + cmd++; + + + BUILD_BUG_ON(20 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) + >> 3) | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, + DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + + write_prot_status = (dma_buffer->data.data3 >> 16) & 0x0000FFFF; + interrupt_status = (dma_buffer->data.data4 >> 0) & 0x0000FFFF; + controller_status = (dma_buffer->data.data4 >> 16) & 0x0000FFFF; + +#if VERBOSE + pr_info("\n%s: sflash status %x %x %x %x\n", __func__, + dma_buffer->data.sfstat[0], + dma_buffer->data.sfstat[1], + dma_buffer->data.sfstat[2], + dma_buffer->data.sfstat[3]); + + pr_info("%s: controller_status = %x\n", __func__, + controller_status); + pr_info("%s: interrupt_status = %x\n", __func__, + interrupt_status); + pr_info("%s: write_prot_status = %x\n", __func__, + write_prot_status); +#endif + /* Check for errors, protection violations etc */ + if ((controller_status != 0) + || (dma_buffer->data.sfstat[0] & 0x110) + || (dma_buffer->data.sfstat[1] & 0x110) + || (dma_buffer->data.sfstat[2] & 0x110) + || (dma_buffer->data.sfstat[3] & 0x110)) { + pr_err("%s: ECC/MPU/OP error\n", __func__); + err = -EIO; + } + + if (!(write_prot_status & ONENAND_WP_LS)) { + pr_err("%s: Unexpected status ofs = 0x%llx," + "wp_status = %x\n", + __func__, ofs, write_prot_status); + err = -EIO; + } + + if (err) + break; + } + + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + +#if VERBOSE + pr_info("\n%s: ret %d\n", __func__, err); + pr_info("====================================================" + "=============\n"); +#endif + return err; +} + +static int msm_onenand_suspend(struct mtd_info *mtd) +{ + return 0; +} + +static void msm_onenand_resume(struct mtd_info *mtd) +{ +} + +int msm_onenand_scan(struct mtd_info *mtd, int maxchips) +{ + struct msm_nand_chip *chip = mtd->priv; + + /* Probe and check whether onenand device is present */ + if (flash_onenand_probe(chip)) + return -ENODEV; + + mtd->size = 0x1000000 << ((onenand_info.device_id & 0xF0) >> 4); + mtd->writesize = onenand_info.data_buf_size; + mtd->oobsize = mtd->writesize >> 5; + mtd->erasesize = mtd->writesize << 6; + mtd->oobavail = msm_onenand_oob_64.oobavail; + mtd->ecclayout = &msm_onenand_oob_64; + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->_erase = msm_onenand_erase; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = msm_onenand_read; + mtd->_write = msm_onenand_write; + mtd->_read_oob = msm_onenand_read_oob; + mtd->_write_oob = msm_onenand_write_oob; + mtd->_lock = msm_onenand_lock; + mtd->_unlock = msm_onenand_unlock; + mtd->_suspend = msm_onenand_suspend; + mtd->_resume = msm_onenand_resume; + mtd->_block_isbad = msm_onenand_block_isbad; + mtd->_block_markbad = msm_onenand_block_markbad; + mtd->owner = THIS_MODULE; + + pr_info("Found a supported onenand device\n"); + + return 0; +} + +/** + * msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device + * @param mtd MTD device structure + * @param maxchips Number of chips to scan for + * + * This fills out all the not initialized function pointers + * with the defaults. + * The flash ID is read and the mtd/chip structures are + * filled with the appropriate values. + */ +int msm_nand_scan(struct mtd_info *mtd, int maxchips) +{ + struct msm_nand_chip *chip = mtd->priv; + uint32_t flash_id = 0, i, mtd_writesize; + uint8_t dev_found = 0; + uint8_t wide_bus; + uint32_t manid; + uint32_t devid; + uint32_t devcfg; + struct nand_flash_dev *flashdev = NULL; + struct nand_manufacturers *flashman = NULL; + + /* Probe the Flash device for ONFI compliance */ + if (!flash_onfi_probe(chip)) { + dev_found = 1; + } else { + /* Read the Flash ID from the Nand Flash Device */ + flash_id = flash_read_id(chip); + manid = flash_id & 0xFF; + devid = (flash_id >> 8) & 0xFF; + devcfg = (flash_id >> 24) & 0xFF; + + for (i = 0; !flashman && nand_manuf_ids[i].id; ++i) + if (nand_manuf_ids[i].id == manid) + flashman = &nand_manuf_ids[i]; + for (i = 0; !flashdev && nand_flash_ids[i].id; ++i) + if (nand_flash_ids[i].id == devid) + flashdev = &nand_flash_ids[i]; + if (!flashdev || !flashman) { + pr_err("ERROR: unknown nand device manuf=%x devid=%x\n", + manid, devid); + return -ENOENT; + } else + dev_found = 1; + + if (!flashdev->pagesize) { + supported_flash.flash_id = flash_id; + supported_flash.density = flashdev->chipsize << 20; + supported_flash.widebus = devcfg & (1 << 6) ? 1 : 0; + supported_flash.pagesize = 1024 << (devcfg & 0x3); + supported_flash.blksize = (64 * 1024) << + ((devcfg >> 4) & 0x3); + supported_flash.oobsize = (8 << ((devcfg >> 2) & 0x3)) * + (supported_flash.pagesize >> 9); + + if ((supported_flash.oobsize > 64) && + (supported_flash.pagesize == 2048)) { + pr_info("msm_nand: Found a 2K page device with" + " %d oobsize - changing oobsize to 64 " + "bytes.\n", supported_flash.oobsize); + supported_flash.oobsize = 64; + } + } else { + supported_flash.flash_id = flash_id; + supported_flash.density = flashdev->chipsize << 20; + supported_flash.widebus = flashdev->options & + NAND_BUSWIDTH_16 ? 1 : 0; + supported_flash.pagesize = flashdev->pagesize; + supported_flash.blksize = flashdev->erasesize; + supported_flash.oobsize = flashdev->pagesize >> 5; + } + } + + if (dev_found) { + (!interleave_enable) ? (i = 1) : (i = 2); + wide_bus = supported_flash.widebus; + mtd->size = supported_flash.density * i; + mtd->writesize = supported_flash.pagesize * i; + mtd->oobsize = supported_flash.oobsize * i; + mtd->erasesize = supported_flash.blksize * i; + + if (!interleave_enable) + mtd_writesize = mtd->writesize; + else + mtd_writesize = mtd->writesize >> 1; + + /* Check whether controller and NAND device support 8bit ECC*/ + if ((flash_rd_reg(chip, MSM_NAND_HW_INFO) == 0x307) + && (supported_flash.ecc_correctability >= 8)) { + pr_info("Found supported NAND device for %dbit ECC\n", + supported_flash.ecc_correctability); + enable_bch_ecc = 1; + } else { + pr_info("Found a supported NAND device\n"); + } + pr_info("NAND Id : 0x%x\n", supported_flash.flash_id); + pr_info("Buswidth : %d Bits\n", (wide_bus) ? 16 : 8); + pr_info("Density : %lld MByte\n", (mtd->size>>20)); + pr_info("Pagesize : %d Bytes\n", mtd->writesize); + pr_info("Erasesize: %d Bytes\n", mtd->erasesize); + pr_info("Oobsize : %d Bytes\n", mtd->oobsize); + } else { + pr_err("Unsupported Nand,Id: 0x%x \n", flash_id); + return -ENODEV; + } + + /* Size of each codeword is 532Bytes incase of 8bit BCH ECC*/ + chip->cw_size = enable_bch_ecc ? 532 : 528; + chip->CFG0 = (((mtd_writesize >> 9)-1) << 6) /* 4/8 cw/pg for 2/4k */ + | (516 << 9) /* 516 user data bytes */ + | (10 << 19) /* 10 parity bytes */ + | (5 << 27) /* 5 address cycles */ + | (0 << 30) /* Do not read status before data */ + | (1 << 31) /* Send read cmd */ + /* 0 spare bytes for 16 bit nand or 1/2 spare bytes for 8 bit */ + | (wide_bus ? 0 << 23 : (enable_bch_ecc ? 2 << 23 : 1 << 23)); + + chip->CFG1 = (0 << 0) /* Enable ecc */ + | (7 << 2) /* 8 recovery cycles */ + | (0 << 5) /* Allow CS deassertion */ + /* Bad block marker location */ + | ((mtd_writesize - (chip->cw_size * ( + (mtd_writesize >> 9) - 1)) + 1) << 6) + | (0 << 16) /* Bad block in user data area */ + | (2 << 17) /* 6 cycle tWB/tRB */ + | ((wide_bus) ? CFG1_WIDE_FLASH : 0); /* Wide flash bit */ + + chip->ecc_buf_cfg = 0x203; + chip->CFG0_RAW = 0xA80420C0; + chip->CFG1_RAW = 0x5045D; + + if (enable_bch_ecc) { + chip->CFG1 |= (1 << 27); /* Enable BCH engine */ + chip->ecc_bch_cfg = (0 << 0) /* Enable ECC*/ + | (0 << 1) /* Enable/Disable SW reset of ECC engine */ + | (1 << 4) /* 8bit ecc*/ + | ((wide_bus) ? (14 << 8) : (13 << 8))/*parity bytes*/ + | (516 << 16) /* 516 user data bytes */ + | (1 << 30); /* Turn on ECC engine clocks always */ + chip->CFG0_RAW = 0xA80428C0; /* CW size is increased to 532B */ + } + + /* + * For 4bit RS ECC (default ECC), parity bytes = 10 (for x8 and x16 I/O) + * For 8bit BCH ECC, parity bytes = 13 (x8) or 14 (x16 I/O). + */ + chip->ecc_parity_bytes = enable_bch_ecc ? (wide_bus ? 14 : 13) : 10; + + pr_info("CFG0 Init : 0x%08x\n", chip->CFG0); + pr_info("CFG1 Init : 0x%08x\n", chip->CFG1); + pr_info("ECCBUFCFG : 0x%08x\n", chip->ecc_buf_cfg); + + if (mtd->oobsize == 64) { + mtd->oobavail = msm_nand_oob_64.oobavail; + mtd->ecclayout = &msm_nand_oob_64; + } else if (mtd->oobsize == 128) { + mtd->oobavail = msm_nand_oob_128.oobavail; + mtd->ecclayout = &msm_nand_oob_128; + } else if (mtd->oobsize == 224) { + mtd->oobavail = wide_bus ? msm_nand_oob_224_x16.oobavail : + msm_nand_oob_224_x8.oobavail; + mtd->ecclayout = wide_bus ? &msm_nand_oob_224_x16 : + &msm_nand_oob_224_x8; + } else if (mtd->oobsize == 256) { + mtd->oobavail = msm_nand_oob_256.oobavail; + mtd->ecclayout = &msm_nand_oob_256; + } else { + pr_err("Unsupported Nand, oobsize: 0x%x \n", + mtd->oobsize); + return -ENODEV; + } + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + /* mtd->ecctype = MTD_ECC_SW; */ + mtd->_erase = msm_nand_erase; + mtd->_block_isbad = msm_nand_block_isbad; + mtd->_block_markbad = msm_nand_block_markbad; + mtd->_point = NULL; + mtd->_unpoint = NULL; + mtd->_read = msm_nand_read; + mtd->_write = msm_nand_write; + mtd->_read_oob = msm_nand_read_oob; + mtd->_write_oob = msm_nand_write_oob; + if (dual_nand_ctlr_present) { + mtd->_read_oob = msm_nand_read_oob_dualnandc; + mtd->_write_oob = msm_nand_write_oob_dualnandc; + if (interleave_enable) { + mtd->_erase = msm_nand_erase_dualnandc; + mtd->_block_isbad = msm_nand_block_isbad_dualnandc; + } + } + + /* mtd->sync = msm_nand_sync; */ + mtd->_lock = NULL; + /* mtd->_unlock = msm_nand_unlock; */ + mtd->_suspend = msm_nand_suspend; + mtd->_resume = msm_nand_resume; + mtd->owner = THIS_MODULE; + + /* Unlock whole block */ + /* msm_nand_unlock_all(mtd); */ + + /* return this->scan_bbt(mtd); */ + return 0; +} +EXPORT_SYMBOL_GPL(msm_nand_scan); + +/** + * msm_nand_release - [msm_nand Interface] Free resources held by the msm_nand device + * @param mtd MTD device structure + */ +void msm_nand_release(struct mtd_info *mtd) +{ + /* struct msm_nand_chip *this = mtd->priv; */ + + /* Deregister the device */ + mtd_device_unregister(mtd); +} +EXPORT_SYMBOL_GPL(msm_nand_release); + +struct msm_nand_info { + struct mtd_info mtd; + struct mtd_partition *parts; + struct msm_nand_chip msm_nand; +}; + +/* duplicating the NC01 XFR contents to NC10 */ +static int msm_nand_nc10_xfr_settings(struct mtd_info *mtd) +{ + struct msm_nand_chip *chip = mtd->priv; + + struct { + dmov_s cmd[2]; + unsigned cmdptr; + } *dma_buffer; + dmov_s *cmd; + + wait_event(chip->wait_queue, + (dma_buffer = msm_nand_get_dma_buffer( + chip, sizeof(*dma_buffer)))); + + cmd = dma_buffer->cmd; + + /* Copying XFR register contents from NC01 --> NC10 */ + cmd->cmd = 0; + cmd->src = NC01(MSM_NAND_XFR_STEP1); + cmd->dst = NC10(MSM_NAND_XFR_STEP1); + cmd->len = 28; + cmd++; + + BUILD_BUG_ON(2 != ARRAY_SIZE(dma_buffer->cmd)); + BUG_ON(cmd - dma_buffer->cmd > ARRAY_SIZE(dma_buffer->cmd)); + dma_buffer->cmd[0].cmd |= CMD_OCB; + cmd[-1].cmd |= CMD_OCU | CMD_LC; + dma_buffer->cmdptr = (msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) + | CMD_PTR_LP; + + mb(); + msm_dmov_exec_cmd(chip->dma_channel, DMOV_CMD_PTR_LIST + | DMOV_CMD_ADDR(msm_virt_to_dma(chip, + &dma_buffer->cmdptr))); + mb(); + msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer)); + return 0; +} + +static int setup_mtd_device(struct platform_device *pdev, + struct msm_nand_info *info) +{ + int i, err; + struct flash_platform_data *pdata = pdev->dev.platform_data; + + if (pdata) { + for (i = 0; i < pdata->nr_parts; i++) { + pdata->parts[i].offset = pdata->parts[i].offset + * info->mtd.erasesize; + pdata->parts[i].size = pdata->parts[i].size + * info->mtd.erasesize; + } + err = mtd_device_register(&info->mtd, pdata->parts, + pdata->nr_parts); + } else { + err = mtd_device_register(&info->mtd, NULL, 0); + } + return err; +} + +static int __devinit msm_nand_probe(struct platform_device *pdev) +{ + struct msm_nand_info *info; + struct resource *res; + int err; + struct flash_platform_data *plat_data; + + plat_data = pdev->dev.platform_data; + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "msm_nand_phys"); + if (!res || !res->start) { + pr_err("%s: msm_nand_phys resource invalid/absent\n", + __func__); + return -ENODEV; + } + msm_nand_phys = res->start; + pr_info("%s: phys addr 0x%lx \n", __func__, msm_nand_phys); + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "msm_nandc01_phys"); + if (!res || !res->start) + goto no_dual_nand_ctlr_support; + msm_nandc01_phys = res->start; + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "msm_nandc10_phys"); + if (!res || !res->start) + goto no_dual_nand_ctlr_support; + msm_nandc10_phys = res->start; + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "msm_nandc11_phys"); + if (!res || !res->start) + goto no_dual_nand_ctlr_support; + msm_nandc11_phys = res->start; + + res = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "ebi2_reg_base"); + if (!res || !res->start) + goto no_dual_nand_ctlr_support; + ebi2_register_base = res->start; + + dual_nand_ctlr_present = 1; + if (plat_data != NULL) + interleave_enable = plat_data->interleave; + else + interleave_enable = 0; + + if (!interleave_enable) + pr_info("%s: Dual Nand Ctrl in ping-pong mode\n", __func__); + else + pr_info("%s: Dual Nand Ctrl in interleave mode\n", __func__); + +no_dual_nand_ctlr_support: + res = platform_get_resource_byname(pdev, + IORESOURCE_DMA, "msm_nand_dmac"); + if (!res || !res->start) { + pr_err("%s: invalid msm_nand_dmac resource\n", __func__); + return -ENODEV; + } + + info = kzalloc(sizeof(struct msm_nand_info), GFP_KERNEL); + if (!info) { + pr_err("%s: No memory for msm_nand_info\n", __func__); + return -ENOMEM; + } + + info->msm_nand.dev = &pdev->dev; + + init_waitqueue_head(&info->msm_nand.wait_queue); + + info->msm_nand.dma_channel = res->start; + pr_info("%s: dmac 0x%x\n", __func__, info->msm_nand.dma_channel); + + /* this currently fails if dev is passed in */ + info->msm_nand.dma_buffer = + dma_alloc_coherent(/*dev*/ NULL, MSM_NAND_DMA_BUFFER_SIZE, + &info->msm_nand.dma_addr, GFP_KERNEL); + if (info->msm_nand.dma_buffer == NULL) { + pr_err("%s: No memory for msm_nand.dma_buffer\n", __func__); + err = -ENOMEM; + goto out_free_info; + } + + pr_info("%s: allocated dma buffer at %p, dma_addr %x\n", + __func__, info->msm_nand.dma_buffer, info->msm_nand.dma_addr); + + /* Let default be VERSION_1 for backward compatibility */ + info->msm_nand.uncorrectable_bit_mask = BIT(3); + info->msm_nand.num_err_mask = 0x7; + + if (plat_data && (plat_data->version == VERSION_2)) { + info->msm_nand.uncorrectable_bit_mask = BIT(8); + info->msm_nand.num_err_mask = 0x1F; + } + + info->mtd.name = dev_name(&pdev->dev); + info->mtd.priv = &info->msm_nand; + info->mtd.owner = THIS_MODULE; + + /* config ebi2_cfg register only for ping pong mode!!! */ + if (!interleave_enable && dual_nand_ctlr_present) + flash_wr_reg(&info->msm_nand, EBI2_CFG_REG, 0x4010080); + + if (dual_nand_ctlr_present) + msm_nand_nc10_xfr_settings(&info->mtd); + + if (msm_nand_scan(&info->mtd, 1)) + if (msm_onenand_scan(&info->mtd, 1)) { + pr_err("%s: No nand device found\n", __func__); + err = -ENXIO; + goto out_free_dma_buffer; + } + + err = setup_mtd_device(pdev, info); + if (err < 0) { + pr_err("%s: setup_mtd_device failed with err=%d\n", + __func__, err); + goto out_free_dma_buffer; + } + + dev_set_drvdata(&pdev->dev, info); + + return 0; + +out_free_dma_buffer: + dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE, + info->msm_nand.dma_buffer, + info->msm_nand.dma_addr); +out_free_info: + kfree(info); + + return err; +} + +static int __devexit msm_nand_remove(struct platform_device *pdev) +{ + struct msm_nand_info *info = dev_get_drvdata(&pdev->dev); + + dev_set_drvdata(&pdev->dev, NULL); + + if (info) { + msm_nand_release(&info->mtd); + dma_free_coherent(NULL, MSM_NAND_DMA_BUFFER_SIZE, + info->msm_nand.dma_buffer, + info->msm_nand.dma_addr); + kfree(info); + } + + return 0; +} + +#define DRIVER_NAME "msm_nand" + +static struct platform_driver msm_nand_driver = { + .probe = msm_nand_probe, + .remove = __devexit_p(msm_nand_remove), + .driver = { + .name = DRIVER_NAME, + } +}; + +MODULE_ALIAS(DRIVER_NAME); + +static int __init msm_nand_init(void) +{ + return platform_driver_register(&msm_nand_driver); +} + +static void __exit msm_nand_exit(void) +{ + platform_driver_unregister(&msm_nand_driver); +} + +module_init(msm_nand_init); +module_exit(msm_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("msm_nand flash driver code"); diff --git a/drivers/mtd/devices/msm_nand.h b/drivers/mtd/devices/msm_nand.h new file mode 100644 index 000000000000..8156b922e750 --- /dev/null +++ b/drivers/mtd/devices/msm_nand.h @@ -0,0 +1,192 @@ +/* drivers/mtd/devices/msm_nand.h + * + * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved. + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __DRIVERS_MTD_DEVICES_MSM_NAND_H +#define __DRIVERS_MTD_DEVICES_MSM_NAND_H + +#include + +extern unsigned long msm_nand_phys; +extern unsigned long msm_nandc01_phys; +extern unsigned long msm_nandc10_phys; +extern unsigned long msm_nandc11_phys; +extern unsigned long ebi2_register_base; + +#define NC01(X) ((X) + msm_nandc01_phys - msm_nand_phys) +#define NC10(X) ((X) + msm_nandc10_phys - msm_nand_phys) +#define NC11(X) ((X) + msm_nandc11_phys - msm_nand_phys) + +#define MSM_NAND_REG(off) (msm_nand_phys + (off)) + +#define MSM_NAND_FLASH_CMD MSM_NAND_REG(0x0000) +#define MSM_NAND_ADDR0 MSM_NAND_REG(0x0004) +#define MSM_NAND_ADDR1 MSM_NAND_REG(0x0008) +#define MSM_NAND_FLASH_CHIP_SELECT MSM_NAND_REG(0x000C) +#define MSM_NAND_EXEC_CMD MSM_NAND_REG(0x0010) +#define MSM_NAND_FLASH_STATUS MSM_NAND_REG(0x0014) +#define MSM_NAND_BUFFER_STATUS MSM_NAND_REG(0x0018) +#define MSM_NAND_SFLASHC_STATUS MSM_NAND_REG(0x001C) +#define MSM_NAND_DEV0_CFG0 MSM_NAND_REG(0x0020) +#define MSM_NAND_DEV0_CFG1 MSM_NAND_REG(0x0024) +#define MSM_NAND_DEV0_ECC_CFG MSM_NAND_REG(0x0028) +#define MSM_NAND_DEV1_ECC_CFG MSM_NAND_REG(0x002C) +#define MSM_NAND_DEV1_CFG0 MSM_NAND_REG(0x0030) +#define MSM_NAND_DEV1_CFG1 MSM_NAND_REG(0x0034) +#define MSM_NAND_SFLASHC_CMD MSM_NAND_REG(0x0038) +#define MSM_NAND_SFLASHC_EXEC_CMD MSM_NAND_REG(0x003C) +#define MSM_NAND_READ_ID MSM_NAND_REG(0x0040) +#define MSM_NAND_READ_STATUS MSM_NAND_REG(0x0044) +#define MSM_NAND_CONFIG_DATA MSM_NAND_REG(0x0050) +#define MSM_NAND_CONFIG MSM_NAND_REG(0x0054) +#define MSM_NAND_CONFIG_MODE MSM_NAND_REG(0x0058) +#define MSM_NAND_CONFIG_STATUS MSM_NAND_REG(0x0060) +#define MSM_NAND_MACRO1_REG MSM_NAND_REG(0x0064) +#define MSM_NAND_XFR_STEP1 MSM_NAND_REG(0x0070) +#define MSM_NAND_XFR_STEP2 MSM_NAND_REG(0x0074) +#define MSM_NAND_XFR_STEP3 MSM_NAND_REG(0x0078) +#define MSM_NAND_XFR_STEP4 MSM_NAND_REG(0x007C) +#define MSM_NAND_XFR_STEP5 MSM_NAND_REG(0x0080) +#define MSM_NAND_XFR_STEP6 MSM_NAND_REG(0x0084) +#define MSM_NAND_XFR_STEP7 MSM_NAND_REG(0x0088) +#define MSM_NAND_GENP_REG0 MSM_NAND_REG(0x0090) +#define MSM_NAND_GENP_REG1 MSM_NAND_REG(0x0094) +#define MSM_NAND_GENP_REG2 MSM_NAND_REG(0x0098) +#define MSM_NAND_GENP_REG3 MSM_NAND_REG(0x009C) +#define MSM_NAND_DEV_CMD0 MSM_NAND_REG(0x00A0) +#define MSM_NAND_DEV_CMD1 MSM_NAND_REG(0x00A4) +#define MSM_NAND_DEV_CMD2 MSM_NAND_REG(0x00A8) +#define MSM_NAND_DEV_CMD_VLD MSM_NAND_REG(0x00AC) +#define MSM_NAND_EBI2_MISR_SIG_REG MSM_NAND_REG(0x00B0) +#define MSM_NAND_ADDR2 MSM_NAND_REG(0x00C0) +#define MSM_NAND_ADDR3 MSM_NAND_REG(0x00C4) +#define MSM_NAND_ADDR4 MSM_NAND_REG(0x00C8) +#define MSM_NAND_ADDR5 MSM_NAND_REG(0x00CC) +#define MSM_NAND_DEV_CMD3 MSM_NAND_REG(0x00D0) +#define MSM_NAND_DEV_CMD4 MSM_NAND_REG(0x00D4) +#define MSM_NAND_DEV_CMD5 MSM_NAND_REG(0x00D8) +#define MSM_NAND_DEV_CMD6 MSM_NAND_REG(0x00DC) +#define MSM_NAND_SFLASHC_BURST_CFG MSM_NAND_REG(0x00E0) +#define MSM_NAND_ADDR6 MSM_NAND_REG(0x00E4) +#define MSM_NAND_EBI2_ECC_BUF_CFG MSM_NAND_REG(0x00F0) +#define MSM_NAND_HW_INFO MSM_NAND_REG(0x00FC) +#define MSM_NAND_FLASH_BUFFER MSM_NAND_REG(0x0100) + +/* device commands */ + +#define MSM_NAND_CMD_SOFT_RESET 0x01 +#define MSM_NAND_CMD_PAGE_READ 0x32 +#define MSM_NAND_CMD_PAGE_READ_ECC 0x33 +#define MSM_NAND_CMD_PAGE_READ_ALL 0x34 +#define MSM_NAND_CMD_SEQ_PAGE_READ 0x15 +#define MSM_NAND_CMD_PRG_PAGE 0x36 +#define MSM_NAND_CMD_PRG_PAGE_ECC 0x37 +#define MSM_NAND_CMD_PRG_PAGE_ALL 0x39 +#define MSM_NAND_CMD_BLOCK_ERASE 0x3A +#define MSM_NAND_CMD_FETCH_ID 0x0B +#define MSM_NAND_CMD_STATUS 0x0C +#define MSM_NAND_CMD_RESET 0x0D + +/* Sflash Commands */ + +#define MSM_NAND_SFCMD_DATXS 0x0 +#define MSM_NAND_SFCMD_CMDXS 0x1 +#define MSM_NAND_SFCMD_BURST 0x0 +#define MSM_NAND_SFCMD_ASYNC 0x1 +#define MSM_NAND_SFCMD_ABORT 0x1 +#define MSM_NAND_SFCMD_REGRD 0x2 +#define MSM_NAND_SFCMD_REGWR 0x3 +#define MSM_NAND_SFCMD_INTLO 0x4 +#define MSM_NAND_SFCMD_INTHI 0x5 +#define MSM_NAND_SFCMD_DATRD 0x6 +#define MSM_NAND_SFCMD_DATWR 0x7 + +#define SFLASH_PREPCMD(numxfr, offval, delval, trnstp, mode, opcode) \ + ((numxfr<<20)|(offval<<12)|(delval<<6)|(trnstp<<5)|(mode<<4)|opcode) + +#define SFLASH_BCFG 0x20100327 + +/* Onenand addresses */ + +#define ONENAND_MANUFACTURER_ID 0xF000 +#define ONENAND_DEVICE_ID 0xF001 +#define ONENAND_VERSION_ID 0xF002 +#define ONENAND_DATA_BUFFER_SIZE 0xF003 +#define ONENAND_BOOT_BUFFER_SIZE 0xF004 +#define ONENAND_AMOUNT_OF_BUFFERS 0xF005 +#define ONENAND_TECHNOLOGY 0xF006 +#define ONENAND_START_ADDRESS_1 0xF100 +#define ONENAND_START_ADDRESS_2 0xF101 +#define ONENAND_START_ADDRESS_3 0xF102 +#define ONENAND_START_ADDRESS_4 0xF103 +#define ONENAND_START_ADDRESS_5 0xF104 +#define ONENAND_START_ADDRESS_6 0xF105 +#define ONENAND_START_ADDRESS_7 0xF106 +#define ONENAND_START_ADDRESS_8 0xF107 +#define ONENAND_START_BUFFER 0xF200 +#define ONENAND_COMMAND 0xF220 +#define ONENAND_SYSTEM_CONFIG_1 0xF221 +#define ONENAND_SYSTEM_CONFIG_2 0xF222 +#define ONENAND_CONTROLLER_STATUS 0xF240 +#define ONENAND_INTERRUPT_STATUS 0xF241 +#define ONENAND_START_BLOCK_ADDRESS 0xF24C +#define ONENAND_WRITE_PROT_STATUS 0xF24E +#define ONENAND_ECC_STATUS 0xFF00 +#define ONENAND_ECC_ERRPOS_MAIN0 0xFF01 +#define ONENAND_ECC_ERRPOS_SPARE0 0xFF02 +#define ONENAND_ECC_ERRPOS_MAIN1 0xFF03 +#define ONENAND_ECC_ERRPOS_SPARE1 0xFF04 +#define ONENAND_ECC_ERRPOS_MAIN2 0xFF05 +#define ONENAND_ECC_ERRPOS_SPARE2 0xFF06 +#define ONENAND_ECC_ERRPOS_MAIN3 0xFF07 +#define ONENAND_ECC_ERRPOS_SPARE3 0xFF08 + +/* Onenand commands */ +#define ONENAND_WP_US (1 << 2) +#define ONENAND_WP_LS (1 << 1) + +#define ONENAND_CMDLOAD 0x0000 +#define ONENAND_CMDLOADSPARE 0x0013 +#define ONENAND_CMDPROG 0x0080 +#define ONENAND_CMDPROGSPARE 0x001A +#define ONENAND_CMDERAS 0x0094 +#define ONENAND_CMD_UNLOCK 0x0023 +#define ONENAND_CMD_LOCK 0x002A + +#define ONENAND_SYSCFG1_ECCENA(mode) (0x40E0 | (mode ? 0 : 0x8002)) +#define ONENAND_SYSCFG1_ECCDIS(mode) (0x41E0 | (mode ? 0 : 0x8002)) + +#define ONENAND_CLRINTR 0x0000 +#define ONENAND_STARTADDR1_RES 0x07FF +#define ONENAND_STARTADDR3_RES 0x07FF + +#define DATARAM0_0 0x8 +#define DEVICE_FLASHCORE_0 (0 << 15) +#define DEVICE_FLASHCORE_1 (1 << 15) +#define DEVICE_BUFFERRAM_0 (0 << 15) +#define DEVICE_BUFFERRAM_1 (1 << 15) +#define ONENAND_DEVICE_IS_DDP (1 << 3) + +#define CLEAN_DATA_16 0xFFFF +#define CLEAN_DATA_32 0xFFFFFFFF + +#define EBI2_REG(off) (ebi2_register_base + (off)) +#define EBI2_CHIP_SELECT_CFG0 EBI2_REG(0x0000) +#define EBI2_CFG_REG EBI2_REG(0x0004) +#define EBI2_NAND_ADM_MUX EBI2_REG(0x005C) + +extern struct flash_platform_data msm_nand_data; + +#endif diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 36eace03b2ac..a33bf3161725 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -231,7 +231,7 @@ struct nand_oobfree { */ struct nand_ecclayout_user { __u32 eccbytes; - __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES]; + __u32 eccpos[256]; __u32 oobavail; struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; };