[lib][bio] Add erase geometry to lib/bio
Extend the block i/o system to allow registered devices to advertise an erase geometry. Devices which do not need to be explicitly erased may skip this. Extent the Zynq spiflash code to publish the erase geometry as read from the flash device. Signed-off-by: John Grossman <johngro@google.com> Change-Id: I42e16fbe9a0fcf8334c317f16dea1c551c203eb2
This commit is contained in:
committed by
Travis Geiselbrecht
parent
8028f42df6
commit
8a7219ddb1
@@ -22,22 +22,34 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/types.h>
|
||||
#include <list.h>
|
||||
|
||||
typedef uint32_t bnum_t;
|
||||
|
||||
typedef struct bio_erase_geometry_info {
|
||||
off_t start; // start of the region in bytes.
|
||||
off_t size;
|
||||
size_t erase_size;
|
||||
size_t erase_shift;
|
||||
} bio_erase_geometry_info_t;
|
||||
|
||||
typedef struct bdev {
|
||||
struct list_node node;
|
||||
volatile int ref;
|
||||
|
||||
/* info about the block device */
|
||||
char *name;
|
||||
off_t size;
|
||||
off_t total_size;
|
||||
|
||||
size_t block_size;
|
||||
size_t block_shift;
|
||||
bnum_t block_count;
|
||||
|
||||
size_t geometry_count;
|
||||
const bio_erase_geometry_info_t* geometry;
|
||||
|
||||
/* function pointers */
|
||||
ssize_t (*read)(struct bdev *, void *buf, off_t offset, size_t len);
|
||||
ssize_t (*read_block)(struct bdev *, void *buf, bnum_t block, uint count);
|
||||
@@ -63,13 +75,21 @@ void bio_register_device(bdev_t *dev);
|
||||
void bio_unregister_device(bdev_t *dev);
|
||||
|
||||
/* used during bdev construction */
|
||||
void bio_initialize_bdev(bdev_t *dev, const char *name, size_t block_size, bnum_t block_count);
|
||||
void bio_initialize_bdev(bdev_t* dev,
|
||||
const char* name,
|
||||
size_t block_size,
|
||||
bnum_t block_count,
|
||||
size_t geometry_count,
|
||||
const bio_erase_geometry_info_t* geometry);
|
||||
|
||||
/* debug stuff */
|
||||
void bio_dump_devices(void);
|
||||
|
||||
/* subdevice support */
|
||||
status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_t startblock, bnum_t block_count);
|
||||
status_t bio_publish_subdevice(const char *parent_dev,
|
||||
const char *subdev,
|
||||
bnum_t startblock,
|
||||
bnum_t block_count);
|
||||
|
||||
/* memory based block device */
|
||||
int create_membdev(const char *name, void *ptr, size_t len);
|
||||
@@ -80,6 +100,33 @@ size_t bio_trim_range(const bdev_t *dev, off_t offset, size_t len);
|
||||
/* helper routine to trim to a block range in the device */
|
||||
uint bio_trim_block_range(const bdev_t *dev, bnum_t block, uint count);
|
||||
|
||||
/* utility routine */
|
||||
static inline bool bio_does_overlap(uint64_t start1, uint64_t len1,
|
||||
uint64_t start2, uint64_t len2)
|
||||
{
|
||||
uint64_t end1 = start1 + len1;
|
||||
uint64_t end2 = start2 + len2;
|
||||
|
||||
DEBUG_ASSERT(end1 >= start1);
|
||||
DEBUG_ASSERT(end2 >= start2);
|
||||
|
||||
return (((start1 >= start2) && (start1 < end2)) ||
|
||||
((start2 >= start1) && (start2 < end1)));
|
||||
}
|
||||
|
||||
static inline bool bio_contains_range(uint64_t container_start, uint64_t container_len,
|
||||
uint64_t contained_start, uint64_t contained_len)
|
||||
{
|
||||
uint64_t container_end = container_start + container_len;
|
||||
uint64_t contained_end = contained_start + contained_len;
|
||||
|
||||
DEBUG_ASSERT(container_end >= container_start);
|
||||
DEBUG_ASSERT(contained_end >= contained_start);
|
||||
|
||||
return ((container_start <= contained_start) &&
|
||||
(container_end >= contained_end));
|
||||
}
|
||||
|
||||
/* generic bio ioctls */
|
||||
enum bio_ioctl_num {
|
||||
BIO_IOCTL_NULL = 0,
|
||||
|
||||
@@ -229,12 +229,16 @@ static ssize_t bio_default_write_block(struct bdev *dev, const void *buf, bnum_t
|
||||
|
||||
static void bdev_inc_ref(bdev_t *dev)
|
||||
{
|
||||
LTRACEF("Add ref \"%s\" %d -> %d\n", dev->name, dev->ref, dev->ref + 1);
|
||||
atomic_add(&dev->ref, 1);
|
||||
}
|
||||
|
||||
static void bdev_dec_ref(bdev_t *dev)
|
||||
{
|
||||
int oldval = atomic_add(&dev->ref, -1);
|
||||
|
||||
LTRACEF("Dec ref \"%s\" %d -> %d\n", dev->name, oldval, dev->ref);
|
||||
|
||||
if (oldval == 1) {
|
||||
// last ref, remove it
|
||||
DEBUG_ASSERT(!list_in_list(&dev->node));
|
||||
@@ -246,7 +250,6 @@ static void bdev_dec_ref(bdev_t *dev)
|
||||
dev->close(dev);
|
||||
|
||||
free(dev->name);
|
||||
free(dev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,12 +258,12 @@ size_t bio_trim_range(const bdev_t *dev, off_t offset, size_t len)
|
||||
/* range check */
|
||||
if (offset < 0)
|
||||
return 0;
|
||||
if (offset >= dev->size)
|
||||
if (offset >= dev->total_size)
|
||||
return 0;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
if (offset + len > dev->size)
|
||||
len = dev->size - offset;
|
||||
if (offset + len > dev->total_size)
|
||||
len = dev->total_size - offset;
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -281,6 +284,8 @@ bdev_t *bio_open(const char *name)
|
||||
{
|
||||
bdev_t *bdev = NULL;
|
||||
|
||||
LTRACEF(" '%s'\n", name);
|
||||
|
||||
/* see if it's in our list */
|
||||
bdev_t *entry;
|
||||
mutex_acquire(&bdevs->lock);
|
||||
@@ -300,7 +305,7 @@ bdev_t *bio_open(const char *name)
|
||||
void bio_close(bdev_t *dev)
|
||||
{
|
||||
DEBUG_ASSERT(dev);
|
||||
|
||||
LTRACEF(" '%s'\n", dev->name);
|
||||
bdev_dec_ref(dev);
|
||||
}
|
||||
|
||||
@@ -385,20 +390,70 @@ int bio_ioctl(bdev_t *dev, int request, void *argp)
|
||||
}
|
||||
}
|
||||
|
||||
void bio_initialize_bdev(bdev_t *dev, const char *name, size_t block_size, bnum_t block_count)
|
||||
void bio_initialize_bdev(bdev_t *dev,
|
||||
const char *name,
|
||||
size_t block_size,
|
||||
bnum_t block_count,
|
||||
size_t geometry_count,
|
||||
const bio_erase_geometry_info_t* geometry)
|
||||
{
|
||||
DEBUG_ASSERT(dev);
|
||||
DEBUG_ASSERT(name);
|
||||
DEBUG_ASSERT(ispow2(block_size));
|
||||
|
||||
// Block size must be finite powers of 2
|
||||
DEBUG_ASSERT(block_size && ispow2(block_size));
|
||||
|
||||
list_clear_node(&dev->node);
|
||||
dev->name = strdup(name);
|
||||
dev->block_size = block_size;
|
||||
dev->block_shift = log2_uint(block_size);
|
||||
dev->block_count = block_count;
|
||||
dev->size = (off_t)block_count * block_size;
|
||||
dev->block_shift = log2_uint(block_size);
|
||||
dev->total_size = (off_t)block_count << dev->block_shift;
|
||||
dev->geometry_count = geometry_count;
|
||||
dev->geometry = geometry;
|
||||
dev->ref = 0;
|
||||
|
||||
#if DEBUG
|
||||
// If we have been supplied information about our erase geometry, sanity
|
||||
// check it in debug bulids.
|
||||
if (geometry_count && geometry) {
|
||||
for (size_t i = 0; i < geometry_count; ++i) {
|
||||
bio_erase_geometry_info_t* info = geometry + i;
|
||||
|
||||
// Erase sizes must be powers of two and agree with the supplied erase shift.
|
||||
DEBUG_ASSERT(info->erase_size);
|
||||
DEBUG_ASSERT(info->erase_size == ((size_t)1 << info->erase_shift));
|
||||
|
||||
info->start = desc->start;
|
||||
info->erase_size = desc->erase_size;
|
||||
info->erase_shift = log2_uint(desc->erase_size);
|
||||
info->size = ((off_t)desc->block_count) << desc->block_size;
|
||||
|
||||
// Make sure that region is aligned on both a program and erase block boundary.
|
||||
DEBUG_ASSERT(!(info->start & (((off_t)1 << info->block_shift) - 1)));
|
||||
DEBUG_ASSERT(!(info->start & (((off_t)1 << info->erase_shift) - 1)));
|
||||
|
||||
// Make sure that region's length is an integral multiple of both the
|
||||
// program and erase block size.
|
||||
DEBUG_ASSERT(!(info->size & (((off_t)1 << dev->block_shift) - 1)));
|
||||
DEBUG_ASSERT(!(info->size & (((off_t)1 << info->erase_shift) - 1)));
|
||||
}
|
||||
|
||||
// Make sure that none of the regions overlap each other and that they are
|
||||
// listed in ascending order.
|
||||
for (size_t i = 0; (i + 1) < geometry_count; ++i) {
|
||||
bio_geometry_info_t* r1 = dev->geometry + i;
|
||||
bio_geometry_info_t* r2 = dev->geometry + i + 1;
|
||||
DEBUG_ASSERT(r1->start <= r2->start);
|
||||
|
||||
for (size_t j = (i + 1); j < geometry_count; ++j) {
|
||||
bio_geometry_info_t* r2 = dev->geometry + j;
|
||||
DEBUG_ASSERT(!bio_does_overlap(r1->start, r1->size, r2->start, r2->size));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set up the default hooks, the sub driver should override the block operations at least */
|
||||
dev->read = bio_default_read;
|
||||
dev->read_block = bio_default_read_block;
|
||||
@@ -417,7 +472,7 @@ void bio_register_device(bdev_t *dev)
|
||||
bdev_inc_ref(dev);
|
||||
|
||||
mutex_acquire(&bdevs->lock);
|
||||
list_add_head(&bdevs->list, &dev->node);
|
||||
list_add_tail(&bdevs->list, &dev->node);
|
||||
mutex_release(&bdevs->lock);
|
||||
}
|
||||
|
||||
@@ -441,7 +496,22 @@ void bio_dump_devices(void)
|
||||
bdev_t *entry;
|
||||
mutex_acquire(&bdevs->lock);
|
||||
list_for_every_entry(&bdevs->list, entry, bdev_t, node) {
|
||||
printf("\t%s, size %lld, bsize %zd, ref %d\n", entry->name, entry->size, entry->block_size, entry->ref);
|
||||
|
||||
printf("\t%s, size %lld, bsize %zd, ref %d",
|
||||
entry->name, entry->total_size, entry->block_size, entry->ref);
|
||||
|
||||
if (!entry->geometry_count || !entry->geometry) {
|
||||
printf(" (no erase geometry)\n");
|
||||
} else {
|
||||
for (size_t i = 0; i < entry->geometry_count; ++i) {
|
||||
const bio_erase_geometry_info_t* geo = entry->geometry + i;
|
||||
printf("\n\t\terase_region[%zu] : start %lld size %lld erase size %zu",
|
||||
i, geo->start, geo->size, geo->erase_size);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
mutex_release(&bdevs->lock);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ int create_membdev(const char *name, void *ptr, size_t len)
|
||||
mem_bdev_t *mem = malloc(sizeof(mem_bdev_t));
|
||||
|
||||
/* set up the base device */
|
||||
bio_initialize_bdev(&mem->dev, name, BLOCKSIZE, len / BLOCKSIZE);
|
||||
bio_initialize_bdev(&mem->dev, name, BLOCKSIZE, len / BLOCKSIZE, 0, NULL);
|
||||
|
||||
/* our bits */
|
||||
mem->ptr = ptr;
|
||||
|
||||
109
lib/bio/subdev.c
109
lib/bio/subdev.c
@@ -21,6 +21,7 @@
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <debug.h>
|
||||
#include <err.h>
|
||||
#include <trace.h>
|
||||
#include <stdlib.h>
|
||||
#include <lib/bio.h>
|
||||
@@ -34,6 +35,10 @@ typedef struct {
|
||||
// we're a subdevice of this
|
||||
bdev_t *parent;
|
||||
|
||||
// Storage for our erase geometry info. Subdevices are only permitted to
|
||||
// have homogeneous erase geometry.
|
||||
bio_erase_geometry_info_t geometry;
|
||||
|
||||
// we're this many blocks into it
|
||||
bnum_t offset;
|
||||
} subdev_t;
|
||||
@@ -81,20 +86,93 @@ static void subdev_close(struct bdev *_dev)
|
||||
subdev->parent = NULL;
|
||||
}
|
||||
|
||||
status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_t startblock, bnum_t block_count)
|
||||
#define BAIL(__err) do { err = __err; goto bailout; } while (0)
|
||||
status_t bio_publish_subdevice(const char *parent_dev,
|
||||
const char *subdev,
|
||||
bnum_t startblock,
|
||||
bnum_t block_count)
|
||||
{
|
||||
LTRACEF("parent %s, sub %s, startblock %u, count %u\n", parent_dev, subdev, startblock, block_count);
|
||||
status_t err = NO_ERROR;
|
||||
bdev_t *parent = NULL;
|
||||
subdev_t *sub = NULL;
|
||||
size_t geometry_count;
|
||||
bio_erase_geometry_info_t* geometry;
|
||||
|
||||
bdev_t *parent = bio_open(parent_dev);
|
||||
if (!parent)
|
||||
return -1;
|
||||
LTRACEF("parent \"%s\", sub \"%s\", startblock %u, count %u\n", parent_dev, subdev, startblock, block_count);
|
||||
|
||||
/* make sure we're able to do this */
|
||||
if (startblock + block_count > parent->block_count)
|
||||
return -1;
|
||||
// Make sure our parent exists
|
||||
parent = bio_open(parent_dev);
|
||||
if (!parent) {
|
||||
LTRACEF("Failed to find parent \"%s\"\n", parent_dev);
|
||||
BAIL(ERR_NOT_FOUND);
|
||||
}
|
||||
|
||||
subdev_t *sub = malloc(sizeof(subdev_t));
|
||||
bio_initialize_bdev(&sub->dev, subdev, parent->block_size, block_count);
|
||||
// Allocate our sub-device.
|
||||
sub = malloc(sizeof(subdev_t));
|
||||
if (!sub) {
|
||||
LTRACEF("Failed to allocate subdevice\n");
|
||||
BAIL(ERR_NO_MEMORY);
|
||||
}
|
||||
|
||||
/* Make sure we're able to do this. If the device has a specified erase
|
||||
* geometry, the specified sub-region must exist entirely with one of our
|
||||
* parent's erase regions, and be aligned to an erase unit boundary.
|
||||
* Otherwise, the specified region must simply exist within the block range
|
||||
* of the device.
|
||||
*/
|
||||
if (parent->geometry_count && parent->geometry) {
|
||||
uint64_t byte_start = ((uint64_t)startblock << parent->block_shift);
|
||||
uint64_t byte_size = ((uint64_t)block_count << parent->block_shift);
|
||||
const bio_erase_geometry_info_t* geo = NULL;
|
||||
|
||||
LTRACEF("Searching geometry for region which contains @[0x%llx, 0x%llx)\n",
|
||||
byte_start, byte_start + byte_size);
|
||||
|
||||
// Start by finding the erase region which completely contains the requested range.
|
||||
for (size_t i = 0; i < parent->geometry_count; ++i) {
|
||||
geo = parent->geometry + i;
|
||||
|
||||
LTRACEF("Checking geometry @[0x%llx, 0x%llx) erase size 0x%zx\n",
|
||||
geo->start,
|
||||
geo->start + geo->size,
|
||||
geo->erase_size);
|
||||
|
||||
if (bio_contains_range(geo->start, geo->size, byte_start, byte_size))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!geo) {
|
||||
LTRACEF("No suitable erase region found\n");
|
||||
BAIL(ERR_INVALID_ARGS);
|
||||
}
|
||||
|
||||
// Now check out alignment.
|
||||
uint64_t erase_mask = ((uint64_t)0x1 << geo->erase_shift) - 1;
|
||||
if ((byte_start & erase_mask) || (byte_size & erase_mask)) {
|
||||
LTRACEF("Requested region has improper alignment/length\n");
|
||||
BAIL(ERR_INVALID_ARGS);
|
||||
}
|
||||
|
||||
geometry_count = 1;
|
||||
geometry = &sub->geometry;
|
||||
|
||||
geometry->start = 0;
|
||||
geometry->size = byte_size;
|
||||
geometry->erase_size = geo->erase_size;
|
||||
geometry->erase_shift = geo->erase_shift;
|
||||
} else {
|
||||
bnum_t endblock = startblock + block_count;
|
||||
|
||||
if ((endblock < startblock) || (endblock > parent->block_count))
|
||||
BAIL(ERR_INVALID_ARGS);
|
||||
|
||||
geometry_count = 0;
|
||||
geometry = NULL;
|
||||
}
|
||||
|
||||
bio_initialize_bdev(&sub->dev, subdev,
|
||||
parent->block_size, block_count,
|
||||
geometry_count, geometry);
|
||||
|
||||
sub->parent = parent;
|
||||
sub->offset = startblock;
|
||||
@@ -108,6 +186,13 @@ status_t bio_publish_subdevice(const char *parent_dev, const char *subdev, bnum_
|
||||
|
||||
bio_register_device(&sub->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
bailout:
|
||||
if (err < 0) {
|
||||
if (NULL != parent)
|
||||
bio_close(parent);
|
||||
free(sub);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#undef BAIL
|
||||
|
||||
@@ -72,7 +72,7 @@ static status_t validate_entry(const struct ptable_entry *entry)
|
||||
{
|
||||
if (entry->offset > entry->offset + entry->length)
|
||||
return ERR_GENERIC;
|
||||
if (entry->offset + entry->length > (uint64_t)ptable.bdev->size)
|
||||
if (entry->offset + entry->length > (uint64_t)ptable.bdev->total_size)
|
||||
return ERR_GENERIC;
|
||||
|
||||
bool nullterm = false;
|
||||
@@ -371,26 +371,6 @@ status_t ptable_remove(const char *name)
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool does_overlap(uint64_t offset1, uint64_t len1, uint64_t offset2, uint64_t len2)
|
||||
{
|
||||
size_t end1 = offset1 + len1;
|
||||
size_t end2 = offset2 + len2;
|
||||
|
||||
DEBUG_ASSERT(end1 >= offset1);
|
||||
DEBUG_ASSERT(end2 >= offset2);
|
||||
|
||||
if (offset1 >= offset2 && offset1 < end2) {
|
||||
return true;
|
||||
}
|
||||
if (end1 > offset2 && end1 <= end2) {
|
||||
return true;
|
||||
}
|
||||
if (offset1 < offset2 && end1 > end2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
status_t ptable_add(const char *name, uint64_t offset, uint64_t len, uint32_t flags)
|
||||
{
|
||||
status_t err;
|
||||
@@ -422,7 +402,7 @@ status_t ptable_add(const char *name, uint64_t offset, uint64_t len, uint32_t fl
|
||||
}
|
||||
|
||||
/* make sure its within the bounds of the device */
|
||||
if (offset + len > (uint64_t)ptable.bdev->size) {
|
||||
if (offset + len > (uint64_t)ptable.bdev->total_size) {
|
||||
LTRACEF("outside of device\n");
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
@@ -436,7 +416,7 @@ status_t ptable_add(const char *name, uint64_t offset, uint64_t len, uint32_t fl
|
||||
const struct ptable_entry *entry = &mentry->entry;
|
||||
|
||||
/* check to see if we overlap */
|
||||
if (does_overlap(offset, len, entry->offset, entry->length)) {
|
||||
if (bio_does_overlap(offset, len, entry->offset, entry->length)) {
|
||||
LTRACEF("overlaps with existing partition\n");
|
||||
return ERR_INVALID_ARGS;
|
||||
}
|
||||
@@ -498,8 +478,8 @@ off_t ptable_allocate(uint64_t length, uint flags)
|
||||
if (list_is_empty(&ptable.list)) {
|
||||
/* if the ptable is empty, see if we can simply fit in flash and alloc at the start or end */
|
||||
LTRACEF("empty list\n");
|
||||
if (length <= (uint64_t)ptable.bdev->size) {
|
||||
offset = ALLOC_END ? ptable.bdev->size - length : 0;
|
||||
if (length <= (uint64_t)ptable.bdev->total_size) {
|
||||
offset = ALLOC_END ? ptable.bdev->total_size - length : 0;
|
||||
LTRACEF("spot at 0x%llx\n", offset);
|
||||
}
|
||||
} else {
|
||||
@@ -533,8 +513,10 @@ off_t ptable_allocate(uint64_t length, uint flags)
|
||||
/* see if we can fit off the end */
|
||||
DEBUG_ASSERT(lastentry); /* should always have a valid tail */
|
||||
|
||||
if (lastentry->offset + lastentry->length + length <= (uint64_t)ptable.bdev->size)
|
||||
offset = ALLOC_END ? (uint64_t)ptable.bdev->size - length : lastentry->offset + lastentry->length;
|
||||
if (lastentry->offset + lastentry->length + length <= (uint64_t)ptable.bdev->total_size)
|
||||
offset = ALLOC_END
|
||||
? (uint64_t)ptable.bdev->total_size - length
|
||||
: lastentry->offset + lastentry->length;
|
||||
}
|
||||
|
||||
#undef ALLOC_END
|
||||
@@ -559,7 +541,7 @@ off_t ptable_allocate_at(off_t _offset, uint64_t length)
|
||||
|
||||
length = ROUNDUP(length, ptable.bdev->block_size);
|
||||
|
||||
if (offset + length < offset || offset + length > (uint64_t)ptable.bdev->size)
|
||||
if (offset + length < offset || offset + length > (uint64_t)ptable.bdev->total_size)
|
||||
return ERR_INVALID_ARGS;
|
||||
|
||||
/* check all ptable entries for overlap with the requested spot */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -78,7 +78,7 @@ void platform_init_blkdev(void)
|
||||
if (get_blkdev_len() == 0)
|
||||
return;
|
||||
|
||||
bio_initialize_bdev(&dev, "block0", 512, get_blkdev_len() / 512);
|
||||
bio_initialize_bdev(&dev, "block0", 512, get_blkdev_len() / 512, 0, NULL);
|
||||
|
||||
// fill in hooks
|
||||
dev.read_block = &read_block;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <string.h>
|
||||
#include <rand.h>
|
||||
#include <reg.h>
|
||||
#include <pow2.h>
|
||||
|
||||
#include <lib/bio.h>
|
||||
#include <lib/console.h>
|
||||
@@ -52,11 +53,14 @@
|
||||
#define STS_ERASE_ERR (1<<5)
|
||||
#define STS_BUSY (1<<0)
|
||||
|
||||
#define MAX_GEOMETRY_COUNT (2)
|
||||
|
||||
struct spi_flash {
|
||||
bool detected;
|
||||
|
||||
struct qspi_ctxt qspi;
|
||||
bdev_t bdev;
|
||||
bio_erase_geometry_info_t geometry[MAX_GEOMETRY_COUNT];
|
||||
|
||||
off_t size;
|
||||
};
|
||||
@@ -250,6 +254,41 @@ status_t spiflash_detect(void)
|
||||
goto nodetect;
|
||||
}
|
||||
|
||||
/* Fill out our geometry info based on the CFI */
|
||||
size_t region_count = buf[0x2C];
|
||||
if (region_count > countof(flash.geometry)) {
|
||||
TRACEF("erase region count (%zu) exceeds max allowed (%zu)\n",
|
||||
region_count, countof(flash.geometry));
|
||||
goto nodetect;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < region_count; i++) {
|
||||
const uint8_t* info = buf + 0x2D + (i << 2);
|
||||
size_t pages = ((((size_t)info[1]) << 8) | info[0]) + 1;
|
||||
size_t erase_size = ((((size_t)info[3]) << 8) | info[2]) << 8;
|
||||
|
||||
if (!ispow2(erase_size)) {
|
||||
TRACEF("Region %zu page size (%zu) is not a power of 2\n",
|
||||
i, erase_size);
|
||||
goto nodetect;
|
||||
}
|
||||
|
||||
flash.geometry[i].erase_size = erase_size;
|
||||
flash.geometry[i].erase_shift = log2_uint(erase_size);
|
||||
flash.geometry[i].start = offset;
|
||||
flash.geometry[i].size = pages << flash.geometry[i].erase_shift;
|
||||
|
||||
size_t erase_mask = ((size_t)0x1 << flash.geometry[i].erase_shift) - 1;
|
||||
if (offset & erase_mask) {
|
||||
TRACEF("Region %zu not aligned to erase boundary (start %zu, erase size %zu)\n",
|
||||
i, offset, erase_size);
|
||||
goto nodetect;
|
||||
}
|
||||
|
||||
offset += flash.geometry[i].size;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
/* read the 16 byte random number out of the OTP area and add to the rand entropy pool */
|
||||
@@ -270,7 +309,9 @@ status_t spiflash_detect(void)
|
||||
}
|
||||
|
||||
/* construct the block device */
|
||||
bio_initialize_bdev(&flash.bdev, "spi0", PAGE_PROGRAM_SIZE, flash.size / PAGE_PROGRAM_SIZE);
|
||||
bio_initialize_bdev(&flash.bdev, "spi0",
|
||||
PAGE_PROGRAM_SIZE, flash.size / PAGE_PROGRAM_SIZE,
|
||||
region_count, flash.geometry);
|
||||
|
||||
/* override our block device hooks */
|
||||
flash.bdev.read = &spiflash_bdev_read;
|
||||
|
||||
Reference in New Issue
Block a user