blob: b1af28cfff7ba14fe57cf72387c29e3403d21cc2 [file] [log] [blame]
/**
* system/src/bld/spi_nor.c
*
* Flash controller functions with spi nor chips.
*
* History:
* 2013/10/12 - [cddiao] creat
*
* Copyright 2008-2015 Ambarella Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 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 "spinor.h"
struct spinorcmd_s{
int (*init)(void);
int (*erase_sector)(u32 sector);
int (*write_sector)(u32 sector, u8 *src);
int (*write_bst)(u32 sector, u8 *src, u32 *page_size);
int (*read)(u32 addr, u32 data_len, const u8 *dst);
}spinorcmd;
int ambspi_send_cmd(struct amb_norflash *flash, u32 cmd, u32 dummy_len, u32 data, u32 len)
{
u32 tmp = 0;
tmp = amba_readl(flash->regbase + REG00);
REGPREP(tmp, REG00_DATALEN_MASK, REG00_DATALEN_SHIFT, 0);
REGPREP(tmp, REG00_DUMMYLEN_MASK, REG00_DUMMYLEN_SHIFT, dummy_len);
REGPREP(tmp, REG00_ADDRLEN_MASK, REG00_ADDRLEN_SHIFT, len);
REGPREP(tmp, REG00_CMDLEN_MASK, REG00_CMDLEN_SHIFT, 1);
amba_writel(flash->regbase + REG00, tmp);
tmp = amba_readl(flash->regbase + REG04);
REGPREP(tmp, REG04_WRITEEN_MASK, REG04_WRITEEN_SHIFT, 1);
REGPREP(tmp, REG04_READEN_MASK, REG04_READEN_SHIFT, 0);
REGPREP(tmp, REG04_DATALANE_MASK, REG04_DATALANE_SHIFT, 0);
REGPREP(tmp, REG04_ADDRLANE_MASK, REG04_ADDRLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDLANE_MASK, REG04_CMDLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDDTR_MASK, REG04_CMDDTR_SHIFT, 0);
REGPREP(tmp, REG04_LSBFRT_MASK, REG04_LSBFRT_SHIFT, 0);
amba_writel(flash->regbase + REG04, tmp);
tmp = amba_readl(flash->regbase + REG0C);
REGPREP(tmp, REG0C_CMD0_MASK, REG0C_CMD0_SHIFT, cmd);
amba_writel(flash->regbase + REG0C, tmp);
if(len){
tmp = data;
amba_writel(flash->regbase + REG14, tmp);
}
tmp = 0x0;
amba_writel(flash->regbase + REG30, tmp);
tmp = 0x20;
amba_writel(flash->regbase + REG3C, tmp);
tmp = amba_readl(flash->regbase + REG50);
REGPREP(tmp, REG50_STRTRX_MASK, REG50_STRTRX_SHIFT, 1);
amba_writel(flash->regbase + REG50, tmp);
for (;;){
tmp = amba_readl(flash->regbase + REG38);
if (REGDUMP(tmp, REG38_DATALENREACHINTR_MASK, REG38_DATALENREACHINTR_SHIFT) == 1){
return 0;
}
}
return -1;
}
EXPORT_SYMBOL_GPL(ambspi_send_cmd);
int ambspi_read_reg(struct amb_norflash *flash, u32 datalen, u32 reg, u8 *value)
{
u32 tmp = 0;
int i;
tmp = amba_readl(flash->regbase + REG28);
for (;REGDUMP(tmp, REG28_RXFIFOLV_MASK, REG28_RXFIFOLV_SHIFT)!= 0;){
amba_readb(flash->regbase + REG200);
tmp = amba_readl(flash->regbase + REG28);
}
tmp = amba_readl(flash->regbase + REG00);
REGPREP(tmp, REG00_CMDLEN_MASK, REG00_CMDLEN_SHIFT, 1);
REGPREP(tmp, REG00_DUMMYLEN_MASK, REG00_DUMMYLEN_SHIFT, 0);
REGPREP(tmp, REG00_ADDRLEN_MASK, REG00_ADDRLEN_SHIFT, 0);
REGPREP(tmp, REG00_DATALEN_MASK, REG00_DATALEN_SHIFT, datalen);
amba_writel(flash->regbase + REG00, tmp);
tmp = amba_readl(flash->regbase + REG04);
REGPREP(tmp, REG04_READEN_MASK, REG04_READEN_SHIFT, 1);
REGPREP(tmp, REG04_WRITEEN_MASK, REG04_WRITEEN_SHIFT, 0);
REGPREP(tmp, REG04_RXLANE_MASK, REG04_RXLANE_SHIFT, 1);
REGPREP(tmp, REG04_DATALANE_MASK, REG04_DATALANE_SHIFT, 1);
REGPREP(tmp, REG04_ADDRLANE_MASK, REG04_ADDRLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDLANE_MASK, REG04_CMDLANE_SHIFT, 0);
REGPREP(tmp, REG04_LSBFRT_MASK, REG04_LSBFRT_SHIFT, 0);
REGPREP(tmp, REG04_CMDDTR_MASK, REG04_CMDDTR_SHIFT, 0);
amba_writel(flash->regbase + REG04, tmp);
tmp = reg;
amba_writel(flash->regbase + REG0C, tmp);
tmp = 0x0;
amba_writel(flash->regbase + REG10, tmp);
tmp = 0x0;
amba_writel(flash->regbase + REG14, tmp);
tmp = 0;
REGPREP(tmp, REG50_STRTRX_MASK, REG50_STRTRX_SHIFT, 1);
amba_writel(flash->regbase + REG50, tmp);
for (;;){
tmp = amba_readl(flash->regbase + REG28);
if(REGDUMP(tmp, REG28_RXFIFOLV_MASK, REG28_RXFIFOLV_SHIFT) == datalen){
break;
}
}
for (i = 0; i < datalen; i++){
*(value+i) = amba_readb(flash->regbase + REG200);
}
return 0;
}
EXPORT_SYMBOL_GPL(ambspi_read_reg);
int ambspi_dma_config(struct amb_norflash *flash)
{
flash->dmabuf = dma_alloc_coherent(flash->dev, AMBA_SPINOR_DMA_BUFF_SIZE,
&flash->dmaaddr, GFP_KERNEL);
if (flash->dmabuf == NULL){
dev_err(flash->dev, "dma_alloc_coherent failed!\n");
return -ENOMEM;
}
return 0;
}
int ambspi_progdata_dma(struct amb_norflash *flash, u32 srcoffset, u32 dst, u32 len)
{
int done;
u32 tmp = 0;
amba_writel(flash->dmaregbase + 0x00, 0x1a800000);// DMA_ch0_control
amba_writel(flash->dmaregbase + 0x0c, 0x0);
amba_writel(flash->dmaregbase + 0x04, flash->dmaaddr + srcoffset);// DMA_ch0_src_addr
amba_writel(flash->dmaregbase + 0x08, (u32)(flash->regbase + REG100));// DMA_ch0_dest_addr
amba_writel(flash->dmaregbase + 0x00, 0x9a800000|len);// DMA_ch0_control
tmp = amba_readl(flash->regbase + REG00);
REGPREP(tmp, REG00_DATALEN_MASK, REG00_DATALEN_SHIFT, len);
REGPREP(tmp, REG00_DUMMYLEN_MASK, REG00_DUMMYLEN_SHIFT, flash->dummy);
REGPREP(tmp, REG00_ADDRLEN_MASK, REG00_ADDRLEN_SHIFT, flash->addr_width);
REGPREP(tmp, REG00_CMDLEN_MASK, REG00_CMDLEN_SHIFT, 1);
amba_writel(flash->regbase + REG00, tmp);
tmp = amba_readl(flash->regbase + REG04);
REGPREP(tmp, REG04_WRITEEN_MASK, REG04_WRITEEN_SHIFT, 1);
REGPREP(tmp, REG04_READEN_MASK, REG04_READEN_SHIFT, 0);
REGPREP(tmp, REG04_DATALANE_MASK, REG04_DATALANE_SHIFT, 0);
REGPREP(tmp, REG04_ADDRLANE_MASK, REG04_ADDRLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDLANE_MASK, REG04_CMDLANE_SHIFT, 0);
REGPREP(tmp, REG04_LSBFRT_MASK, REG04_LSBFRT_SHIFT, 0);
REGPREP(tmp, REG04_CMDDTR_MASK, REG04_CMDDTR_SHIFT, 0);
amba_writel(flash->regbase + REG04, tmp);
tmp = flash->command[0];
amba_writel(flash->regbase + REG0C, tmp);
tmp = 0;
REGPREP(tmp, REG30_DATAREACH_MASK, REG30_DATAREACH_SHIFT, 1);
amba_writel(flash->regbase + REG30, tmp);
tmp = dst;
amba_writel(flash->regbase + REG14, tmp);
tmp = 0;
REGPREP(tmp, REG1C_TXFIFOLV_MASK, REG1C_TXFIFOLV_SHIFT, (256-32));
amba_writel(flash->regbase + REG1C, tmp);
tmp = amba_readl(flash->regbase + REG18);
REGPREP(tmp, REG18_TXDMAEN_MASK, REG18_TXDMAEN_SHIFT, 1);
REGPREP(tmp, REG18_RXDMAEN_MASK, REG18_RXDMAEN_SHIFT, 0);
amba_writel(flash->regbase + REG18, tmp);
do {
tmp = amba_readl(flash->regbase + REG2C);
done = REGDUMP(tmp, REG2C_TXFIFOEMP_MASK, REG2C_TXFIFOEMP_SHIFT);
}while (done != 0x0);
tmp = 0x20;
amba_writel(flash->regbase + REG3C, tmp);
tmp = amba_readl(flash->regbase + REG50);
REGPREP(tmp, REG50_STRTRX_MASK, REG50_STRTRX_SHIFT, 1);
amba_writel(flash->regbase + REG50, tmp);
for (;;){
tmp = amba_readl(flash->regbase + REG38);
if (REGDUMP(tmp,
REG38_DATALENREACHINTR_MASK,
REG38_DATALENREACHINTR_SHIFT) == 1){
return 0;
}
}
return -1;
}
int ambspi_prog_data(struct amb_norflash *flash, u32 srcoffset, u32 dst, u32 len)
{
u32 tmp = 0;
int ret = 0;
u32 tail = 0, bulk = 0, i = 0;
tail = len % 32;
bulk = len / 32;
if (bulk >= 1 ){
ret = ambspi_progdata_dma(flash, srcoffset, dst, len-tail);
}
if (ret){
return ret;
}
if (tail){
if(bulk >= 1){
flash->wait_till_ready(flash);
flash->write_enable(flash);
}
tmp = amba_readl(flash->regbase + REG00);
REGPREP(tmp, REG00_DATALEN_MASK, REG00_DATALEN_SHIFT, tail);
REGPREP(tmp, REG00_DUMMYLEN_MASK, REG00_DUMMYLEN_SHIFT, flash->dummy);
REGPREP(tmp, REG00_ADDRLEN_MASK, REG00_ADDRLEN_SHIFT, flash->addr_width);
REGPREP(tmp, REG00_CMDLEN_MASK, REG00_CMDLEN_SHIFT, 1);
amba_writel(flash->regbase + REG00, tmp);
tmp = amba_readl(flash->regbase + REG04);
REGPREP(tmp, REG04_WRITEEN_MASK, REG04_WRITEEN_SHIFT, 1);
REGPREP(tmp, REG04_READEN_MASK, REG04_READEN_SHIFT, 0);
REGPREP(tmp, REG04_DATALANE_MASK, REG04_DATALANE_SHIFT, 0);
REGPREP(tmp, REG04_ADDRLANE_MASK, REG04_ADDRLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDLANE_MASK, REG04_CMDLANE_SHIFT, 0);
REGPREP(tmp, REG04_LSBFRT_MASK, REG04_LSBFRT_SHIFT, 0);
REGPREP(tmp, REG04_CMDDTR_MASK, REG04_CMDDTR_SHIFT, 0);
amba_writel(flash->regbase + REG04, tmp);
tmp = flash->command[0];
amba_writel(flash->regbase + REG0C, tmp);
tmp = 0;
REGPREP(tmp, REG30_DATAREACH_MASK, REG30_DATAREACH_SHIFT, 1);
amba_writel(flash->regbase + REG30, tmp);
tmp = dst + bulk*32;
amba_writel(flash->regbase + REG14, tmp);
tmp = 0;
REGPREP(tmp, REG1C_TXFIFOLV_MASK, REG1C_TXFIFOLV_SHIFT, (256-32));
amba_writel(flash->regbase + REG1C, tmp);
tmp = amba_readl(flash->regbase + REG18);
REGPREP(tmp, REG18_TXDMAEN_MASK, REG18_TXDMAEN_SHIFT, 0);
REGPREP(tmp, REG18_RXDMAEN_MASK, REG18_RXDMAEN_SHIFT, 0);
amba_writel(flash->regbase + REG18, tmp);
for (i = 0; i < tail; i++){
amba_writeb(flash->regbase + REG100,
*(flash->dmabuf + srcoffset + bulk*32 + i));
}
tmp = 0x20;
amba_writel(flash->regbase + REG3C, tmp);
tmp = amba_readl(flash->regbase + REG50);
REGPREP(tmp, REG50_STRTRX_MASK, REG50_STRTRX_SHIFT, 1);
amba_writel(flash->regbase + REG50, tmp);
for (;;){
tmp = amba_readl(flash->regbase + REG24);
if(REGDUMP(tmp, REG24_TXFIFOLV_MASK, REG24_TXFIFOLV_SHIFT) == 0){
return 0;
}
udelay(100);//must delay
}
}
return -1;
}
EXPORT_SYMBOL_GPL(ambspi_prog_data);
int ambspi_readdata_dma(struct amb_norflash *flash, u32 from,
u32 len)
{
u32 tmp = 0;
amba_writel(flash->dmaregbase + 0x10, 0x2a800000);// DMA_ch1_control
amba_writel(flash->dmaregbase + 0x1c, 0x0);
amba_writel(flash->dmaregbase + 0x14, (u32)(flash->regbase + REG200));// DMA_ch1_src_addr
amba_writel(flash->dmaregbase + 0x18, flash->dmaaddr);// DMA_ch1_dest_addr
amba_writel(flash->dmaregbase + 0x10, 0xaa800000 | len);// DMA_ch1_control
tmp = amba_readl(flash->regbase + REG00);
REGPREP(tmp, REG00_DATALEN_MASK, REG00_DATALEN_SHIFT, len);
REGPREP(tmp, REG00_DUMMYLEN_MASK, REG00_DUMMYLEN_SHIFT, flash->dummy);
REGPREP(tmp, REG00_ADDRLEN_MASK, REG00_ADDRLEN_SHIFT, flash->addr_width);
REGPREP(tmp, REG00_CMDLEN_MASK, REG00_CMDLEN_SHIFT, 1);
amba_writel(flash->regbase + REG00, tmp);
tmp = amba_readl(flash->regbase + REG04);
REGPREP(tmp, REG04_READEN_MASK, REG04_READEN_SHIFT, 1);
REGPREP(tmp, REG04_WRITEEN_MASK, REG04_WRITEEN_SHIFT, 0);
REGPREP(tmp, REG04_DATALANE_MASK, REG04_DATALANE_SHIFT, 1);
REGPREP(tmp, REG04_ADDRLANE_MASK, REG04_ADDRLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDLANE_MASK, REG04_CMDLANE_SHIFT, 0);
REGPREP(tmp, REG04_LSBFRT_MASK, REG04_LSBFRT_SHIFT, 0);
REGPREP(tmp, REG04_CMDDTR_MASK, REG04_CMDDTR_SHIFT, 0);
amba_writel(flash->regbase + REG04, tmp);
tmp = flash->command[0];
amba_writel(flash->regbase + REG0C, tmp);
tmp = from;
amba_writel(flash->regbase + REG14, tmp);
tmp = 0x20;
amba_writel(flash->regbase + REG3C, tmp);
tmp = (32-1); // must use word.can't use rxfifothlv
amba_writel(flash->regbase + REG20, tmp);
tmp = amba_readl(flash->regbase + REG18);
REGPREP(tmp, REG18_RXDMAEN_MASK, REG18_RXDMAEN_SHIFT, 1);
REGPREP(tmp, REG18_TXDMAEN_MASK, REG18_TXDMAEN_SHIFT, 0);
amba_writel(flash->regbase + REG18, tmp);
tmp = amba_readl(flash->regbase + REG50);
REGPREP(tmp, REG50_STRTRX_MASK, REG50_STRTRX_SHIFT, 1);
amba_writel(flash->regbase + REG50, tmp);
for (;;){
tmp = amba_readl(flash->regbase + REG38);
if(REGDUMP(tmp,
REG38_DATALENREACHINTR_MASK,
REG38_DATALENREACHINTR_SHIFT) == 1){
return 0;
}
udelay(10);
}
return -1;
}
int ambspi_read_data(struct amb_norflash *flash, u32 from, u32 len)
{
u32 tmp = 0;
int ret = 0;
u32 tail = 0, bulk = 0, i = 0;
tail = len % 32;
bulk = len / 32;
if (bulk >= 1){
ret = ambspi_readdata_dma(flash, from, len-tail);
}
if (ret){
return ret;
}
if (tail){
tmp = amba_readl(flash->regbase + REG00);
REGPREP(tmp, REG00_DATALEN_MASK, REG00_DATALEN_SHIFT, tail);
REGPREP(tmp, REG00_DUMMYLEN_MASK, REG00_DUMMYLEN_SHIFT, flash->dummy);
REGPREP(tmp, REG00_ADDRLEN_MASK, REG00_ADDRLEN_SHIFT, flash->addr_width);
REGPREP(tmp, REG00_CMDLEN_MASK, REG00_CMDLEN_SHIFT, 1);
amba_writel(flash->regbase + REG00, tmp);
tmp = amba_readl(flash->regbase + REG04);
REGPREP(tmp, REG04_READEN_MASK, REG04_READEN_SHIFT, 1);
REGPREP(tmp, REG04_WRITEEN_MASK, REG04_WRITEEN_SHIFT, 0);
REGPREP(tmp, REG04_DATALANE_MASK, REG04_DATALANE_SHIFT, 1);
REGPREP(tmp, REG04_ADDRLANE_MASK, REG04_ADDRLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDLANE_MASK, REG04_CMDLANE_SHIFT, 0);
REGPREP(tmp, REG04_LSBFRT_MASK, REG04_LSBFRT_SHIFT, 0);
REGPREP(tmp, REG04_CMDDTR_MASK, REG04_CMDDTR_SHIFT, 0);
amba_writel(flash->regbase + REG04, tmp);
tmp = flash->command[0];
amba_writel(flash->regbase + REG0C, tmp);
tmp = from+bulk*32;
amba_writel(flash->regbase + REG14, tmp);
tmp = 0x20;
amba_writel(flash->regbase + REG3C, tmp);
tmp = 31; // the reserved bits must set 0
amba_writel(flash->regbase + REG20, tmp);
tmp = amba_readl(flash->regbase + REG18);
REGPREP(tmp, REG18_RXDMAEN_MASK, REG18_RXDMAEN_SHIFT, 0);
REGPREP(tmp, REG18_TXDMAEN_MASK, REG18_TXDMAEN_SHIFT, 0);
amba_writel(flash->regbase + REG18, tmp);
tmp = amba_readl(flash->regbase + REG50);
REGPREP(tmp, REG50_STRTRX_MASK, REG50_STRTRX_SHIFT, 1);
amba_writel(flash->regbase + REG50, tmp);
for (;;){
tmp = amba_readl(flash->regbase + REG28);
if(REGDUMP(tmp,
REG28_RXFIFOLV_MASK,
REG28_RXFIFOLV_SHIFT) == tail){
break;
}
}
for (i = 0; i < tail; i++){
*(flash->dmabuf+bulk*32+i) = amba_readb(flash->regbase + REG200);
}
ret = 0;
}
return ret;
}
EXPORT_SYMBOL_GPL(ambspi_read_data);
static u32 get_ssi3_freq_hz(void)
{
#define PLL_OUT_ENET 300000000
u32 val;
val = amba_rct_readl(CG_SSI3_REG);
if (val & 0x01000000)
return 0;
if (val == 0)
val = 1;
//return (clk_get_rate(clk_get(NULL, "gclk_core")) << 1) / val;
return (PLL_OUT_ENET) / val;
}
int ambspi_init(struct amb_norflash *flash)
{
u32 divider, tmp = 0;
divider = get_ssi3_freq_hz() / flash->clk;
tmp = amba_readl(flash->regbase + REG08);
REGPREP(tmp, REG08_CHIPSEL_MASK, REG08_CHIPSEL_SHIFT, ~(1 << SPINOR_DEV));
REGPREP(tmp, REG08_CLKDIV_MASK, REG08_CLKDIV_SHIFT, divider);
REGPREP(tmp, REG08_FLOWCON_MASK, REG08_FLOWCON_SHIFT, 1);
REGPREP(tmp, REG08_HOLDPIN_MASK, REG08_HOLDPIN_SHIFT, 3);
amba_writel(flash->regbase + REG08, tmp);
tmp = amba_readl(flash->regbase + REG00);
REGPREP(tmp, REG00_DATALEN_MASK, REG00_DATALEN_SHIFT, 0);
REGPREP(tmp, REG00_DUMMYLEN_MASK, REG00_DUMMYLEN_SHIFT, 0);
REGPREP(tmp, REG00_ADDRLEN_MASK, REG00_ADDRLEN_SHIFT, 0);
REGPREP(tmp, REG00_CMDLEN_MASK, REG00_CMDLEN_SHIFT, 1);
amba_writel(flash->regbase + REG00, tmp);
tmp = amba_readl(flash->regbase + REG04);
REGPREP(tmp, REG04_WRITEEN_MASK, REG04_WRITEEN_SHIFT, 0);
REGPREP(tmp, REG04_READEN_MASK, REG04_READEN_SHIFT, 0);
REGPREP(tmp, REG04_DATALANE_MASK, REG04_DATALANE_SHIFT, 0);
REGPREP(tmp, REG04_ADDRLANE_MASK, REG04_ADDRLANE_SHIFT, 0);
REGPREP(tmp, REG04_CMDLANE_MASK, REG04_CMDLANE_SHIFT, 0);
REGPREP(tmp, REG04_LSBFRT_MASK, REG04_LSBFRT_SHIFT, 0);
REGPREP(tmp, REG04_CMDDTR_MASK, REG04_CMDDTR_SHIFT, 0);
amba_writel(flash->regbase + REG04, tmp);
tmp = 0x20;
amba_writel(flash->regbase + REG30, tmp);
tmp = 0;
REGPREP(tmp, REG40_TXFIFORESET_MASK, REG40_TXFIFORESET_SHIFT, 1);
amba_writel(flash->regbase + REG40, tmp);
tmp = 0;
REGPREP(tmp, REG44_RXFIFORESET_MASK, REG44_RXFIFORESET_SHIFT, 1);
amba_writel(flash->regbase + REG44, tmp);
/* after reset fifo, the 0x28 will become 0x10,
*so , read REG200 times to clear the 0x28, this is a bug in hardware
*/
while (amba_readl(flash->regbase + REG28) != 0) {
tmp = amba_readl(flash->regbase + REG200);
}
tmp = 0;
REGPREP(tmp, REG1C_TXFIFOLV_MASK, REG1C_TXFIFOLV_SHIFT, 0x7f);
amba_writel(flash->regbase + REG1C, tmp);
tmp = 0;
REGPREP(tmp, REG20_RXFIFOLV_MASK, REG20_RXFIFOLV_SHIFT, 0x7f);
amba_writel(flash->regbase + REG20, tmp);
return 0;
}
EXPORT_SYMBOL_GPL(ambspi_init);