Files
Tplink-xx230v_kernel/drivers/mtd/chips/spiflash_tc3162.c

1284 lines
30 KiB
C
Executable File

/*
* SPIFLASH support for TC3162
*/
/*
* MTD driver for the SPI Flash Memory support.
*
* Copyright (c) 2005-2006 Atheros Communications Inc.
* Copyright (C) 2006-2007 FON Technology, SL.
* Copyright (C) 2006-2007 Imre Kaloz <kaloz@openwrt.org>
* Copyright (C) 2006-2007 Felix Fietkau <nbd@openwrt.org>
*
* This code 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.
*
*/
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/gen_probe.h>
#include <asm/delay.h>
#include <asm/io.h>
#include <asm/tc3162/tc3162.h>
#include "spiflash_tc3162.h"
#include <linux/kthread.h>
/* debugging */
/* #define SPIFLASH_DEBUG */
#define TC_SOC
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define FALSE 0
#define TRUE 1
#define ID_MASK 0xffff
#define MANUFACTURER_ID(id) ((id >> 16) & ID_MASK)
#define DEVICE_ID(id) (id & ID_MASK)
#define SIZE_64KiB 0x10000
#define SIZE_64MiB 0x4000000
#define SIZE_32MiB 0x2000000
#define SIZE_16MiB 0x1000000
#define SIZE_8MiB 0x800000
#define SIZE_4MiB 0x400000
#define SIZE_2MiB 0x200000
/* Manufacturers */
#define MANUFACTURER_ST 0x0020
#define MANUFACTURER_WINBOND 0x00ef
#define MANUFACTURER_SST 0x00bf
#define MANUFACTURER_MXIC 0x00c2
#define MANUFACTURER_SPANSION 0x0001
#define MANUFACTURER_EON 0x001c
#define MANUFACTURER_NUMONYX 0x0020
/* GD */
#define MANUFACTURER_GIGADEVICE 0x00c8
#define GD25Q64 0x4017
#define GD25Q128 0x4018
/* ST */
#define M25P16 0x2015
#define M25P32 0x2016
#define M25P64 0x2017
/* Winbond */
#define W25X16 0x3015
#define W25X32 0x3016
#define W25X64 0x3017
#define W25X128 0x3017
#define W25Q16 0x4015
#define W25Q32 0x4016
#define W25Q64 0x4017
#define W25Q128 0x4018
/* SST */
#define SST25VF032B 0x254a
/* MXIC */
#define MX25L3205D 0x2016
#define MX25L6405D 0x2017
#define MX25L12805D 0x2018
#define MX25L25635E 0x2019
/* SPANSION */
#define S25FL016A 0x0214
#define S25FL032A 0x0215
#define S25FL064A 0x0216
#define S25FL128P 0x2018
#define N25Q064 0xba17
/* EON */
#define EN25Q64 0x3017
#if defined(TC_SOC)
static __u32 reg0x28;
DEFINE_SEMAPHORE(SPI_SEM);//Make sure all related SPI operations are atomic
#define SPI_REG_BASE 0xbfbc0000
#define SPI_REG_MASTER 0xbfbc0028
#define SPI_REG_SPACE_CR 0xbfbc003c
#define SPI_REG_MOREBUF 0xbfbc002c
#define SPI_FLASH_DATA2 0x0C
#define SPI_FLASH_DATA3 0x10
#define SPI_FLASH_DATA4 0x14
#define SPI_FLASH_DATA5 0x18
#define SPI_FLASH_DATA6 0x1C
#define SPI_FLASH_DATA7 0x20
#define SPI_FLASH_DATA8 0x24
#endif
static __u32 spiflash_regread32(int reg);
static void spiflash_regwrite32(int reg, __u32 data);
static __u32 spiflash_sendcmd (int op);
struct spi_flash_info {
const u16 mfr_id;
const u16 dev_id;
const u16 extra_id;
const char *name;
const int DeviceSize;
const int EraseSize;
};
#if defined(TCSUPPORT_VOIP)
/*#11542: For voice afftected by Flash action issue*/
typedef struct spiEraseParam_s{
struct mtd_info* tmpMtd;
struct erase_info* tmpInstr;
}spiEraseParam_t;
spiEraseParam_t spiEraseParam;
static struct timer_list eraseCheck_Timer;
DECLARE_WAIT_QUEUE_HEAD(spi_erase_wq);
static struct task_struct *spi_erase_task;
atomic_t spi_erase_flag = ATOMIC_INIT(0);
static void spi_erase_alarm_expires(void)
{
atomic_inc(&spi_erase_flag);
wake_up_interruptible(&spi_erase_wq);
}
#endif
struct spi_chip_info {
struct spi_flash_info *flash;
void (*destroy)(struct spi_chip_info *chip_info);
u32 (*read)(struct map_info *map, u32 from, u32 to, u32 size);
u32 (*write)(struct map_info *map, u32 from, u32 to, u32 size);
u32 (*erase)(struct map_info *map, u32 addr);
};
/* Mapping of generic opcodes to STM serial flash opcodes */
struct opcodes {
__u16 code;
__s8 tx_cnt;
__s8 rx_cnt;
} stm_opcodes[] = {
{STM_OP_WR_ENABLE, 1, 0},
{STM_OP_WR_DISABLE, 1, 0},
{STM_OP_RD_STATUS, 1, 1},
{STM_OP_WR_STATUS, 2, 0},
{STM_OP_RD_DATA, 4, 4},
{STM_OP_FAST_RD_DATA, 1, 0},
{STM_OP_PAGE_PGRM, 8, 0},
{STM_OP_SECTOR_ERASE, 4, 0},
{STM_OP_BULK_ERASE, 1, 0},
{STM_OP_DEEP_PWRDOWN, 1, 0},
{STM_OP_RD_SIG, 4, 1},
{STM_OP_RD_ID, 1, 3}
};
static wait_queue_head_t spiflash_wq;
static spinlock_t spiflash_mutex;
static int spiflash_state;
static __u8 byte_program_mode = 0;
static __u8 four_byte_mode = 0;
static __u32
spiflash_regread32(int reg)
{
//volatile __u32 *addr = (__u32 *)(CR_SPI_BASE + reg);
//return (*addr);
return regRead32((__u32 *)(CR_SPI_BASE + reg));
}
static void
spiflash_regwrite32(int reg, __u32 data)
{
//volatile __u32 *addr = (__u32 *)(CR_SPI_BASE + reg);
regWrite32((__u32 *)(CR_SPI_BASE + reg),data);
//*addr = data;
return;
}
static __u32
spiflash_sendcmd (int op)
{
__u32 reg;
__u32 mask;
struct opcodes *ptr_opcode;
ptr_opcode = &stm_opcodes[op];
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
spiflash_regwrite32(SPI_FLASH_OPCODE, ptr_opcode->code);
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | ptr_opcode->tx_cnt |
(ptr_opcode->rx_cnt << 4) | SPI_CTL_START;
spiflash_regwrite32(SPI_FLASH_CTL, reg);
if (ptr_opcode->rx_cnt > 0) {
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
reg = (__u32) spiflash_regread32(SPI_FLASH_DATA);
switch (ptr_opcode->rx_cnt) {
case 1:
mask = 0x000000ff;
break;
case 2:
mask = 0x0000ffff;
break;
case 3:
mask = 0x00ffffff;
break;
default:
mask = 0xffffffff;
break;
}
reg &= mask;
} else {
reg = 0;
}
return reg;
}
/* Probe SPI flash device
* Function returns 0 for failure.
* and flashconfig_tbl array index for success.
*/
static u32
spiflash_read_id (void)
{
u32 flash_id;
flash_id = spiflash_sendcmd(SPI_RD_ID);
flash_id = ((flash_id & 0xff) << 16) | (flash_id & 0xff00) | ((flash_id >> 16) & 0xff);
return flash_id;
}
static u32
spiflash_erase (struct map_info *map, u32 addr)
{
struct opcodes *ptr_opcode;
__u32 temp, reg;
int finished = FALSE;
ptr_opcode = &stm_opcodes[SPI_SECTOR_ERASE];
temp = ((__u32)addr << 8) | (__u32)(ptr_opcode->code);
spiflash_sendcmd(SPI_WRITE_ENABLE);
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
spiflash_regwrite32(SPI_FLASH_OPCODE, temp);
if(four_byte_mode == 1){
reg = ((__u32)addr & 0xff000000) |(reg & 0x00ffff00) | (ptr_opcode->tx_cnt+1) | SPI_CTL_START;
}else{
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | ptr_opcode->tx_cnt | SPI_CTL_START;
}
spiflash_regwrite32(SPI_FLASH_CTL, reg);
do {
reg = spiflash_sendcmd(SPI_RD_STATUS);
if (!(reg & SPI_STATUS_WIP)) {
finished = TRUE;
}
} while (!finished);
return (0);
}
#if defined(TCSUPPORT_VOIP)
static int spiflash_wait_erase_ready(unsigned long data)
{
int ret;
unsigned long adr, len;
while(!kthread_should_stop())
{
wait_event_interruptible(spi_erase_wq, (atomic_read(&spi_erase_flag) != 0)); /* replace !=0 with == 1 */
atomic_dec(&spi_erase_flag);
struct mtd_info* mtd = spiEraseParam.tmpMtd;
struct erase_info* instr = spiEraseParam.tmpInstr;
struct map_info *map = mtd->priv;
struct spi_chip_info *chip_info = (struct spi_chip_info *)map->fldrv_priv;
adr = instr->addr;
len = instr->len;
while (len) {
ret = chip_info->erase(map, adr);
adr += mtd->erasesize;
len -= mtd->erasesize;
}
up(&SPI_SEM);
instr->state = MTD_ERASE_DONE;
if (instr->callback)
instr->callback(instr);
}
}
#endif
/* wait until the flash chip is ready and grab a lock */
static int spiflash_wait_ready(int state)
{
DECLARE_WAITQUEUE(wait, current);
retry:
spin_lock_bh(&spiflash_mutex);
if (spiflash_state != FL_READY) {
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&spiflash_wq, &wait);
spin_unlock_bh(&spiflash_mutex);
schedule();
remove_wait_queue(&spiflash_wq, &wait);
if(signal_pending(current))
return 0;
goto retry;
}
spiflash_state = state;
return 1;
}
static inline void spiflash_done(void)
{
spiflash_state = FL_READY;
spin_unlock_bh(&spiflash_mutex);
wake_up(&spiflash_wq);
}
static u32
spiflash_read (struct map_info *map, u32 from, u32 to, u32 size)
{
#ifdef TCSUPPORT_MT7510_E1
int i;
for(i=0;i<size;i++){
*(char *)(to+i) = *(char *)(map->virt + from + i);
wmb();
if(*(char *)(to+i) == 0){
*(char *)(to+i) = *(char *)(map->virt + from + i);
wmb();
}
}
#else
memcpy((char *)to, (char *)(map->virt + from), size);
#endif
return (0);
}
static u32
spiflash_write (struct map_info *map, u32 from, u32 to, u32 len)
{
int done = FALSE, page_offset, bytes_left, finished;
#if defined(TC_SOC)
__u32 xact_len, spi_data[8], opcode, reg;
__u32 reg_value;
unsigned char words, bytes, finalrun, i, j;
#else
__u32 xact_len, spi_data = 0, opcode, reg;
#endif
unsigned char *buf;
buf = (unsigned char *) from;
opcode = stm_opcodes[SPI_PAGE_PROGRAM].code;
bytes_left = len;
while (done == FALSE) {
if (byte_program_mode)
xact_len = MIN(bytes_left, sizeof(__u8));
else
#if defined(TC_SOC)
xact_len = MIN(bytes_left, sizeof(__u32)<<3);
#else
xact_len = MIN(bytes_left, sizeof(__u32));
#endif
/* 32-bit writes cannot span across a page boundary
* (256 bytes). This types of writes require two page
* program operations to handle it correctly. The STM part
* will write the overflow data to the beginning of the
* current page as opposed to the subsequent page.
*/
page_offset = (to & (STM_PAGE_SIZE - 1)) + xact_len;
if (page_offset > STM_PAGE_SIZE) {
xact_len -= (page_offset - STM_PAGE_SIZE);
}
spiflash_sendcmd(SPI_WRITE_ENABLE);
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
#if defined(TC_SOC)
words = xact_len >> 2;
// if(!(xact_len % 4))
if(!(xact_len & 0x00000003))
words--;
bytes = 3;
// finalrun = xact_len % 4 - 1;
finalrun = (xact_len & 0x00000003) - 1;
if(finalrun == 0xFF)
finalrun = 3;
for(i = 0; i <= words; i++){
spi_data[i] = 0;//Make sure the initial value of spi_data[i] is 0
if(i == words)
bytes = finalrun;
for(j = 0; j <= bytes; j++){
spi_data[i] |= (buf[j + (i<<2)] << (j<<3));
}
}
#else
switch (xact_len) {
case 1:
spi_data = (__u8)*buf;
break;
case 2:
spi_data = (buf[1] << 8) | buf[0];
break;
case 3:
spi_data = (buf[2] << 16) | (buf[1] << 8) | buf[0];
break;
case 4:
spi_data = (buf[3] << 24) | (buf[2] << 16) |
(buf[1] << 8) | buf[0];
break;
default:
printk("spiflash_write: default case\n");
break;
}
#endif
#if defined(TC_SOC)
if (!byte_program_mode){
/*20101119 pork modified for Slic lower SPI speed request*/
reg_value = regRead32(SPI_REG_MASTER);
// VPint(SPI_REG_MASTER) = 0x38804;//Set bit [2] to 1 to enter more buffer mode
//VPint(SPI_REG_MASTER) = reg_value | (1 << 2);
regWrite32(SPI_REG_MASTER,reg_value|(1<<2));
// VPint(SPI_REG_MOREBUF) = 0x20000100;//Set bits [8:0] to 128 (data bit counts) and bits[29:24] to 32(comman bit counts)
/* write exact data size into flash */
if(four_byte_mode == 1){
//VPint(SPI_REG_MOREBUF) = 0x28000000|(xact_len<<3);//Set bits [8:0] to data bit counts and bits[29:24] to 40(command bit counts)
regWrite32(SPI_REG_MOREBUF,0x28000000|(xact_len<<3));
}else{
regWrite32(SPI_REG_MOREBUF,0x20000000|(xact_len<<3));
//VPint(SPI_REG_MOREBUF) = 0x20000000|(xact_len<<3);//Set bits [8:0] to data bit counts and bits[29:24] to 32(command bit counts)
}
}
#endif
#if defined(TC_SOC)
spiflash_regwrite32(SPI_FLASH_DATA, spi_data[0]);
if (!byte_program_mode){
spiflash_regwrite32(SPI_FLASH_DATA2, spi_data[1]);
spiflash_regwrite32(SPI_FLASH_DATA3, spi_data[2]);
spiflash_regwrite32(SPI_FLASH_DATA4, spi_data[3]);
spiflash_regwrite32(SPI_FLASH_DATA5, spi_data[4]);
spiflash_regwrite32(SPI_FLASH_DATA6, spi_data[5]);
spiflash_regwrite32(SPI_FLASH_DATA7, spi_data[6]);
spiflash_regwrite32(SPI_FLASH_DATA8, spi_data[7]);
if(four_byte_mode == 1){
opcode = ((__u32)to);
}else{
opcode = (0x02 << 24) | ((__u32)to);
}
}
else
#else
spiflash_regwrite32(SPI_FLASH_DATA, spi_data);
#endif
opcode = (opcode & SPI_OPCODE_MASK) | ((__u32)to << 8);
spiflash_regwrite32(SPI_FLASH_OPCODE, opcode);
if(four_byte_mode == 1){
#if defined(TC_SOC)
reg = ((0x02 << 24) | (reg & 0x00ffff00)) | (xact_len + 5) | SPI_CTL_START;
#else
reg = ((__u32)to & 0xff000000 ) | (reg & 0x00ffff00) | (xact_len + 5) | SPI_CTL_START;
#endif
}else{
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | (xact_len + 4) | SPI_CTL_START;
}
spiflash_regwrite32(SPI_FLASH_CTL, reg);
finished = FALSE;
#if defined(TC_SOC)
if (!byte_program_mode){
/*20101119 pork modified for Slic lower SPI speed request*/
//while(VPint(SPI_REG_BASE) & 0x10000);//Make sure the bit spi_master_busy is 0 and then continue
while(regRead32(SPI_REG_BASE) & 0x10000);//Make sure the bit spi_master_busy is 0 and then continue
//VPint(SPI_REG_MOREBUF) = 0x00000000;//Set the default value back
regWrite32(SPI_REG_MOREBUF,0x0);//Set the default value back
// VPint(SPI_REG_MASTER) = 0x38800;//Set the default value back
//VPint(SPI_REG_MASTER) = reg_value;
regWrite32(SPI_REG_MASTER,reg_value);
}
#endif
do {
udelay(1);
reg = spiflash_sendcmd(SPI_RD_STATUS);
if (!(reg & SPI_STATUS_WIP)) {
finished = TRUE;
}
} while (!finished);
bytes_left -= xact_len;
to += xact_len;
buf += xact_len;
if (bytes_left == 0) {
done = TRUE;
}
}
return (0);
}
static struct spi_flash_info flash_tables[] = {
{
mfr_id: MANUFACTURER_ST,
dev_id: M25P64,
name: "ST M25P64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_ST,
dev_id: M25P32,
name: "ST M25P32",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_ST,
dev_id: M25P16,
name: "ST M25P16",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X128,
name: "Winbond W25X128",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X64,
name: "Winbond W25X64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X32,
name: "Winbond W25X32",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25X16,
name: "Winbond W25X16",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q128,
name: "Winbond W25Q128",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q64,
name: "Winbond W25Q64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q32,
name: "Winbond W25Q32",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_WINBOND,
dev_id: W25Q16,
name: "Winbond W25Q16",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_SST,
dev_id: SST25VF032B,
name: "SST 25VF032B",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L3205D,
name: "MX25L3205D",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L6405D,
name: "MX25L6405D",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L12805D,
name: "MX25L12805D",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_MXIC,
dev_id: MX25L25635E,
name: "MX25L25635DE",
DeviceSize: SIZE_32MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL064A,
name: "S25FL064A",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL032A,
name: "S25FL032A",
DeviceSize: SIZE_4MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL016A,
name: "S25FL016A",
DeviceSize: SIZE_2MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_SPANSION,
dev_id: S25FL128P,
name: "S25FL128P",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_EON,
dev_id: EN25Q64,
name: "EN25Q64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_NUMONYX,
dev_id: N25Q064,
name: "N25Q064",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
{
mfr_id: MANUFACTURER_GIGADEVICE,
dev_id: GD25Q128,
name: "GD25Q128",
DeviceSize: SIZE_16MiB,
EraseSize: SIZE_64KiB,
//mode: SPI_FAST_RD_QUAD_IO,
},
{
mfr_id: MANUFACTURER_GIGADEVICE,
dev_id: GD25Q64,
name: "GD25Q64",
DeviceSize: SIZE_8MiB,
EraseSize: SIZE_64KiB,
},
};
static void spiflash_unlock (void)
{
struct opcodes *ptr_opcode;
__u32 reg;
__u32 status_reg;
status_reg = spiflash_sendcmd(SPI_RD_STATUS);
ptr_opcode = &stm_opcodes[SPI_WR_STATUS];
spiflash_sendcmd(SPI_WRITE_ENABLE);
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
status_reg &= ~0x1c;
spiflash_regwrite32(SPI_FLASH_DATA, status_reg);
spiflash_regwrite32(SPI_FLASH_OPCODE, ptr_opcode->code);
reg = (reg & ~SPI_CTL_TX_RX_CNT_MASK) | ptr_opcode->tx_cnt | SPI_CTL_START;
spiflash_regwrite32(SPI_FLASH_CTL, reg);
}
static struct spi_chip_info *spiflash_tc3162_setup(struct map_info *map)
{
struct spi_chip_info *chip_info;
chip_info = kmalloc(sizeof(*chip_info), GFP_KERNEL);
if (!chip_info) {
printk(KERN_WARNING "Failed to allocate memory for chip_info\n");
return NULL;
}
memset(chip_info, 0, sizeof(struct spi_chip_info));
return chip_info;
}
static void spiflash_tc3162_destroy(struct spi_chip_info *chip_info)
{
if (chip_info) {
kfree(chip_info);
}
}
struct spi_chip_info *spiflash_probe_tc3162(struct map_info *map)
{
int i;
struct spi_chip_info *chip_info = NULL;
unsigned long flash_id;
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
if(down_interruptible(&SPI_SEM))
return -ERESTARTSYS;
*((__u32 *)(CR_SPI_BASE | SPI_FLASH_MM)) = reg0x28;
#endif
spin_lock_init(&spiflash_mutex);
init_waitqueue_head(&spiflash_wq);
spiflash_state = FL_READY;
flash_id = spiflash_read_id();
for (i=0; i < ARRAY_SIZE(flash_tables); i++) {
if ((MANUFACTURER_ID(flash_id) == flash_tables[i].mfr_id) &&
(DEVICE_ID(flash_id) == flash_tables[i].dev_id)) {
if (MANUFACTURER_ID(flash_id) == MANUFACTURER_SST) {
spiflash_unlock();
byte_program_mode = 1;
}
if(flash_tables[i].DeviceSize >= SIZE_32MiB){
int tmpVal;
tmpVal = regRead32(SPI_REG_BASE);
tmpVal |=((1<<19)|(1<<20));
//VPint(SPI_REG_BASE) |= ((1<<19)|(1<<20));
regWrite32(SPI_REG_BASE,tmpVal);
four_byte_mode = 1;
}
chip_info = spiflash_tc3162_setup(map);
if (chip_info) {
chip_info->flash = &flash_tables[i];
chip_info->destroy = spiflash_tc3162_destroy;
chip_info->read = spiflash_read;
chip_info->write = spiflash_write;
chip_info->erase = spiflash_erase;
}
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return chip_info;
}
}
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return NULL;
}
#if defined(TCSUPPORT_VOIP)
int mtd_spiflash_erase_sp(struct mtd_info *mtd, struct erase_info *instr)
{
struct map_info *map = mtd->priv;
struct spi_chip_info *chip_info = (struct spi_chip_info *)map->fldrv_priv;
unsigned long adr, len;
int ret = 0;
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
if(down_interruptible(&SPI_SEM))
return -ERESTARTSYS;
*((__u32 *)(CR_SPI_BASE | SPI_FLASH_MM)) = reg0x28;
#endif
if (instr->addr & (mtd->erasesize - 1)){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINVAL;
}
if (instr->len & (mtd->erasesize -1)){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINVAL;
}
if ((instr->len + instr->addr) > mtd->size){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINVAL;
}
memset(&spiEraseParam,0,sizeof(spiEraseParam));
spiEraseParam.tmpMtd = mtd;
spiEraseParam.tmpInstr = instr;
spi_erase_alarm_expires();
return 0;
}
#endif
int mtd_spiflash_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct map_info *map = mtd->priv;
struct spi_chip_info *chip_info = (struct spi_chip_info *)map->fldrv_priv;
unsigned long adr, len;
int ret = 0;
__u32 reg = 0;
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
if(down_interruptible(&SPI_SEM))
return -ERESTARTSYS;
#if defined(TCSUPPORT_VOIP)
/*63368 Register space clk = 233/(5+2) Mhz*/
if(isRT63368){
reg0x28 &= (0xf000ffff);
reg0x28 |= (0x5 << 16);
}else if (isRT65168 || isTC3182) {
reg0x28 &= (0xf000ffff);
reg0x28 |= (0x1 << 16);
/*3182 65168 Register space clk = 102/(1+2) Mhz*/
}
#endif
*((__u32 *)(CR_SPI_BASE | SPI_FLASH_MM)) = reg0x28;
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
#endif
if (!chip_info->erase){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EOPNOTSUPP;
}
if (instr->addr & (mtd->erasesize - 1)){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINVAL;
}
if (instr->len & (mtd->erasesize -1)){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINVAL;
}
if ((instr->len + instr->addr) > mtd->size){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINVAL;
}
adr = instr->addr;
len = instr->len;
while (len) {
if (!spiflash_wait_ready(FL_ERASING)){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINTR;
}
ret = chip_info->erase(map, adr);
spiflash_done();
if (ret){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return ret;
}
adr += mtd->erasesize;
len -= mtd->erasesize;
}
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
instr->state = MTD_ERASE_DONE;
if (instr->callback)
instr->callback(instr);
return 0;
}
int mtd_spiflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
struct spi_chip_info *chip_info = (struct spi_chip_info *)map->fldrv_priv;
int ret = 0;
int reg = 0;
int finished = FALSE;
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
if(down_interruptible(&SPI_SEM))
return -ERESTARTSYS;
int breakCount =0;
do {
breakCount++;
udelay(1);
reg = spiflash_sendcmd(SPI_RD_STATUS);
if(!(reg & SPI_STATUS_WIP)){
finished = TRUE;
}
/*workaround solution when flash status is always in WIP*/
if(breakCount>1000){
break;
}
}while(!finished);
*((__u32 *)(CR_SPI_BASE | SPI_FLASH_MM)) = reg0x28;
#endif
if (!chip_info->read){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EOPNOTSUPP;
}
ret = chip_info->read(map, from, (u32)buf, len);
if (ret){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return ret;
}
if(retlen)
(*retlen) = len;
finished = FALSE;
do {
udelay(1);
reg = spiflash_sendcmd(SPI_RD_STATUS);
if(!(reg & SPI_STATUS_WIP)){
finished = TRUE;
}
}while(!finished);
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return 0;
}
int mtd_spiflash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
struct map_info *map = mtd->priv;
struct spi_chip_info *chip_info = (struct spi_chip_info *)map->fldrv_priv;
int ret = 0;
__u32 reg = 0;
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
if(down_interruptible(&SPI_SEM))
return -ERESTARTSYS;
#if defined(TCSUPPORT_VOIP)
/*63368 Register space clk = 233/(5+2) Mhz*/
if(isRT63368){
reg0x28 &= (0xf000ffff);
reg0x28 |= (0x5 << 16);
}else if (isRT65168 || isTC3182) {
reg0x28 &= (0xf000ffff);
reg0x28 |= (0x1 << 16);
/*3182 65168 Register space clk = 102/(1+2) Mhz*/
}
#endif
*((__u32 *)(CR_SPI_BASE | SPI_FLASH_MM)) = reg0x28;
do {
reg = spiflash_regread32(SPI_FLASH_CTL);
} while (reg & SPI_CTL_BUSY);
#endif
if (!chip_info->write){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EOPNOTSUPP;
}
if (!spiflash_wait_ready(FL_WRITING)){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return -EINTR;
}
ret = chip_info->write(map, (u32)buf, to, len);
spiflash_done();
if (ret){
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return ret;
}
if (retlen)
(*retlen) = len;
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
up(&SPI_SEM);
#endif
return 0;
}
#ifdef TCSUPPORT_DUAL_IMAGE_ENHANCE
int offset = 0;
#endif
static struct mtd_info *spiflash_probe(struct map_info *map)
{
struct spi_chip_info *chip_info = NULL;
struct mtd_info *mtd;
chip_info = spiflash_probe_tc3162(map);
if (!chip_info)
return NULL;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd) {
printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
return NULL;
}
mtd->priv = map;
mtd->type = MTD_NORFLASH;
#if defined(TCSUPPORT_VOIP)
mtd->_erase = mtd_spiflash_erase_sp;
#else
mtd->_erase = mtd_spiflash_erase;
#endif
mtd->_write = mtd_spiflash_write;
mtd->_read = mtd_spiflash_read;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
mtd->writesize = 1;
mtd->owner = THIS_MODULE;
mtd->size = chip_info->flash->DeviceSize;
mtd->erasesize = chip_info->flash->EraseSize;
map->fldrv_priv = chip_info;
printk(KERN_INFO "%s: Found SPIFLASH %dMiB %s\n",
map->name, chip_info->flash->DeviceSize/(1024*1024), chip_info->flash->name);
#ifdef TCSUPPORT_DUAL_IMAGE_ENHANCE
#ifdef TCSUPPORT_DUAL_IMAGE_8M
offset = 0x3e0000;
#else
offset = chip_info->flash->DeviceSize/2;
#endif
#endif
return mtd;
}
static void spiflash_destroy(struct mtd_info *mtd)
{
struct map_info *map = (struct map_info *)mtd->priv;
struct spi_chip_info *chip_info = (struct spi_chip_info *)map->fldrv_priv;
if (chip_info->destroy) {
chip_info->destroy(chip_info);
}
}
static struct mtd_chip_driver spiflash_chipdrv = {
.probe = spiflash_probe,
.destroy = spiflash_destroy,
.name = "spiflash_probe",
.module = THIS_MODULE
};
#if defined(TCSUPPORT_SUPPORT_FLASH)
static int read_proc_support_flash(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
unsigned int flash_id;
int index_flash;
char *buf_proc = NULL;
char buf_line[200];
char buf_name[100];
char *buf_replace = NULL;
int total_len=0;
buf_proc = kmalloc(4*1024, GFP_KERNEL);
if (!buf_proc)
{
printk(KERN_WARNING "Failed to allocate memory for /proc/tc3162/support_flash\n");
return 0;
}
memset(buf_proc,0,4*1024);
for(index_flash=0; index_flash < sizeof(flash_tables)/sizeof(struct spi_flash_info); index_flash++)
{
strcpy(buf_name,flash_tables[index_flash].name);//replace whitespace with '_'
while( (buf_replace=strstr(buf_name, " "))!=NULL)
*buf_replace='#';
if(flash_tables[index_flash].DeviceSize/0x100000 <4)
continue;
flash_id = (flash_tables[index_flash].mfr_id <<16) | ( flash_tables[index_flash].dev_id & 0xffff);
sprintf(buf_line,"%s %#x %d \n",buf_name , flash_id,
flash_tables[index_flash].DeviceSize/0x100000);
total_len += strlen(buf_line);
if(total_len>4*1024)
break;
strcat(buf_proc,buf_line);
}
len = sprintf(page, "%s", buf_proc);
len -= off;
*start = page + off;
if (len > count)
len = count;
else
*eof = 1;
if (len < 0)
len = 0;
kfree(buf_proc);
return len;
}
#endif
static int __init spiflash_probe_init(void)
{
#if defined(TCSUPPORT_SUPPORT_FLASH)
create_proc_read_entry("tc3162/support_flash", 0, NULL, read_proc_support_flash, NULL);
#endif
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
if (isRT63365){
/*63368 Flash space clk = 233/(5+2) Mhz, 63365 Flash space clk = 166/(2+2) Mhz*/
if(isRT63368){
//VPint(SPI_REG_SPACE_CR) = (VPint(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x5);
regWrite32(SPI_REG_SPACE_CR,(regRead32(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x5));
}else{
//VPint(SPI_REG_SPACE_CR) = (VPint(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x4);
regWrite32(SPI_REG_SPACE_CR,(regRead32(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x4));
}
}else if (isRT65168 || isTC3182){
//VPint(SPI_REG_SPACE_CR) = (VPint(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x1);
regWrite32(SPI_REG_SPACE_CR, (regRead32(SPI_REG_SPACE_CR) & 0xfffffff0) | (0x1));
/*3182 65168 Flash space clk = 102/(1+2) Mhz*/
}
reg0x28 = *((__u32 *)(CR_SPI_BASE | SPI_FLASH_MM));
if (isRT63365){
/*63368 Register space clk = 233/(5+2) Mhz, 63365 Register space clk = 166/(4+2) Mhz*/
if (isRT63368) {
reg0x28 &= (0xf000ffff);
reg0x28 |= (0x5 << 16);
}else{
reg0x28 &= (0xf000ffff);
reg0x28 |= (0x4 << 16);
}
}else if (isRT65168 || isTC3182) {
reg0x28 &= (0xf000ffff);
reg0x28 |= (0x1 << 16);
/*3182 65168 Register space clk = 102/(1+2) Mhz*/
}
//VPint(SPI_REG_MASTER) = reg0x28;
regWrite32(SPI_REG_MASTER, reg0x28);
#endif
register_mtd_chip_driver(&spiflash_chipdrv);
#if defined(TCSUPPORT_VOIP)
spi_erase_task = kthread_run(spiflash_wait_erase_ready, NULL, "spi_erase_task");
if(IS_ERR(spi_erase_task))
{
printk("@%s>>%d--spi_erase_task init failed\n", __func__, __LINE__);
return -1;
}
#endif
return 0;
}
static void __exit spiflash_probe_exit(void)
{
#if defined(TCSUPPORT_SUPPORT_FLASH)
remove_proc_entry("tc3162/support_flash", NULL);
#endif
unregister_mtd_chip_driver(&spiflash_chipdrv);
}
module_init(spiflash_probe_init);
module_exit(spiflash_probe_exit);
#if defined(TC_SOC) && defined(CONFIG_MIPS_TC3262)
EXPORT_SYMBOL(SPI_SEM);
#endif