aboutsummaryrefslogtreecommitdiff
path: root/pflash.c
blob: 950bc74107cae7f5435de0da6425f9c8853d9563 (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
#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;
}