summaryrefslogtreecommitdiff
path: root/common/spi_flash_reg.c
blob: 3cee0f3bd44678ef18cb65a6a5c3ac9b44a8a051 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
 * 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;
}