| /* |
| * Copyright (c) 2010 Quantenna Communications, Inc. |
| * All rights reserved. |
| * |
| * SPI driver |
| */ |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Includes |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include <linux/types.h> |
| #include <linux/init.h> |
| #include <linux/module.h> |
| #include <linux/io.h> |
| #include <linux/delay.h> |
| |
| #include <common/ruby_platform.h> |
| #include <common/ruby_spi_api.h> |
| #include "spi_api.h" |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Types |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // Globals |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| /****************************************************************************** |
| Function:spi_protect_mode |
| Purpose:check the id if we support, and see if the device support protec mode |
| Returns:0 or < 0 |
| Note: |
| *****************************************************************************/ |
| int spi_protect_mode(struct flash_info *device) |
| { |
| uint32_t spi_ctrl_val; |
| int status = EOPNOTSUPP; |
| |
| printk(KERN_INFO "SPI device:%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) { |
| printk(KERN_INFO "SPI device is in Quad mode\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); |
| } |
| |
| if (spi_read_scur() & SPI_SCUR_WPSEL) { |
| printk(KERN_INFO "SPI Device Is In Protected Mode\n"); |
| status = 0; |
| } else { |
| printk(KERN_INFO "Setting SPI Device To Protected Mode..... \n"); |
| spi_write_prot_select(device); |
| /* |
| * This is the place where we check if Device |
| * support individual unprotect sector |
| */ |
| |
| if (spi_read_scur() & SPI_SCUR_WPSEL) { |
| printk(KERN_INFO "SPI Device Is In Protected Mode\n"); |
| status = 0; |
| } else { |
| printk(KERN_INFO "SPI Device Do Not Support Individual Unprotect Mode\n"); |
| status = EOPNOTSUPP; |
| } |
| } |
| break; |
| case WINBOND: |
| if (((spi_read_wps()) & SPI_WPS_SELECT)){ |
| printk(KERN_INFO "SPI Device Is In Protected Mode\n"); |
| status = 0; |
| } else { |
| spi_write_prot_select(device); |
| if ((spi_read_wps() & SPI_WPS_SELECT)) { |
| printk(KERN_INFO "SPI Device Is In Protected Mode\n"); |
| status = 0; |
| } else { |
| printk(KERN_INFO "SPI Device Do Not Support Individual Unprotect Mode\n"); |
| status = EOPNOTSUPP; |
| } |
| } |
| |
| break; |
| |
| default: |
| printk(KERN_INFO "SPI Device Do Not Support Individual Unprotect Mode\n"); |
| status = EOPNOTSUPP; |
| } |
| |
| return status; |
| } |
| |
| /****************************************************************************** |
| * Function:spi_read_scur |
| * Purpose:Read security register |
| * Returns: SPI Status Bits |
| * 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 dynamic protect block mode |
| * Returns:status of the dynamic protect mode |
| * 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:protect whole chipset |
| * 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_api_flash_status()) == -1){ |
| printk(KERN_ERR "Time Out On Write Operation\n"); |
| spi_lock(); |
| return ETIME; |
| } |
| spi_lock(); |
| return 0; |
| } |
| |
| /****************************************************************************** |
| * Function:spi_gang_block_unlock |
| * Purpose:unprotect whole chipset |
| * 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_api_flash_status()) == -1){ |
| printk(KERN_ERR "Time Out On Write Operation\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(); |
| printk(KERN_INFO "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_api_flash_status()) == -1){ |
| printk(KERN_ERR "Time Out On Write Operation\n"); |
| spi_lock(); |
| return ETIME; |
| } |
| spi_lock(); |
| if (spi_read_scur() & SPI_SCUR_WPSEL) { |
| printk(KERN_INFO "Individual Unprotected Mode Is Enabled\n"); |
| return 0; |
| } else { |
| printk(KERN_INFO "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; |
| } |
| } |
| |
| /****************************************************************************** |
| * 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_api_flash_status()) == -1){ |
| printk(KERN_ERR "Time Out On Write Operation\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_api_flash_status()) == -1){ |
| printk(KERN_ERR "Time Out On Write Operation\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 |
| 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:Flash 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(const 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(const 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(const struct flash_info *device) |
| { |
| int ret = EOPNOTSUPP; |
| |
| switch(device->single_unprotect_mode){ |
| case MACRONIX: |
| case WINBOND: |
| if ((spi_api_flash_status()) == -1){ |
| printk(KERN_ERR "Time Out On Write Operation\n"); |
| spi_lock(); |
| return ETIME; |
| } |
| |
| 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; |
| |
| 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); |
| return (SWAP32(readl(RUBY_SPI_COMMIT))&RUBY_SPI_READ_STATUS_MASK); |
| } |
| |
| |
| int spi_device_erase(struct flash_info *device, u32 flash_addr, unsigned es) |
| { |
| int ret = 0; |
| int i; |
| int n_of_64k; |
| |
| if ((es != device->sector_size) && |
| ((es != SPI_SECTOR_4K) || !(device->flags & SECTOR_ERASE_OP20))) { |
| printk(KERN_INFO "ERROR erase size%x\n", es); |
| return -1; |
| } |
| |
| 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 proctect mode is lower 64K you can protect/unprotect |
| * 4K chuncks and anywhere is 64K |
| * to make our life easeir we will use default 64K size, but will build some |
| * intelleigent to erase the lower 64K or upper 64K of the flash |
| */ |
| 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++) { |
| ret = spi_unprotect_sector(device, flash_addr) ; |
| if (ret){ |
| printk(KERN_INFO "ERROR: Failed to unprotect Sector %x \n", flash_addr); |
| return -1; |
| } |
| 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(device); |
| if (ret || device->sector_size == SPI_SECTOR_4K) |
| break; |
| flash_addr += SPI_SECTOR_4K; |
| } |
| |
| } else { |
| ret = spi_unprotect_sector(device, flash_addr) ; |
| if (ret){ |
| printk(KERN_INFO "ERROR: Failed to unprotect Sector %x \n", flash_addr); |
| return -1; |
| } |
| 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: |
| 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; |
| } |
| |
| |