blob: 5cace6b11c872def25cda50a90f99999076346e2 [file] [log] [blame]
/*
* 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 <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/crc16.h>
#include <linux/bitrev.h>
#include <asm/dma.h>
#include <asm/mach/flash.h>
#include <mach/dma.h>
#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;
int (*read_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *);
if (!dual_nand_ctlr_present)
read_oob = msm_nand_read_oob;
else
read_oob = msm_nand_read_oob_dualnandc;
ops.mode = MTD_OPS_PLACE_OOB;
ops.retlen = 0;
ops.ooblen = 0;
ops.oobbuf = NULL;
ret = 0;
*retlen = 0;
if ((from & (mtd->writesize - 1)) == 0 && len == mtd->writesize) {
/* reading a page on page boundary */
ops.len = len;
ops.datbuf = buf;
ret = read_oob(mtd, from, &ops);
*retlen = ops.retlen;
} else if (len > 0) {
/* reading any size on any offset. partial page is supported */
u8 *bounce_buf;
loff_t aligned_from;
loff_t offset;
size_t actual_len;
bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL);
if (!bounce_buf) {
pr_err("%s: could not allocate memory\n", __func__);
ret = -ENOMEM;
goto out;
}
ops.len = mtd->writesize;
offset = from & (mtd->writesize - 1);
aligned_from = from - offset;
for (;;) {
int no_copy;
actual_len = mtd->writesize - offset;
if (actual_len > len)
actual_len = len;
no_copy = (offset == 0 && actual_len == mtd->writesize);
ops.datbuf = (no_copy) ? buf : bounce_buf;
ret = read_oob(mtd, aligned_from, &ops);
if (ret < 0)
break;
if (!no_copy)
memcpy(buf, bounce_buf + offset, actual_len);
len -= actual_len;
*retlen += actual_len;
if (len == 0)
break;
buf += actual_len;
offset = 0;
aligned_from += mtd->writesize;
}
kfree(bounce_buf);
}
out:
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;
int (*write_oob)(struct mtd_info *, loff_t, struct mtd_oob_ops *);
if (!dual_nand_ctlr_present)
write_oob = msm_nand_write_oob;
else
write_oob = msm_nand_write_oob_dualnandc;
ops.mode = MTD_OPS_PLACE_OOB;
ops.retlen = 0;
ops.ooblen = 0;
ops.oobbuf = NULL;
ret = 0;
*retlen = 0;
if (!virt_addr_valid(buf) &&
((to | len) & (mtd->writesize - 1)) == 0 &&
((unsigned long) buf & ~PAGE_MASK) + len > PAGE_SIZE) {
/*
* Handle writing of large size write buffer in vmalloc
* address space that does not fit in an MMU page.
* The destination address must be on page boundary,
* and the size must be multiple of NAND page size.
* Writing partial page is not supported.
*/
ops.len = mtd->writesize;
for (;;) {
ops.datbuf = (uint8_t *) buf;
ret = write_oob(mtd, to, &ops);
if (ret < 0)
break;
len -= mtd->writesize;
*retlen += mtd->writesize;
if (len == 0)
break;
buf += mtd->writesize;
to += mtd->writesize;
}
} else {
ops.len = len;
ops.datbuf = (uint8_t *) buf;
ret = write_oob(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;
}
static const unsigned int bch_sup_cntrl[] = {
0x307, /* MSM7x2xA */
0x4030, /* MDM 9x15 */
};
static inline bool msm_nand_has_bch_ecc_engine(unsigned int hw_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(bch_sup_cntrl); i++) {
if (hw_id == bch_sup_cntrl[i])
return true;
}
return false;
}
/**
* 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;
unsigned int hw_id;
/* 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;
mtd->writebufsize = mtd->writesize;
if (!interleave_enable)
mtd_writesize = mtd->writesize;
else
mtd_writesize = mtd->writesize >> 1;
/* Check whether controller and NAND device support 8bit ECC*/
hw_id = flash_rd_reg(chip, MSM_NAND_HW_INFO);
if (msm_nand_has_bch_ecc_engine(hw_id)
&& (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 Controller ID : 0x%x\n", hw_id);
pr_info("NAND Device 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");