| #include <stddef.h> |
| #include "bios.h" |
| #include "pflash.h" |
| #include "stdio.h" |
| |
| #define CLEAR_STATUS_CMD 0x50 |
| #define READ_STATUS_CMD 0x70 |
| #define QUERY_CMD 0x98 |
| #define READ_ARRAY_CMD 0xff |
| |
| static void *pflash_detect(uint8_t *top_addr) |
| { |
| volatile uint8_t *p = top_addr; |
| uint8_t save; |
| int i, blocks, sector_len; |
| |
| /* The low byte of the address is part of the command, so it must be 0. */ |
| if ((uintptr_t)p & 256) |
| panic(); |
| |
| p -= 256; |
| for (i = 0; i < 256; i++) |
| if (p[i] != CLEAR_STATUS_CMD) |
| break; |
| if (i == 256) |
| return NULL; |
| |
| save = p[i]; |
| p[i] = CLEAR_STATUS_CMD; |
| if (p[i] == CLEAR_STATUS_CMD) { |
| /* behaves as RAM */ |
| p[i] = save; |
| return NULL; |
| } |
| p[i] = READ_STATUS_CMD; |
| if (p[i] != 0) { |
| /* doesn't behave as flash */ |
| return NULL; |
| } |
| |
| /* 0x2d-0x2e: blocks_per_device - 1, little endian */ |
| /* 0x2f-0x30: sector_len / 256 */ |
| p[i] = QUERY_CMD; |
| blocks = p[0x2d] + (p[0x2e] << 8) + 1; |
| sector_len = (p[0x2f] + (p[0x30] << 8)) << 8; |
| |
| p[i] = READ_ARRAY_CMD; |
| return top_addr - blocks * sector_len; |
| } |
| |
| void *pflash_base(int n, size_t *size) |
| { |
| uint8_t *top = NULL; |
| uint8_t *prev; |
| while (n-- >= 0) { |
| prev = top; |
| top = pflash_detect(top); |
| if (!top) |
| return NULL; |
| *size = prev - top; |
| } |
| return top; |
| } |