blob: a815b66d8702e42add49c2a3a1cb64b219751884 [file] [log] [blame]
/*
* Copyright C 2009 by Amlogic, Inc. All Rights Reserved.
* Description: esmt spi chips support
* Author: pfs
*/
#define DEBUG
#include <common.h>
#include <malloc.h>
#include <spi_flash.h>
#include "spi_flash_amlogic.h"
struct esmt_spi_flash_params {
uint32_t id;
uint32_t sector_size;
uint32_t block_size;
uint32_t chip_size;
const char *name;
};
/* spi_flash needs to be first so upper layers can free() it */
struct esmt_spi_flash {
struct spi_flash flash;
const struct esmt_spi_flash_params *params;
};
static inline struct esmt_spi_flash * to_esmt_spi_flash(struct spi_flash *flash)
{
return container_of(flash, struct esmt_spi_flash, flash);
}
#define ESMT_ID_F25L08QA 0x4014
#define ESMT_ID_F25L16PA 0x2115
#define ESMT_ID_F25L32PA 0x2116
#define ESMT_ID_F25L32QA 0x4116
static const struct esmt_spi_flash_params esmt_spi_flash_table[] = {
{
.id = ESMT_ID_F25L08QA,
.sector_size= 4*1024,
.block_size =64*1024 ,
.chip_size = 1*1024*1024,
.name = "F25L08QA",
},
{
.id = ESMT_ID_F25L16PA,
.sector_size= 4*1024,
.block_size =64*1024 ,
.chip_size = 2*1024*1024,
.name = "F25L16PA",
},
{
.id = ESMT_ID_F25L32PA,
.sector_size= 4*1024,
.block_size =64*1024 ,
.chip_size =4*1024*1024,
.name = "F25L32PA",
},
{
.id = ESMT_ID_F25L32QA,
.sector_size= 4*1024,
.block_size =64*1024 ,
.chip_size =4*1024*1024,
.name = "F25L32QA",
},
};
#if 0
int spi_flash_read_aml_esmt(struct spi_flash *flash,u32 offset, size_t len, void *buf){
struct spi_slave *spi = flash->spi;
//int ret;
u32 temp_addr;
int temp_length;
temp_addr = offset;
temp_length = len;
unsigned flags;
spi_claim_bus(spi); /*FIXME for spi_xfer release bus*/
while (temp_length>0) {
flags=(temp_addr & 0xffffff)|( (temp_length>=32?32:temp_length) << SPI_FLASH_BYTES_LEN);
spi_flash_adr_write(spi, flags);
flags=(1<<SPI_FLASH_READ);
spi_flash_cmd(spi,flags,NULL,0);
flags=SPI_XFER_READCACHE;
spi_xfer(spi,(temp_length>=32?32:temp_length)*8,NULL,buf,flags);
temp_addr += (temp_length>=32?32:temp_length);
buf += (temp_length>=32?32:temp_length);
temp_length -= (temp_length>=32?32:temp_length);
}
return 0;
}
#endif
static int esmt_read(struct spi_flash *flash,u32 offset, size_t len, void *buf){
// struct esmt_spi_flash *stm = to_esmt_spi_flash(flash);
int ret;
spi_claim_bus(flash->spi);
ret = spi_flash_read_amlogic(flash, offset, len,buf);
spi_release_bus(flash->spi);
return ret;
}
#if 0
int spi_flash_write_esmt(struct spi_flash *flash,u32 offset, size_t len, const void *buf){
struct spi_slave *spi = flash->spi;
int ret;
unsigned temp_addr;
int temp_length;
temp_addr = offset;
temp_length = len;
unsigned flags;
while (temp_length>0) {
flags=(temp_addr & 0xffffff)|( (temp_length>=32?32:temp_length) << SPI_FLASH_BYTES_LEN);
spi_flash_adr_write(spi, flags);
flags=SPI_XFER_WRITECACHE;
spi_xfer(spi,(temp_length>=32?32:temp_length)*8,buf,NULL,flags);
flags=(1<<SPI_FLASH_WREN);
spi_flash_cmd(spi,flags,NULL,0);
flags=(1<<SPI_FLASH_PP);
spi_flash_cmd(spi,flags,NULL,0);
ret=1;
while ( (ret&1) == 1 ) {
flags=1<<SPI_FLASH_RDSR;
spi_flash_cmd(spi,flags,&ret,2); //2 byte status
}
temp_addr += (temp_length>=32?32:temp_length);
buf += (temp_length>=32?32:temp_length);
temp_length -= (temp_length>=32?32:temp_length);
}
#ifdef SPI_WRITE_PROTECT
spi_enable_write_protect();
#endif
return 0;
}
#endif
static int esmt_write(struct spi_flash *flash,u32 offset, size_t len, const void *buf){
int ret;
// struct esmt_spi_flash *stm = to_esmt_spi_flash(flash);
spi_claim_bus(flash->spi);
ret = spi_flash_write_amlogic(flash, offset, len,buf);
spi_release_bus(flash->spi);
return ret;
}
/*
* This function currently uses sector erase only.
* probably speed things up by using bulk erase
* when possible.
*/
int esmt_erase(struct spi_flash *flash, u32 offset, size_t len){
struct esmt_spi_flash *stm = to_esmt_spi_flash(flash);
u32 sector_size;
int ret;
sector_size = stm->params->sector_size;
spi_claim_bus(flash->spi);
ret = spi_flash_erase_amlogic(flash, offset, len, sector_size);
spi_release_bus(flash->spi);
return ret;
#if 0
struct spi_slave * slave=flash->spi;
unsigned long sector_size;
unsigned long page_addr;
size_t actual;
int ret;
unsigned var;
sector_size = stm->params->sector_size;
if (offset % sector_size || len % sector_size) {
debug("SF: Erase offset/length not multiple of sector size\n");
return -1;
}
page_addr = offset / sector_size;
spi_claim_bus(flash->spi);
// CLEAR_PERI_REG_BITS(PERIPHS_SPI_FLASH_CTRL, SPI_ENABLE_AHB);
#ifdef SPI_WRITE_PROTECT
spi_disable_write_protect();
#endif
for (actual = 0; actual < len; actual+=sector_size) {
debug("Erase:%x\n",actual);
var=(offset+actual) & 0xffffff;
spi_flash_adr_write(slave,var);
var=1<<SPI_FLASH_WREN;
spi_flash_cmd(slave,var,NULL,0);
var=1<<SPI_FLASH_SE;
spi_flash_cmd(slave,var,NULL,0);
ret=1;
while ( (ret&1) == 1 ) {
var=1<<SPI_FLASH_RDSR;
spi_flash_cmd(slave,var,&ret,2); //2 byte status
}
}
debug("SF: ESMT: Successfully erased %u bytes @ 0x%x\n",
len, offset);
ret = 0;
spi_release_bus(flash->spi);
// SET_PERI_REG_MASK(P_SPI_FLASH_CTRL, SPI_ENABLE_AHB);
return ret;
#endif
}
struct spi_flash *spi_flash_probe_esmt(struct spi_slave *spi, u8 *idcode)
{
const struct esmt_spi_flash_params *params;
unsigned long sector_size;
struct esmt_spi_flash *stm;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(esmt_spi_flash_table); i++) {
params = &esmt_spi_flash_table[i];
if (params->id == ((idcode[1] << 8) | idcode[2]))
break;
}
if (i == ARRAY_SIZE(esmt_spi_flash_table)) {
debug("SF: Unsupported ESMT ID %02x%02x\n",
idcode[1], idcode[2]);
return NULL;
}
stm = malloc(sizeof(struct esmt_spi_flash));
if (!stm) {
debug("SF: Failed to allocate memory\n");
return NULL;
}
stm->params = params;
stm->flash.spi = spi;
stm->flash.name = params->name;
stm->flash.write = esmt_write;
stm->flash.erase = esmt_erase;
stm->flash.read = esmt_read;
stm->flash.size = params->chip_size ;
sector_size = params->sector_size;
debug("SF: Detected %s with sector size %u, total %u bytes\n",
params->name, (unsigned int)sector_size, stm->flash.size);
return &stm->flash;
}