blob: 06ee697437dce94afdc5073e40d93cda8a256e59 [file] [log] [blame]
/*
* Copyright (c) 2010 Quantenna Communications, Inc.
* All rights reserved.
*
* SPI driver
*/
///////////////////////////////////////////////////////////////////////////////
// Includes
///////////////////////////////////////////////////////////////////////////////
//
#include "ruby.h"
#include "ruby_spi_api.h"
#include <asm/errno.h>
#include "spi_api.h"
///////////////////////////////////////////////////////////////////////////////
// Types
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Globals
///////////////////////////////////////////////////////////////////////////////
/******************************************************************************
Function:spi_protect_mode
Purpose:check device ID,and see if the
device support protect/unprotect mode
Returns:0 or < 0
Note:
*****************************************************************************/
int spi_protect_mode(struct flash_info *device)
{
uint32_t spi_ctrl_val;
int status = EOPNOTSUPP;
printf("SPI:%0x\n",device->jedec_id);
switch(device->single_unprotect_mode){
case MACRONIX:
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
if (spi_read_status() & SPI_SR_QUAD_MODE) {
printf("SPI Quad\n");
writel((SPI_SR_QUAD_MODE_MASK(spi_ctrl_val)), RUBY_SPI_CONTROL);
} else {
writel(SPI_SR_SINGLE_MODE_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
}
/*
* This is the place where we check if Device
* support individual unprotect sector
*/
if (spi_read_scur() & SPI_SCUR_WPSEL) {
printf("SPI PM ON\n");
status = 0;
} else {
printf("Set SPI PM \n");
spi_write_prot_select(device);
if (spi_read_scur() & SPI_SCUR_WPSEL) {
printf("SPI PM ON\n");
status = 0;
} else {
printf("SPI PM OFF\n");
status = EOPNOTSUPP;
}
}
break;
case WINBOND:
if (((spi_read_wps()) & SPI_WPS_SELECT)){
printf("SPI PM\n");
status = 0;
} else {
spi_write_prot_select(device);
if ((spi_read_wps() & SPI_WPS_SELECT)) {
printf("SPI PM ON\n");
status = 0;
} else {
printf("SPI PM OFF\n");
status = EOPNOTSUPP;
}
}
break;
default:
printf("SPI PM OFF\n");
status = EOPNOTSUPP;
}
return status;
}
/******************************************************************************
* Function:spi_read_scur
* Purpose:Read security register
* Returns:status of the security register
* Note:
******************************************************************************/
uint32_t spi_read_scur(void)
{
uint32_t spi_ctrl_val;
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
writel(RUBY_SPI_READ_SCUR_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(0, RUBY_SPI_READ_SCUR);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
return (SWAP32(readl(RUBY_SPI_COMMIT))&RUBY_SPI_READ_STATUS_MASK);
}
/******************************************************************************
* Function:spi_read_dpb_reg
* Purpose:read individual sector
* Returns: status of the dynamic register
* Note:
*********************************************************************************/
uint32_t spi_read_dpb_reg(uint32_t addr)
{
uint32_t spi_ctrl_val;
uint32_t log_addr, sector_addr;
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
log_addr = addr & ADDRESS_MASK;
sector_addr = log_addr & SECTOR_MASK;
writel(RUBY_SPI_READ_DPB_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(sector_addr, RUBY_SPI_READ_DPB);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
return (SWAP32(readl(RUBY_SPI_COMMIT))&RUBY_SPI_READ_STATUS_MASK);
}
/******************************************************************************
* Function:spi_gang_block_lock
* Purpose:Block/protect the entire chip sectors
* Returns:0 or < 0
* Note:
**********************************************************************************/
int spi_gang_block_lock(void)
{
uint32_t spi_ctrl_val;
spi_unlock();
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
writel(RUBY_SPI_GBLOCK_LOCK_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(0, RUBY_SPI_GBLOCK_LOCK);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
if ((spi_flash_wait_ready(SPI_WRITE_DELAY)) == -1){
printf("ERROR:WR Failed\n");
spi_lock();
return ETIME;
}
spi_lock();
return 0;
}
/******************************************************************************
* Function:spi_gang_block_unlock
* Purpose:issue a unlock before any write to flash
* Returns: 0 or < 0
* Note:
**********************************************************************************/
int spi_gang_block_unlock(void)
{
uint32_t spi_ctrl_val;
spi_unlock();
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
writel(RUBY_SPI_GBLOCK_UNLOCK_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(0, RUBY_SPI_GBLOCK_UNLOCK);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
if ((spi_flash_wait_ready(SPI_WRITE_DELAY)) == -1){
printf("ERROR:WR Failed\n");
spi_lock();
return ETIME;
}
spi_lock();
return 0;
}
/******************************************************************************
* Function:spi_write_prot_select
* Purpose:Check if the device support individual unprotect mode
* Returns: 0 or < 0
* Note:
**********************************************************************************/
int spi_write_prot_select(struct flash_info *device)
{
uint32_t spi_ctrl_val;
switch(device->single_unprotect_mode){
case MACRONIX:
spi_unlock();
if (spi_read_scur() & SPI_SCUR_WPSEL) {
spi_lock();
printf("Individual Unprotedted Mode Is Enabled \n");
return 0;
}
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
writel(RUBY_SPI_WRITE_PRO_SEL_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(0, RUBY_SPI_WRITE_PRO_SEL);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
if ((spi_flash_wait_ready(SPI_WRITE_DELAY)) == -1){
printf("ERROR:Write Failed\n");
spi_lock();
return ETIME;
}
spi_lock();
if (spi_read_scur() & SPI_SCUR_WPSEL) {
printf("Individual Unprotected Mode Is Enabled\n");
return 0;
} else {
printf("Individual Unprotected Mode Is Disabled \n");
return EOPNOTSUPP;
}
break;
case WINBOND:
spi_unlock();
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
writel(RUBY_SPI_WRITE_WPS_SEL_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(SPI_WPS_ENABLE, RUBY_SPI_WRITE_REG3);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
spi_lock();
return 0;
default:
return EOPNOTSUPP;
}
return EOPNOTSUPP;
}
/******************************************************************************
* Function:spi_clear_dpb_reg
* Purpose:unproctect individual sector
* Returns:0 or < 0
* Note:
**********************************************************************************/
int spi_clear_dpb_reg(uint32_t addr)
{
uint32_t spi_ctrl_val;
uint32_t log_addr, sector_addr;
spi_unlock();
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
log_addr = addr & ADDRESS_MASK;
sector_addr = log_addr & SECTOR_MASK;
writel(RUBY_SPI_WRITE_DPB_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(sector_addr, RUBY_SPI_WRITE_DPB);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
if ((spi_flash_wait_ready(SPI_WRITE_DELAY)) == -1){
printf("ERROR:Wr\n");
spi_lock();
return ETIME;
}
spi_lock();
return 0;
}
/******************************************************************************
* * Function:spi_clear_ibup_reg
* * Purpose:unproctect individual sector
* * Returns:0 or < 0
* * Note:
* **********************************************************************************/
int spi_clear_ibup_reg(uint32_t addr)
{
uint32_t spi_ctrl_val;
uint32_t log_addr, sector_addr;
spi_unlock();
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
log_addr = addr & ADDRESS_MASK;
sector_addr = log_addr & SECTOR_MASK;
writel(RUBY_SPI_WRITE_IBUP_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(sector_addr, RUBY_SPI_WRITE_IBUP);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
if ((spi_flash_wait_ready(SPI_WRITE_DELAY)) == -1){
printf("ERROR:Wr\n");
spi_lock();
return ETIME;
}
spi_lock();
return 0;
}
/******************************************************************************
* Function:spi_lock
* Purpose:issue a lock after write is complete
* Returns:0
* Note:
**********************************************************************************/
int spi_lock(void)
{
writel(0, RUBY_SPI_WRITE_DIS);
return 0;
}
/******************************************************************************
* Function:spi_unlock
* Purpose:issue a unlock before any write to flash
* Returns: 0
* Note:
**********************************************************************************/
int spi_unlock(void)
{
writel(0, RUBY_SPI_WRITE_EN);
return 0;
}
/******************************************************************************
Function:spi_read_id
Purpose:Reads spi device ID
Returns: ID of a device
Note:
*****************************************************************************/
uint32_t spi_read_id(void)
{
return SWAP32(readl(RUBY_SPI_READ_ID))&(RUBY_SPI_READ_ID_MASK);
}
/******************************************************************************
Function:spi_read_status
Purpose:Reads spi status reg
Returns:Status
Note:
*****************************************************************************/
uint32_t spi_read_status(void)
{
return SWAP32(readl(RUBY_SPI_READ_STATUS))&(RUBY_SPI_READ_STATUS_MASK);
}
/******************************************************************************
Function:spi_write_status
Purpose:write spi status reg
Returns:0
Note:
*****************************************************************************/
int spi_write_status(uint32_t status)
{
writel(status, RUBY_SPI_WRITE_STATUS);
return 0;
}
/******************************************************************************
* Function:spi_unprotect_all
* Purpose:unprotect the whole flash device
* Returns:0 or < 0
* Note:
******************************************************************************/
int spi_unprotect_all(struct flash_info *device)
{
int ret = EOPNOTSUPP;
switch(device->single_unprotect_mode){
case MACRONIX:
case WINBOND:
ret = spi_gang_block_unlock();
break;
default:
ret = 0;
}
return ret;
}
/******************************************************************************
* Function:spi_unprotect_sector
* Purpose:unprotect the a individual sector
* Returns:0 or < 0
* Note:
******************************************************************************/
int spi_unprotect_sector(struct flash_info *device, uint32_t flash_addr)
{
int ret = EOPNOTSUPP;
switch(device->single_unprotect_mode){
case MACRONIX:
ret = spi_clear_dpb_reg(flash_addr) ;
break;
case WINBOND:
ret = spi_clear_ibup_reg(flash_addr) ;
break;
default:
ret = 0;
}
return ret;
}
/******************************************************************************
* Function:spi_protect_all
* Purpose:protect whole chipset device
* Returns:0 or < 0
* Note:
******************************************************************************/
int spi_protect_all(struct flash_info *device)
{
int ret = EOPNOTSUPP;
switch(device->single_unprotect_mode){
case MACRONIX:
case WINBOND:
spi_flash_wait_ready(SPI_ERASE_TIMEOUT);
ret = spi_gang_block_lock();
break;
default:
ret = 0;
}
return ret;
}
/******************************************************************************
* Function:spi_read_wps
* Purpose:Read security register
* Returns: SPI Status Bits
* Note:
******************************************************************************/
uint32_t spi_read_wps(void)
{
uint32_t spi_ctrl_val;
uint32_t spi_status = 0;
spi_ctrl_val = readl(RUBY_SPI_CONTROL);
writel(RUBY_SPI_READ_SCUR_MASK(spi_ctrl_val), RUBY_SPI_CONTROL);
writel(0, RUBY_SPI_READ_REG3);
writel(spi_ctrl_val, RUBY_SPI_CONTROL);
spi_status = SWAP32(readl(RUBY_SPI_COMMIT)) & (RUBY_SPI_READ_STATUS_MASK);
return (spi_status);
}
int spi_device_erase(struct flash_info *device, u32 flash_addr)
{
int ret = 0;
int i;
int n_of_64k;
switch(device->single_unprotect_mode){
case MACRONIX:
case WINBOND:
/* check if the address is below the first lower 64K or upper end 64K,
* Flash specs for unproctect mode is lower 64K you can unprotect
* 4K chuncks and anywhere is 64K
*/
n_of_64k = flash_addr / SPI_SECTOR_64K;
if ((n_of_64k == 0) || (n_of_64k == device->n_sectors - 1)) {
for (i = 0; i < SPI_SECTOR_INDEX; i++) {
/* Per Sector Unprotect */
ret = spi_unprotect_sector(device, flash_addr) ;
if (ret)
return -1;
/* Per-sector erase */
spi_flash_write_enable();
if ( device->sector_size * device->n_sectors > RUBY_SPI_BOUNDARY_4B ){
writel(SPI_MEM_ADDR_4B(flash_addr), RUBY_SPI_SECTOR_ERASE_20_4B);
} else {
writel(SPI_MEM_ADDR(flash_addr), RUBY_SPI_SECTOR_ERASE_20);
}
ret = spi_flash_wait_ready(SPI_ERASE_TIMEOUT);
if (ret)
return -1;
if (device->sector_size == SPI_SECTOR_4K)
break;
flash_addr += SPI_SECTOR_4K;
}
} else {
/*
* Do 64K chunck at a time
* Per Sector Unprotect
*/
ret = spi_unprotect_sector(device, flash_addr) ;
if (ret){
return -1;
}
/* Per-sector erase */
spi_flash_write_enable();
if ( device->sector_size * device->n_sectors > RUBY_SPI_BOUNDARY_4B ){
writel(SPI_MEM_ADDR_4B(flash_addr), RUBY_SPI_SECTOR_ERASE_D8_4B);
} else {
writel(SPI_MEM_ADDR(flash_addr), RUBY_SPI_SECTOR_ERASE_D8);
}
}
break;
default:
/* Per-sector erase */
spi_flash_write_enable();
if ( device->sector_size * device->n_sectors > RUBY_SPI_BOUNDARY_4B ){
if (device->flags & SECTOR_ERASE_OP20) {
writel(SPI_MEM_ADDR_4B(flash_addr), RUBY_SPI_SECTOR_ERASE_20_4B);
} else {
writel(SPI_MEM_ADDR_4B(flash_addr), RUBY_SPI_SECTOR_ERASE_D8_4B);
}
} else {
if (device->flags & SECTOR_ERASE_OP20) {
writel(SPI_MEM_ADDR(flash_addr), RUBY_SPI_SECTOR_ERASE_20);
} else {
writel(SPI_MEM_ADDR(flash_addr), RUBY_SPI_SECTOR_ERASE_D8);
}
}
}
return ret;
}