Files
Kernel/drivers/mtd/brcmnand/brcmnand_priv.h

511 lines
14 KiB
C

/*
* drivers/mtd/brcmnand/brcmnand_priv.h
*
* Copyright (c) 2005-2009 Broadcom Corp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Data structures for Broadcom NAND controller
*
* when who what
* 20060729 tht Original coding
*/
#ifndef _BRCMNAND_PRIV_H_
#define _BRCMNAND_PRIV_H_
#include <linux/vmalloc.h>
#include <linux/mtd/brcmnand.h>
#ifdef CONFIG_MTD_BRCMNAND_USE_ISR
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/list.h>
//#include "edu.h"
#endif
#define BRCMNAND_CORRECTABLE_ECC_ERROR (1)
#define BRCMNAND_SUCCESS (0)
#define BRCMNAND_UNCORRECTABLE_ECC_ERROR (-1)
#define BRCMNAND_FLASH_STATUS_ERROR (-2)
#define BRCMNAND_TIMED_OUT (-3)
#ifdef CONFIG_MTD_BRCMNAND_EDU
#define BRCMEDU_CORRECTABLE_ECC_ERROR (4)
#define BRCMEDU_UNCORRECTABLE_ECC_ERROR (-4)
#define BRCMEDU_MEM_BUS_ERROR (-5)
#define BRCMNAND_malloc(size) kmalloc(size, GFP_DMA)
#define BRCMNAND_free(addr) kfree(addr)
#else
#define BRCMNAND_malloc(size) vmalloc(size)
#define BRCMNAND_free(addr) vfree(addr)
#endif
#if 0 /* TO */
typedef u8 uint8;
typedef u16 uint16;
typedef u32 uint32;
#endif
#define BRCMNAND_FCACHE_SIZE 512
#define ECCSIZE(chip) BRCMNAND_FCACHE_SIZE /* Always 512B for Brcm NAND controller */
#define MTD_OOB_NOT_WRITEABLE 0x8000
#define MTD_CAP_MLC_NANDFLASH (MTD_WRITEABLE | MTD_OOB_NOT_WRITEABLE)
#define MTD_IS_MLC(mtd) ((((mtd)->flags & MTD_CAP_MLC_NANDFLASH) == MTD_CAP_MLC_NANDFLASH) &&\
(((mtd)->flags & MTD_OOB_NOT_WRITEABLE) == MTD_OOB_NOT_WRITEABLE))
/*
* NUM_NAND_CS here is strictly based on the number of CS in the NAND registers
* It does not have the same value as NUM_CS in brcmstb/setup.c
* It is not the same as NAND_MAX_CS, the later being the bit fields found in NAND_CS_NAND_SELECT.
*/
/*
* # number of CS supported by EBI
*/
#ifdef BCHP_NAND_CS_NAND_SELECT_EBI_CS_7_SEL_MASK
/* Version < 3 */
#define NAND_MAX_CS 8
#elif defined(BCHP_NAND_CS_NAND_SELECT_EBI_CS_3_SEL_MASK)
/* 7420Cx */
#define NAND_MAX_CS 4
#else
/* 3548 */
#define NAND_MAX_CS 2
#endif
/*
* Number of CS seen by NAND
*/
#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3
#define NUM_NAND_CS 4
#else
#define NUM_NAND_CS 2
#endif
#ifdef CONFIG_MTD_BRCMNAND_USE_ISR
//#define BCM_BASE_ADDRESS 0xb0000000
/* CP0 hazard avoidance. */
#define BARRIER __asm__ __volatile__(".set noreorder\n\t" \
"nop; nop; nop; nop; nop; nop;\n\t" \
".set reorder\n\t")
/*
* Right now we submit a full page Read for queueing, so with a 8KB page,
* and an ECC step of 512B, the queue depth is 16. Add 2 for dummy elements
* during EDU WAR
*/
#if CONFIG_MTD_BRCMNAND_VERSION <= CONFIG_MTD_BRCMNAND_VERS_3_3
#define MAX_NAND_PAGE_SIZE (4<<10)
#else
#define MAX_NAND_PAGE_SIZE (8<<10)
#endif
/* Max queue size is (PageSize/512B_ECCSize)+2 spare for WAR */
#define MAX_JOB_QUEUE_SIZE ((MAX_NAND_PAGE_SIZE>>9))
typedef enum {
ISR_OP_QUEUED = 0,
ISR_OP_SUBMITTED = 1,
ISR_OP_NEED_WAR = 2,
ISR_OP_COMPLETED = 3,
ISR_OP_TIMEDOUT = 4,
ISR_OP_COMP_WITH_ERROR = 5,
} isrOpStatus_t;
typedef struct eduIsrNode {
struct list_head list;
spinlock_t lock; // per Node update lock
// int cmd; // 1 == Read, 0 == Write
// ISR stuffs
uint32_t mask; /* Clear status mask */
uint32_t expect; /* Status on success */
uint32_t error; /* Status on error */
uint32_t intr; /* Interrupt bits */
uint32_t status; /* Status read during ISR. There may be several interrupts before completion */
isrOpStatus_t opComplete; /* Completion status */
/* Controller Level params (for queueing) */
struct mtd_info* mtd;
void* buffer;
u_char* oobarea;
loff_t offset;
int ret;
int needBBT;
/* EDU level params (for ISR) */
uint32_t edu_ldw;
uint32_t physAddr;
uint32_t hif_intr2;
uint32_t edu_status;
int refCount; /* Marked for re-use when refCount=0 */
unsigned long expired; /* Time stamp for expiration, 3 secs from submission */
} eduIsrNode_t;
/*
* Read/Write Job Q.
* Process one page at a time, and queue 512B sector Read or Write EDU jobs.
* ISR will wake up the process context thread iff
* 1-EDU reports an error, in which case the process context thread need to be awaken
* in order to do WAR
* 2-Q is empty, in which case the page read/write op is complete.
*/
typedef struct jobQ_t {
struct list_head jobQ; /* Nodes queued for EDU jobs */
struct list_head availList; /* Free Nodes */
spinlock_t lock; /* Queues guarding spin lock */
int needWakeUp; /* Wake up Process context thread to do EDU WAR */
int cmd; /* 1 == Read, 0 == Write */
int corrected; /* Number of correctable errors */
} isrJobQ_t;
extern isrJobQ_t gJobQ;
void ISR_init(void);
/*
* Submit the first entry that is in queued state,
* assuming queue lock has been held by caller.
*
* @doubleBuffering indicates whether we need to submit just 1 job or until EDU is full (double buffering)
* Return the number of job submitted for read.
*
* In current version (v3.3 controller), since EDU only have 1 register for EDU_ERR_STATUS,
* we can't really do double-buffering without losing the returned status of the previous read-op.
*/
#undef EDU_DOUBLE_BUFFER_READ
int brcmnand_isr_submit_job(void);
eduIsrNode_t* ISR_queue_read_request(struct mtd_info *mtd,
void* buffer, u_char* oobarea, loff_t offset);
eduIsrNode_t* ISR_queue_write_request(struct mtd_info *mtd,
const void* buffer, const u_char* oobarea, loff_t offset);
eduIsrNode_t* ISR_push_request(struct mtd_info *mtd,
void* buffer, u_char* oobarea, loff_t offset);
int brcmnand_edu_read_completion(struct mtd_info* mtd,
void* buffer, u_char* oobarea, loff_t offset, uint32_t intr_status);
int brcmnand_edu_read_comp_intr(struct mtd_info* mtd,
void* buffer, u_char* oobarea, loff_t offset, uint32_t intr_status);
#ifdef CONFIG_MTD_BRCMNAND_ISR_QUEUE
int brcmnand_edu_write_completion(struct mtd_info *mtd,
const void* buffer, const u_char* oobarea, loff_t offset, uint32_t intr_status,
int needBBT);
int
brcmnand_edu_write_war(struct mtd_info *mtd,
const void* buffer, const u_char* oobarea, loff_t offset, uint32_t intr_status,
int needBBT);
#endif
eduIsrNode_t* ISR_find_request( isrOpStatus_t opStatus);
uint32_t ISR_wait_for_completion(void);
/*
* wait for completion with read/write Queue
*/
int ISR_wait_for_queue_completion(void);
int ISR_cache_is_valid(void);
static __inline__ uint32_t ISR_volatileRead(uint32_t addr)
{
return (uint32_t) BDEV_RD(addr);
}
static __inline__ void ISR_volatileWrite(uint32_t addr, uint32_t data)
{
BDEV_WR(addr, data);
}
static __inline__ void ISR_enable_irq(eduIsrNode_t* req)
{
//uint32_t intrMask;
//unsigned long flags;
//spin_lock_irqsave(&gEduIsrData.lock, flags);
// Clear status bits
ISR_volatileWrite(BCHP_HIF_INTR2_CPU_CLEAR, req->mask);
// Enable interrupt
ISR_volatileWrite(BCHP_HIF_INTR2_CPU_MASK_CLEAR, req->intr);
//spin_unlock_irqrestore(&gEduIsrData.lock, flags);
}
static __inline__ void ISR_disable_irq(uint32_t mask)
{
/* Disable L2 interrupts */
ISR_volatileWrite(BCHP_HIF_INTR2_CPU_MASK_SET, mask);
}
/*
* For debugging
*/
#ifdef DEBUG_ISR
static void __inline__
ISR_print_queue(void)
{
eduIsrNode_t* req;
//struct list_head* node;
int i = 0;
list_for_each_entry(req, &gJobQ.jobQ, list) {
printk("i=%d, cmd=%d, offset=%08llx, flashAddr=%08x, opComp=%d, status=%08x\n",
i, gJobQ.cmd, req->offset, req->edu_ldw,req->opComplete, req->status);
i++;
}
}
static void __inline__
ISR_print_avail_list(void)
{
eduIsrNode_t* req;
//struct list_head* node;
int i = 0;
printk("AvailList=%p, next=%p\n", &gJobQ.availList, gJobQ.availList.next);
list_for_each_entry(req, &gJobQ.availList, list) {
printk("i=%d, req=%p, list=%p\n", i, req, &req->list);
i++;
}
}
#else
#define IS_print_queue()
#define ISR_print_avail_list()
#endif // DEBUG_ISR
#endif // CONFIG_MTD_BRCMNAND_USE_ISR
static inline u_int64_t device_size(struct mtd_info *mtd)
{
//return mtd->size == 0 ? (u_int64_t) mtd->num_eraseblocks * mtd->erasesize : (u_int64_t) mtd->size;
return mtd->size;
}
/**
* brcmnand_scan - [BrcmNAND Interface] Scan for the BrcmNAND device
* @param mtd MTD device structure
* @cs Chip Select number
* @param numchips Number of chips (from CFE or from nandcs= kernel arg)
*
* 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.
*
*/
extern int brcmnand_scan(struct mtd_info *mtd , int cs, int maxchips);
/**
* brcmnand_release - [BrcmNAND Interface] Free resources held by the BrcmNAND device
* @param mtd MTD device structure
*/
extern void brcmnand_release(struct mtd_info *mtd);
/* BrcmNAND BBT interface */
/* Auto-format scan layout for BCH-8 with 16B OOB */
#define BRCMNAND_BBT_AUTO_PLACE 0x80000000
extern uint8_t* brcmnand_transfer_oob(struct brcmnand_chip *chip, uint8_t *oob,
struct mtd_oob_ops *ops, int len);
extern uint8_t* brcmnand_fill_oob(struct brcmnand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops);
/* Read the OOB bytes and tell whether a block is bad without consulting the BBT */
extern int brcmnand_isbad_raw (struct mtd_info *mtd, loff_t offs);
extern int brcmnand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
extern int brcmnand_default_bbt(struct mtd_info *mtd);
extern int brcmnand_update_bbt (struct mtd_info *mtd, loff_t offs);
//extern void* get_brcmnand_handle(void);
extern void print_oobbuf(const unsigned char* buf, int len);
extern void print_databuf(const unsigned char* buf, int len);
#ifdef CONFIG_MTD_BRCMNAND_CORRECTABLE_ERR_HANDLING
extern int brcmnand_cet_update(struct mtd_info *mtd, loff_t from, int *status);
extern int brcmnand_cet_prepare_reboot(struct mtd_info *mtd);
extern int brcmnand_cet_erasecallback(struct mtd_info *mtd, u_int32_t addr);
extern int brcmnand_create_cet(struct mtd_info *mtd);
#endif
/*
* Disable ECC, and return the original ACC register (for restore)
*/
uint32_t brcmnand_disable_read_ecc(int cs);
void brcmnand_restore_ecc(int cs, uint32_t orig_acc0);
void brcmnand_post_mortem_dump(struct mtd_info* mtd, loff_t offset);
static unsigned int __maybe_unused brcmnand_get_bbt_size(struct mtd_info* mtd)
{
struct brcmnand_chip * chip = mtd->priv;
// return ((device_size(mtd) > (512 << 20)) ? 4<<20 : 1<<20);
return chip->bbtSize;
}
#if CONFIG_MTD_BRCMNAND_VERSION >= CONFIG_MTD_BRCMNAND_VERS_3_3
static inline uint32_t bchp_nand_acc_control(int cs)
{
switch (cs) {
case 0: return BCHP_NAND_ACC_CONTROL;
case 1: return BCHP_NAND_ACC_CONTROL_CS1;
#ifdef BCHP_NAND_ACC_CONTROL_CS2
case 2: return BCHP_NAND_ACC_CONTROL_CS2;
#endif
#ifdef BCHP_NAND_ACC_CONTROL_CS3
case 3: return BCHP_NAND_ACC_CONTROL_CS3;
#endif
}
return 0;
}
static inline uint32_t bchp_nand_config(int cs)
{
switch (cs) {
case 0: return BCHP_NAND_CONFIG;
case 1: return BCHP_NAND_CONFIG_CS1;
#ifdef BCHP_NAND_CONFIG_CS2
case 2: return BCHP_NAND_CONFIG_CS2;
#endif
#ifdef BCHP_NAND_CONFIG_CS3
case 3: return BCHP_NAND_CONFIG_CS3;
#endif
}
return 0;
}
static inline uint32_t bchp_nand_timing1(int cs)
{
switch (cs) {
case 0: return BCHP_NAND_TIMING_1;
case 1: return BCHP_NAND_TIMING_1_CS1;
#ifdef BCHP_NAND_TIMING_1_CS2
case 2: return BCHP_NAND_TIMING_1_CS2;
#endif
#ifdef BCHP_NAND_TIMING_1_CS3
case 3: return BCHP_NAND_TIMING_1_CS3;
#endif
}
return 0;
}
static inline uint32_t bchp_nand_timing2(int cs)
{
switch (cs) {
case 0: return BCHP_NAND_TIMING_2;
case 1: return BCHP_NAND_TIMING_2_CS1;
#ifdef BCHP_NAND_TIMING_2_CS2
case 2: return BCHP_NAND_TIMING_2_CS2;
#endif
#ifdef BCHP_NAND_TIMING_2_CS3
case 3: return BCHP_NAND_TIMING_2_CS3;
#endif
}
return 0;
}
#else
#define bchp_nand_acc_control(cs) BCHP_NAND_ACC_CONTROL
#define bchp_nand_config(cs) BCHP_NAND_CONFIG
#define bchp_nand_timing1(cs) BCHP_NAND_TIMING_1
#define bchp_nand_timing2(cs) BCHP_NAND_TIMING_2
#endif
/***********************************************************************
* Register access macros - sample usage:
*
* DEV_RD(0xb0404000) -> reads 0xb0404000
* BDEV_RD(0x404000) -> reads 0xb0404000
* BDEV_RD(BCHP_SUN_TOP_CTRL_PROD_REVISION) -> reads 0xb0404000
*
* _RB means read back after writing.
***********************************************************************/
#define BPHYSADDR(x) ((x) | 0x10000000)
#define BVIRTADDR(x) KSEG1ADDR(BPHYSADDR(x))
#define DEV_RD(x) (*((volatile unsigned long *)(x)))
#define DEV_WR(x, y) do { *((volatile unsigned long *)(x)) = (y); } while (0)
#define DEV_UNSET(x, y) do { DEV_WR((x), DEV_RD(x) & ~(y)); } while (0)
#define DEV_SET(x, y) do { DEV_WR((x), DEV_RD(x) | (y)); } while (0)
#define DEV_WR_RB(x, y) do { DEV_WR((x), (y)); DEV_RD(x); } while (0)
#define DEV_SET_RB(x, y) do { DEV_SET((x), (y)); DEV_RD(x); } while (0)
#define DEV_UNSET_RB(x, y) do { DEV_UNSET((x), (y)); DEV_RD(x); } while (0)
#define BDEV_RD(x) (DEV_RD(BVIRTADDR(x)))
#define BDEV_WR(x, y) do { DEV_WR(BVIRTADDR(x), (y)); } while (0)
#define BDEV_UNSET(x, y) do { BDEV_WR((x), BDEV_RD(x) & ~(y)); } while (0)
#define BDEV_SET(x, y) do { BDEV_WR((x), BDEV_RD(x) | (y)); } while (0)
#define BDEV_SET_RB(x, y) do { BDEV_SET((x), (y)); BDEV_RD(x); } while (0)
#define BDEV_UNSET_RB(x, y) do { BDEV_UNSET((x), (y)); BDEV_RD(x); } while (0)
#define BDEV_WR_RB(x, y) do { BDEV_WR((x), (y)); BDEV_RD(x); } while (0)
#define BDEV_RD_F(reg, field) \
((BDEV_RD(BCHP_##reg) & BCHP_##reg##_##field##_MASK) >> \
BCHP_##reg##_##field##_SHIFT)
#define BDEV_WR_F(reg, field, val) do { \
BDEV_WR(BCHP_##reg, \
(BDEV_RD(BCHP_##reg) & ~BCHP_##reg##_##field##_MASK) | \
(((val) << BCHP_##reg##_##field##_SHIFT) & \
BCHP_##reg##_##field##_MASK)); \
} while (0)
#define BDEV_WR_F_RB(reg, field, val) do { \
BDEV_WR(BCHP_##reg, \
(BDEV_RD(BCHP_##reg) & ~BCHP_##reg##_##field##_MASK) | \
(((val) << BCHP_##reg##_##field##_SHIFT) & \
BCHP_##reg##_##field##_MASK)); \
BDEV_RD(BCHP_##reg); \
} while (0)
#endif