|
14 | 14 | #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ |
15 | 15 | #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ |
16 | 16 | #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ |
| 17 | +#define SPINOR_REG_CYPRESS_STR1V 0x00800000 |
17 | 18 | #define SPINOR_REG_CYPRESS_CFR1V 0x00800002 |
18 | 19 | #define SPINOR_REG_CYPRESS_CFR1_QUAD_EN BIT(1) /* Quad Enable */ |
19 | 20 | #define SPINOR_REG_CYPRESS_CFR2V 0x00800003 |
20 | 21 | #define SPINOR_REG_CYPRESS_CFR2_MEMLAT_11_24 0xb |
| 22 | +#define SPINOR_REG_CYPRESS_CFR2_ADRBYT BIT(7) |
21 | 23 | #define SPINOR_REG_CYPRESS_CFR3V 0x00800004 |
22 | 24 | #define SPINOR_REG_CYPRESS_CFR3_PGSZ BIT(4) /* Page size. */ |
23 | 25 | #define SPINOR_REG_CYPRESS_CFR5V 0x00800006 |
@@ -188,6 +190,117 @@ static int cypress_nor_quad_enable_volatile(struct spi_nor *nor) |
188 | 190 | return 0; |
189 | 191 | } |
190 | 192 |
|
| 193 | +/** |
| 194 | + * cypress_nor_determine_addr_mode_by_sr1() - Determine current address mode |
| 195 | + * (3 or 4-byte) by querying status |
| 196 | + * register 1 (SR1). |
| 197 | + * @nor: pointer to a 'struct spi_nor' |
| 198 | + * @addr_mode: ponter to a buffer where we return the determined |
| 199 | + * address mode. |
| 200 | + * |
| 201 | + * This function tries to determine current address mode by comparing SR1 value |
| 202 | + * from RDSR1(no address), RDAR(3-byte address), and RDAR(4-byte address). |
| 203 | + * |
| 204 | + * Return: 0 on success, -errno otherwise. |
| 205 | + */ |
| 206 | +static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor, |
| 207 | + u8 *addr_mode) |
| 208 | +{ |
| 209 | + struct spi_mem_op op = |
| 210 | + CYPRESS_NOR_RD_ANY_REG_OP(3, SPINOR_REG_CYPRESS_STR1V, 0, |
| 211 | + nor->bouncebuf); |
| 212 | + bool is3byte, is4byte; |
| 213 | + int ret; |
| 214 | + |
| 215 | + ret = spi_nor_read_sr(nor, &nor->bouncebuf[1]); |
| 216 | + if (ret) |
| 217 | + return ret; |
| 218 | + |
| 219 | + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); |
| 220 | + if (ret) |
| 221 | + return ret; |
| 222 | + |
| 223 | + is3byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); |
| 224 | + |
| 225 | + op = (struct spi_mem_op) |
| 226 | + CYPRESS_NOR_RD_ANY_REG_OP(4, SPINOR_REG_CYPRESS_STR1V, 0, |
| 227 | + nor->bouncebuf); |
| 228 | + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); |
| 229 | + if (ret) |
| 230 | + return ret; |
| 231 | + |
| 232 | + is4byte = (nor->bouncebuf[0] == nor->bouncebuf[1]); |
| 233 | + |
| 234 | + if (is3byte == is4byte) |
| 235 | + return -EIO; |
| 236 | + if (is3byte) |
| 237 | + *addr_mode = 3; |
| 238 | + else |
| 239 | + *addr_mode = 4; |
| 240 | + |
| 241 | + return 0; |
| 242 | +} |
| 243 | + |
| 244 | +/** |
| 245 | + * cypress_nor_set_addr_mode_nbytes() - Set the number of address bytes mode of |
| 246 | + * current address mode. |
| 247 | + * @nor: pointer to a 'struct spi_nor' |
| 248 | + * |
| 249 | + * Determine current address mode by reading SR1 with different methods, then |
| 250 | + * query CFR2V[7] to confirm. If determination is failed, force enter to 4-byte |
| 251 | + * address mode. |
| 252 | + * |
| 253 | + * Return: 0 on success, -errno otherwise. |
| 254 | + */ |
| 255 | +static int cypress_nor_set_addr_mode_nbytes(struct spi_nor *nor) |
| 256 | +{ |
| 257 | + struct spi_mem_op op; |
| 258 | + u8 addr_mode; |
| 259 | + int ret; |
| 260 | + |
| 261 | + /* |
| 262 | + * Read SR1 by RDSR1 and RDAR(3- AND 4-byte addr). Use write enable |
| 263 | + * that sets bit-1 in SR1. |
| 264 | + */ |
| 265 | + ret = spi_nor_write_enable(nor); |
| 266 | + if (ret) |
| 267 | + return ret; |
| 268 | + ret = cypress_nor_determine_addr_mode_by_sr1(nor, &addr_mode); |
| 269 | + if (ret) { |
| 270 | + ret = spi_nor_set_4byte_addr_mode(nor, true); |
| 271 | + if (ret) |
| 272 | + return ret; |
| 273 | + return spi_nor_write_disable(nor); |
| 274 | + } |
| 275 | + ret = spi_nor_write_disable(nor); |
| 276 | + if (ret) |
| 277 | + return ret; |
| 278 | + |
| 279 | + /* |
| 280 | + * Query CFR2V and make sure no contradiction between determined address |
| 281 | + * mode and CFR2V[7]. |
| 282 | + */ |
| 283 | + op = (struct spi_mem_op) |
| 284 | + CYPRESS_NOR_RD_ANY_REG_OP(addr_mode, SPINOR_REG_CYPRESS_CFR2V, |
| 285 | + 0, nor->bouncebuf); |
| 286 | + ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto); |
| 287 | + if (ret) |
| 288 | + return ret; |
| 289 | + |
| 290 | + if (nor->bouncebuf[0] & SPINOR_REG_CYPRESS_CFR2_ADRBYT) { |
| 291 | + if (addr_mode != 4) |
| 292 | + return spi_nor_set_4byte_addr_mode(nor, true); |
| 293 | + } else { |
| 294 | + if (addr_mode != 3) |
| 295 | + return spi_nor_set_4byte_addr_mode(nor, true); |
| 296 | + } |
| 297 | + |
| 298 | + nor->params->addr_nbytes = addr_mode; |
| 299 | + nor->params->addr_mode_nbytes = addr_mode; |
| 300 | + |
| 301 | + return 0; |
| 302 | +} |
| 303 | + |
191 | 304 | /** |
192 | 305 | * cypress_nor_set_page_size() - Set page size which corresponds to the flash |
193 | 306 | * configuration. |
@@ -227,9 +340,9 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor, |
227 | 340 | struct spi_mem_op op; |
228 | 341 | int ret; |
229 | 342 |
|
230 | | - /* 4-byte address mode is enabled by default */ |
231 | | - nor->params->addr_nbytes = 4; |
232 | | - nor->params->addr_mode_nbytes = 4; |
| 343 | + ret = cypress_nor_set_addr_mode_nbytes(nor); |
| 344 | + if (ret) |
| 345 | + return ret; |
233 | 346 |
|
234 | 347 | /* Read Architecture Configuration Register (ARCFN) */ |
235 | 348 | op = (struct spi_mem_op) |
@@ -280,6 +393,12 @@ s25hx_t_post_bfpt_fixup(struct spi_nor *nor, |
280 | 393 | const struct sfdp_parameter_header *bfpt_header, |
281 | 394 | const struct sfdp_bfpt *bfpt) |
282 | 395 | { |
| 396 | + int ret; |
| 397 | + |
| 398 | + ret = cypress_nor_set_addr_mode_nbytes(nor); |
| 399 | + if (ret) |
| 400 | + return ret; |
| 401 | + |
283 | 402 | /* Replace Quad Enable with volatile version */ |
284 | 403 | nor->params->quad_enable = cypress_nor_quad_enable_volatile; |
285 | 404 |
|
@@ -375,6 +494,12 @@ static int s28hx_t_post_bfpt_fixup(struct spi_nor *nor, |
375 | 494 | const struct sfdp_parameter_header *bfpt_header, |
376 | 495 | const struct sfdp_bfpt *bfpt) |
377 | 496 | { |
| 497 | + int ret; |
| 498 | + |
| 499 | + ret = cypress_nor_set_addr_mode_nbytes(nor); |
| 500 | + if (ret) |
| 501 | + return ret; |
| 502 | + |
378 | 503 | return cypress_nor_set_page_size(nor); |
379 | 504 | } |
380 | 505 |
|
|
0 commit comments