191 lines
5.7 KiB
C
191 lines
5.7 KiB
C
|
/*
|
||
|
* Copyright 2015 The Chromium OS Authors. All rights reserved.
|
||
|
* Use of this source code is governed by a BSD-style license that can be
|
||
|
* found in the LICENSE file.
|
||
|
*
|
||
|
* SPI flash protection register translation functions for Chrome OS EC.
|
||
|
*/
|
||
|
|
||
|
#include "common.h"
|
||
|
#include "spi_flash_reg.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
/* Bit state for protect range table */
|
||
|
enum bit_state {
|
||
|
OFF = 0,
|
||
|
ON = 1,
|
||
|
IGN = -1, /* Don't care / Ignore */
|
||
|
};
|
||
|
|
||
|
struct protect_range {
|
||
|
enum bit_state cmp;
|
||
|
enum bit_state sec;
|
||
|
enum bit_state tb;
|
||
|
enum bit_state bp[3]; /* Ordered {BP2, BP1, BP0} */
|
||
|
uint32_t protect_start;
|
||
|
uint32_t protect_len;
|
||
|
};
|
||
|
|
||
|
/* Compare macro for (x =? b) for 'IGN' comparison */
|
||
|
#define COMPARE_BIT(a, b) ((a) != IGN && (a) != !!(b))
|
||
|
/* Assignment macro where 'IGN' = 0 */
|
||
|
#define GET_BIT(a) ((a) == IGN ? 0 : (a))
|
||
|
|
||
|
/*
|
||
|
* Define flags and protect table for each SPI ROM part. It's not necessary
|
||
|
* to define all ranges in the datasheet since we'll usually protect only
|
||
|
* none or half of the ROM. The table is searched sequentially, so ordering
|
||
|
* according to likely configurations improves performance slightly.
|
||
|
*/
|
||
|
#if defined(CONFIG_SPI_FLASH_W25X40) || defined(CONFIG_SPI_FLASH_GD25Q41B)
|
||
|
static const struct protect_range spi_flash_protect_ranges[] = {
|
||
|
{ IGN, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */
|
||
|
{ IGN, IGN, 1, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/2 */
|
||
|
{ IGN, IGN, 1, { 0, 1, 0 }, 0, 0x20000 }, /* Lower 1/4 */
|
||
|
};
|
||
|
|
||
|
#elif defined(CONFIG_SPI_FLASH_W25Q40) || defined(CONFIG_SPI_FLASH_GD25LQ40)
|
||
|
/* Verified for W25Q40BV and W25Q40EW */
|
||
|
/* For GD25LQ40, BP3 and BP4 have same meaning as TB and SEC */
|
||
|
static const struct protect_range spi_flash_protect_ranges[] = {
|
||
|
/* CMP = 0 */
|
||
|
{ 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */
|
||
|
{ 0, 0, 1, { 0, 1, 0 }, 0, 0x20000 }, /* Lower 1/4 */
|
||
|
{ 0, 0, 1, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/2 */
|
||
|
/* CMP = 1 */
|
||
|
{ 1, 0, 0, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/2 */
|
||
|
{ 1, 0, IGN, { 1, IGN, IGN }, 0, 0 }, /* None (W25Q40EW only) */
|
||
|
};
|
||
|
|
||
|
#elif defined(CONFIG_SPI_FLASH_W25Q64)
|
||
|
static const struct protect_range spi_flash_protect_ranges[] = {
|
||
|
{ 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */
|
||
|
{ 0, 0, 1, { 1, 1, 0 }, 0, 0x400000 }, /* Lower 1/2 */
|
||
|
{ 0, 0, 1, { 1, 0, 1 }, 0, 0x200000 }, /* Lower 1/4 */
|
||
|
};
|
||
|
|
||
|
#elif defined(CONFIG_SPI_FLASH_W25Q80)
|
||
|
static const struct protect_range spi_flash_protect_ranges[] = {
|
||
|
/* CMP = 0 */
|
||
|
{ 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */
|
||
|
{ 0, 0, 1, { 0, 1, 0 }, 0, 0x20000 }, /* Lower 1/8 */
|
||
|
{ 0, 0, 1, { 0, 1, 1 }, 0, 0x40000 }, /* Lower 1/4 */
|
||
|
{ 0, 0, 1, { 1, 0, 0 }, 0, 0x80000 }, /* Lower 1/2 */
|
||
|
};
|
||
|
#elif defined(CONFIG_SPI_FLASH_W25Q128)
|
||
|
static const struct protect_range spi_flash_protect_ranges[] = {
|
||
|
/* CMP = 0 */
|
||
|
{ 0, IGN, IGN, { 0, 0, 0 }, 0, 0 }, /* No protection */
|
||
|
{ 0, 0, 1, { 1, 0, 0 }, 0, 0x20000 }, /* Lower 1/8 */
|
||
|
{ 0, 0, 1, { 1, 0, 1 }, 0, 0x40000 }, /* Lower 1/4 */
|
||
|
{ 0, 0, 1, { 1, 1, 0 }, 0, 0x80000 }, /* Lower 1/2 */
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* Computes block write protection range from registers
|
||
|
* Returns start == len == 0 for no protection
|
||
|
*
|
||
|
* @param sr1 Status register 1
|
||
|
* @param sr2 Status register 2
|
||
|
* @param start Output pointer for protection start offset
|
||
|
* @param len Output pointer for protection length
|
||
|
*
|
||
|
* @return EC_SUCCESS, or non-zero if any error.
|
||
|
*/
|
||
|
int spi_flash_reg_to_protect(uint8_t sr1, uint8_t sr2, unsigned int *start,
|
||
|
unsigned int *len)
|
||
|
{
|
||
|
const struct protect_range *range;
|
||
|
int i;
|
||
|
uint8_t cmp;
|
||
|
uint8_t sec;
|
||
|
uint8_t tb;
|
||
|
uint8_t bp;
|
||
|
|
||
|
/* Determine flags */
|
||
|
cmp = (sr2 & SPI_FLASH_SR2_CMP) ? 1 : 0;
|
||
|
sec = (sr1 & SPI_FLASH_SR1_SEC) ? 1 : 0;
|
||
|
tb = (sr1 & SPI_FLASH_SR1_TB) ? 1 : 0;
|
||
|
bp = (sr1 & (SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0))
|
||
|
>> 2;
|
||
|
|
||
|
/* Bad pointers or invalid data */
|
||
|
if (!start || !len || sr1 == 0xff || sr2 == 0xff)
|
||
|
return EC_ERROR_INVAL;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(spi_flash_protect_ranges); ++i) {
|
||
|
range = &spi_flash_protect_ranges[i];
|
||
|
if (COMPARE_BIT(range->cmp, cmp))
|
||
|
continue;
|
||
|
if (COMPARE_BIT(range->sec, sec))
|
||
|
continue;
|
||
|
if (COMPARE_BIT(range->tb, tb))
|
||
|
continue;
|
||
|
if (COMPARE_BIT(range->bp[0], bp & 0x4))
|
||
|
continue;
|
||
|
if (COMPARE_BIT(range->bp[1], bp & 0x2))
|
||
|
continue;
|
||
|
if (COMPARE_BIT(range->bp[2], bp & 0x1))
|
||
|
continue;
|
||
|
|
||
|
*start = range->protect_start;
|
||
|
*len = range->protect_len;
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/* Invalid range, or valid range missing from our table */
|
||
|
return EC_ERROR_INVAL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Computes block write protection registers from range
|
||
|
*
|
||
|
* @param start Desired protection start offset
|
||
|
* @param len Desired protection length
|
||
|
* @param sr1 Output pointer for status register 1
|
||
|
* @param sr2 Output pointer for status register 2
|
||
|
*
|
||
|
* @return EC_SUCCESS, or non-zero if any error.
|
||
|
*/
|
||
|
int spi_flash_protect_to_reg(unsigned int start, unsigned int len, uint8_t *sr1,
|
||
|
uint8_t *sr2)
|
||
|
{
|
||
|
const struct protect_range *range;
|
||
|
int i;
|
||
|
char cmp = 0;
|
||
|
char sec = 0;
|
||
|
char tb = 0;
|
||
|
char bp = 0;
|
||
|
|
||
|
/* Bad pointers */
|
||
|
if (!sr1 || !sr2)
|
||
|
return EC_ERROR_INVAL;
|
||
|
|
||
|
/* Invalid data */
|
||
|
if ((start && !len) || start + len > CONFIG_FLASH_SIZE)
|
||
|
return EC_ERROR_INVAL;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(spi_flash_protect_ranges); ++i) {
|
||
|
range = &spi_flash_protect_ranges[i];
|
||
|
if (range->protect_start == start &&
|
||
|
range->protect_len == len) {
|
||
|
cmp = GET_BIT(range->cmp);
|
||
|
sec = GET_BIT(range->sec);
|
||
|
tb = GET_BIT(range->tb);
|
||
|
bp = GET_BIT(range->bp[0]) << 2 |
|
||
|
GET_BIT(range->bp[1]) << 1 |
|
||
|
GET_BIT(range->bp[2]);
|
||
|
|
||
|
*sr1 = (sec ? SPI_FLASH_SR1_SEC : 0) |
|
||
|
(tb ? SPI_FLASH_SR1_TB : 0) |
|
||
|
(bp << 2);
|
||
|
*sr2 = (cmp ? SPI_FLASH_SR2_CMP : 0);
|
||
|
return EC_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Invalid range, or valid range missing from our table */
|
||
|
return EC_ERROR_INVAL;
|
||
|
}
|