008b6012d06511130330f6e31261a37817e195cf
[oweals/openwrt.git] /
1 From 986172ece10eee928ce66597d76f3f40ac3d25f7 Mon Sep 17 00:00:00 2001
2 From: Yunhui Cui <yunhui.cui@nxp.com>
3 Date: Mon, 8 Aug 2016 14:24:13 +0800
4 Subject: [PATCH 86/93] driver: spi: add spansion s25fs-s family
5  protect/unprotect
6
7 In order to support spansion s25fs512s flash protect/unprotect:
8
9 [1] Fill callbak flash->lock/unlock/is_locked by spansion_lock/
10 unlock/is_locked.
11
12 [2] Achieve protect/unprotected by operating sr1nv, cr1nv.
13
14 Signed-off-by: Yunhui Cui <yunhui.cui@nxp.com>
15 ---
16  drivers/mtd/spi/spi_flash.c |  194 +++++++++++++++++++++++++++++++++++++++++++
17  1 file changed, 194 insertions(+)
18
19 diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
20 index e04bd55..87a92e9 100644
21 --- a/drivers/mtd/spi/spi_flash.c
22 +++ b/drivers/mtd/spi/spi_flash.c
23 @@ -877,6 +877,193 @@ int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len)
24  }
25  #endif
26  
27 +#if defined(CONFIG_SPI_FLASH_SPANSION)
28 +/*
29 + * Return 1 if the entire region is locked, 0 otherwise
30 + */
31 +static int spansion_is_locked_sr(struct spi_flash *flash, u32 ofs, u32 len,
32 +                           u8 sr)
33 +{
34 +       loff_t lock_offs;
35 +       u32 lock_len;
36 +
37 +       stm_get_locked_range(flash, sr, &lock_offs, &lock_len);
38 +
39 +       return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
40 +}
41 +
42 +/*
43 + * Check if a region of the flash is (completely) locked. See spansion_lock() for
44 + * more info.
45 + *
46 + * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
47 + * negative on errors.
48 + */
49 +int spansion_is_locked(struct spi_flash *flash, u32 ofs, size_t len)
50 +{
51 +       u8 cmd[4];
52 +       u32 sr1nv_offset = 0x0;
53 +       u8 sr1nv;
54 +       int ret;
55 +
56 +       cmd[0] = CMD_SPANSION_RDAR;
57 +       cmd[1] = sr1nv_offset >> 16;
58 +       cmd[2] = sr1nv_offset >> 8;
59 +       cmd[3] = sr1nv_offset >> 0;
60 +
61 +       ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
62 +       if (ret)
63 +               return -EIO;
64 +
65 +       return spansion_is_locked_sr(flash, ofs, len, sr1nv);
66 +}
67 +
68 +/*
69 + * Lock a region of the flash. Compatible with Spansion s25fs-s family flash.
70 + * Supports only the block protection bits BP{0,1,2} in the Status Register-1
71 + * Non-Volatile(SR1NV).
72 + *
73 + * Sample table portion for 64MB flash (S25FS512S):
74 + * Configuration Register-1 Non-Volatile(CR1NV[5])== 0
75 + *
76 + *  |  BP2  |  BP1  |  BP0  |  Prot Length  | Protected Portion
77 + *  ------------------------------------------------------------
78 + *  |   0   |   0   |   0   |  NONE          | NONE
79 + *  |   0   |   0   |   1   |  1  MB         | Upper 1/64
80 + *  |   0   |   1   |   0   |  2  MB         | Upper 1/32
81 + *  |   0   |   1   |   1   |  4  MB         | Upper 1/16
82 + *  |   1   |   0   |   0   |  8  MB         | Upper 1/8
83 + *  |   1   |   0   |   1   |  16 MB         | Upper 1/4
84 + *  |   1   |   1   |   0   |  32 MB         | Upper 1/2
85 + *  |   1   |   1   |   1   |  64 MB         | ALL
86 + *
87 + * When CR1NV[5] == 1, the Lower memory array are protected.
88 + *
89 + * Returns negative on errors, 0 on success.
90 + */
91 +int spansion_lock(struct spi_flash *flash, u32 ofs, size_t len)
92 +{
93 +       u8 status_old, status_new;
94 +       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
95 +       u8 shift = ffs(mask) - 1, pow, val;
96 +       int ret;
97 +       u8 cmd[4];
98 +       u32 sr1nv_offset = 0x0;
99 +       u8 sr1nv;
100 +
101 +       cmd[0] = CMD_SPANSION_RDAR;
102 +       cmd[1] = sr1nv_offset >> 16;
103 +       cmd[2] = sr1nv_offset >> 8;
104 +       cmd[3] = sr1nv_offset >> 0;
105 +
106 +       ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
107 +       if (ret)
108 +               return -EIO;
109 +       status_old = sr1nv;
110 +
111 +       /* SPI NOR always locks to the end */
112 +       if (ofs + len != flash->size) {
113 +               /* Does combined region extend to end? */
114 +               if (!stm_is_locked_sr(flash, ofs + len, flash->size - ofs - len,
115 +                                     status_old))
116 +                       return -EINVAL;
117 +               len = flash->size - ofs;
118 +       }
119 +
120 +       /*
121 +        * Need smallest pow such that:
122 +        *
123 +        *   1 / (2^pow) <= (len / size)
124 +        *
125 +        * so (assuming power-of-2 size) we do:
126 +        *
127 +        *   pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
128 +        */
129 +       pow = ilog2(flash->size) - ilog2(len);
130 +       val = mask - (pow << shift);
131 +       if (val & ~mask)
132 +               return -EINVAL;
133 +
134 +       /* Don't "lock" with no region! */
135 +       if (!(val & mask))
136 +               return -EINVAL;
137 +
138 +       status_new = (status_old & ~mask) | val;
139 +
140 +       /* Only modify protection if it will not unlock other areas */
141 +       if ((status_new & mask) <= (status_old & mask))
142 +               return -EINVAL;
143 +
144 +       cmd[0] = CMD_SPANSION_WRAR;
145 +       ret = spi_flash_cmd_write(flash->spi, cmd, 4, &status_new, 1);
146 +       if (ret)
147 +               return -EIO;
148 +
149 +       return 0;
150 +}
151 +
152 +/*
153 + * Unlock a region of the flash. See spansion_lock() for more info
154 + *
155 + * Returns negative on errors, 0 on success.
156 + */
157 +int spansion_unlock(struct spi_flash *flash, u32 ofs, size_t len)
158 +{
159 +       uint8_t status_old, status_new;
160 +       u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
161 +       u8 shift = ffs(mask) - 1, pow, val;
162 +       int ret;
163 +
164 +       u8 cmd[4];
165 +       u32 sr1nv_offset = 0x0;
166 +       u8 sr1nv;
167 +
168 +       cmd[0] = CMD_SPANSION_RDAR;
169 +       cmd[1] = sr1nv_offset >> 16;
170 +       cmd[2] = sr1nv_offset >> 8;
171 +       cmd[3] = sr1nv_offset >> 0;
172 +
173 +       ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
174 +       if (ret)
175 +               return -EIO;
176 +       status_old = sr1nv;
177 +
178 +       /* Cannot unlock; would unlock larger region than requested */
179 +       if (spansion_is_locked_sr(flash, ofs - flash->erase_size, flash->erase_size,
180 +                            status_old))
181 +               return -EINVAL;
182 +       /*
183 +        * Need largest pow such that:
184 +        *
185 +        *   1 / (2^pow) >= (len / size)
186 +        *
187 +        * so (assuming power-of-2 size) we do:
188 +        *
189 +        *   pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
190 +        */
191 +       pow = ilog2(flash->size) - order_base_2(flash->size - (ofs + len));
192 +       if (ofs + len == flash->size) {
193 +               val = 0; /* fully unlocked */
194 +       } else {
195 +               val = mask - (pow << shift);
196 +               /* Some power-of-two sizes are not supported */
197 +               if (val & ~mask)
198 +                       return -EINVAL;
199 +       }
200 +       status_new = (status_old & ~mask) | val;
201 +
202 +       /* Only modify protection if it will not lock other areas */
203 +       if ((status_new & mask) >= (status_old & mask))
204 +               return -EINVAL;
205 +
206 +       cmd[0] = CMD_SPANSION_WRAR;
207 +       ret = spi_flash_cmd_write(flash->spi, cmd, 4, &status_new, 1);
208 +       if (ret)
209 +               return -EIO;
210 +
211 +       return 0;
212 +}
213 +#endif
214  
215  #ifdef CONFIG_SPI_FLASH_MACRONIX
216  static int spi_flash_set_qeb_mxic(struct spi_flash *flash)
217 @@ -1132,6 +1319,13 @@ int spi_flash_scan(struct spi_flash *flash)
218                 flash->flash_is_locked = stm_is_locked;
219  #endif
220                 break;
221 +#if defined(CONFIG_SPI_FLASH_SPANSION)
222 +       case SPI_FLASH_CFI_MFR_SPANSION:
223 +               flash->flash_lock = spansion_lock;
224 +               flash->flash_unlock = spansion_unlock;
225 +               flash->flash_is_locked = spansion_is_locked;
226 +#endif
227 +               break;
228         default:
229                 debug("SF: Lock ops not supported for %02x flash\n", idcode[0]);
230         }
231 -- 
232 1.7.9.5
233