Created
October 31, 2012 13:02
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/**************************************************************************** | |
** | |
** 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 *)¶m->data, sizeof(param->data)); | |
} | |
/* @LDK@ read */ | |
else { | |
READ_FROM_DPRAM((void *)¶m->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 *)¶m, (void *)arg, sizeof(param)); | |
dpram_mem_rw(¶m); | |
if (!param.dir) { | |
return copy_to_user((unsigned long *)arg, ¶m, 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"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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(¶m, mBuf, sizeof(PARAM)); | |
// copy user data to efs | |
param.booting_now = RECOVERY_ENTER_MODE; | |
memcpy(mBuf,¶m,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(¶m, mBuf, sizeof(PARAM)); | |
// copy user data to efs | |
param.booting_now = RECOVERY_END_MODE; | |
memcpy(mBuf,¶m,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(¶m, mBuf, sizeof(PARAM)); | |
// copy user data to efs | |
param.ram_dump_level = ram_dump_level; | |
param.ram_dump_level_init = 0x1234; | |
memcpy(mBuf,¶m,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(¶m, 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(¶m, 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(¶m, mBuf, sizeof(PARAM)); | |
// copy user data to cal result | |
memcpy(¶m.result, &((CAL_RESULT_PARAM*)buf)->result, sizeof(char)); | |
memcpy(¶m.date, ((CAL_RESULT_PARAM*)buf)->date, DATE_SIZE); | |
memcpy(mBuf, ¶m, 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(¶m, 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(¶m, mBuf, sizeof(PARAM)); | |
// copy user data to cal result | |
memcpy(¶m.acc_offset, buf, ACCL_OFFSET_SIZE); | |
memcpy(mBuf, ¶m, 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