Skip to content

Instantly share code, notes, and snippets.

@OuNao
Created October 31, 2012 13:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OuNao/3986902 to your computer and use it in GitHub Desktop.
Save OuNao/3986902 to your computer and use it in GitHub Desktop.
msm_nand driver for GT-I5500 - now working YAFFS2. New msm_onenand_read_dpram/read_param/write_param functions. Modified dpram.c. Modified param.c.
/****************************************************************************
**
** COPYRIGHT(C) : Samsung Electronics Co.Ltd, 2006-2010 ALL RIGHTS RESERVED
**
****************************************************************************/
#define NO_TTY_DPRAM 1
#define NO_TTY_TX_RETRY 1
#define _ENABLE_ERROR_DEVICE
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/irq.h>
#ifdef _ENABLE_ERROR_DEVICE
#include <linux/poll.h>
#include <linux/cdev.h>
#endif /* _ENABLE_ERROR_DEVICE */
#include <asm/irq.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <asm/uaccess.h>
#include <mach/gpio.h>
#include <mach/msm_iomap.h>
#include <linux/proc_fs.h>
#include <linux/wakelock.h>
#include "dpram.h"
#ifndef CONFIG_YAFFS_FS
// DGS
#include "../tfsr/Inc/FSR.h"
#include "../tfsr/Inc/FSR_BML.h"
#include "../tfsr/Inc/FSR_LLD_4K_OneNAND.h"
#endif /* CONFIG_YAFFS_FS */
#include "../../arch/arm/mach-msm/smd_private.h"
#include "../../arch/arm/mach-msm/proc_comm.h"
#define DRIVER_NAME "DPRAM"
#define DRIVER_MAJOR_NUM 255
#undef _DEBUG
#ifdef _DEBUG
#define dprintk(s, args...) printk("[DPRAM] %s:%d - " s, __func__, __LINE__, ##args)
#else
#define dprintk(s, args...)
#endif /* _DEBUG */
#define WRITE_TO_DPRAM(dest, src, size) \
_memcpy((void *)(SmemBase + dest), src, size)
#define READ_FROM_DPRAM(dest, src, size) \
_memcpy(dest, (void *)(SmemBase + src), size)
#ifdef _ENABLE_ERROR_DEVICE
#define DPRAM_ERR_MSG_LEN 65
#define DPRAM_ERR_DEVICE "dpramerr"
#endif /* _ENABLE_ERROR_DEVICE */
#define MSM_A2M_INT(n) (MSM_CSR_BASE + 0x400 + (n) * 4)
static volatile unsigned char *SmemBase;
static int DpramInited = 0;
/* DGS Info Cache */
static unsigned char aDGSBuf[4096];
#define DGS_TEST 0
static struct tty_driver *dpram_tty_driver;
static dpram_tasklet_data_t dpram_tasklet_data[MAX_INDEX];
static dpram_device_t dpram_table[MAX_INDEX] = {
{
.in_head_addr = DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS,
.in_tail_addr = DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS,
.in_buff_addr = DPRAM_PHONE2PDA_FORMATTED_BUFFER_ADDRESS,
.in_buff_size = DPRAM_PHONE2PDA_FORMATTED_SIZE,
.out_head_addr = DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS,
.out_tail_addr = DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS,
.out_buff_addr = DPRAM_PDA2PHONE_FORMATTED_BUFFER_ADDRESS,
.out_buff_size = DPRAM_PDA2PHONE_FORMATTED_SIZE,
.mask_req_ack = INT_MASK_REQ_ACK_F,
.mask_res_ack = INT_MASK_RES_ACK_F,
.mask_send = INT_MASK_SEND_F,
},
{
.in_head_addr = DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS,
.in_tail_addr = DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS,
.in_buff_addr = DPRAM_PHONE2PDA_RAW_BUFFER_ADDRESS,
.in_buff_size = DPRAM_PHONE2PDA_RAW_SIZE,
.out_head_addr = DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS,
.out_tail_addr = DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS,
.out_buff_addr = DPRAM_PDA2PHONE_RAW_BUFFER_ADDRESS,
.out_buff_size = DPRAM_PDA2PHONE_RAW_SIZE,
.mask_req_ack = INT_MASK_REQ_ACK_R,
.mask_res_ack = INT_MASK_RES_ACK_R,
.mask_send = INT_MASK_SEND_R,
},
};
static struct tty_struct *dpram_tty[MAX_INDEX];
static struct ktermios *dpram_termios[MAX_INDEX];
static struct ktermios *dpram_termios_locked[MAX_INDEX];
#ifdef CONFIG_YAFFS_FS
extern int msm_onenand_read_dpram(char *mBuf, unsigned size);
#endif /* CONFIG_YAFFS_FS */
extern void *smem_alloc(unsigned, unsigned);
// hsil for cpufreq
extern int cpufreq_direct_set_policy(unsigned int cpu, const char *buf);
//Get charging status & charger Connect value!!!
extern void get_charger_type(void);
extern void msm_batt_check_event(void);
extern int get_charging_status(void);
static void print_smem(void);
static void dpram_ramdump(void);
static int dpram_get_dgs(void);
static void res_ack_tasklet_handler(unsigned long data);
static void send_tasklet_handler(unsigned long data);
#ifndef CONFIG_YAFFS_FS
// [BML functions for DGS
INT32 (*bml_open)(UINT32 nVol, UINT32 nFlag);
VOID (*bml_acquireSM)(UINT32 nVol);
INT32 (*ond_4k_read)(UINT32 nDev, UINT32 nPbn, UINT32 nPgOffset, UINT8 *pMBuf, FSRSpareBuf *pSBuf, UINT32 nFlag);
VOID (*bml_release)(UINT32 nVol);
EXPORT_SYMBOL(bml_open);
EXPORT_SYMBOL(bml_acquireSM);
EXPORT_SYMBOL(ond_4k_read);
EXPORT_SYMBOL(bml_release);
// ]
#endif /* CONFIG_YAFFS_FS */
static DECLARE_TASKLET(fmt_send_tasklet, send_tasklet_handler, 0);
static DECLARE_TASKLET(raw_send_tasklet, send_tasklet_handler, 0);
static DECLARE_TASKLET(fmt_res_ack_tasklet, res_ack_tasklet_handler,
(unsigned long)&dpram_table[FORMATTED_INDEX]);
static DECLARE_TASKLET(raw_res_ack_tasklet, res_ack_tasklet_handler,
(unsigned long)&dpram_table[RAW_INDEX]);
#ifdef _ENABLE_ERROR_DEVICE
static unsigned int dpram_err_len;
static char dpram_err_buf[DPRAM_ERR_MSG_LEN];
struct class *dpram_class;
static DECLARE_WAIT_QUEUE_HEAD(dpram_err_wait_q);
static struct fasync_struct *dpram_err_async_q;
#endif /* _ENABLE_ERROR_DEVICE */
static DECLARE_MUTEX(write_mutex);
struct wake_lock imei_wake_lock;
struct wake_lock dpram_wake_lock;
struct wake_lock silent_wake_lock;
/* tty related functions. */
static inline void byte_align(unsigned long dest, unsigned long src)
{
u16 *p_src;
volatile u16 *p_dest;
if (!(dest % 2) && !(src % 2)) {
p_dest = (u16 *)dest;
p_src = (u16 *)src;
*p_dest = (*p_dest & 0xFF00) | (*p_src & 0x00FF);
}
else if ((dest % 2) && (src % 2)) {
p_dest = (u16 *)(dest - 1);
p_src = (u16 *)(src - 1);
*p_dest = (*p_dest & 0x00FF) | (*p_src & 0xFF00);
}
else if (!(dest % 2) && (src % 2)) {
p_dest = (u16 *)dest;
p_src = (u16 *)(src - 1);
*p_dest = (*p_dest & 0xFF00) | ((*p_src >> 8) & 0x00FF);
}
else if ((dest % 2) && !(src % 2)) {
p_dest = (u16 *)(dest - 1);
p_src = (u16 *)src;
*p_dest = (*p_dest & 0x00FF) | ((*p_src << 8) & 0xFF00);
}
else {
dprintk("oops.~\n");
}
}
static inline void _memcpy(void *p_dest, const void *p_src, int size)
{
unsigned long dest = (unsigned long)p_dest;
unsigned long src = (unsigned long)p_src;
if (size <= 0) {
return;
}
if (dest & 1) {
byte_align(dest, src);
dest++, src++;
size--;
}
if (size & 1) {
byte_align(dest + size - 1, src + size - 1);
size--;
}
if (src & 1) {
unsigned char *s = (unsigned char *)src;
volatile u16 *d = (unsigned short *)dest;
size >>= 1;
while (size--) {
*d++ = s[0] | (s[1] << 8);
s += 2;
}
}
else {
u16 *s = (u16 *)src;
volatile u16 *d = (unsigned short *)dest;
size >>= 1;
while (size--) { *d++ = *s++; }
}
}
static inline int _memcmp(u8 *dest, u8 *src, int size)
{
int i = 0;
while (i++ < size) {
if (*(dest + i) != *(src + i)) {
return 1;
}
}
return 0;
}
static inline int WRITE_TO_DPRAM_VERIFY(u32 dest, void *src, int size)
{
int cnt = 3;
while (cnt--) {
_memcpy((void *)(SmemBase + dest), (void *)src, size);
if (!_memcmp((u8 *)(SmemBase + dest), (u8 *)src, size))
return 0;
}
return -1;
}
static inline int READ_FROM_DPRAM_VERIFY(void *dest, u32 src, int size)
{
int cnt = 3;
while (cnt--) {
_memcpy((void *)dest, (void *)(SmemBase + src), size);
if (!_memcmp((u8 *)dest, (u8 *)(SmemBase + src), size))
return 0;
}
return -1;
}
static void send_interrupt_to_phone(u16 irq_mask)
{
WRITE_TO_DPRAM(DPRAM_PDA2PHONE_INTERRUPT_ADDRESS, &irq_mask,
DPRAM_INTERRUPT_PORT_SIZE);
writel(1, MSM_A2M_INT(3));
// dprintk("PDA -> Phone interrupt!\n");
}
#ifdef NO_TTY_DPRAM
#define yisprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
void yhexdump(const char *buf, int len)
{
char str[80], octet[10];
int ofs, i, l;
// printk("<yhexdump()> : ADDR - [0x%08x], len -[%d]\n", buf, len);
for (ofs = 0; ofs < len; ofs += 16) {
sprintf( str, "%03d: ", ofs );
for (i = 0; i < 16; i++) {
if ((i + ofs) < len)
sprintf( octet, "%02x ", buf[ofs + i] );
else
strcpy( octet, " " );
strcat( str, octet );
}
strcat( str, " " );
l = strlen( str );
for (i = 0; (i < 16) && ((i + ofs) < len); i++)
str[l++] = yisprint( buf[ofs + i] ) ? buf[ofs + i] : '.';
str[l] = '\0';
printk( "%s\n", str );
}
}
EXPORT_SYMBOL(yhexdump);
char multipdp_rbuf[128 * 1024];
static int dpram_write(dpram_device_t *device, const unsigned char *buf, int len);
static int (*multipdp_rx_noti_func)(char *, int);
static inline int dpram_tty_insert_data(dpram_device_t *device, const u8 *psrc, u16 size);
int multipdp_buf_copy(int index, char *dpram, int size)
{
// int i;
if( index < 0 || index > sizeof(multipdp_rbuf) || (index + size) > sizeof(multipdp_rbuf))
return -1;
//printk("multipdp_buf_copy:index=%d size=%d\n", index, size);
memcpy( (void *)&multipdp_rbuf[index], (void *)dpram, size);
return( size);
}
EXPORT_SYMBOL(multipdp_buf_copy);
int multipdp_rx_noti_regi( int (*rx_cfunc)(char *, int))
{
multipdp_rx_noti_func = rx_cfunc;
return 0;
}
EXPORT_SYMBOL(multipdp_rx_noti_regi);
int multipdp_rx_datalen;
int multipdp_rx_data(dpram_device_t *device, int len)
{
static int inuse_flag = 0;
int ret = 0;
if( len == 0 )
return 0;
if( inuse_flag )
printk("***** inuse_flag = %d\n", inuse_flag);
inuse_flag ++;
//yhexdump(multipdp_rbuf, len);
//multipdp_rbuf
if( multipdp_rx_noti_func)
{
//printk("multipdp_rx_data Before(noti_func) : len=%d\n",len);
multipdp_rx_datalen = len;
ret = multipdp_rx_noti_func(multipdp_rbuf, len);
//memset(multipdp_rbuf, 0x00, len);
//printk("multipdp_rx_data After(noti_func) : ret=%d\n",ret);
}
inuse_flag --;
return(ret);
}
int multipdp_dump(void)
{
yhexdump(multipdp_rbuf, multipdp_rx_datalen);
return 0;
}
EXPORT_SYMBOL(multipdp_dump);
int multipdp_write(const unsigned char *buf, int len)
{
int i, ret;
// FORMATTED_INDEX : dpram0, RAW_INDEX : dpram1
dpram_device_t *device = &dpram_table[RAW_INDEX];
#ifdef NO_TTY_TX_RETRY
for(i =0; i<10; i++)
{
ret = dpram_write(device, buf, len);
if( ret > 0 )
{
break;
}
printk(KERN_DEBUG "dpram_write() failed: %d, i(%d)\n", ret, i);
}
if ( i>=10) {
printk(KERN_DEBUG "dpram_write() failed: %d\n", ret);
}
return ret;
#endif
}
EXPORT_SYMBOL(multipdp_write);
#endif
struct class *sec_class;
struct device *dpram_dev;
// hsil
#define POWER_DOWN_TIME ( 20 * HZ )
struct device *pm_dev;
void power_down_registertimer(struct timer_list* ptimer, unsigned long timeover );
void power_down_timeout(unsigned long arg);
struct timer_list power_down_timer;
// hsil for cpufreq
struct device *cpu_gov_dev;
static ssize_t show_info(struct device *d,
struct device_attribute *attr, char *buf)
{
char *p = buf;
u16 magic, enable;
u16 fmt_in_head, fmt_in_tail, fmt_out_head, fmt_out_tail;
u16 raw_in_head, raw_in_tail, raw_out_head, raw_out_tail;
u16 in_interrupt = 0, out_interrupt = 0;
#ifdef _ENABLE_ERROR_DEVICE
char err_buf[DPRAM_ERR_MSG_LEN];
#endif /* _ENABLE_ERROR_DEVICE */
READ_FROM_DPRAM((void *)&magic, DPRAM_MAGIC_CODE_ADDRESS, sizeof(magic));
READ_FROM_DPRAM((void *)&enable, DPRAM_ACCESS_ENABLE_ADDRESS, sizeof(enable));
READ_FROM_DPRAM((void *)&fmt_in_head, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS,
sizeof(fmt_in_head));
READ_FROM_DPRAM((void *)&fmt_in_tail, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS,
sizeof(fmt_in_tail));
READ_FROM_DPRAM((void *)&fmt_out_head, DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS,
sizeof(fmt_out_head));
READ_FROM_DPRAM((void *)&fmt_out_tail, DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS,
sizeof(fmt_out_tail));
READ_FROM_DPRAM((void *)&raw_in_head, DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS,
sizeof(raw_in_head));
READ_FROM_DPRAM((void *)&raw_in_tail, DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS,
sizeof(raw_in_tail));
READ_FROM_DPRAM((void *)&raw_out_head, DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS,
sizeof(raw_out_head));
READ_FROM_DPRAM((void *)&raw_out_tail, DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS,
sizeof(raw_out_tail));
READ_FROM_DPRAM((void *)&in_interrupt, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS,
DPRAM_INTERRUPT_PORT_SIZE);
READ_FROM_DPRAM((void *)&out_interrupt, DPRAM_PDA2PHONE_INTERRUPT_ADDRESS,
DPRAM_INTERRUPT_PORT_SIZE);
#ifdef _ENABLE_ERROR_DEVICE
memset((void *)err_buf, '\0', DPRAM_ERR_MSG_LEN);
memcpy(err_buf, dpram_err_buf, DPRAM_ERR_MSG_LEN - 1);
#endif /* _ENABLE_ERROR_DEVICE */
p += sprintf(p,
"-------------------------------------\n"
"| NAME\t\t\t| VALUE\n"
"-------------------------------------\n"
"| MAGIC CODE\t\t| 0x%04x\n"
"| ENABLE CODE\t\t| 0x%04x\n"
"| PHONE->PDA FMT HEAD\t| %u\n"
"| PHONE->PDA FMT TAIL\t| %u\n"
"| PDA->PHONE FMT HEAD\t| %u\n"
"| PDA->PHONE FMT TAIL\t| %u\n"
"| PHONE->PDA RAW HEAD\t| %u\n"
"| PHONE->PDA RAW TAIL\t| %u\n"
"| PDA->PHONE RAW HEAD\t| %u\n"
"| PDA->PHONE RAW TAIL\t| %u\n"
"| PHONE->PDA INT.\t| 0x%04x\n"
"| PDA->PHONE INT.\t| 0x%04x\n"
#ifdef _ENABLE_ERROR_DEVICE
"| LAST PHONE ERR MSG\t| %s\n"
#endif /* _ENABLE_ERROR_DEVICE */
"-------------------------------------\n",
magic, enable,
fmt_in_head, fmt_in_tail, fmt_out_head, fmt_out_tail,
raw_in_head, raw_in_tail, raw_out_head, raw_out_tail,
in_interrupt, out_interrupt,
#ifdef _ENABLE_ERROR_DEVICE
(buf[0] != '\0' ? buf : "NONE")
#endif /* _ENABLE_ERROR_DEVICE */
);
return p - buf;
}
static DEVICE_ATTR(info, S_IRUGO|S_IWUSR, show_info, NULL);
#ifdef CONFIG_DPRAM_WHITELIST
static ssize_t store_whitelist(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
int i;
switch (buf[0]) {
case 0x7F:
#if 0 //1
printk("SEND WHITELIST USING DPARM FMT COMMAND\n");
for(i=0; i<count; i++)
printk("%02x", buf[i]);
printk("\n");
#endif
dpram_write(&dpram_table[0], buf, count);
break;
default:
break;
}
return count;
}
static DEVICE_ATTR(whitelist, S_IRUGO|S_IWUSR, NULL, store_whitelist);
#endif
// hsil
static ssize_t store_power_down(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
int i;
char *after;
unsigned long value = simple_strtoul(buf, &after, 10);
if (value == 1)
{
printk("[HSIL] %s(%d)\n", __func__, __LINE__);
power_down_registertimer(&power_down_timer, POWER_DOWN_TIME);
}
return count;
}
static DEVICE_ATTR(power_down, S_IRUGO|S_IWUSR, NULL, store_power_down);
// hsil for cpufreq
static ssize_t store_cpu_gov(struct device *d,
struct device_attribute *attr, const char *buf, size_t count)
{
int i;
char *after;
unsigned long value = simple_strtoul(buf, &after, 10);
if (value == 1)
{
printk("[HSIL] %s(%d)\n", __func__, __LINE__);
cpufreq_direct_set_policy(0, "performance");
}
else if (value == 0)
{
printk("[HSIL] %s(%d)\n", __func__, __LINE__);
cpufreq_direct_set_policy(0, "ondemand");
}
else
printk("[HSIL] %s : No format\n", __func__);
return count;
}
static DEVICE_ATTR(cpu_gov, S_IRUGO|S_IWUSR, NULL, store_cpu_gov);
static int dpram_write(dpram_device_t *device,
const unsigned char *buf, int len)
{
int retval = 0;
int size = 0;
u16 head, tail;
u16 irq_mask = 0;
down(&write_mutex);
READ_FROM_DPRAM_VERIFY(&head, device->out_head_addr, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, device->out_tail_addr, sizeof(tail));
// +++++++++ head ---------- tail ++++++++++ //
if (head < tail) {
size = tail - head - 1;
size = (len > size) ? size : len;
WRITE_TO_DPRAM(device->out_buff_addr + head, buf, size);
retval = size;
}
// tail +++++++++++++++ head --------------- //
else if (tail == 0) {
size = device->out_buff_size - head - 1;
size = (len > size) ? size : len;
WRITE_TO_DPRAM(device->out_buff_addr + head, buf, size);
retval = size;
}
// ------ tail +++++++++++ head ------------ //
else {
size = device->out_buff_size - head;
size = (len > size) ? size : len;
WRITE_TO_DPRAM(device->out_buff_addr + head, buf, size);
retval = size;
if (len > retval) {
size = (len - retval > tail - 1) ? tail - 1 : len - retval;
WRITE_TO_DPRAM(device->out_buff_addr, buf + retval, size);
retval += size;
}
}
/* @LDK@ calculate new head */
head = (u16)((head + retval) % device->out_buff_size);
WRITE_TO_DPRAM_VERIFY(device->out_head_addr, &head, sizeof(head));
/* @LDK@ send interrupt to the phone, if.. */
irq_mask = INT_MASK_VALID;
if (retval > 0)
irq_mask |= device->mask_send;
if (len > retval)
irq_mask |= device->mask_req_ack;
send_interrupt_to_phone(irq_mask);
up(&write_mutex);
return retval;
}
static inline
int dpram_tty_insert_data(dpram_device_t *device, const u8 *psrc, u16 size)
{
#define CLUSTER_SEGMENT 1550
u16 copied_size = 0;
int retval = 0;
// ... ..... multipdp. .... raw data. ....
if (size > CLUSTER_SEGMENT){
while (size) {
copied_size = (size > CLUSTER_SEGMENT) ? CLUSTER_SEGMENT : size;
tty_insert_flip_string(device->serial.tty, psrc + retval, copied_size);
size = size - copied_size;
retval += copied_size;
}
return retval;
}
return tty_insert_flip_string(device->serial.tty, psrc, size);
}
static int dpram_read(dpram_device_t *device, const u16 non_cmd)
{
int retval = 0;
int size = 0;
u16 head, tail;
#ifdef NO_TTY_DPRAM
struct tty_struct *tty = device->serial.tty;
#endif
READ_FROM_DPRAM_VERIFY(&head, device->in_head_addr, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, device->in_tail_addr, sizeof(tail));
if (head != tail) {
u16 up_tail = 0;
// ------- tail ++++++++++++ head -------- //
if (head > tail) {
size = head - tail;
#ifdef NO_TTY_DPRAM
if( tty->index != 1) { //index : 0=dpram0, 1=dpram1
retval = dpram_tty_insert_data(device, (unsigned char *)(SmemBase + (device->in_buff_addr + tail)), size);
}else { //2: dpram1
retval = multipdp_buf_copy( 0, (unsigned char *)(SmemBase + (device->in_buff_addr + tail)), size);
}
#endif
if (retval != size)
dprintk("Size Mismatch : Real Size = %d, Returned Size = %d\n", size, retval);
}
// +++++++ head ------------ tail ++++++++ //
else {
int tmp_size = 0;
// Total Size.
size = device->in_buff_size - tail + head;
// 1. tail -> buffer end.
tmp_size = device->in_buff_size - tail;
#ifdef NO_TTY_DPRAM
if( tty->index != 1) { //index : 0=dpram0, 1=dpram1
retval = dpram_tty_insert_data(device, (unsigned char *)(SmemBase + (device->in_buff_addr + tail)), tmp_size);
}
else {
retval = multipdp_buf_copy( 0, (unsigned char *)(SmemBase + (device->in_buff_addr + tail)), tmp_size);
}
#endif
// 2. buffer start -> head.
if (size > tmp_size) {
#ifdef NO_TTY_DPRAM
if( tty->index != 1) { //index : 0=dpram0, 1=dpram1
dpram_tty_insert_data(device, (unsigned char *)(SmemBase + device->in_buff_addr), size - tmp_size);
}
else {
multipdp_buf_copy( tmp_size, (unsigned char *)(SmemBase + device->in_buff_addr), size - tmp_size);
}
#endif
retval += (size - tmp_size);
}
}
/* new tail */
up_tail = (u16)((tail + retval) % device->in_buff_size);
WRITE_TO_DPRAM_VERIFY(device->in_tail_addr, &up_tail, sizeof(up_tail));
}
if (non_cmd & device->mask_req_ack)
send_interrupt_to_phone(INT_NON_COMMAND(device->mask_res_ack));
#ifdef NO_TTY_DPRAM
if( tty->index == 1)
multipdp_rx_data(device, retval);
#endif
return retval;
}
static void dpram_clear(void)
{
long i = 0;
unsigned long flags;
u16 value = 0;
dprintk("SmemBase = %x\n", (unsigned int)SmemBase);
/* @LDK@ clear DPRAM except interrupt area */
local_irq_save(flags);
for (i=DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS; i<DPRAM_SIZE - (DPRAM_INTERRUPT_PORT_SIZE * 2); i+=2)
{
*((u16 *)(SmemBase + i)) = 0;
}
//for LPM mode booting
*((u16 *)(SmemBase + DPRAM_PDA2PHONE_RAW_BUFFER_ADDRESS)) = 0x01;
local_irq_restore(flags);
READ_FROM_DPRAM(&value, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, sizeof(value));
}
static void dpram_init_and_report(void)
{
const u16 magic_code = 0x00aa;
// const u16 init_start = INT_COMMAND(INT_MASK_CMD_INIT_START);
// const u16 init_end = INT_COMMAND(INT_MASK_CMD_INIT_END);
const u16 init_end = INT_COMMAND(INT_MASK_CMD_INIT_END|INT_MASK_CP_AIRPLANE_BOOT|INT_MASK_CP_AP_ANDROID);
u16 ac_code = 0;
dprintk("start\n");
#if 0
/* @LDK@ send init start code to phone */
WRITE_TO_DPRAM(DPRAM_PDA2PHONE_INTERRUPT_ADDRESS,
&init_start, DPRAM_INTERRUPT_PORT_SIZE);
writel(1, MSM_A2M_INT(3));
/* @LDK@ write DPRAM disable code */
WRITE_TO_DPRAM(DPRAM_ACCESS_ENABLE_ADDRESS, &ac_code, sizeof(ac_code));
#endif
/* @LDK@ dpram clear */
dpram_clear();
/* @LDK@ write magic code */
WRITE_TO_DPRAM(DPRAM_MAGIC_CODE_ADDRESS, &magic_code, sizeof(magic_code));
/* @LDK@ write DPRAM enable code */
ac_code = 0x0001;
WRITE_TO_DPRAM(DPRAM_ACCESS_ENABLE_ADDRESS, &ac_code, sizeof(ac_code));
/* @LDK@ send init end code to phone */
WRITE_TO_DPRAM(DPRAM_PDA2PHONE_INTERRUPT_ADDRESS,
&init_end, DPRAM_INTERRUPT_PORT_SIZE);
writel(1, MSM_A2M_INT(3));
dprintk("finish\n");
}
static inline int dpram_get_read_available(dpram_device_t *device)
{
u16 head, tail;
READ_FROM_DPRAM_VERIFY(&head, device->in_head_addr, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, device->in_tail_addr, sizeof(tail));
dprintk("%s : head = 0x%x\n", __func__, head);
dprintk("%s : tail = 0x%x\n", __func__, tail);
return head - tail;
}
static void dpram_drop_data(dpram_device_t *device)
{
u16 head;
READ_FROM_DPRAM_VERIFY(&head, device->in_head_addr, sizeof(head));
WRITE_TO_DPRAM_VERIFY(device->in_tail_addr, &head, sizeof(head));
}
static void dpram_phone_on(void)
{
dprintk("\n");
dpram_init_and_report();
}
static void dpram_phone_off(void)
{
}
static int dpram_phone_getstatus(void)
{
return 0;
}
static void dpram_phone_reset(void)
{
const u16 reboot_magic_code = 0x3569;
u16 magic_read;
dprintk("[RAM DUMP] REBOOT_MAGIC_CODE\n");
READ_FROM_DPRAM((void *)&magic_read, DPRAM_MAGIC_CODE_ADDRESS, sizeof(magic_read));
dprintk("[RAM DUMP] Prev Magic Code : 0x%x\n", magic_read);
WRITE_TO_DPRAM(DPRAM_MAGIC_CODE_ADDRESS, &reboot_magic_code, sizeof(reboot_magic_code));
dprintk("[RAM DUMP] SMSM WRITE\n");
READ_FROM_DPRAM((void *)&magic_read, DPRAM_MAGIC_CODE_ADDRESS, sizeof(magic_read));
dprintk("[RAM DUMP] Cur Magic Code : 0x%x\n", magic_read);
msleep(100);
smsm_reset_modem(SMSM_SYSTEM_DOWNLOAD);
}
static void dpram_mem_rw(struct _mem_param *param)
{
/* @LDK@ write */
if (param->dir) {
WRITE_TO_DPRAM(param->addr, (void *)&param->data, sizeof(param->data));
}
/* @LDK@ read */
else {
READ_FROM_DPRAM((void *)&param->data, param->addr, sizeof(param->data));
}
}
static int dpram_get_dgs(void)
{
int nRet;
#if DGS_TEST
typedef struct{
UINT8 header_code[4]; // magic ?ѹ? // {0x7F,0xAA,0xAF,0x7E}
UINT8 model[20]; // ?𵨸?
UINT8 nature[40]; // ??????
UINT8 custt_code[8]; // ?Å·???
UINT8 date[14]; // ???? ??????
UINT8 charger[24]; // ??????
UINT8 version[20]; // SW ??????
UINT8 checksum[10]; // binary ckecksum
UINT8 crcsum[10]; // binary CRC
UINT8 Unique_Number[20]; // UN number
UINT8 mem_name[20]; // ?Þ¸??? ??
UINT8 sec_code[20]; //
UINT8 etc[40]; // ??Ÿ
}NAND_HEAD_INFO;
NAND_HEAD_INFO header_info = {{0x7F,0xAA,0xAF,0x7E}, {"GT-I5500"}, {"EUR-Open"}, {"TIM"},
{"2010-03-25"}, {"LKH"}, {"I5500AIJC3"}, {"C721"}, {"41D7"},
{"7011000003"}, {"KAC00200JM-D4YY000"}, {"1108-000061"}, {""}};
#endif
printk("[DPRAM] Start getting DGS info.\n");
#ifndef CONFIG_YAFFS_FS
if(bml_open == NULL || bml_acquireSM == NULL || ond_4k_read == NULL || bml_release == NULL)
{
printk(KERN_ERR "[DPRAM] bml functions are not initilized yet!!\n");
return -1;
}
if (bml_open(0, FSR_BML_FLAG_NONE) != FSR_BML_SUCCESS)
{
printk("[DPRAM] BML_Open Error !!\n");
return -1;
}
printk("[DPRAM] Try to read DGS Info.\n");
/* Acquire SM */
bml_acquireSM(0);
/*
* Read DGS Page
* Read 5th Page of 2047 Block
*/
nRet = ond_4k_read(0, 2047, 5, aDGSBuf, NULL, FSR_LLD_FLAG_ECC_OFF);
/* Release SM*/
bml_release(0);
#else /* CONFIG_YAFFS_FS */
nRet = msm_onenand_read_dpram(aDGSBuf, sizeof(aDGSBuf));
#endif /* CONFIG_YAFFS_FS */
#if DGS_TEST
memcpy(aDGSBuf, &header_info,sizeof(NAND_HEAD_INFO));
#endif
#ifndef CONFIG_YAFFS_FS
if(nRet != FSR_LLD_SUCCESS)
#else /* CONFIG_YAFFS_FS */
if(0 != nRet)
#endif /* CONFIG_YAFFS_FS */
{
printk("[DPRAM] Reading DGS information failed !!\n");
return -1;
}
printk("[DPRAM] DSG buffer = %s \n", aDGSBuf);
printk("[OneDRAM] %s complete\n", __func__);
return 0;
}
static void dpram_ramdump(void)
{
printk("[DPRAM] RAMDUMP MODE START!\n");
// printk("[DPRAM] write 0xCCCC flag at MSM_SHARED_RAM_BASE + 0x30 address\n");
// printk("[DPRAM] MSM_SHARED_RAM_BASE: 0x%08x\n", (unsigned int) MSM_SHARED_RAM_BASE);
writel(0xCCCC, MSM_SHARED_RAM_BASE + 0x30);
printk("[DPRAM] call msm_proc_comm_reset_modem_now func\n");
msm_proc_comm_reset_modem_now();
}
static void print_smem(void)
{
u16 magic, enable;
u16 fmt_in_head, fmt_in_tail, fmt_out_head, fmt_out_tail;
u16 raw_in_head, raw_in_tail, raw_out_head, raw_out_tail;
u16 in_interrupt = 0, out_interrupt = 0;
u8 raw_out_buf;
// u16 dump_data = 0;
#if 1
struct file *filp;
// int i;
int writelen;
mm_segment_t old_fs;
static char buf[1024*32];
// static int buf[1024];
int count, chr_count;
// char *src;
// fl_owner_t id;
#endif
READ_FROM_DPRAM((void *)&magic, DPRAM_MAGIC_CODE_ADDRESS, sizeof(magic));
READ_FROM_DPRAM((void *)&enable, DPRAM_ACCESS_ENABLE_ADDRESS, sizeof(enable));
READ_FROM_DPRAM((void *)&fmt_in_head, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS, sizeof(fmt_in_head));
READ_FROM_DPRAM((void *)&fmt_in_tail, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS, sizeof(fmt_in_tail));
READ_FROM_DPRAM((void *)&fmt_out_head, DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS, sizeof(fmt_out_head));
READ_FROM_DPRAM((void *)&fmt_out_tail, DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS, sizeof(fmt_out_tail));
READ_FROM_DPRAM((void *)&raw_in_head, DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS, sizeof(raw_in_head));
READ_FROM_DPRAM((void *)&raw_in_tail, DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS, sizeof(raw_in_tail));
READ_FROM_DPRAM((void *)&raw_out_head, DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS, sizeof(raw_out_head));
READ_FROM_DPRAM((void *)&raw_out_tail, DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS, sizeof(raw_out_tail));
READ_FROM_DPRAM((void *)&raw_out_buf, DPRAM_PDA2PHONE_RAW_BUFFER_ADDRESS, sizeof(raw_out_buf));
READ_FROM_DPRAM((void *)&in_interrupt, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, DPRAM_INTERRUPT_PORT_SIZE);
READ_FROM_DPRAM((void *)&out_interrupt, DPRAM_PDA2PHONE_INTERRUPT_ADDRESS, DPRAM_INTERRUPT_PORT_SIZE);
dprintk("\n");
printk("#####################################\n");
printk("######### DPRAM DUMP DATA (0604) #########\n");
printk("#####################################\n");
printk("-------------------------------------\n"
"| NAME\t\t\t| VALUE\n"
"-------------------------------------\n"
"| MAGIC CODE\t\t| 0x%04x\n"
"| ENABLE CODE\t\t| 0x%04x\n"
"| PHONE->PDA FMT HEAD\t| %u\n"
"| PHONE->PDA FMT TAIL\t| %u\n"
"| PDA->PHONE FMT HEAD\t| %u\n"
"| PDA->PHONE FMT TAIL\t| %u\n"
"| PHONE->PDA RAW HEAD\t| %u\n"
"| PHONE->PDA RAW TAIL\t| %u\n"
"| PDA->PHONE RAW HEAD\t| %u\n"
"| PDA->PHONE RAW TAIL\t| %u\n"
"| PDA->PHONE RAW BUFF\t| %u\n"
"| PHONE->PDA INT.\t| 0x%04x\n"
"| PDA->PHONE INT.\t| 0x%04x\n"
"-------------------------------------\n",
magic, enable,
fmt_in_head, fmt_in_tail, fmt_out_head, fmt_out_tail,
raw_in_head, raw_in_tail, raw_out_head, raw_out_tail, raw_out_buf,
in_interrupt, out_interrupt
);
#if 1
// id = current->files;
count = 1024 * 8;
chr_count = 0;
old_fs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open("/sdcard/dpram_dump",O_CREAT|O_WRONLY,0666);
if(!filp)
printk("Can't creat /sdcard/dpram_dump file\n");
else
{
memcpy((void *)buf, (void *)SmemBase, 1024*32);
writelen = filp->f_op->write(filp,(void *)buf,1024*32,&filp->f_pos);
}
set_fs(old_fs);
#if 0
printk("\n");
printk("#####################################\n");
printk("# PDA2PHONE FORMATTED BUFFER DUMP #\n");
printk("#####################################\n");
printk("-------------------------------------\n");
printk("|\tADDRESS\t|\tVALUE\t|\n");
printk("-------------------------------------\n");
for (i=DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS; i<DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS; i=i+0x0002)
{
READ_FROM_DPRAM((void *)&dump_data, i, sizeof(dump_data));
printk("|\t0x%04x\t|\t0x%04x\t\t\n", i, dump_data);
}
printk("\n");
printk("#####################################\n");
printk("# PDA2PHONE RAW BUFFER DUMP #\n");
printk("#####################################\n");
printk("-------------------------------------\n");
printk("|\tADDRESS\t|\tVALUE\t|\n");
printk("-------------------------------------\n");
for (i=DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS; i<DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS; i=i+0x0002)
{
READ_FROM_DPRAM((void *)&dump_data, i, sizeof(dump_data));
printk("| 0x%04x\t\t| 0x%04x\t\n", i, dump_data);
}
printk("\n");
printk("#####################################\n");
printk("# PHONE2PDA FORMATTED BUFFER DUMP #\n");
printk("#####################################\n");
printk("-------------------------------------\n");
printk("|\tADDRESS\t|\tVALUE\t|\n");
printk("-------------------------------------\n");
for (i=DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS; i<DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS; i=i+0x0002)
{
READ_FROM_DPRAM((void *)&dump_data, i, sizeof(dump_data));
printk("| 0x%04x\t\t| 0x%04x\t\n", i, dump_data);
}
printk("\n");
printk("#####################################\n");
printk("# PHONE2PDA RAW BUFFER DUMP #\n");
printk("#####################################\n");
printk("-------------------------------------\n");
printk("|\tADDRESS\t|\tVALUE\t|\n");
printk("-------------------------------------\n");
for (i=DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS; i<DPRAM_PDA2PHONE_INTERRUPT_ADDRESS; i=i+0x0002)
{
READ_FROM_DPRAM((void *)&dump_data, i, sizeof(dump_data));
printk("| 0x%04x\t\t| 0x%04x\t\n", i, dump_data);
}
#endif
#endif
}
void request_phone_power_off_reset(int flag);
/* dpram tty file operations. */
static int dpram_tty_open(struct tty_struct *tty, struct file *file)
{
dpram_device_t *device = &dpram_table[tty->index];
dprintk("tty->index = %d\n", tty->index);
device->serial.tty = tty;
device->serial.open_count++;
if (device->serial.open_count > 1) {
device->serial.open_count--;
return -EBUSY;
}
#if 1 // hobac.
if (tty->index == 1) // dpram1
{
struct termios termios;
mm_segment_t oldfs;
oldfs = get_fs(); set_fs(get_ds());
if (file->f_op->ioctl)
{
file->f_op->ioctl(file->f_dentry->d_inode, file,
TCGETA, (unsigned long)&termios);
}
else if (file->f_op->unlocked_ioctl)
{
file->f_op->unlocked_ioctl(file, TCGETA, (unsigned long)&termios);
}
set_fs(oldfs);
termios.c_cflag = CS8 | CREAD | HUPCL | CLOCAL | B115200;
termios.c_iflag = IGNBRK | IGNPAR;
termios.c_lflag = 0;
termios.c_oflag = 0;
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 1;
oldfs = get_fs(); set_fs(get_ds());
if (file->f_op->ioctl)
{
file->f_op->ioctl(file->f_dentry->d_inode, file,
TCSETA, (unsigned long)&termios);
}
else if (file->f_op->unlocked_ioctl)
{
file->f_op->unlocked_ioctl(file, TCSETA, (unsigned long)&termios);
}
set_fs(oldfs);
}
#endif
tty->driver_data = (void *)device;
tty->low_latency = 1;
return 0;
}
static void dpram_tty_close(struct tty_struct *tty, struct file *file)
{
dpram_device_t *device = (dpram_device_t *)tty->driver_data;
if (device && (device == &dpram_table[tty->index])) {
down(&device->serial.sem);
device->serial.open_count--;
device->serial.tty = NULL;
up(&device->serial.sem);
}
}
static int dpram_tty_write(struct tty_struct *tty,
const unsigned char *buffer, int count)
{
dpram_device_t *device = (dpram_device_t *)tty->driver_data;
if (!device) {
return 0;
}
return dpram_write(device, buffer, count);
}
static int dpram_tty_write_room(struct tty_struct *tty)
{
int avail;
u16 head, tail;
dpram_device_t *device = (dpram_device_t *)tty->driver_data;
if (device != NULL) {
READ_FROM_DPRAM_VERIFY(&head, device->out_head_addr, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, device->out_tail_addr, sizeof(tail));
avail = (head < tail) ? tail - head - 1 :
device->out_buff_size + tail - head - 1;
return avail;
}
return 0;
}
static int dpram_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int val;
switch (cmd) {
case HN_DPRAM_PHONE_ON:
if (DpramInited)
{
dprintk("Doubled Phone On Cmd : do nothing\n");
return 0;
}
dprintk("[Version 3] HN_DPRAM_PHONE_ON\n");
dpram_phone_on();
DpramInited = 1;
return 0;
case HN_DPRAM_PHONE_OFF:
dprintk("HN_DPRAM_PHONE_OFF\n");
dpram_phone_off();
return 0;
case HN_DPRAM_PHONE_GETSTATUS:
dprintk("HN_DPRAM_PHONE_GETSTATUS\n");
val = dpram_phone_getstatus();
return copy_to_user((unsigned int *)arg, &val, sizeof(val));
case HN_DPRAM_PHONE_RESET:
dprintk("[RAM DUMP]HN_DPRAM_PHONE_RESET\n");
dpram_phone_reset();
return 0;
case HN_DPRAM_MEM_RW:
{
struct _mem_param param;
dprintk("HN_DPRAM_MEM_RW\n");
val = copy_from_user((void *)&param, (void *)arg, sizeof(param));
dpram_mem_rw(&param);
if (!param.dir) {
return copy_to_user((unsigned long *)arg, &param, sizeof(param));
}
return 0;
}
case HN_DPRAM_DUMP:
print_smem();
return 0;
case HN_DPRAM_WAKELOCK:
wake_lock(&imei_wake_lock);
return 0;
case HN_DPRAM_WAKEUNLOCK:
wake_unlock(&imei_wake_lock);
return 0;
case DPRAM_GET_DGS_INFO:
printk("[%s] DGS ioctl called\n", __func__);
if( !dpram_get_dgs() )
return copy_to_user((unsigned long *)arg, aDGSBuf, 256);
else
printk(KERN_ERR "[%s] Get DGS info failed\n", __func__);
return 0;
case HN_DPRAM_RAMDUMP:
dpram_ramdump();
return 0;
default:
dprintk("default\n");
break;
}
return -ENOIOCTLCMD;
}
static int dpram_tty_chars_in_buffer(struct tty_struct *tty)
{
int data;
u16 head, tail;
dpram_device_t *device = (dpram_device_t *)tty->driver_data;
if (device != NULL) {
READ_FROM_DPRAM_VERIFY(&head, device->out_head_addr, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, device->out_tail_addr, sizeof(tail));
data = (head > tail) ? head - tail - 1 :
device->out_buff_size - tail + head;
return data;
}
return 0;
}
#ifdef _ENABLE_ERROR_DEVICE
static int dpram_err_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long flags;
ssize_t ret;
size_t ncopy;
add_wait_queue(&dpram_err_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
while (1) {
local_irq_save(flags);
if (dpram_err_len) {
ncopy = min(count, dpram_err_len);
if (copy_to_user(buf, dpram_err_buf, ncopy)) {
ret = -EFAULT;
}
else {
ret = ncopy;
}
dpram_err_len = 0;
local_irq_restore(flags);
break;
}
local_irq_restore(flags);
if (filp->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
schedule();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&dpram_err_wait_q, &wait);
return ret;
}
static int dpram_err_fasync(int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, &dpram_err_async_q);
}
static unsigned int dpram_err_poll(struct file *filp,
struct poll_table_struct *wait)
{
poll_wait(filp, &dpram_err_wait_q, wait);
return ((dpram_err_len) ? (POLLIN | POLLRDNORM) : 0);
}
#endif /* _ENABLE_ERROR_DEVICE */
/* handlers. */
static void res_ack_tasklet_handler(unsigned long data)
{
dpram_device_t *device = (dpram_device_t *)data;
if (device && device->serial.tty) {
struct tty_struct *tty = device->serial.tty;
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc->ops->write_wakeup) {
(tty->ldisc->ops->write_wakeup)(tty);
}
wake_up_interruptible(&tty->write_wait);
}
}
static void send_tasklet_handler(unsigned long data)
{
dpram_tasklet_data_t *tasklet_data = (dpram_tasklet_data_t *)data;
dpram_device_t *device = tasklet_data->device;
u16 non_cmd = tasklet_data->non_cmd;
int ret = 0;
if (device != NULL && device->serial.tty) {
struct tty_struct *tty = device->serial.tty;
ret = dpram_read(device, non_cmd);
if (ret == -EAGAIN) {
if (non_cmd & INT_MASK_SEND_F) tasklet_schedule(&fmt_send_tasklet);
if (non_cmd & INT_MASK_SEND_R) tasklet_schedule(&raw_send_tasklet);
return ;
}
#ifdef NO_TTY_DPRAM
if( tty->index != 1) //index : 0=dpram0, 1=dpram1
#endif
tty_flip_buffer_push(tty);
}
else {
dpram_drop_data(device);
}
}
static void cmd_req_active_handler(void)
{
send_interrupt_to_phone(INT_COMMAND(INT_MASK_CMD_RES_ACTIVE));
}
static void cmd_error_display_handler(void)
{
char buf[DPRAM_ERR_MSG_LEN];
#ifdef _ENABLE_ERROR_DEVICE
unsigned long flags;
#endif /* _ENABLE_ERROR_DEVICE */
//for silent reset
printk("[DPRAM] %s : silent reset,\n", __func__);
wake_lock(&silent_wake_lock);
memset((void *)buf, 0, sizeof (buf));
buf[0] = '1';
buf[1] = ' ';
READ_FROM_DPRAM((buf + 2), DPRAM_PHONE2PDA_FORMATTED_BUFFER_ADDRESS,
sizeof (buf) - 3);
dprintk("[PHONE ERROR] ->> %s\n", buf);
#ifdef _ENABLE_ERROR_DEVICE
local_irq_save(flags);
memcpy(dpram_err_buf, buf, DPRAM_ERR_MSG_LEN);
dpram_err_len = 64;
local_irq_restore(flags);
wake_up_interruptible(&dpram_err_wait_q);
kill_fasync(&dpram_err_async_q, SIGIO, POLL_IN);
#endif /* _ENABLE_ERROR_DEVICE */
}
static void cmd_phone_start_handler(void)
{
dprintk("\n");
dpram_init_and_report();
}
static void cmd_req_time_sync_handler(void)
{
/* TODO: add your codes here.. */
}
static void cmd_phone_deep_sleep_handler(void)
{
/* TODO: add your codes here.. */
}
static void cmd_nv_rebuilding_handler(void)
{
/* TODO: add your codes here.. */
}
static void cmd_emer_down_handler(void)
{
/* TODO: add your codes here.. */
}
static void cmd_chg_detect_noti(void)
{
u16 value;
u16 irq_clear = 0x0000;
get_charger_type();
get_charging_status();
msm_batt_check_event();
READ_FROM_DPRAM(&value, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, sizeof(value));
if(value == 0x40C0) {
WRITE_TO_DPRAM(DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, &irq_clear, DPRAM_INTERRUPT_PORT_SIZE);
printk("[DPRAM:%s] chg_detect irq: 0x%x cleared.\n", __func__, value);
}
else {
READ_FROM_DPRAM(&value, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, sizeof(value));
printk("[DPRAM:%s] changed irq: 0x%x detected.\n", __func__, value);
}
}
static void cmd_chg_state_changed(void)
{
u16 value;
u16 irq_clear = 0x0000;
get_charging_status();
msm_batt_check_event();
READ_FROM_DPRAM(&value, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, sizeof(value));
if(value == 0x50C0) {
WRITE_TO_DPRAM(DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, &irq_clear, DPRAM_INTERRUPT_PORT_SIZE);
printk("[DPRAM:%s] chg_state irq: 0x%x cleared.\n", __func__, value);
}
else {
READ_FROM_DPRAM(&value, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, sizeof(value));
printk("[DPRAM:%s] changed irq: 0x%x detected.\n", __func__, value);
}
}
static void command_handler(u16 cmd)
{
switch (cmd) {
case INT_MASK_CMD_REQ_ACTIVE:
cmd_req_active_handler();
break;
case INT_MASK_CMD_ERR_DISPLAY:
cmd_error_display_handler();
break;
case INT_MASK_CMD_PHONE_START:
cmd_phone_start_handler();
break;
case INT_MASK_CMD_REQ_TIME_SYNC:
cmd_req_time_sync_handler();
break;
case INT_MASK_CMD_PHONE_DEEP_SLEEP:
cmd_phone_deep_sleep_handler();
break;
case INT_MASK_CMD_NV_REBUILDING:
cmd_nv_rebuilding_handler();
break;
case INT_MASK_CMD_EMER_DOWN:
cmd_emer_down_handler();
break;
case INT_MASK_CMD_CHG_DETECT_NOTI:
cmd_chg_detect_noti();
break;
case INT_MASK_CMD_CHG_STATE_CHANGED:
cmd_chg_state_changed();
break;
default:
dprintk("Unknown command..\n");
}
}
static void non_command_handler(u16 non_cmd)
{
u16 head, tail;
/* @LDK@ formatted check. */
READ_FROM_DPRAM_VERIFY(&head, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS, sizeof(tail));
if (head != tail)
non_cmd |= INT_MASK_SEND_F;
/* @LDK@ raw check. */
READ_FROM_DPRAM_VERIFY(&head, DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS, sizeof(tail));
if (head != tail)
non_cmd |= INT_MASK_SEND_R;
/* @LDK@ +++ scheduling.. +++ */
if (non_cmd & INT_MASK_SEND_F) {
wake_lock_timeout(&dpram_wake_lock, HZ/2);
dpram_tasklet_data[FORMATTED_INDEX].device = &dpram_table[FORMATTED_INDEX];
dpram_tasklet_data[FORMATTED_INDEX].non_cmd = non_cmd;
fmt_send_tasklet.data = (unsigned long)&dpram_tasklet_data[FORMATTED_INDEX];
tasklet_schedule(&fmt_send_tasklet);
}
if (non_cmd & INT_MASK_SEND_R) {
wake_lock_timeout(&dpram_wake_lock, 6*HZ);
dpram_tasklet_data[RAW_INDEX].device = &dpram_table[RAW_INDEX];
dpram_tasklet_data[RAW_INDEX].non_cmd = non_cmd;
raw_send_tasklet.data = (unsigned long)&dpram_tasklet_data[RAW_INDEX];
/* @LDK@ raw buffer op. -> soft irq level. */
tasklet_hi_schedule(&raw_send_tasklet);
}
if (non_cmd & INT_MASK_RES_ACK_F) {
wake_lock_timeout(&dpram_wake_lock, HZ/2);
tasklet_schedule(&fmt_res_ack_tasklet);
}
if (non_cmd & INT_MASK_RES_ACK_R) {
wake_lock_timeout(&dpram_wake_lock, 6*HZ);
tasklet_hi_schedule(&raw_res_ack_tasklet);
}
}
static inline
void check_int_pin_level(void)
{
}
/* @LDK@ interrupt handlers. */
static irqreturn_t dpram_interrupt(int irq, void *dev_id)
{
u16 irq_mask = 0;
#if 0
u16 fih, fit, foh, fot;
u16 rih, rit, roh, rot;
#endif
dprintk("%s : interrupt handler\n", __func__);
// wake_lock_timeout(&dpram_wake_lock, HZ/2);
READ_FROM_DPRAM(&irq_mask, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, sizeof(irq_mask));
#if 0
printk("=====>[%s,%d] irq_mask: %x\n", __func__, __LINE__, irq_mask);
READ_FROM_DPRAM_VERIFY(&fih, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS, sizeof(fih));
READ_FROM_DPRAM_VERIFY(&fit, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS, sizeof(fit));
READ_FROM_DPRAM_VERIFY(&foh, DPRAM_PDA2PHONE_FORMATTED_HEAD_ADDRESS, sizeof(foh));
READ_FROM_DPRAM_VERIFY(&fot, DPRAM_PDA2PHONE_FORMATTED_TAIL_ADDRESS, sizeof(fot));
READ_FROM_DPRAM_VERIFY(&rih, DPRAM_PHONE2PDA_RAW_HEAD_ADDRESS, sizeof(rih));
READ_FROM_DPRAM_VERIFY(&rit, DPRAM_PHONE2PDA_RAW_TAIL_ADDRESS, sizeof(rit));
READ_FROM_DPRAM_VERIFY(&roh, DPRAM_PDA2PHONE_RAW_HEAD_ADDRESS, sizeof(roh));
READ_FROM_DPRAM_VERIFY(&rot, DPRAM_PDA2PHONE_RAW_TAIL_ADDRESS, sizeof(rot));
printk("\n fmt_in H:%4d, T:%4d\n fmt_out H:%4d, T:%4d\n raw_in H:%4d, T:%4d\n raw out H:%4d, T:%4d\n", fih, fit, foh, fot, rih, rit, roh, rot);
#endif
/* valid bit verification. @LDK@ */
if (!(irq_mask & INT_MASK_VALID)) {
dprintk("Invalid interrupt mask: 0x%04x\n", irq_mask);
return IRQ_NONE;
}
/* command or non-command? @LDK@ */
if (irq_mask & INT_MASK_COMMAND) {
irq_mask &= ~(INT_MASK_VALID | INT_MASK_COMMAND);
wake_lock_timeout(&dpram_wake_lock, HZ/2);
command_handler(irq_mask);
}
else {
irq_mask &= ~INT_MASK_VALID;
non_command_handler(irq_mask);
//wake_lock_timeout(&dpram_wake_lock, 6*HZ);
}
return IRQ_HANDLED;
}
#if 0
static irqreturn_t phone_active_interrupt(int irq, void *dev_id)
{
return IRQ_HANDLED;
}
#endif
/* basic functions. */
#ifdef _ENABLE_ERROR_DEVICE
static struct file_operations dpram_err_ops = {
.owner = THIS_MODULE,
.read = dpram_err_read,
.fasync = dpram_err_fasync,
.poll = dpram_err_poll,
.llseek = no_llseek,
/* TODO: add more operations */
};
#endif /* _ENABLE_ERROR_DEVICE */
static struct tty_operations dpram_tty_ops = {
.open = dpram_tty_open,
.close = dpram_tty_close,
.write = dpram_tty_write,
.write_room = dpram_tty_write_room,
.ioctl = dpram_tty_ioctl,
.chars_in_buffer = dpram_tty_chars_in_buffer,
/* TODO: add more operations */
};
#ifdef _ENABLE_ERROR_DEVICE
static void unregister_dpram_err_device(void)
{
unregister_chrdev(DRIVER_MAJOR_NUM, DPRAM_ERR_DEVICE);
class_destroy(dpram_class);
}
static int register_dpram_err_device(void)
{
/* @LDK@ 1 = formatted, 2 = raw, so error device is '0' */
struct device *dpram_err_dev_t;
int ret = register_chrdev(DRIVER_MAJOR_NUM, DPRAM_ERR_DEVICE, &dpram_err_ops);
if ( ret < 0 )
{
return ret;
}
dpram_class = class_create(THIS_MODULE, "err");
if (IS_ERR(dpram_class))
{
unregister_dpram_err_device();
return -EFAULT;
}
dpram_err_dev_t = device_create(dpram_class, NULL,
MKDEV(DRIVER_MAJOR_NUM, 0), NULL, DPRAM_ERR_DEVICE);
if (IS_ERR(dpram_err_dev_t))
{
unregister_dpram_err_device();
return -EFAULT;
}
return 0;
}
#endif /* _ENABLE_ERROR_DEVICE */
static int register_dpram_driver(void)
{
int retval = 0;
/* @LDK@ allocate tty driver */
dpram_tty_driver = alloc_tty_driver(MAX_INDEX);
if (!dpram_tty_driver) {
return -ENOMEM;
}
/* @LDK@ initialize tty driver */
dpram_tty_driver->owner = THIS_MODULE;
dpram_tty_driver->magic = TTY_DRIVER_MAGIC;
dpram_tty_driver->driver_name = DRIVER_NAME;
dpram_tty_driver->name = "dpram";
dpram_tty_driver->major = DRIVER_MAJOR_NUM;
dpram_tty_driver->minor_start = 1;
dpram_tty_driver->num = 2; // original
dpram_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
dpram_tty_driver->subtype = SERIAL_TYPE_NORMAL;
dpram_tty_driver->flags = TTY_DRIVER_REAL_RAW;
dpram_tty_driver->init_termios = tty_std_termios;
dpram_tty_driver->init_termios.c_cflag =
(B115200 | CS8 | CREAD | CLOCAL | HUPCL);
tty_set_operations(dpram_tty_driver, &dpram_tty_ops);
dpram_tty_driver->ttys = dpram_tty;
dpram_tty_driver->termios = dpram_termios;
dpram_tty_driver->termios_locked = dpram_termios_locked;
/* @LDK@ register tty driver */
retval = tty_register_driver(dpram_tty_driver);
if (retval) {
dprintk("tty_register_driver error\n");
put_tty_driver(dpram_tty_driver);
return retval;
}
return 0;
}
static void unregister_dpram_driver(void)
{
tty_unregister_driver(dpram_tty_driver);
}
static void init_devices(void)
{
int i;
for (i = 0; i < MAX_INDEX; i++) {
init_MUTEX(&dpram_table[i].serial.sem);
dpram_table[i].serial.open_count = 0;
dpram_table[i].serial.tty = NULL;
}
}
static void kill_tasklets(void)
{
tasklet_kill(&fmt_res_ack_tasklet);
tasklet_kill(&raw_res_ack_tasklet);
tasklet_kill(&fmt_send_tasklet);
tasklet_kill(&raw_send_tasklet);
}
static int register_interrupt_handler(void)
{
int retval = 0;
/* @LDK@ interrupt area read - pin level will be driven high. */
dprintk("Dpram clear start\n");
dpram_clear();
/* @LDK@ Phone active INT. */
/* @LDK@ dpram interrupt */
retval = request_irq(INT_A9_M2A_3, dpram_interrupt, IRQF_TRIGGER_RISING, DRIVER_NAME, NULL);
if (retval) {
dprintk("DPRAM interrupt handler failed.\n");
unregister_dpram_driver();
return -1;
}
dprintk("INT_A9_M2A_3 interrupt handler success\n");
#if 0
/* @LDK@ phone active interrupt */
retval = request_irq(phone_active_irq, phone_active_interrupt,
IRQF_DISABLED, "Phone Active", NULL);
if (retval) {
dprintk("Phone active interrupt handler failed.\n");
free_irq(dpram_irq, NULL);
unregister_dpram_driver();
return -1;
}
#endif
return 0;
}
static void check_miss_interrupt(void)
{
unsigned long flags;
u16 head, tail;
u16 value;
dprintk("%s\n", __func__);
READ_FROM_DPRAM_VERIFY(&head, DPRAM_PHONE2PDA_FORMATTED_HEAD_ADDRESS, sizeof(head));
READ_FROM_DPRAM_VERIFY(&tail, DPRAM_PHONE2PDA_FORMATTED_TAIL_ADDRESS, sizeof(tail));
dprintk("%s : head = 0x%x\n", __func__, head);
dprintk("%s : tail = 0x%x\n", __func__, tail);
if (head != tail)
{
dprintk("there is a missed interrupt. try to read it!\n");
printk("[DPRAM:%s] there is a missed interrupt. try to read it!\n", __func__);
local_irq_save(flags);
dpram_interrupt(INT_A9_M2A_3, NULL);
local_irq_restore(flags);
}
READ_FROM_DPRAM(&value, DPRAM_PHONE2PDA_INTERRUPT_ADDRESS, sizeof(value));
if(value == 0x40C0 || value == 0x50C0) {
printk("[DPRAM:%s] there is a missed battery interrupt. try to read it!\n", __func__);
local_irq_save(flags);
dpram_interrupt(INT_A9_M2A_3, NULL);
local_irq_restore(flags);
}
}
static int dpram_suspend(struct platform_device *dev, pm_message_t state)
{
return 0;
}
static int dpram_resume(struct platform_device *dev)
{
check_miss_interrupt();
return 0;
}
enum {
RESET,
POWEROFF,
};
void request_phone_power_off_reset(int flag)
{
unsigned char fmt_cmd_reset[12] = {0x7F, 0x0A, 0x00, 0x5C, 0x07, 0x00, 0x5C, 0x00, 0x01, 0x03, 0x05, 0x7E};
unsigned char fmt_cmd_powoff[12] = {0x7F, 0x0A, 0x00, 0x5D, 0x07, 0x00, 0x5D, 0x00, 0x01, 0x02, 0x01, 0x7E};
switch(flag) {
case RESET :
printk("Dpram Reset's called\n");
dpram_write(&dpram_table[0], fmt_cmd_reset, sizeof(fmt_cmd_reset));
break;
case POWEROFF :
printk("Dpram Poweroff's called\n");
dpram_write(&dpram_table[0], fmt_cmd_powoff, sizeof(fmt_cmd_powoff));
break;
}
}
static int __devinit dpram_probe(struct platform_device *dev)
{
int retval;
/* allocate smem dpram area */
dprintk("SMEM_DPRAM allocation\n");
SmemBase = (volatile unsigned char *)(smem_alloc(SMEM_ID_VENDOR0, 0x4000*2));
if (!SmemBase)
{
dprintk("smem_alloc failed : SmemBase = 0x%x\n", (unsigned int)SmemBase);
return -1;
}
dprintk("SmemBase = 0x%x\n", (unsigned int)SmemBase);
/* @LDK@ register dpram (tty) driver */
retval = register_dpram_driver();
if (retval) {
dprintk("Failed to register dpram (tty) driver.\n");
return -1;
}
#ifdef _ENABLE_ERROR_DEVICE
/* @LDK@ register dpram error device */
retval = register_dpram_err_device();
if (retval) {
dprintk("Failed to register dpram error device.\n");
unregister_dpram_driver();
return -1;
}
memset((void *)dpram_err_buf, '\0', sizeof dpram_err_buf);
#endif /* _ENABLE_ERROR_DEVICE */
/* @LDK@ register interrupt handler */
dprintk("Register interrupt handler\n");
if ((retval = register_interrupt_handler()) < 0) {
return -1;
}
/* @LDK@ initialize device table */
init_devices();
/* @LDK@ check out missing interrupt from the phone */
//check_miss_interrupt();
return 0;
}
static int __devexit dpram_remove(struct platform_device *dev)
{
/* @LDK@ unregister dpram (tty) driver */
unregister_dpram_driver();
#ifdef _ENABLE_ERROR_DEVICE
/* @LDK@ unregister dpram error device */
unregister_dpram_err_device();
#endif
kill_tasklets();
return 0;
}
static struct platform_driver platform_dpram_driver = {
.probe = dpram_probe,
.remove = __devexit_p(dpram_remove),
.suspend = dpram_suspend,
.resume = dpram_resume,
.driver = {
.name = "dpram",
},
};
struct smem_info {
unsigned int info;
};
struct smem_info *smem_flag;
int silent_value = 0;
int dump_enable_flag = 0;
EXPORT_SYMBOL(dump_enable_flag);
// hsil
void power_down_registertimer(struct timer_list* ptimer, unsigned long timeover )
{
printk("%s\n",__func__);
init_timer(ptimer);
ptimer->expires = get_jiffies_64() + timeover;
ptimer->data = (long) NULL;
ptimer->function = power_down_timeout;
add_timer(ptimer);
}
void power_down_timeout(unsigned long arg)
{
printk("%s\n",__func__);
smem_flag->info = 0xAEAEAEAE;
msm_proc_comm_reset_modem_now();
}
static int silent_read_proc_debug(char *page, char **start, off_t offset,
int count, int *eof, void *data)
{
*eof = 1;
return sprintf(page, "%u\n", silent_value);
}
static int silent_write_proc_debug(struct file *file, const char *buffer,
unsigned long count, void *data)
{
char *buf;
if (count < 1)
return -EINVAL;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, buffer, count)) {
kfree(buf);
return -EFAULT;
}
if (buf[0] == '0') {
silent_value = 0;
printk("Set silent : %d\n", silent_value);
} else if (buf[0] == '1') {
silent_value = 1;
printk("Set silent : %d\n", silent_value);
} else {
kfree(buf);
return -EINVAL;
}
kfree(buf);
return count;
}
static int dump_read_proc_debug(char *page, char **start, off_t offset,
int count, int *eof, void *data)
{
*eof = 1;
return sprintf(page, "%u\n", dump_enable_flag);
}
static int dump_write_proc_debug(struct file *file, const char *buffer,
unsigned long count, void *data)
{
char *buf;
if (count < 1)
return -EINVAL;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, buffer, count)) {
kfree(buf);
return -EFAULT;
}
if (buf[0] == '0') { // low (no RAM dump)
dump_enable_flag = 0;
smem_flag->info = 0xAEAEAEAE;
} else if (buf[0] == '1') { // middle (kernel fault)
dump_enable_flag = 1;
smem_flag->info = 0xA9A9A9A9;
} else if (buf[0] == '2') { // high (user fault)
dump_enable_flag = 2;
smem_flag->info = 0xA9A9A9A9;
} else {
kfree(buf);
return -EINVAL;
}
printk("dump_enable_flag : %d, smem_flag : 0x%08x\n", dump_enable_flag, smem_flag->info);
kfree(buf);
return count;
}
/* init & cleanup. */
static int __init dpram_init(void)
{
int ret;
struct proc_dir_entry *ent;
ret = platform_driver_register(&platform_dpram_driver);
if (ret) {
goto error_return;
}
platform_device_register_simple("dpram", -1, NULL, 0);
wake_lock_init(&imei_wake_lock, WAKE_LOCK_SUSPEND, "IEMI");
wake_lock_init(&dpram_wake_lock, WAKE_LOCK_SUSPEND, "DPRAM");
wake_lock_init(&silent_wake_lock, WAKE_LOCK_SUSPEND, "SILENT_RESET");
// For silent ram dump mode
ent = create_proc_entry("silent", S_IRWXUGO, NULL);
ent->read_proc = silent_read_proc_debug;
ent->write_proc = silent_write_proc_debug;
smem_flag = (struct smem_info *) smem_alloc(SMEM_ID_VENDOR2, sizeof(struct smem_info));
if(smem_flag->info == 0xAEAEAEAE)
silent_value = 1;
ent = create_proc_entry("dump_enable", S_IRWXUGO, NULL);
ent->read_proc = dump_read_proc_debug;
ent->write_proc = dump_write_proc_debug;
smem_flag->info = 0xAEAEAEAE;
printk("[Silent Value] : %d\n", silent_value);
sec_class = class_create(THIS_MODULE, "sec");
if(IS_ERR(sec_class))
pr_err("Failed to create class(sec)!\n");
dpram_dev = device_create(sec_class, NULL, 0, NULL, "dpram");
if(IS_ERR(dpram_dev))
pr_err("Failed to create device(dpram)!\n");
if(device_create_file(dpram_dev, &dev_attr_info) < 0)
pr_err("Failed to create device file(%s)!\n", dev_attr_info.attr.name);
#ifdef CONFIG_DPRAM_WHITELIST
if(device_create_file(dpram_dev, &dev_attr_whitelist) < 0)
pr_err("Failed to create device file(%s)!\n", dev_attr_whitelist.attr.name);
#endif
// hsil
pm_dev = device_create(sec_class, NULL, 0, NULL, "pm");
if(IS_ERR(pm_dev))
pr_err("Failed to create device(pm)!\n");
if(device_create_file(pm_dev, &dev_attr_info) < 0)
pr_err("Failed to create device file(%s)!\n", dev_attr_info.attr.name);
if(device_create_file(pm_dev, &dev_attr_power_down) < 0)
pr_err("Failed to create device file(%s)!\n", dev_attr_power_down.attr.name);
// hsil for cpufreq
cpu_gov_dev = device_create(sec_class, NULL, 0, NULL, "cpu");
if(IS_ERR(cpu_gov_dev))
pr_err("Failed to create device(cpu)!\n");
if(device_create_file(cpu_gov_dev, &dev_attr_info) < 0)
pr_err("Failed to create device file(%s)!\n", dev_attr_info.attr.name);
if(device_create_file(cpu_gov_dev, &dev_attr_cpu_gov) < 0)
pr_err("Failed to create device file(%s)!\n", dev_attr_cpu_gov.attr.name);
error_return:
return ret;
}
static void __exit dpram_exit(void)
{
wake_lock_destroy(&dpram_wake_lock);
wake_lock_destroy(&imei_wake_lock);
wake_lock_destroy(&silent_wake_lock);
platform_driver_unregister(&platform_dpram_driver);
}
module_init(dpram_init);
module_exit(dpram_exit);
MODULE_AUTHOR("SAMSUNG ELECTRONICS CO., LTD");
MODULE_DESCRIPTION("DPRAM Device Driver for Linux MITs.");
MODULE_LICENSE("GPL");
/*
* Copyright (C) 2007 Google, Inc.
* Copyright (c) 2008-2010, 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/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.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 <linux/slab.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;
unsigned crci_mask;
#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 0xA80420C0
#define MSM_NAND_CFG1_RAW 0x5045D
#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;
uint32_t ecc_buf_cfg;
};
struct mtd_info *current_mtd = NULL;
unsigned param_start_block;
unsigned param_end_block;
#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) \
((void)(*(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_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},
}
};
/*
* flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page
* For now, we expose only 64 out of 80 ecc bytes
*/
static struct nand_ecclayout msm_flexonenand_oob_128 = {
.eccbytes = 64,
.eccpos = {
6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
102, 103, 104, 105
},
.oobavail = 32,
.oobfree = {
{2, 4}, {18, 4}, {34, 4}, {50, 4},
{66, 4}, {82, 4}, {98, 4}, {114, 4}
}
};
/*
* onenand_oob_128 - oob info for OneNAND with 4KB page
*
* Based on specification:
* 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010
*
* For eccpos we expose only 64 bytes out of 72 (see struct nand_ecclayout)
*
* oobfree uses the spare area fields marked as
* "Managed by internal ECC logic for Logical Sector Number area"
*/
static struct nand_ecclayout msm_onenand_oob_128 = {
.eccbytes = 64,
.eccpos = {
7, 8, 9, 10, 11, 12, 13, 14, 15,
23, 24, 25, 26, 27, 28, 29, 30, 31,
39, 40, 41, 42, 43, 44, 45, 46, 47,
55, 56, 57, 58, 59, 60, 61, 62, 63,
71, 72, 73, 74, 75, 76, 77, 78, 79,
87, 88, 89, 90, 91, 92, 93, 94, 95,
103, 104, 105, 106, 107, 108, 109, 110, 111,
119
},
.oobavail = 24,
.oobfree = {
{2, 3}, {18, 3}, {34, 3}, {50, 3},
{66, 3}, {82, 3}, {98, 3}, {114, 3}
}
};
/**
* 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;
dsb();
msm_dmov_exec_cmd(
chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
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;
dsb();
msm_dmov_exec_cmd(
chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
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 mask;
uint32_t density;
uint32_t widebus;
uint32_t pagesize;
uint32_t blksize;
uint32_t oobsize;
};
static struct flash_identification supported_flash[] =
{
/* Flash ID ID Mask Density(MB) Wid Pgsz Blksz oobsz Manuf */
{0x00000000, 0xFFFFFFFF, 0, 0, 0, 0, 0, }, /*ONFI*/
{0x5590bc2c, 0xFFFFFFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Micr*/
{0x1500aaec, 0xFF00FFFF, (256<<20), 0, 2048, (2048<<6), 64, }, /*Sams*/
{0x5500baec, 0xFF00FFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Sams*/
{0x6600bcec, 0xFF00FFFF, (512<<20), 1, 4096, (4096<<6), 128,}, /*Sams*/
{0x1500aa98, 0xFFFFFFFF, (256<<20), 0, 2048, (2048<<6), 64, }, /*Tosh*/
{0x5500ba98, 0xFFFFFFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Tosh*/
{0xd580b12c, 0xFFFFFFFF, (128<<20), 1, 2048, (2048<<6), 64, }, /*Micr*/
{0x5580baad, 0xFFFFFFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Hynx*/
{0x5510baad, 0xFFFFFFFF, (256<<20), 1, 2048, (2048<<6), 64, }, /*Hynx*/
{0x1590ac2c, 0xFFFFFFFF, (512<<20), 1, 2048, (2048<<6), 64, }, /*Micr*/
{0x6601b3ec, 0xFF00FFFF, (1024<<20), 1, 4096, (4096<<6), 128,}, /*Sams*/
{0x55d1b32c, 0xFFFFFFFF, (1024<<20), 1, 2048, (2048<<6), 64, }, /*Micr*/
/* Note: Width flag is 0 for 8 bit Flash and 1 for 16 bit flash */
/* Note: The First row will be filled at runtime during ONFI probe */
};
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_correctaility;
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
/* 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[0].flash_id =
flash_read_id(chip);
supported_flash[0].widebus =
onfi_param_page_ptr->
features_supported & 0x01;
supported_flash[0].pagesize =
onfi_param_page_ptr->
number_of_data_bytes_per_page;
supported_flash[0].blksize =
onfi_param_page_ptr->
number_of_pages_per_block *
supported_flash[0].pagesize;
supported_flash[0].oobsize =
onfi_param_page_ptr->
number_of_spare_bytes_per_page;
supported_flash[0].density =
onfi_param_page_ptr->
number_of_blocks_per_logical_unit
* supported_flash[0].blksize;
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 (!strcmp(onfi_param_page_ptr->device_model,
"MT29F4G08ABC"))
supported_flash[0].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 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 (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_OOB_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_OOB_RAW\n", __func__, ops->len);
return -EINVAL;
}
}
if (ops->mode != MTD_OOB_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_OOB_AUTO)
start_sector = cwperpage - 1;
if (ops->oobbuf && !ops->datbuf) {
page_count = ops->ooblen / ((ops->mode == MTD_OOB_AUTO) ?
mtd->oobavail : mtd->oobsize);
if ((page_count == 0) && (ops->ooblen))
page_count = 1;
} else if (ops->mode != MTD_OOB_RAW)
page_count = ops->len / mtd->writesize;
else
page_count = ops->len / (mtd->writesize + mtd->oobsize);
#if 0 /* yaffs reads more oob data than it needs */
if (ops->ooblen >= sectoroobsize * 4) {
pr_err("%s: unsupported ops->ooblen, %d\n",
__func__, ops->ooblen);
return -EINVAL;
}
#endif
#if VERBOSE
pr_info("msm_nand_read_oob %llx %p %x %p %x\n",
from, ops->datbuf, ops->len, ops->oobbuf, ops->ooblen);
#endif
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("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 * 0x210;
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_OOB_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;
} else {
dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
dma_buffer->data.cfg0 = (MSM_NAND_CFG0_RAW
& ~(7U << 6)) | ((cwperpage-1) << 6);
dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW |
(chip->CFG1 & CFG1_WIDE_FLASH);
}
dma_buffer->data.addr0 = (page << 16) | oob_col;
/* qc example is (page >> 16) && 0xff !? */
dma_buffer->data.addr1 = (page >> 16) & 0xff;
/* flash0 + undoc bit */
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;
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_OOB_RAW)
sectordatasize = (n < (cwperpage - 1))
? 516 : (512 - ((cwperpage - 1) << 2));
else
sectordatasize = 528;
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_OOB_AUTO)) {
cmd->cmd = 0;
if (n == (cwperpage - 1)) {
cmd->src = MSM_NAND_FLASH_BUFFER +
(512 - ((cwperpage - 1) << 2));
sectoroobsize = (cwperpage << 2);
if (ops->mode != MTD_OOB_AUTO)
sectoroobsize += 10;
} else {
cmd->src = MSM_NAND_FLASH_BUFFER + 516;
sectoroobsize = 10;
}
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
/* 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_OOB_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 && 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) {
for (n = 0; n < ops->ooblen; n++) {
if (ops->oobbuf[n] != 0xff) {
pageerr = rawerr;
break;
}
}
}
}
if (pageerr) {
for (n = start_sector; n < cwperpage; n++) {
if (dma_buffer->data.result[n].buffer_status
& 0x8) {
/* 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 & 0x7;
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 {
pr_info("status: %x %x %x %x %x %x %x %x %x \
%x %x %x %x %x %x %x \n",
dma_buffer->data.result[0].flash_status,
dma_buffer->data.result[0].buffer_status,
dma_buffer->data.result[1].flash_status,
dma_buffer->data.result[1].buffer_status,
dma_buffer->data.result[2].flash_status,
dma_buffer->data.result[2].buffer_status,
dma_buffer->data.result[3].flash_status,
dma_buffer->data.result[3].buffer_status,
dma_buffer->data.result[4].flash_status,
dma_buffer->data.result[4].buffer_status,
dma_buffer->data.result[5].flash_status,
dma_buffer->data.result[5].buffer_status,
dma_buffer->data.result[6].flash_status,
dma_buffer->data.result[6].buffer_status,
dma_buffer->data.result[7].flash_status,
dma_buffer->data.result[7].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_OOB_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);
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 exec;
uint32_t ecccfg;
uint32_t ebi2_cfg;
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;
uint32_t ebi2_cfg_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;
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_OOB_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_OOB_RAW\n", __func__, ops->len);
return -EINVAL;
}
}
if (ops->mode != MTD_OOB_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_OOB_AUTO)
start_sector = cwperpage - 1;
if (ops->oobbuf && !ops->datbuf) {
page_count = ops->ooblen / ((ops->mode == MTD_OOB_AUTO) ?
mtd->oobavail : mtd->oobsize);
if ((page_count == 0) && (ops->ooblen))
page_count = 1;
} else if (ops->mode != MTD_OOB_RAW)
page_count = ops->len / mtd->writesize;
else
page_count = ops->len / (mtd->writesize + mtd->oobsize);
#if VERBOSE
pr_info("msm_nand_read_oob_dualnandc %llx %p %x %p %x\n",
from, ops->datbuf, ops->len, ops->oobbuf, ops->ooblen);
#endif
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 * 0x210;
if (chip->CFG1 & CFG1_WIDE_FLASH)
oob_col >>= 1;
err = 0;
while (page_count-- > 0) {
cmd = dma_buffer->cmd;
if (ops->mode != MTD_OOB_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;
} else {
dma_buffer->data.cmd = MSM_NAND_CMD_PAGE_READ;
dma_buffer->data.cfg0 = ((MSM_NAND_CFG0_RAW &
~(7U << 6)) | ((((cwperpage >> 1)-1) << 6)));
dma_buffer->data.cfg1 = MSM_NAND_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) | oob_col;
dma_buffer->data.nandc10_addr0 = 0x108;
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;
/* config ebi2 cfg reg for pingpong ( 0xA000_0004 ) */
dma_buffer->data.ebi2_cfg = 0x4010080;
dma_buffer->data.ebi2_cfg_default = 0x4010000;
dma_buffer->data.ebi2_chip_select_cfg0 = 0x00000805;
dma_buffer->data.default_ebi2_chip_select_cfg0 = 0x00000801;
/* flash0 + undoc bit */
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;
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) {
/* config ebi2 cfg reg */
cmd->cmd = 0;
cmd->src = msm_virt_to_dma(chip,
&dma_buffer->data.ebi2_cfg);
cmd->dst = EBI2_CFG_REG;
cmd->len = 4;
cmd++;
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);
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 {
if (!interleave_enable) {
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 --> 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) {
if (ops->mode != MTD_OOB_RAW)
sectordatasize = (n < (cwperpage - 1))
? 516 : (512 - ((cwperpage - 1) << 2));
else
sectordatasize = 528;
if (n % 2 == 0) {
/* 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++;
/* 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++;
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 {
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++;
/* 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 {
/* 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++;
}
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_OOB_AUTO)) {
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_OOB_AUTO)
sectoroobsize += 10;
} else {
if (n % 2 == 0) {
cmd->src =
NC01(MSM_NAND_FLASH_BUFFER)
+ 516;
sectoroobsize = 10;
} else {
cmd->src =
NC10(MSM_NAND_FLASH_BUFFER)
+ 516;
sectoroobsize = 10;
}
}
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++;
cmd->cmd = 0;
cmd->src = msm_virt_to_dma(chip,
&dma_buffer->data.ebi2_cfg_default);
cmd->dst = EBI2_CFG_REG;
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
/* 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_OOB_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 && 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) {
for (n = 0; n < ops->ooblen; n++) {
if (ops->oobbuf[n] != 0xff) {
pageerr = rawerr;
break;
}
}
}
}
if (pageerr) {
for (n = start_sector; n < cwperpage; n++) {
if (dma_buffer->data.result[n].buffer_status
& 0x8) {
/* 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 & 0x7;
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 if (!interleave_enable) {
pr_info("status: %x %x %x %x %x %x %x %x %x \
%x %x %x %x %x %x %x \n",
dma_buffer->data.result[0].flash_status,
dma_buffer->data.result[0].buffer_status,
dma_buffer->data.result[1].flash_status,
dma_buffer->data.result[1].buffer_status,
dma_buffer->data.result[2].flash_status,
dma_buffer->data.result[2].buffer_status,
dma_buffer->data.result[3].flash_status,
dma_buffer->data.result[3].buffer_status,
dma_buffer->data.result[4].flash_status,
dma_buffer->data.result[4].buffer_status,
dma_buffer->data.result[5].flash_status,
dma_buffer->data.result[5].buffer_status,
dma_buffer->data.result[6].flash_status,
dma_buffer->data.result[6].buffer_status,
dma_buffer->data.result[7].flash_status,
dma_buffer->data.result[7].buffer_status);
} else {
pr_info("status: %x %x %x %x %x %x %x %x %x \
%x %x %x %x %x %x %x \
%x %x %x %x %x %x %x %x %x \
%x %x %x %x %x %x %x \n",
dma_buffer->data.result[0].flash_status,
dma_buffer->data.result[0].buffer_status,
dma_buffer->data.result[1].flash_status,
dma_buffer->data.result[1].buffer_status,
dma_buffer->data.result[2].flash_status,
dma_buffer->data.result[2].buffer_status,
dma_buffer->data.result[3].flash_status,
dma_buffer->data.result[3].buffer_status,
dma_buffer->data.result[4].flash_status,
dma_buffer->data.result[4].buffer_status,
dma_buffer->data.result[5].flash_status,
dma_buffer->data.result[5].buffer_status,
dma_buffer->data.result[6].flash_status,
dma_buffer->data.result[6].buffer_status,
dma_buffer->data.result[7].flash_status,
dma_buffer->data.result[7].buffer_status,
dma_buffer->data.result[8].flash_status,
dma_buffer->data.result[8].buffer_status,
dma_buffer->data.result[9].flash_status,
dma_buffer->data.result[9].buffer_status,
dma_buffer->data.result[10].flash_status,
dma_buffer->data.result[10].buffer_status,
dma_buffer->data.result[11].flash_status,
dma_buffer->data.result[11].buffer_status,
dma_buffer->data.result[12].flash_status,
dma_buffer->data.result[12].buffer_status,
dma_buffer->data.result[13].flash_status,
dma_buffer->data.result[13].buffer_status,
dma_buffer->data.result[14].flash_status,
dma_buffer->data.result[14].buffer_status,
dma_buffer->data.result[15].flash_status,
dma_buffer->data.result[15].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_OOB_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);
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;
uint8_t *org_buf = NULL;
size_t org_len = len;
loff_t org_from = from;
/* printk("msm_nand_read %llx %x\n", from, len); */
ops.mode = MTD_OOB_PLACE;
ops.len = len;
ops.retlen = 0;
ops.ooblen = 0;
ops.datbuf = buf;
ops.oobbuf = NULL;
/* support for non page alligned read */
if (ops.datbuf != NULL && (ops.len % mtd->writesize) != 0) {
ops.len += mtd->writesize;
ops.len ^= ops.len & (mtd->writesize - 1);
org_buf = ops.datbuf;
from ^= from & (mtd->writesize - 1);
ops.datbuf = kmalloc(ops.len, GFP_KERNEL);
if (!ops.datbuf){
ops.datbuf = org_buf;
org_buf = NULL;
ops.len = org_len;
pr_err("%s: allocation of temporary buffer has failed",
__func__);
}
}
if (!dual_nand_ctlr_present)
ret = msm_nand_read_oob(mtd, from, &ops);
else
ret = msm_nand_read_oob_dualnandc(mtd, from, &ops);
if (org_buf)
{
memcpy(org_buf, ops.datbuf + (org_from - from), org_len);
ops.len = org_len;
kfree(ops.datbuf);
ops.datbuf = org_buf;
ops.retlen = org_len;
}
*retlen = ops.retlen;
return ret;
}
static int
msm_nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
{
struct msm_nand_chip *chip = mtd->priv;
struct {
dmov_s cmd[8 * 7 + 2];
unsigned cmdptr;
struct {
uint32_t cmd;
uint32_t addr0;
uint32_t addr1;
uint32_t chipsel;
uint32_t cfg0;
uint32_t cfg1;
uint32_t 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;
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 (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_OOB_RAW) {
if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) {
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_OOB_RAW mode\n",
__func__, ops->len);
return -EINVAL;
}
}
if (ops->datbuf == NULL) {
pr_err("%s: unsupported ops->datbuf == NULL\n", __func__);
return -EINVAL;
}
#if 0 /* yaffs writes more oob data than it needs */
if (ops->ooblen >= sectoroobsize * 4) {
pr_err("%s: unsupported ops->ooblen, %d\n",
__func__, ops->ooblen);
return -EINVAL;
}
#endif
if (ops->mode != MTD_OOB_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_OOB_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;
/* CMD / ADDR0 / ADDR1 / CHIPSEL program values */
if (ops->mode != MTD_OOB_RAW) {
dma_buffer->data.cfg0 = chip->CFG0;
dma_buffer->data.cfg1 = chip->CFG1;
} else {
dma_buffer->data.cfg0 = (MSM_NAND_CFG0_RAW &
~(7U << 6)) | ((cwperpage-1) << 6);
dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW |
(chip->CFG1 & CFG1_WIDE_FLASH);
}
dma_buffer->data.cmd = MSM_NAND_CMD_PRG_PAGE;
dma_buffer->data.addr0 = page << 16;
dma_buffer->data.addr1 = (page >> 16) & 0xff;
dma_buffer->data.chipsel = 0 | 4; /* flash0 + undoc bit */
/* 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;
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_OOB_RAW)
sectordatawritesize = (n < (cwperpage - 1)) ?
516 : (512 - ((cwperpage - 1) << 2));
else
sectordatawritesize = 528;
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_OOB_AUTO) {
/* skip ecc bytes in oobbuf */
if (oob_len < 10) {
oob_dma_addr_curr += 10;
oob_len -= 10;
} 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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
/* 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
pr_info("write pg %d: status: %x %x %x %x %x %x %x %x\n", page,
dma_buffer->data.flash_status[0],
dma_buffer->data.flash_status[1],
dma_buffer->data.flash_status[2],
dma_buffer->data.flash_status[3],
dma_buffer->data.flash_status[4],
dma_buffer->data.flash_status[5],
dma_buffer->data.flash_status[6],
dma_buffer->data.flash_status[7]);
#endif
if (err)
break;
pages_written++;
page++;
}
if (ops->mode != MTD_OOB_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);
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 exec;
uint32_t ecccfg;
uint32_t ebi2_cfg;
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 ebi2_cfg_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;
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 (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_OOB_RAW) {
if (ops->ooblen != 0 && ops->mode != MTD_OOB_AUTO) {
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_OOB_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_OOB_RAW && ops->ooblen != 0 && ops->ooboffs != 0) {
pr_err("%s: unsupported ops->ooboffs, %d\n",
__func__, ops->ooboffs);
return -EINVAL;
}
#if VERBOSE
pr_info("msm_nand_write_oob_dualnandc %llx %p %x %p %x\n",
to, ops->datbuf, ops->len, ops->oobbuf, ops->ooblen);
#endif
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_OOB_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))));
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_OOB_RAW) {
dma_buffer->data.cfg0 = ((chip->CFG0 & ~(7U << 6))
| (1 << 4)) | ((((cwperpage >> 1)-1)) << 6);
dma_buffer->data.cfg1 = chip->CFG1;
} else {
dma_buffer->data.cfg0 = ((MSM_NAND_CFG0_RAW &
~(7U << 6)) | (1<<4)) | (((cwperpage >> 1)-1) << 6);
dma_buffer->data.cfg1 = MSM_NAND_CFG1_RAW |
(chip->CFG1 & CFG1_WIDE_FLASH);
}
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;
/* config ebi2 cfg reg ( 0xA000_0004 ) */
dma_buffer->data.ebi2_cfg = 0x4010080;
dma_buffer->data.ebi2_cfg_default = 0x4010000;
if (!interleave_enable) {
dma_buffer->data.nandc01_addr0 = (page << 16) | 0x0;
dma_buffer->data.nandc10_addr0 = (page << 16) | 0x108;
} 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) {
/* config ebi2 cfg reg */
cmd->cmd = 0;
cmd->src = msm_virt_to_dma(chip,
&dma_buffer->data.ebi2_cfg);
cmd->dst = EBI2_CFG_REG;
cmd->len = 4;
cmd++;
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++;
cmd->cmd = 0;
cmd->src = msm_virt_to_dma(chip,
&dma_buffer->data.cfg0);
cmd->dst = NC11(MSM_NAND_DEV0_CFG0);
cmd->len = 8;
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_OOB_RAW)
sectordatawritesize = (n < (cwperpage - 1)) ?
516 : (512 - ((cwperpage - 1) << 2));
else
sectordatawritesize = 528;
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_OOB_AUTO) {
/* skip ecc bytes in oobbuf */
if (oob_len < 10) {
oob_dma_addr_curr += 10;
oob_len -= 10;
} else {
oob_dma_addr_curr += oob_len;
oob_len = 0;
}
}
}
if (n % 2 == 0) {
/* 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++;
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++;
}
} else {
/* 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 --> 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++;
}
}
/* 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++;
cmd->cmd = 0;
cmd->src = msm_virt_to_dma(chip,
&dma_buffer->data.ebi2_cfg_default);
cmd->dst = EBI2_CFG_REG;
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);
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(
msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
/* 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
if (!interleave_enable) {
pr_info("write pg %d: status: %x %x %x %x %x %x %x %x\n", page,
dma_buffer->data.flash_status[0],
dma_buffer->data.flash_status[1],
dma_buffer->data.flash_status[2],
dma_buffer->data.flash_status[3],
dma_buffer->data.flash_status[4],
dma_buffer->data.flash_status[5],
dma_buffer->data.flash_status[6],
dma_buffer->data.flash_status[7]);
} else {
pr_info("write pg %d: status: %x %x %x %x %x %x %x %x \
%x %x %x %x %x %x %x %x \n", page,
dma_buffer->data.flash_status[0],
dma_buffer->data.flash_status[1],
dma_buffer->data.flash_status[2],
dma_buffer->data.flash_status[3],
dma_buffer->data.flash_status[4],
dma_buffer->data.flash_status[5],
dma_buffer->data.flash_status[6],
dma_buffer->data.flash_status[7],
dma_buffer->data.flash_status[8],
dma_buffer->data.flash_status[9],
dma_buffer->data.flash_status[10],
dma_buffer->data.flash_status[11],
dma_buffer->data.flash_status[12],
dma_buffer->data.flash_status[13],
dma_buffer->data.flash_status[14],
dma_buffer->data.flash_status[15]);
}
#endif
if (err)
break;
pages_written++;
page++;
}
if (ops->mode != MTD_OOB_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);
return err;
}
static int msm_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
int ret;
struct mtd_oob_ops ops;
ops.mode = MTD_OOB_PLACE;
ops.len = len;
ops.retlen = 0;
ops.ooblen = 0;
ops.datbuf = (uint8_t *)buf;
ops.oobbuf = NULL;
if (!dual_nand_ctlr_present)
ret = msm_nand_write_oob(mtd, to, &ops);
else
ret = msm_nand_write_oob_dualnandc(mtd, to, &ops);
*retlen = ops.retlen;
return ret;
}
static int
msm_nand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
int err;
struct msm_nand_chip *chip = mtd->priv;
struct {
dmov_s cmd[6];
unsigned cmdptr;
unsigned data[10];
} *dma_buffer;
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))));
dma_buffer->data[0] = MSM_NAND_CMD_BLOCK_ERASE;
dma_buffer->data[1] = page;
dma_buffer->data[2] = 0;
dma_buffer->data[3] = 0 | 4;
dma_buffer->data[4] = 1;
dma_buffer->data[5] = 0xeeeeeeee;
dma_buffer->data[6] = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */
dma_buffer->data[7] = chip->CFG1;
dma_buffer->data[8] = 0x00000020;
dma_buffer->data[9] = 0x000000C0;
BUILD_BUG_ON(9 != ARRAY_SIZE(dma_buffer->data) - 1);
dma_buffer->cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB;
dma_buffer->cmd[0].src = msm_virt_to_dma(chip, &dma_buffer->data[0]);
dma_buffer->cmd[0].dst = MSM_NAND_FLASH_CMD;
dma_buffer->cmd[0].len = 16;
dma_buffer->cmd[1].cmd = 0;
dma_buffer->cmd[1].src = msm_virt_to_dma(chip, &dma_buffer->data[6]);
dma_buffer->cmd[1].dst = MSM_NAND_DEV0_CFG0;
dma_buffer->cmd[1].len = 8;
dma_buffer->cmd[2].cmd = 0;
dma_buffer->cmd[2].src = msm_virt_to_dma(chip, &dma_buffer->data[4]);
dma_buffer->cmd[2].dst = MSM_NAND_EXEC_CMD;
dma_buffer->cmd[2].len = 4;
dma_buffer->cmd[3].cmd = SRC_CRCI_NAND_DATA;
dma_buffer->cmd[3].src = MSM_NAND_FLASH_STATUS;
dma_buffer->cmd[3].dst = msm_virt_to_dma(chip, &dma_buffer->data[5]);
dma_buffer->cmd[3].len = 4;
dma_buffer->cmd[4].cmd = 0;
dma_buffer->cmd[4].src = msm_virt_to_dma(chip, &dma_buffer->data[8]);
dma_buffer->cmd[4].dst = MSM_NAND_FLASH_STATUS;
dma_buffer->cmd[4].len = 4;
dma_buffer->cmd[5].cmd = CMD_OCU | CMD_LC;
dma_buffer->cmd[5].src = msm_virt_to_dma(chip, &dma_buffer->data[9]);
dma_buffer->cmd[5].dst = MSM_NAND_READ_STATUS;
dma_buffer->cmd[5].len = 4;
BUILD_BUG_ON(5 != ARRAY_SIZE(dma_buffer->cmd) - 1);
dma_buffer->cmdptr =
(msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
dsb();
msm_dmov_exec_cmd(
chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
/* we fail if there was an operation error, a mpu error, or the
* erase success bit was not set.
*/
if (dma_buffer->data[5] & 0x110 || !(dma_buffer->data[5] & 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;
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;
unsigned data[12];
} *dma_buffer;
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))));
dma_buffer->data[0] = MSM_NAND_CMD_BLOCK_ERASE;
dma_buffer->data[1] = page;
dma_buffer->data[2] = 0;
dma_buffer->data[3] = (1<<4) | 4;
dma_buffer->data[4] = (1<<4) | 5;
dma_buffer->data[5] = 1;
dma_buffer->data[6] = 0xeeeeeeee;
dma_buffer->data[7] = 0xeeeeeeee;
dma_buffer->data[8] = chip->CFG0 & (~(7 << 6)); /* CW_PER_PAGE = 0 */
dma_buffer->data[9] = chip->CFG1;
dma_buffer->data[10] = 0x00000020;
dma_buffer->data[11] = 0x000000C0;
dma_buffer->ebi2_chip_select_cfg0 = 0x00000805;
dma_buffer->adm_mux_data_ack_req_nc01 = 0x00000A3C;
dma_buffer->adm_mux_cmd_ack_req_nc01 = 0x0000053C;
dma_buffer->adm_mux_data_ack_req_nc10 = 0x00000F28;
dma_buffer->adm_mux_cmd_ack_req_nc10 = 0x00000F14;
dma_buffer->adm_default_mux = 0x00000FC0;
dma_buffer->default_ebi2_chip_select_cfg0 = 0x00000801;
BUILD_BUG_ON(11 != ARRAY_SIZE(dma_buffer->data) - 1);
/* enable CS1 */
dma_buffer->cmd[0].cmd = 0 | CMD_OCB;
dma_buffer->cmd[0].src = msm_virt_to_dma(chip,
&dma_buffer->ebi2_chip_select_cfg0);
dma_buffer->cmd[0].dst = EBI2_CHIP_SELECT_CFG0;
dma_buffer->cmd[0].len = 4;
/* erase CS0 block now !!! */
/* 0xF14 */
dma_buffer->cmd[1].cmd = 0;
dma_buffer->cmd[1].src = msm_virt_to_dma(chip,
&dma_buffer->adm_mux_cmd_ack_req_nc10);
dma_buffer->cmd[1].dst = EBI2_NAND_ADM_MUX;
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[0]);
dma_buffer->cmd[2].dst = NC01(MSM_NAND_FLASH_CMD);
dma_buffer->cmd[2].len = 16;
dma_buffer->cmd[3].cmd = 0;
dma_buffer->cmd[3].src = msm_virt_to_dma(chip, &dma_buffer->data[8]);
dma_buffer->cmd[3].dst = NC01(MSM_NAND_DEV0_CFG0);
dma_buffer->cmd[3].len = 8;
dma_buffer->cmd[4].cmd = 0;
dma_buffer->cmd[4].src = msm_virt_to_dma(chip, &dma_buffer->data[5]);
dma_buffer->cmd[4].dst = NC01(MSM_NAND_EXEC_CMD);
dma_buffer->cmd[4].len = 4;
/* 0xF28 */
dma_buffer->cmd[5].cmd = 0;
dma_buffer->cmd[5].src = msm_virt_to_dma(chip,
&dma_buffer->adm_mux_data_ack_req_nc10);
dma_buffer->cmd[5].dst = EBI2_NAND_ADM_MUX;
dma_buffer->cmd[5].len = 4;
dma_buffer->cmd[6].cmd = SRC_CRCI_NAND_DATA;
dma_buffer->cmd[6].src = NC01(MSM_NAND_FLASH_STATUS);
dma_buffer->cmd[6].dst = msm_virt_to_dma(chip, &dma_buffer->data[6]);
dma_buffer->cmd[6].len = 4;
/* erase CS1 block now !!! */
/* 0x53C */
dma_buffer->cmd[7].cmd = 0;
dma_buffer->cmd[7].src = msm_virt_to_dma(chip,
&dma_buffer->adm_mux_cmd_ack_req_nc01);
dma_buffer->cmd[7].dst = EBI2_NAND_ADM_MUX;
dma_buffer->cmd[7].len = 4;
dma_buffer->cmd[8].cmd = DST_CRCI_NAND_CMD;
dma_buffer->cmd[8].src = msm_virt_to_dma(chip, &dma_buffer->data[0]);
dma_buffer->cmd[8].dst = NC10(MSM_NAND_FLASH_CMD);
dma_buffer->cmd[8].len = 12;
dma_buffer->cmd[9].cmd = 0;
dma_buffer->cmd[9].src = msm_virt_to_dma(chip, &dma_buffer->data[4]);
dma_buffer->cmd[9].dst = NC10(MSM_NAND_FLASH_CHIP_SELECT);
dma_buffer->cmd[9].len = 4;
dma_buffer->cmd[10].cmd = 0;
dma_buffer->cmd[10].src = msm_virt_to_dma(chip, &dma_buffer->data[8]);
dma_buffer->cmd[10].dst = NC10(MSM_NAND_DEV1_CFG0);
dma_buffer->cmd[10].len = 8;
dma_buffer->cmd[11].cmd = 0;
dma_buffer->cmd[11].src = msm_virt_to_dma(chip, &dma_buffer->data[5]);
dma_buffer->cmd[11].dst = NC10(MSM_NAND_EXEC_CMD);
dma_buffer->cmd[11].len = 4;
/* 0xA3C */
dma_buffer->cmd[12].cmd = 0;
dma_buffer->cmd[12].src = msm_virt_to_dma(chip,
&dma_buffer->adm_mux_data_ack_req_nc01);
dma_buffer->cmd[12].dst = EBI2_NAND_ADM_MUX;
dma_buffer->cmd[12].len = 4;
dma_buffer->cmd[13].cmd = SRC_CRCI_NAND_DATA;
dma_buffer->cmd[13].src = NC10(MSM_NAND_FLASH_STATUS);
dma_buffer->cmd[13].dst = msm_virt_to_dma(chip, &dma_buffer->data[7]);
dma_buffer->cmd[13].len = 4;
dma_buffer->cmd[14].cmd = 0;
dma_buffer->cmd[14].src = msm_virt_to_dma(chip, &dma_buffer->data[8]);
dma_buffer->cmd[14].dst = NC11(MSM_NAND_FLASH_STATUS);
dma_buffer->cmd[14].len = 4;
dma_buffer->cmd[15].cmd = 0;
dma_buffer->cmd[15].src = msm_virt_to_dma(chip, &dma_buffer->data[9]);
dma_buffer->cmd[15].dst = NC11(MSM_NAND_READ_STATUS);
dma_buffer->cmd[15].len = 4;
dma_buffer->cmd[16].cmd = 0;
dma_buffer->cmd[16].src = msm_virt_to_dma(chip,
&dma_buffer->adm_default_mux);
dma_buffer->cmd[16].dst = EBI2_NAND_ADM_MUX;
dma_buffer->cmd[16].len = 4;
/* disable CS1 */
dma_buffer->cmd[17].cmd = CMD_OCU | CMD_LC;
dma_buffer->cmd[17].src = msm_virt_to_dma(chip,
&dma_buffer->default_ebi2_chip_select_cfg0);
dma_buffer->cmd[17].dst = EBI2_CHIP_SELECT_CFG0;
dma_buffer->cmd[17].len = 4;
BUILD_BUG_ON(17 != ARRAY_SIZE(dma_buffer->cmd) - 1);
dma_buffer->cmdptr =
(msm_virt_to_dma(chip, dma_buffer->cmd) >> 3) | CMD_PTR_LP;
dsb();
msm_dmov_exec_cmd(
chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
/* we fail if there was an operation error, a mpu error, or the
* erase success bit was not set.
*/
if (dma_buffer->data[6] & 0x110 || !(dma_buffer->data[6] & 0x80)
|| dma_buffer->data[6] & 0x110 || !(dma_buffer->data[6] & 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 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 ( & 0x%x )\n",
__func__, (uint32_t)ofs, mtd->erasesize - 1);
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 = MSM_NAND_CFG0_RAW & ~(7U << 6);
dma_buffer->data.cfg1 = MSM_NAND_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 = 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;
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 - (528*(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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
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 = MSM_NAND_CFG0_RAW & ~(7U << 6);
dma_buffer->data.cfg1 = MSM_NAND_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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST |
DMOV_CMD_ADDR(msm_virt_to_dma(chip, &dma_buffer->cmdptr)));
dsb();
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_OOB_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;
}
int msm_nand_read_dpram(char *mBuf, unsigned size)
{
char spare_buf[128] = { 0, };
struct mtd_oob_ops ops = { 0, };
unsigned offset = 0;
if(NULL == current_mtd)
{
printk("[msm_nand_read_dpram] MTD not initialized\n");
return -1;
}
if(size < current_mtd->writesize)
{
printk("[msm_nand_read_dpram] given buffer has invalid size\n");
return -1;
}
/* needed data is hardcoded at 5th page of the last block */
offset = ((5 * current_mtd->writesize) + (current_mtd->erasesize * (((unsigned )current_mtd->size / (unsigned )current_mtd->erasesize) - 1)));
ops.mode = MTD_OOB_RAW;
ops.len = current_mtd->writesize + current_mtd->oobsize;
ops.ooblen = current_mtd->oobsize;
ops.datbuf = mBuf;
ops.oobbuf = spare_buf;
printk("[msm_nand_read_dpram] number of blocks = %u, offset = %u, page size = %u, block size = %u\n", (unsigned )current_mtd->size / (unsigned )current_mtd->erasesize, offset, current_mtd->writesize, current_mtd->erasesize);
return msm_nand_read_oob(current_mtd, offset, &ops);
}
EXPORT_SYMBOL(msm_nand_read_dpram);
void msm_read_param(char *mBuf)
{
char data_buf[4096] = { 0, };
char spare_buf[128] = { 0, };
struct mtd_oob_ops ops = { 0, };
int data_size = 0;
if (current_mtd->oobsize == 64) {
data_size = 2048;
}
else if (current_mtd->oobsize == 128) {
data_size = 4096;
}
ops.mode = MTD_OOB_RAW;
ops.len = data_size+current_mtd->oobsize;
ops.retlen = 0;
ops.ooblen = current_mtd->oobsize;
ops.datbuf = data_buf;
ops.oobbuf = spare_buf;
// erasize == size of entire block == page size * pages per block
while(msm_nand_block_isbad(current_mtd, (param_start_block * current_mtd->erasesize)))
{
printk("msm_read_param: bad block\n");
param_start_block++;
}
if ( param_start_block >= param_end_block) {
param_start_block = param_end_block - 1;
printk("All nand block in param partition has been crashed\n");
}
msm_nand_read_oob(current_mtd, (param_start_block * current_mtd->erasesize), &ops);
memcpy(mBuf,data_buf,sizeof(data_buf));
}
EXPORT_SYMBOL(msm_read_param);
void msm_write_param(char *mBuf)
{
char data_buf[4096] = { 0, };
char spare_buf[128] = { 0, };
struct mtd_oob_ops ops = { 0, };
struct erase_info *param_erase_info = 0;
int data_size = 0;
if (current_mtd->oobsize == 64) {
data_size = 2048;
}
else if (current_mtd->oobsize == 128) {
data_size = 4096;
}
param_erase_info = kzalloc(sizeof(struct erase_info), GFP_KERNEL);
if(0 == param_erase_info)
{
printk("msm_write_param: memory allocation error\n");
return;
}
param_erase_info->mtd = current_mtd;
// erasize == size of entire block == page size * pages per block
param_erase_info->addr = param_start_block * current_mtd->erasesize;
param_erase_info->len = current_mtd->erasesize;
if(!msm_nand_erase(current_mtd, param_erase_info)) {
pr_info("parameter block erase success\n");
}
memset(spare_buf,0xFF,current_mtd->oobsize);
memcpy(data_buf,mBuf,sizeof(data_buf));
ops.mode = MTD_OOB_RAW;
ops.len = data_size+current_mtd->oobsize;
ops.retlen = 0;
ops.ooblen = current_mtd->oobsize;
ops.datbuf = data_buf;
ops.oobbuf = spare_buf;
msm_nand_write_oob(current_mtd, param_erase_info->addr, &ops);
kfree(param_erase_info);
}
EXPORT_SYMBOL(msm_write_param);
/**
* 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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST
| DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
/* 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 & 0x0050) != 0x0050)
|| (onenand_info.data_buf_size != 0x0800)
|| (onenand_info.boot_buf_size != 0x0200)
|| (onenand_info.num_of_buffers != 0x0101)
|| (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[78];
unsigned cmdptr;
struct {
uint32_t sfbcfg;
uint32_t sfcmd[14];
uint32_t sfexec;
uint32_t sfstat[14];
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[10];
} 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_OOB_PLACE) && (ops->mode != MTD_OOB_AUTO) &&
(ops->mode != MTD_OOB_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_OOB_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_OOB_RAW\n", __func__, ops->len);
return -EINVAL;
}
}
if ((ops->mode == MTD_OOB_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_OOB_AUTO) ?
mtd->oobavail : mtd->oobsize);
else if (ops->mode != MTD_OOB_RAW)
page_count = ops->len / mtd->writesize;
else
page_count = ops->len / (mtd->writesize + mtd->oobsize);
if ((ops->mode == MTD_OOB_AUTO) && (ops->oobbuf != NULL)) {
if (page_count * mtd->oobavail > ops->ooblen) {
pr_err("%s: unsupported ops->ooblen for "
"AUTO, %d\n", __func__, ops->ooblen);
return -EINVAL;
}
}
if ((ops->mode == MTD_OOB_PLACE) && (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_OOB_PLACE) && (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_OOB_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(256, 0, 0,
MSM_NAND_SFCMD_DATXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATRD);
dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(256, 0, 0,
MSM_NAND_SFCMD_DATXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATRD);
dma_buffer->data.sfcmd[9] = SFLASH_PREPCMD(256, 0, 0,
MSM_NAND_SFCMD_DATXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATRD);
dma_buffer->data.sfcmd[10] = SFLASH_PREPCMD(256, 0, 0,
MSM_NAND_SFCMD_DATXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATRD);
dma_buffer->data.sfcmd[11] = SFLASH_PREPCMD(32, 0, 0,
MSM_NAND_SFCMD_DATXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATRD);
dma_buffer->data.sfcmd[12] = SFLASH_PREPCMD(4, 10, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_REGWR);
dma_buffer->data.sfcmd[13] = SFLASH_PREPCMD(32, 0, 0,
MSM_NAND_SFCMD_DATXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATRD);
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.sfstat[10] = CLEAN_DATA_32;
dma_buffer->data.sfstat[11] = CLEAN_DATA_32;
dma_buffer->data.sfstat[12] = CLEAN_DATA_32;
dma_buffer->data.sfstat[13] = 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] = 0x0600;
dma_buffer->data.macro[5] = 0x0700;
dma_buffer->data.macro[6] = 0x0800;
dma_buffer->data.macro[7] = 0x0900;
dma_buffer->data.macro[8] = 0x8010;
dma_buffer->data.macro[9] = 0x8030;
/*************************************************************/
/* 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 < 8; 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_OOB_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[11]);
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[8]);
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[11]);
cmd->len = 4;
cmd++;
/* Transfer nand ctlr buffer contents into usr buf */
if (ops->mode == MTD_OOB_AUTO) {
for (i = 0; i < 4; 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_OOB_PLACE) {
cmd->cmd = 0;
cmd->src = MSM_NAND_FLASH_BUFFER;
cmd->dst = oob_dma_addr_curr;
cmd->len = 64;
oob_dma_addr_curr += 64;
cmd++;
}
if (ops->mode == MTD_OOB_RAW) {
cmd->cmd = 0;
cmd->src = MSM_NAND_FLASH_BUFFER;
cmd->dst = data_dma_addr_curr;
cmd->len = 64;
data_dma_addr_curr += 64;
cmd++;
}
}
// read second spareRAM
if ((ops->oobbuf) || (ops->mode == MTD_OOB_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[13]);
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[9]);
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[13]);
cmd->len = 4;
cmd++;
/* Transfer nand ctlr buffer contents into usr buf */
if (ops->mode == MTD_OOB_AUTO) {
for (i = 4; i < 8; i++) {
cmd->cmd = 0;
cmd->src = MSM_NAND_FLASH_BUFFER +
(mtd->ecclayout->oobfree[i].offset - 64);
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_OOB_PLACE) {
cmd->cmd = 0;
cmd->src = MSM_NAND_FLASH_BUFFER;
cmd->dst = oob_dma_addr_curr;
cmd->len = 64;
oob_dma_addr_curr += 64;
cmd++;
}
if (ops->mode == MTD_OOB_RAW) {
cmd->cmd = 0;
cmd->src = MSM_NAND_FLASH_BUFFER;
cmd->dst = data_dma_addr_curr;
cmd->len = 64;
data_dma_addr_curr += 64;
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[12]);
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[12]);
cmd->len = 4;
cmd++;
BUILD_BUG_ON(78 != 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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
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 %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],
dma_buffer->data.sfstat[10],
dma_buffer->data.sfstat[11],
dma_buffer->data.sfstat[12],
dma_buffer->data.sfstat[13]);
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[12] & 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->datbuf))
|| ((dma_buffer->data.sfstat[8] & 0x110) &&
(ops->datbuf))
|| ((dma_buffer->data.sfstat[9] & 0x110) &&
(ops->datbuf))
|| ((dma_buffer->data.sfstat[10] & 0x110) &&
(ops->datbuf))
|| ((dma_buffer->data.sfstat[11] & 0x110) &&
((ops->oobbuf)
|| (ops->mode == MTD_OOB_RAW)))
|| ((dma_buffer->data.sfstat[13] & 0x110) &&
((ops->oobbuf)
|| (ops->mode == MTD_OOB_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_OOB_RAW)
ops->retlen = mtd->writesize * pages_read;
else
ops->retlen = (mtd->writesize + mtd->oobsize)
* pages_read;
}
if (ops->oobbuf != NULL) {
if (ops->mode == MTD_OOB_AUTO)
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_OOB_PLACE;
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[78];
unsigned cmdptr;
struct {
uint32_t sfbcfg;
uint32_t sfcmd[15];
uint32_t sfexec;
uint32_t sfstat[15];
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[10];
} 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_OOB_PLACE) && (ops->mode != MTD_OOB_AUTO) &&
(ops->mode != MTD_OOB_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_OOB_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_OOB_RAW\n", __func__, ops->len);
return -EINVAL;
}
}
if ((ops->mode == MTD_OOB_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_OOB_AUTO) ?
mtd->oobavail : mtd->oobsize);
else if (ops->mode != MTD_OOB_RAW)
page_count = ops->len / mtd->writesize;
else
page_count = ops->len / (mtd->writesize + mtd->oobsize);
if ((ops->mode == MTD_OOB_AUTO) && (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_OOB_PLACE) && (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_OOB_PLACE) && (ops->ooblen != 0) &&
(ops->ooboffs != 0)) {
pr_err("%s: unsupported ops->ooboffs, %d\n",
__func__, ops->ooboffs);
return -EINVAL;
}
init_spare_bytes = kmalloc(mtd->oobsize, GFP_KERNEL);
if (!init_spare_bytes) {
pr_err("%s: failed to alloc init_spare_bytes buffer\n",
__func__);
return -ENOMEM;
}
for (i = 0; i < mtd->oobsize; i++)
init_spare_bytes[i] = 0xFF;
if ((ops->oobbuf) && (ops->mode == MTD_OOB_AUTO)) {
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, mtd->oobsize,
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_OOB_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(256, 0, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATWR);
dma_buffer->data.sfcmd[6] = SFLASH_PREPCMD(256, 0, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATWR);
dma_buffer->data.sfcmd[7] = SFLASH_PREPCMD(256, 0, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATWR);
dma_buffer->data.sfcmd[8] = SFLASH_PREPCMD(256, 0, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATWR);
dma_buffer->data.sfcmd[9] = SFLASH_PREPCMD(32, 0, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATWR);
dma_buffer->data.sfcmd[10] = SFLASH_PREPCMD(1, 6, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_REGWR);
dma_buffer->data.sfcmd[11] = SFLASH_PREPCMD(0, 0, 32,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_INTHI);
dma_buffer->data.sfcmd[12] = SFLASH_PREPCMD(3, 7, 0,
MSM_NAND_SFCMD_DATXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_REGRD);
dma_buffer->data.sfcmd[13] = SFLASH_PREPCMD(4, 10, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_REGWR);
dma_buffer->data.sfcmd[14] = SFLASH_PREPCMD(32, 0, 0,
MSM_NAND_SFCMD_CMDXS,
nand_sfcmd_mode,
MSM_NAND_SFCMD_DATWR);
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.sfstat[10] = CLEAN_DATA_32;
dma_buffer->data.sfstat[11] = CLEAN_DATA_32;
dma_buffer->data.sfstat[12] = CLEAN_DATA_32;
dma_buffer->data.sfstat[13] = CLEAN_DATA_32;
dma_buffer->data.sfstat[14] = 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] = 0x0600;
dma_buffer->data.macro[5] = 0x0700;
dma_buffer->data.macro[6] = 0x0800;
dma_buffer->data.macro[7] = 0x0900;
dma_buffer->data.macro[8] = 0x8010;
dma_buffer->data.macro[9] = 0x8030;
/*************************************************************/
/* 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 < 8; 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[9]);
cmd->dst = MSM_NAND_SFLASHC_CMD;
cmd->len = 4;
cmd++;
if ((ops->oobbuf) || (ops->mode == MTD_OOB_RAW)) {
/* Transfer user buf contents into nand ctlr buffer */
if (ops->mode == MTD_OOB_AUTO) {
cmd->cmd = 0;
cmd->src = init_dma_addr;
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
cmd++;
}
if (ops->mode == MTD_OOB_PLACE) {
cmd->cmd = 0;
cmd->src = oob_dma_addr_curr;
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
oob_dma_addr_curr += 64;
cmd++;
}
if (ops->mode == MTD_OOB_RAW) {
cmd->cmd = 0;
cmd->src = data_dma_addr_curr;
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
data_dma_addr_curr += 64;
cmd++;
}
} else {
cmd->cmd = 0;
cmd->src = init_dma_addr;
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
cmd++;
}
/* Write the MACRO1 register */
cmd->cmd = 0;
cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.macro[8]);
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[9]);
cmd->len = 4;
cmd++;
//write the 2o spareRAM
/* 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[14]);
cmd->dst = MSM_NAND_SFLASHC_CMD;
cmd->len = 4;
cmd++;
if ((ops->oobbuf) || (ops->mode == MTD_OOB_RAW)) {
/* Transfer user buf contents into nand ctlr buffer */
if (ops->mode == MTD_OOB_AUTO) {
cmd->cmd = 0;
cmd->src = (init_dma_addr + 64);
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
cmd++;
}
if (ops->mode == MTD_OOB_PLACE) {
cmd->cmd = 0;
cmd->src = oob_dma_addr_curr;
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
oob_dma_addr_curr += 64;
cmd++;
}
if (ops->mode == MTD_OOB_RAW) {
cmd->cmd = 0;
cmd->src = data_dma_addr_curr;
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
data_dma_addr_curr += 64;
cmd++;
}
} else {
cmd->cmd = 0;
cmd->src = init_dma_addr;
cmd->dst = MSM_NAND_FLASH_BUFFER;
cmd->len = 64;
cmd++;
}
/* Write the MACRO1 register */
cmd->cmd = 0;
cmd->src = msm_virt_to_dma(chip, &dma_buffer->data.macro[9]);
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[14]);
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[10]);
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[10]);
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[11]);
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[11]);
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[12]);
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[12]);
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[13]);
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[13]);
cmd->len = 4;
cmd++;
BUILD_BUG_ON(78 != 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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
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 %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],
dma_buffer->data.sfstat[10],
dma_buffer->data.sfstat[11],
dma_buffer->data.sfstat[12],
dma_buffer->data.sfstat[13],
dma_buffer->data.sfstat[14]);
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[10] & 0x110)
|| (dma_buffer->data.sfstat[11] & 0x110)
|| (dma_buffer->data.sfstat[12] & 0x110)
|| (dma_buffer->data.sfstat[13] & 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->datbuf))
|| ((dma_buffer->data.sfstat[6] & 0x110) &&
(ops->datbuf))
|| ((dma_buffer->data.sfstat[7] & 0x110) &&
(ops->datbuf))
|| ((dma_buffer->data.sfstat[8] & 0x110) &&
(ops->datbuf))
|| ((dma_buffer->data.sfstat[9] & 0x110) &&
((ops->oobbuf)
|| (ops->mode == MTD_OOB_RAW)))
|| ((dma_buffer->data.sfstat[14] & 0x110) &&
((ops->oobbuf)
|| (ops->mode == MTD_OOB_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, mtd->oobsize, 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_OOB_RAW)
ops->retlen = mtd->writesize * pages_written;
else
ops->retlen = (mtd->writesize + mtd->oobsize)
* pages_written;
}
if (ops->oobbuf != NULL) {
if (ops->mode == MTD_OOB_AUTO)
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_OOB_PLACE;
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST
| DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
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(4224, GFP_KERNEL|GFP_DMA);
if (buffer == 0) {
pr_err("%s: Could not kmalloc for buffer\n",
__func__);
return -ENOMEM;
}
memset(buffer, 0x00, 4224);
oobptr = &(buffer[4096]);
ops.mode = MTD_OOB_RAW;
ops.len = 4224;
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) ||
(oobptr[64] != 0xFF) || (oobptr[65] != 0xFF) ||
(oobptr[80] != 0xFF) || (oobptr[81] != 0xFF) ||
(oobptr[96] != 0xFF) || (oobptr[97] != 0xFF) ||
(oobptr[112] != 0xFF) || (oobptr[113] != 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_OOB_RAW;
ops.len = 4224;
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask,
DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
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;
}
int msm_onenand_read_dpram(char *mBuf, unsigned size)
{
//char spare_buf[128] = { 0, };
struct mtd_oob_ops ops = { 0, };
unsigned offset = 0;
if(NULL == current_mtd)
{
printk("[msm_nand_read_dpram] MTD not initialized\n");
return -1;
}
if(size < current_mtd->writesize)
{
printk("[msm_nand_read_dpram] given buffer has invalid size\n");
return -1;
}
/* needed data is hardcoded at 5th page of the last block */
offset = ((5 * current_mtd->writesize) + (current_mtd->erasesize * (((unsigned )current_mtd->size / (unsigned )current_mtd->erasesize) - 1)));
//ops.mode = MTD_OOB_RAW;
ops.mode = MTD_OOB_PLACE;
ops.datbuf = mBuf;
//ops.len = current_mtd->writesize + current_mtd->oobsize;
ops.len = current_mtd->writesize;
ops.oobbuf = NULL;
ops.ooblen = 0;
ops.retlen = 0;
ops.oobretlen = 0;
printk("[msm_onenand_read_dpram] number of blocks = %u, offset = %u, page size = %u, block size = %u\n", (unsigned )current_mtd->size / (unsigned )current_mtd->erasesize, offset, current_mtd->writesize, current_mtd->erasesize);
return msm_onenand_read_oob(current_mtd, offset, &ops);
}
EXPORT_SYMBOL(msm_onenand_read_dpram);
void msm_onenand_read_param(char *mBuf)
{
char data_buf[4096] = { 0, };
struct mtd_oob_ops ops = { 0, };
int data_size = 0;
if (current_mtd->oobsize == 64) {
data_size = 2048;
}
else if (current_mtd->oobsize == 128) {
data_size = 4096;
}
ops.mode = MTD_OOB_PLACE;
ops.len = data_size;
ops.retlen = 0;
ops.oobretlen = 0;
ops.ooblen = 0;
ops.datbuf = data_buf;
ops.oobbuf = NULL;
// erasize == size of entire block == page size * pages per block
while(msm_onenand_block_isbad(current_mtd, (param_start_block * current_mtd->erasesize)))
{
printk("msm_read_param: bad block\n");
param_start_block++;
}
if ( param_start_block >= param_end_block) {
param_start_block = param_end_block - 1;
printk("All nand block in param partition has been crashed\n");
}
msm_onenand_read_oob(current_mtd, (param_start_block * current_mtd->erasesize), &ops);
memcpy(mBuf,data_buf,sizeof(data_buf));
}
EXPORT_SYMBOL(msm_onenand_read_param);
void msm_onenand_write_param(char *mBuf)
{
char data_buf[4096] = { 0, };
struct mtd_oob_ops ops = { 0, };
struct erase_info *param_erase_info = 0;
int data_size = 0;
if (current_mtd->oobsize == 64) {
data_size = 2048;
}
else if (current_mtd->oobsize == 128) {
data_size = 4096;
}
param_erase_info = kzalloc(sizeof(struct erase_info), GFP_KERNEL);
if(0 == param_erase_info)
{
printk("msm_write_param: memory allocation error\n");
return;
}
param_erase_info->mtd = current_mtd;
// erasize == size of entire block == page size * pages per block
param_erase_info->addr = param_start_block * current_mtd->erasesize;
param_erase_info->len = current_mtd->erasesize;
if(!msm_onenand_erase(current_mtd, param_erase_info)) {
pr_info("parameter block erase success\n");
}
memcpy(data_buf,mBuf,sizeof(data_buf));
ops.mode = MTD_OOB_PLACE;
ops.len = data_size;
ops.retlen = 0;
ops.oobretlen = 0;
ops.ooblen = 0;
ops.datbuf = data_buf;
ops.oobbuf = NULL;
msm_onenand_write_oob(current_mtd, param_erase_info->addr, &ops);
kfree(param_erase_info);
}
EXPORT_SYMBOL(msm_onenand_write_param);
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;
int i;
/* 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 << 1;
mtd->oobsize = mtd->writesize >> 5;
mtd->erasesize = mtd->writesize << 6;
mtd->oobavail = msm_onenand_oob_128.oobavail;
mtd->ecclayout = &msm_onenand_oob_128;
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");
current_mtd = mtd; // for PARAMETER block
for ( i = 0 ; i < msm_nand_data.nr_parts ; i++) {
if (!strcmp(msm_nand_data.parts[i].name , "parameter")) {
param_start_block = msm_nand_data.parts[i].offset;
param_end_block = msm_nand_data.parts[i].offset + msm_nand_data.parts[i].size; // should match with bootloader
}
}
return 0;
}
/**
* msm_nand_scan - [msm_nand Interface] Scan for the msm_nand device
* @param mtd MTD device structure
* @param maxchips Number of chips to scan for
*
* This fills out all the not initialized function pointers
* with the defaults.
* The flash ID is read and the mtd/chip structures are
* filled with the appropriate values.
*/
int msm_nand_scan(struct mtd_info *mtd, int maxchips)
{
struct msm_nand_chip *chip = mtd->priv;
uint32_t flash_id = 0, i = 1, mtd_writesize;
uint8_t dev_found = 0;
uint8_t wide_bus;
uint8_t index;
/* Probe the Flash device for ONFI compliance */
#if defined (CONFIG_MACH_COOPER) || defined (CONFIG_MACH_EUROPA)
/* Read the Flash ID from the Nand Flash Device */
flash_id = flash_read_id(chip);
for (index = 1; index < ARRAY_SIZE(supported_flash); index++)
if ((flash_id & supported_flash[index].mask) ==
(supported_flash[index].flash_id &
(supported_flash[index].mask))) {
dev_found = 1;
break;
}
#else
if (!flash_onfi_probe(chip)) {
index = 0;
dev_found = 1;
} else {
/* Read the Flash ID from the Nand Flash Device */
flash_id = flash_read_id(chip);
for (index = 1; index < ARRAY_SIZE(supported_flash); index++)
if ((flash_id & supported_flash[index].mask) ==
(supported_flash[index].flash_id &
(supported_flash[index].mask))) {
dev_found = 1;
break;
}
}
#endif
if (dev_found) {
(!interleave_enable) ? (i = 1) : (i = 2);
wide_bus = supported_flash[index].widebus;
mtd->size = supported_flash[index].density * i;
mtd->writesize = supported_flash[index].pagesize * i;
mtd->oobsize = supported_flash[index].oobsize * i;
mtd->erasesize = supported_flash[index].blksize * i;
if (!interleave_enable)
mtd_writesize = mtd->writesize;
else
mtd_writesize = mtd->writesize >> 1;
pr_info("Found a supported NAND device\n");
pr_info("NAND Id : 0x%x\n", supported_flash[index].
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;
}
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 spare bytes for 8 bit */
| ((wide_bus) ? (0 << 23) : (1 << 23));
chip->CFG1 = (0 << 0) /* Enable ecc */
| (7 << 2) /* 8 recovery cycles */
| (0 << 5) /* Allow CS deassertion */
| ((mtd_writesize - (528 * ((mtd_writesize >> 9) - 1)) + 1)
<< 6) /* Bad block marker location */
| (0 << 16) /* Bad block in user data area */
| (2 << 17) /* 6 cycle tWB/tRB */
| (wide_bus << 1); /* Wide flash bit */
chip->ecc_buf_cfg = 0x203;
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 == 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); */
current_mtd = mtd; // for PARAMETER block
for ( i = 0 ; i < msm_nand_data.nr_parts ; i++) {
if (!strcmp(msm_nand_data.parts[i].name , "parameter")) {
param_start_block = msm_nand_data.parts[i].offset;
param_end_block = msm_nand_data.parts[i].offset + msm_nand_data.parts[i].size; // should match with bootloader
}
}
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; */
#ifdef CONFIG_MTD_PARTITIONS
/* Deregister partitions */
del_mtd_partitions(mtd);
#endif
/* Deregister the device */
del_mtd_device(mtd);
}
EXPORT_SYMBOL_GPL(msm_nand_release);
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "cmdlinepart", NULL, };
#endif
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;
dsb();
msm_dmov_exec_cmd(chip->dma_channel, crci_mask, DMOV_CMD_PTR_LIST
| DMOV_CMD_ADDR(msm_virt_to_dma(chip,
&dma_buffer->cmdptr)));
dsb();
msm_nand_release_dma_buffer(chip, dma_buffer, sizeof(*dma_buffer));
return 0;
}
#ifdef CONFIG_MTD_PARTITIONS
static void setup_mtd_device(struct platform_device *pdev,
struct msm_nand_info *info)
{
int i, nr_parts;
struct flash_platform_data *pdata = pdev->dev.platform_data;
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;
}
nr_parts = parse_mtd_partitions(&info->mtd, part_probes, &info->parts,
0);
if (nr_parts > 0)
add_mtd_partitions(&info->mtd, info->parts, nr_parts);
else if (nr_parts <= 0 && pdata && pdata->parts)
add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
else
add_mtd_device(&info->mtd);
}
#else
static void setup_mtd_device(struct platform_device *pdev,
struct msm_nand_info *info)
{
add_mtd_device(&info->mtd);
}
#endif
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;
#if defined (CONFIG_MACH_COOPER)
dual_nand_ctlr_present = 0;
interleave_enable = 0;
#else
dual_nand_ctlr_present = 1;
if (plat_data != NULL)
interleave_enable = plat_data->interleave;
else
interleave_enable = 0;
#endif
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);
crci_mask = msm_dmov_build_crci_mask(2,
DMOV_NAND_CRCI_DATA, DMOV_NAND_CRCI_CMD);
info->mtd.name = dev_name(&pdev->dev);
info->mtd.priv = &info->msm_nand;
info->mtd.owner = THIS_MODULE;
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;
}
setup_mtd_device(pdev, info);
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) {
#ifdef CONFIG_MTD_PARTITIONS
if (info->parts)
del_mtd_partitions(&info->mtd);
else
#endif
del_mtd_device(&info->mtd);
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");
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <asm/unistd.h>
#include <asm/errno.h>
#include <asm/uaccess.h>
#include <linux/device.h>
extern int msm_onenand_read_param(char *mBuf);
extern int msm_onenand_write_param(char *mBuf);
#include "param.h"
//#include <samsung_flash.h>
#if defined(CONFIG_MACH_ROOKIE) || defined(CONFIG_MACH_ESCAPE) || defined(CONFIG_MACH_GIO)
#include "../../arch/arm/mach-msm/smd_private.h"
#include "../../arch/arm/mach-msm/proc_comm.h"
#endif
//#define PARAM_nID FSR_PARTID_BML9
//#define NAND_PAGE_PER_UNIT 64
//#define NAND_SECTOR_PER_PAGE 8
#define NAND_PAGE_SIZE 0x1000
#if defined(CONFIG_MACH_GIO)
#define CAL_PARAM // sensor calibration value
#endif
#ifdef CAL_PARAM
#define DATE_SIZE 13
#define ACCL_OFFSET_SIZE 25
extern struct device *kr3dm_dev_t;
typedef struct _cal_param {
char result;
char date[DATE_SIZE];
char acc_offset[ACCL_OFFSET_SIZE];
} CAL_RESULT_PARAM;
#endif
struct proc_dir_entry *dir1;
// must be same as bootable/bootloader/lk/app/aboot/common.h
/* PARAM STRUCTURE */
typedef struct _param {
int booting_now; // 1:boot 0:enter service
int fota_mode; // 1:recovery mode 0:boot
int status; // delta update ex) 0x001: KERNEL_UPDATE_SUCCESS , 0x011: KERNEL_UPDATE_SUCCESS + MODEM_UPDATE_SUCCESS
int gota_mode; // 1: gota, 0: nomal
int recovery_command;
char efs_info[32];
char keystr[32]; // Å° ½ºÆ®¸µ À¯Ãâ ¹æÁö.
#if 1 //defined(CONFIG_MACH_ROOKIE) || defined(CONFIG_MACH_ESCAPE) || defined(CONFIG_MACH_GIO)
int ram_dump_level;
int ram_dump_level_init;
#endif
unsigned int first_boot_done; //rooting information
unsigned int custom_download_cnt;
char current_binary[30];
#ifdef CAL_PARAM
char result;
char date[DATE_SIZE];
char acc_offset[ACCL_OFFSET_SIZE];
#endif
} PARAM;
//FSRPartI pstPartI;
//int param_n1stVun;
char mBuf[NAND_PAGE_SIZE];
//extern struct proc_dir_entry *fsr_proc_dir;
/*static int get_param_start_unit(void)
{
int cnt;
if(param_n1stVun == 0) {
samsung_get_full_bmlparti(&pstPartI);
for(cnt = 0; cnt < pstPartI.nNumOfPartEntry; cnt++)
if(pstPartI.stPEntry[cnt].nID == PARAM_nID)
break;
param_n1stVun = pstPartI.stPEntry[cnt].n1stVun;
}
return param_n1stVun;
}*/
static int param_read_proc_debug(char *page, char **start, off_t offset, int count, int *eof, void *data)
{
int err;
PARAM efs;
*eof = 1;
memset(mBuf, 0xff, NAND_PAGE_SIZE);
// read first page from param block
err = msm_onenand_read_param(mBuf);
if(err) {
printk("PARAMERTER READ FAIL!\n");
return err;
}
memcpy(&efs, mBuf, sizeof(PARAM));
printk("PARAM booting_now : %d\n",efs.booting_now);
printk("PARAM fota_mode : %d\n",efs.fota_mode);
printk("PARAM efs_info : %s\n",efs.efs_info);
return sprintf(page, "%s\n", efs.efs_info);
}
static int param_write_proc_debug(struct file *file, const char *buffer,
unsigned long count, void *data)
{
char *buf;
int err;
PARAM efs;
if (count < 1)
return -EINVAL;
if(count > sizeof(efs.efs_info))
return -EFAULT;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, buffer, count)) {
kfree(buf);
return -EFAULT;
}
memset(mBuf, 0xff, NAND_PAGE_SIZE);
err = msm_onenand_read_param(mBuf);
if(err) {
printk("PARAMERTER READ FAIL!\n");
return err;
}
memcpy(&efs, mBuf, sizeof(PARAM));
// copy user data to efs
memset(efs.efs_info, 0x0, sizeof(efs.efs_info));
memcpy(efs.efs_info, buf, (int)count);
memcpy(mBuf, &efs, sizeof(PARAM));
// write first page from param block
err = msm_onenand_write_param(mBuf);
if(err) {
printk("PARAMERTER WRITE FAIL!\n");
kfree(buf);
return err;
}
kfree(buf);
return count;
}
static int param_keystr_read_proc_debug(char *page, char **start, off_t offset, int count, int *eof, void *data)
{
int err;
PARAM efs;
*eof = 1;
memset(mBuf, 0xff, NAND_PAGE_SIZE);
// read first page from param block
err = msm_onenand_read_param(mBuf);
if(err) {
printk("PARAMERTER READ FAIL!\n");
return err;
}
memcpy(&efs, mBuf, sizeof(PARAM));
printk("PARAM::booting_now : %d\n",efs.booting_now);
printk("PARAM::fota_mode : %d\n",efs.fota_mode);
printk("PARAM::efs_info : %s\n",efs.efs_info);
printk("PARAM::keystr : %s\n",efs.keystr);
return sprintf(page, "%s\n", efs.keystr);
}
static int param_keystr_write_proc_debug(struct file *file, const char *buffer,
unsigned long count, void *data)
{
char *buf;
int err;
PARAM efs;
if (count < 1)
return -EINVAL;
if(count > sizeof(efs.keystr))
return -EFAULT;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
if (copy_from_user(buf, buffer, count)) {
kfree(buf);
return -EFAULT;
}
memset(mBuf, 0xff, NAND_PAGE_SIZE);
err = msm_onenand_read_param(mBuf);
if(err) {
printk("PARAMERTER READ FAIL!\n");
return err;
}
memcpy(&efs, mBuf, sizeof(PARAM));
// copy user data to efs
memset(efs.keystr, 0x0, sizeof(efs.keystr));
memcpy(efs.keystr, buf, (int)count);
memcpy(mBuf, &efs, sizeof(PARAM));
// write first page from param block
err = msm_onenand_write_param(mBuf);
if(err) {
printk("PARAMERTER WRITE FAIL!\n");
kfree(buf);
return err;
}
kfree(buf);
return count;
}
extern int (*set_recovery_mode)(void);
int _set_recovery_mode(void)
{
int err;
PARAM param;
printk("_set_recovery_mode++");
memset(mBuf, 0xff, NAND_PAGE_SIZE);
err = msm_onenand_read_param(mBuf);
if(err) {
printk("PARAMERTER READ FAIL!\n");
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
// copy user data to efs
param.booting_now = RECOVERY_ENTER_MODE;
memcpy(mBuf,&param,sizeof(PARAM));
// write first page to param block
err = msm_onenand_write_param(mBuf);
if(err) {
printk("PARAMERTER WRITE FAIL!\n");
return err;
}
return 0;
}
extern int (*set_recovery_mode_done)(void);
int _set_recovery_mode_done(void)
{
int err;
PARAM param;
printk("_set_recovery_mode_done++");
memset(mBuf, 0xff, NAND_PAGE_SIZE);
err = msm_onenand_read_param(mBuf);
if(err) {
printk("PARAMERTER READ FAIL!\n");
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
// copy user data to efs
param.booting_now = RECOVERY_END_MODE;
memcpy(mBuf,&param,sizeof(PARAM));
// write first page to param block
err = msm_onenand_write_param(mBuf);
if(err) {
printk("PARAMERTER WRITE FAIL!\n");
return err;
}
printk("_set_recovery_mode_done--");
return 0;
}
#if defined(CONFIG_MACH_ROOKIE) || defined(CONFIG_MACH_ESCAPE) || defined(CONFIG_MACH_GIO)
extern int (*set_ram_dump_level)(int ram_dump_level);
int _set_ram_dump_level(int ram_dump_level)
{
int err;
unsigned int nByteRet;
PARAM param;
FSRChangePA stChangePA;
memset(mBuf, 0xff, NAND_PAGE_SIZE);
err = samsung_bml_read(get_param_start_unit() * NAND_PAGE_PER_UNIT * NAND_SECTOR_PER_PAGE, NAND_SECTOR_PER_PAGE, mBuf, NULL);
if(err) {
printk("PARAMERTER BML READ FAIL!\n");
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
// copy user data to efs
param.ram_dump_level = ram_dump_level;
param.ram_dump_level_init = 0x1234;
memcpy(mBuf,&param,sizeof(PARAM));
stChangePA.nPartID = PARAM_nID;
stChangePA.nNewAttr = FSR_BML_PI_ATTR_RW;
if (FSR_BML_IOCtl(0, FSR_BML_IOCTL_CHANGE_PART_ATTR , (UINT8 *) &stChangePA, sizeof(stChangePA), NULL, 0, &nByteRet) != FSR_BML_SUCCESS) {
return FS_DEVICE_FAIL;
}
err = samsung_bml_erase(get_param_start_unit(), 1);
if(err) {
printk("PARAMERTER BML ERASE FAIL!\n");
return err;
}
// write first page to param block
err = samsung_bml_write(get_param_start_unit() * NAND_PAGE_PER_UNIT, 1, mBuf, NULL);
if(err) {
printk("PARAMERTER BML WRITE FAIL!\n");
return err;
}
stChangePA.nNewAttr = FSR_BML_PI_ATTR_RO;
if (FSR_BML_IOCtl(0, FSR_BML_IOCTL_CHANGE_PART_ATTR , (UINT8 *) &stChangePA, sizeof(stChangePA), NULL, 0, &nByteRet) != FSR_BML_SUCCESS) {
return FS_DEVICE_FAIL;
}
return 0;
}
extern int (*get_ram_dump_level)(void);
int _get_ram_dump_level(void)
{
int err;
unsigned int nByteRet;
PARAM param;
FSRChangePA stChangePA;
memset(mBuf, 0xff, NAND_PAGE_SIZE);
err = samsung_bml_read(get_param_start_unit() * NAND_PAGE_PER_UNIT * NAND_SECTOR_PER_PAGE, NAND_SECTOR_PER_PAGE, mBuf, NULL);
if(err) {
printk("PARAMERTER BML READ FAIL!\n");
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
if(param.ram_dump_level_init == 0x1234)
return param.ram_dump_level;
else
return -1;
}
#endif
#ifdef CAL_PARAM
static int cal_result_param_read(struct device *dev, struct device_attribute *attr, char *buf)
{
int err;
PARAM param;
memset(mBuf, 0xff, NAND_PAGE_SIZE);
// read first page from param block
err = samsung_bml_read(get_param_start_unit() * NAND_PAGE_PER_UNIT * NAND_SECTOR_PER_PAGE, NAND_SECTOR_PER_PAGE, mBuf, NULL);
if(err) {
printk("PARAMERTER BML READ FAIL!\n");
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
printk("ACC CAL PARAM result : %c\n", param.result);
printk("ACC CAL PARAM date : %s\n", param.date);
return sprintf(buf, "%c%s\n", param.result, param.date);
}
static int cal_result_param_write(struct device *dev, struct device_attribute *attr, const char *buffer, size_t size)
{
int err;
char *buf;
unsigned int nByteRet = 0;
PARAM param;
FSRChangePA stChangePA;
printk("[%s] size = %d\n", __func__, size);
printk("[%s] buffer = %s\n", __func__, buffer);
if (size < 1)
return -EINVAL;
if(size > (DATE_SIZE+1)){
printk(KERN_ERR "[%s] size of written buffer is bigger than PARAM structure\n", __func__);
return -EFAULT;
}
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
{
printk(KERN_ERR "[%s] Memory Allocation Failed.\n", __func__);
return -ENOMEM;
}
#if 0
if ((err = copy_from_user(buf, buffer, size))){
printk(KERN_ERR "[%s] Failed from copy user data. err = %d\n", __func__, err);
kfree(buf);
return -EFAULT;
}
#endif
memcpy(buf, buffer, size);
printk("[%s] new result.result = %c\n", __func__, ((CAL_RESULT_PARAM*)buf)->result);
printk("[%s] new result.date = %s\n", __func__, ((CAL_RESULT_PARAM*)buf)->date);
// initialize buffer
memset(mBuf, 0xff, NAND_PAGE_SIZE);
#if 0 // for format 2nd unit of param part
stChangePA.nPartID = PARAM_nID;
stChangePA.nNewAttr = FSR_BML_PI_ATTR_RW;
if (FSR_BML_IOCtl(0, FSR_BML_IOCTL_CHANGE_PART_ATTR , (UINT8 *) &stChangePA, sizeof(stChangePA), NULL, 0, &nByteRet) != FSR_BML_SUCCESS) {
kfree(buf);
return FS_DEVICE_FAIL;
}
err = samsung_bml_erase(get_param_start_unit(), 1);
if(err) {
printk("PARAMERTER BML ERASE FAIL!\n");
kfree(buf);
return err;
}
#endif
// read first page of cal param block
err = samsung_bml_read(get_param_start_unit() * NAND_PAGE_PER_UNIT * NAND_SECTOR_PER_PAGE, NAND_SECTOR_PER_PAGE, mBuf, NULL);
if(err) {
printk("PARAMERTER BML READ FAIL!\n");
kfree(buf);
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
// copy user data to cal result
memcpy(&param.result, &((CAL_RESULT_PARAM*)buf)->result, sizeof(char));
memcpy(&param.date, ((CAL_RESULT_PARAM*)buf)->date, DATE_SIZE);
memcpy(mBuf, &param, sizeof(PARAM));
stChangePA.nPartID = PARAM_nID;
stChangePA.nNewAttr = FSR_BML_PI_ATTR_RW;
if (FSR_BML_IOCtl(0, FSR_BML_IOCTL_CHANGE_PART_ATTR , (UINT8 *) &stChangePA, sizeof(stChangePA), NULL, 0, &nByteRet) != FSR_BML_SUCCESS) {
kfree(buf);
return FS_DEVICE_FAIL;
}
err = samsung_bml_erase(get_param_start_unit(), 1);
if(err) {
printk("PARAMERTER BML ERASE FAIL!\n");
kfree(buf);
return err;
}
// write back to chagned cal result
err = samsung_bml_write(get_param_start_unit() * NAND_PAGE_PER_UNIT, 1, mBuf, NULL);
if(err) {
printk("PARAMERTER BML WRITE FAIL!\n");
kfree(buf);
return err;
}
stChangePA.nNewAttr = FSR_BML_PI_ATTR_RO;
if (FSR_BML_IOCtl(0, FSR_BML_IOCTL_CHANGE_PART_ATTR , (UINT8 *) &stChangePA, sizeof(stChangePA), NULL, 0, &nByteRet) != FSR_BML_SUCCESS) {
kfree(buf);
return FS_DEVICE_FAIL;
}
kfree(buf);
return size;
}
static int cal_check(const char *buf, size_t size)
{
int i=2;
printk("[%s] buf = %s\n", __func__, buf);
while(i < size)
{
//printk("%d line buf : %c, Zero : %c\n", i, buf[i], '0');
if(buf[i] != '0')
{
return 1;
}
i++;
if( (i == 6) || (i == 13) ) i=i+3;
}
return 0;
}
static int cal_offset_param_read(struct device *dev, struct device_attribute *attr, char *buf)
{
int err, result;
PARAM param;
memset(mBuf, 0xff, NAND_PAGE_SIZE);
// read first page from param block
err = samsung_bml_read(get_param_start_unit() * NAND_PAGE_PER_UNIT * NAND_SECTOR_PER_PAGE, NAND_SECTOR_PER_PAGE, mBuf, NULL);
if(err) {
printk("PARAMERTER BML READ FAIL!\n");
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
//printk("ACC CAL PARAM offset : %s\n", cal_result.acc_offset);
result = cal_check(param.acc_offset, 19);
//printk("[%s] cal_result = %d\n", __func__, result);
if(!result) return sprintf(buf, "%d\n", 1);
return sprintf(buf, "%s\n", param.acc_offset);
}
static int cal_offset_param_write(struct device *dev, struct device_attribute *attr, const char *buffer, size_t size)
{
int err, cal_result=0;
char *buf;
unsigned int nByteRet = 0;
PARAM param;
FSRChangePA stChangePA;
printk("[%s] size = %d\n", __func__, size);
printk("[%s] buffer = %s\n", __func__, buffer);
if (size < 1)
{
return -EINVAL;
}
else if(size > ACCL_OFFSET_SIZE)
{
printk(KERN_ERR "[%s] size of written buffer is bigger than PARAM structure\n", __func__);
return -EFAULT;
}
else
{
cal_result=cal_check(buffer, size);
//printk("[%s] cal_result = %d\n", __func__, cal_result);
}
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
{
printk(KERN_ERR "[%s] Memory Allocation Failed.\n", __func__);
if(cal_result) sprintf(buffer,"%d\n", 1);
return -ENOMEM;
}
#if 0
if ((err = copy_from_user(buf, buffer, size))){
printk(KERN_ERR "[%s] Failed from copy user data. err = %d\n", __func__, err);
kfree(buf);
return -EFAULT;
}
#endif
memcpy(buf, buffer, size);
printk("[%s] new offset = %s\n", __func__, buf);
// initialize buffer
memset(mBuf, 0xff, NAND_PAGE_SIZE);
// read first page of cal param block
err = samsung_bml_read(get_param_start_unit() * NAND_PAGE_PER_UNIT * NAND_SECTOR_PER_PAGE, NAND_SECTOR_PER_PAGE, mBuf, NULL);
if(err) {
printk("PARAMERTER BML READ FAIL!\n");
kfree(buf);
if(cal_result) sprintf(buffer,"%d\n", 1);
return err;
}
memcpy(&param, mBuf, sizeof(PARAM));
// copy user data to cal result
memcpy(&param.acc_offset, buf, ACCL_OFFSET_SIZE);
memcpy(mBuf, &param, sizeof(PARAM));
stChangePA.nPartID = PARAM_nID;
stChangePA.nNewAttr = FSR_BML_PI_ATTR_RW;
if (FSR_BML_IOCtl(0, FSR_BML_IOCTL_CHANGE_PART_ATTR , (UINT8 *) &stChangePA, sizeof(stChangePA), NULL, 0, &nByteRet) != FSR_BML_SUCCESS) {
kfree(buf);
if(cal_result) sprintf(buffer,"%d\n", 1);
return FS_DEVICE_FAIL;
}
err = samsung_bml_erase(get_param_start_unit(), 1);
if(err) {
printk("PARAMERTER BML ERASE FAIL!\n");
kfree(buf);
if(cal_result) sprintf(buffer,"%d\n", 1);
return err;
}
// write back to chagned cal result
err = samsung_bml_write(get_param_start_unit() * NAND_PAGE_PER_UNIT, 1, mBuf, NULL);
if(err) {
printk("PARAMERTER BML WRITE FAIL!\n");
kfree(buf);
if(cal_result) sprintf(buffer,"%d\n", 1);
return err;
}
stChangePA.nNewAttr = FSR_BML_PI_ATTR_RO;
if (FSR_BML_IOCtl(0, FSR_BML_IOCTL_CHANGE_PART_ATTR , (UINT8 *) &stChangePA, sizeof(stChangePA), NULL, 0, &nByteRet) != FSR_BML_SUCCESS) {
kfree(buf);
if(cal_result) sprintf(buffer,"%d\n", 1);
return FS_DEVICE_FAIL;
}
kfree(buf);
if(!cal_result)
{
sprintf(buffer,"%d\n", 0);
}
else
{
sprintf(buffer, "%s\n", param.acc_offset);
}
return size;
}
static DEVICE_ATTR(cal_result, 0644, cal_result_param_read, cal_result_param_write);
static DEVICE_ATTR(cal_offset, 0644, cal_offset_param_read, cal_offset_param_write);
#endif
static int __init param_init(void)
{
struct proc_dir_entry *ent, *ent2, *dir1;
#if defined(CONFIG_MACH_ROOKIE) || defined(CONFIG_MACH_ESCAPE) || defined(CONFIG_MACH_GIO)
samsung_vendor1_id* smem_vendor1 = (samsung_vendor1_id *)smem_alloc(SMEM_ID_VENDOR1, sizeof(samsung_vendor1_id));
int ram_dump_level;
#endif
dir1 = proc_mkdir("LinuStoreIII", NULL);
ent = create_proc_entry("efs_info", S_IFREG | S_IWUSR | S_IRUGO, dir1);
ent->read_proc = param_read_proc_debug;
ent->write_proc = param_write_proc_debug;
ent2 = create_proc_entry("keystr", S_IFREG | S_IWUSR | S_IRUGO, dir1);
ent2->read_proc = param_keystr_read_proc_debug;
ent2->write_proc = param_keystr_write_proc_debug;
set_recovery_mode = _set_recovery_mode;
set_recovery_mode_done = _set_recovery_mode_done;
#if defined(CONFIG_MACH_ROOKIE) || defined(CONFIG_MACH_ESCAPE) || defined(CONFIG_MACH_GIO)
set_ram_dump_level = _set_ram_dump_level;
get_ram_dump_level = _get_ram_dump_level;
ram_dump_level = _get_ram_dump_level();
if(ram_dump_level == -1){
ram_dump_level = smem_vendor1->ram_dump_level;
_set_ram_dump_level(ram_dump_level);
printk("[%s] Initialize Ramdump Level\n", __FUNCTION__);
}
smem_vendor1->ram_dump_level = ram_dump_level;
#endif
#ifdef CAL_PARAM
if (device_create_file(kr3dm_dev_t, &dev_attr_cal_result) < 0)
printk("Failed to create device file(%s)!\n", dev_attr_cal_result.attr.name);
if (device_create_file(kr3dm_dev_t, &dev_attr_cal_offset) < 0)
printk("Failed to create device file(%s)!\n", dev_attr_cal_offset.attr.name);
#endif
return 0;
}
static void __exit param_exit(void)
{
remove_proc_entry("efs_info", dir1);
remove_proc_entry("keystr", dir1);
}
module_init(param_init);
module_exit(param_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Samsung Electronics");
MODULE_DESCRIPTION("Samsung Param Operation");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment