1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-16 08:12:53 +08:00
corundum/utils/flash_bpi.c
Alex Forencich 448fa8eb4c Use SPDX
Signed-off-by: Alex Forencich <alex@alexforencich.com>
2023-06-26 11:44:57 -07:00

644 lines
19 KiB
C

// SPDX-License-Identifier: BSD-2-Clause-Views
/*
* Copyright (c) 2020-2023 The Regents of the University of California
*/
#include "flash.h"
#include <stdio.h>
#include <stdlib.h>
#define reg_read32(reg) (*((volatile uint32_t *)(reg)))
#define reg_write32(reg, val) (*((volatile uint32_t *)(reg))) = (val)
#define CFI_QUERY_ADDR 0x55
#define CFI_QUERY_DATA 0x98
#define CFI_READ_ARRAY 0xFF
#define CFI_READ_ARRAY_ALT 0xF0
#define CFI_ID_0 0x10
#define CFI_ID_1 0x11
#define CFI_ID_2 0x12
#define CFI_PRI_CMD_SET_0 0x13
#define CFI_PRI_CMD_SET_1 0x14
#define CFI_DEVICE_SIZE 0x27
#define CFI_WRITE_BUFFER_SIZE_0 0x2A
#define CFI_WRITE_BUFFER_SIZE_1 0x2B
#define CFI_ERASE_REGION_COUNT 0x2C
#define CFI_ERASE_REGION_1_INFO_0 0x2D
#define CFI_ERASE_REGION_1_INFO_1 0x2E
#define CFI_ERASE_REGION_1_INFO_2 0x2F
#define CFI_ERASE_REGION_1_INFO_3 0x30
#define CFI_ERASE_REGION_2_INFO_0 0x31
#define CFI_ERASE_REGION_2_INFO_1 0x32
#define CFI_ERASE_REGION_2_INFO_2 0x33
#define CFI_ERASE_REGION_2_INFO_3 0x34
#define BPI_INTEL_READ_ARRAY 0xFF
#define BPI_INTEL_READ_STATUS_REG 0x70
#define BPI_INTEL_READ_ID 0x90
#define BPI_INTEL_CLEAR_STATUS_REG 0x50
#define BPI_INTEL_READ_CONFIG_REG_SETUP 0x60
#define BPI_INTEL_SET_READ_CONFIG_REG 0x03
#define BPI_INTEL_BLOCK_LOCK_SETUP 0x60
#define BPI_INTEL_BLOCK_LOCK 0x01
#define BPI_INTEL_BLOCK_UNLOCK 0xD0
#define BPI_INTEL_BLOCK_ERASE_SETUP 0x20
#define BPI_INTEL_BLOCK_ERASE_CONFIRM 0xD0
#define BPI_INTEL_BUFFERED_PROGRAM_SETUP 0xE8
#define BPI_INTEL_BUFFERED_PROGRAM_CONFIRM 0xD0
#define BPI_AMD_UNLOCK_ADDR_1 0x555
#define BPI_AMD_UNLOCK_DATA_1 0xAA
#define BPI_AMD_UNLOCK_ADDR_2 0x2AA
#define BPI_AMD_UNLOCK_DATA_2 0x55
#define BPI_AMD_UNLOCK_BYPASS_ENTER_ADDR 0x555
#define BPI_AMD_UNLOCK_BYPASS_ENTER_DATA 0x20
#define BPI_AMD_UNLOCK_BYPASS_RESET_1 0x90
#define BPI_AMD_UNLOCK_BYPASS_RESET_2 0x00
#define BPI_AMD_BLOCK_ERASE_SETUP 0x80
#define BPI_AMD_BLOCK_ERASE_CONFIRM 0x30
#define BPI_AMD_BUFFERED_PROGRAM_SETUP 0x25
#define BPI_AMD_BUFFERED_PROGRAM_CONFIRM 0x29
#define BPI_AMD_READ_ARRAY 0xF0
#define BPI_MICRON_READ_ARRAY 0xFF
#define BPI_MICRON_READ_STATUS_REG 0x70
#define BPI_MICRON_READ_ID 0x90
#define BPI_MICRON_CLEAR_STATUS_REG 0x50
#define BPI_MICRON_READ_CONFIG_REG_SETUP 0x60
#define BPI_MICRON_SET_READ_CONFIG_REG 0x03
#define BPI_MICRON_BLOCK_LOCK_SETUP 0x60
#define BPI_MICRON_BLOCK_LOCK 0x01
#define BPI_MICRON_BLOCK_UNLOCK 0xD0
#define BPI_MICRON_BLOCK_ERASE_SETUP 0x20
#define BPI_MICRON_BLOCK_ERASE_CONFIRM 0xD0
#define BPI_MICRON_BUFFERED_PROGRAM_SETUP 0xE9
#define BPI_MICRON_BUFFERED_PROGRAM_CONFIRM 0xD0
#define FLASH_CE_N (1 << 0)
#define FLASH_OE_N (1 << 1)
#define FLASH_WE_N (1 << 2)
#define FLASH_ADV_N (1 << 3)
#define FLASH_DQ_OE (1 << 8)
#define FLASH_REGION_OE (1 << 16)
void bpi_flash_set_addr(struct flash_device *fdev, size_t addr)
{
reg_write32(fdev->addr_reg, addr);
}
uint16_t bpi_flash_read_cur(struct flash_device *fdev)
{
uint16_t val;
reg_write32(fdev->ctrl_reg, FLASH_REGION_OE | FLASH_WE_N);
reg_read32(fdev->data_reg); // dummy read
val = reg_read32(fdev->data_reg);
reg_write32(fdev->ctrl_reg, FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N);
return val;
}
uint16_t bpi_flash_read_word(struct flash_device *fdev, size_t addr)
{
bpi_flash_set_addr(fdev, addr);
return bpi_flash_read_cur(fdev);
}
void bpi_flash_write_cur(struct flash_device *fdev, uint16_t data)
{
reg_write32(fdev->data_reg, data);
reg_write32(fdev->ctrl_reg, FLASH_REGION_OE | FLASH_DQ_OE | FLASH_OE_N);
reg_read32(fdev->data_reg); // dummy read
reg_write32(fdev->ctrl_reg, FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N);
}
void bpi_flash_write_word(struct flash_device *fdev, size_t addr, uint16_t data)
{
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, data);
}
void bpi_flash_deselect(struct flash_device *fdev)
{
bpi_flash_write_word(fdev, 0, CFI_READ_ARRAY);
reg_write32(fdev->ctrl_reg, FLASH_CE_N | FLASH_OE_N | FLASH_WE_N | FLASH_ADV_N);
}
// Intel flash ops (0x0001)
uint16_t bpi_flash_intel_read_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_INTEL_READ_STATUS_REG);
return bpi_flash_read_cur(fdev);
}
void bpi_flash_intel_clear_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_INTEL_CLEAR_STATUS_REG);
}
void bpi_flash_intel_init(struct flash_device *fdev)
{
bpi_flash_intel_clear_status_register(fdev);
}
int bpi_flash_intel_sector_erase(struct flash_device *fdev, size_t addr)
{
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_LOCK_SETUP);
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_UNLOCK);
if (bpi_flash_intel_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to unlock block\n");
return -1;
}
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_ERASE_SETUP);
bpi_flash_write_cur(fdev, BPI_INTEL_BLOCK_ERASE_CONFIRM);
while (!(bpi_flash_intel_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_intel_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to erase block\n");
return -1;
}
return 0;
}
int bpi_flash_intel_buffered_program(struct flash_device *fdev, size_t addr, size_t len, const void *src)
{
const uint8_t *s = (const uint8_t *)src;
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_INTEL_BUFFERED_PROGRAM_SETUP);
bpi_flash_write_cur(fdev, len-1);
for (size_t i = 0; i < len; i++)
{
bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8));
s += 2;
}
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_INTEL_BUFFERED_PROGRAM_CONFIRM);
while (!(bpi_flash_intel_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_intel_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to write block\n");
return -1;
}
return 0;
}
const struct flash_ops bpi_flash_intel_ops = {
.init = bpi_flash_intel_init,
.sector_erase = bpi_flash_intel_sector_erase,
.buffered_program = bpi_flash_intel_buffered_program
};
// AMD flash ops (0x0002)
void bpi_flash_amd_unlock(struct flash_device *fdev)
{
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_UNLOCK_DATA_1);
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_2, BPI_AMD_UNLOCK_DATA_2);
}
void bpi_flash_amd_write_buffer_abort_reset(struct flash_device *fdev)
{
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_READ_ARRAY);
}
void bpi_flash_amd_init(struct flash_device *fdev)
{
// write-to-buffer-abort reset (just in case)
bpi_flash_amd_write_buffer_abort_reset(fdev);
}
int bpi_flash_amd_wait_for_operation(struct flash_device *fdev, uint16_t stop_mask)
{
uint16_t read_1, read_2, read_3;
while (1)
{
read_1 = bpi_flash_read_cur(fdev);
read_2 = bpi_flash_read_cur(fdev);
read_3 = bpi_flash_read_cur(fdev);
if ((read_1 ^ read_2) & (read_2 ^ read_3) & 0x40)
{
if (read_1 & stop_mask)
{
return read_1;
}
}
else
{
return 0;
}
}
}
int bpi_flash_amd_sector_erase(struct flash_device *fdev, size_t addr)
{
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, BPI_AMD_UNLOCK_ADDR_1, BPI_AMD_BLOCK_ERASE_SETUP);
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, addr, BPI_AMD_BLOCK_ERASE_CONFIRM);
while (!(bpi_flash_read_cur(fdev) & 0x08)) {};
if (bpi_flash_amd_wait_for_operation(fdev, 0x20) & 0x20)
{
// write-to-buffer-abort reset
bpi_flash_amd_write_buffer_abort_reset(fdev);
fprintf(stderr, "Failed to erase block\n");
return -1;
}
return 0;
}
int bpi_flash_amd_buffered_program(struct flash_device *fdev, size_t addr, size_t len, const void *src)
{
const uint8_t *s = (const uint8_t *)src;
bpi_flash_amd_unlock(fdev);
bpi_flash_write_word(fdev, addr, BPI_AMD_BUFFERED_PROGRAM_SETUP);
bpi_flash_write_cur(fdev, len-1);
for (size_t i = 0; i < len; i++)
{
bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8));
s += 2;
}
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_AMD_BUFFERED_PROGRAM_CONFIRM);
if (bpi_flash_amd_wait_for_operation(fdev, 0x22) & 0x22)
{
// write-to-buffer-abort reset
bpi_flash_amd_write_buffer_abort_reset(fdev);
fprintf(stderr, "Failed to write block\n");
return -1;
}
return 0;
}
const struct flash_ops bpi_flash_amd_ops = {
.init = bpi_flash_amd_init,
.sector_erase = bpi_flash_amd_sector_erase,
.buffered_program = bpi_flash_amd_buffered_program
};
// Micron flash ops (0x0002)
uint16_t bpi_flash_micron_read_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_MICRON_READ_STATUS_REG);
return bpi_flash_read_cur(fdev);
}
void bpi_flash_micron_clear_status_register(struct flash_device *fdev)
{
bpi_flash_write_cur(fdev, BPI_MICRON_CLEAR_STATUS_REG);
}
void bpi_flash_micron_init(struct flash_device *fdev)
{
bpi_flash_micron_clear_status_register(fdev);
}
int bpi_flash_micron_sector_erase(struct flash_device *fdev, size_t addr)
{
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_LOCK_SETUP);
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_UNLOCK);
if (bpi_flash_micron_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to unlock block\n");
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return -1;
}
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_ERASE_SETUP);
bpi_flash_write_cur(fdev, BPI_MICRON_BLOCK_ERASE_CONFIRM);
while (!(bpi_flash_micron_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_micron_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to erase block\n");
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return -1;
}
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return 0;
}
int bpi_flash_micron_buffered_program(struct flash_device *fdev, size_t addr, size_t len, const void *src)
{
const uint8_t *s = (const uint8_t *)src;
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_MICRON_BUFFERED_PROGRAM_SETUP);
bpi_flash_write_cur(fdev, len-1);
for (size_t i = 0; i < len; i++)
{
bpi_flash_write_word(fdev, addr+i, s[0] | (s[1] << 8));
s += 2;
}
bpi_flash_set_addr(fdev, addr);
bpi_flash_write_cur(fdev, BPI_MICRON_BUFFERED_PROGRAM_CONFIRM);
while (!(bpi_flash_micron_read_status_register(fdev) & 0x80)) {};
if (bpi_flash_micron_read_status_register(fdev) & 0x30)
{
fprintf(stderr, "Failed to write block\n");
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return -1;
}
bpi_flash_write_cur(fdev, CFI_READ_ARRAY);
return 0;
}
const struct flash_ops bpi_flash_micron_ops = {
.init = bpi_flash_micron_init,
.sector_erase = bpi_flash_micron_sector_erase,
.buffered_program = bpi_flash_micron_buffered_program
};
void bpi_flash_release(struct flash_device *fdev)
{
bpi_flash_deselect(fdev);
}
int bpi_flash_init(struct flash_device *fdev)
{
int ret = 0;
if (!fdev)
return -1;
// CFI query
bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA);
if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q')
{
// may be Intel flash in sync read mode; attempt switch to async
bpi_flash_write_word(fdev, 0xf94f, BPI_INTEL_READ_CONFIG_REG_SETUP);
bpi_flash_write_word(fdev, 0xf94f, BPI_INTEL_SET_READ_CONFIG_REG);
bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA);
}
if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q' && ((bpi_flash_read_cur(fdev) ^ bpi_flash_read_cur(fdev)) & 0x44))
{
// may be AMD flash in write buffer abort; perform write buffer abort reset
bpi_flash_amd_write_buffer_abort_reset(fdev);
bpi_flash_write_word(fdev, CFI_QUERY_ADDR, CFI_QUERY_DATA);
}
if (bpi_flash_read_word(fdev, CFI_ID_0) != 'Q' ||
bpi_flash_read_word(fdev, CFI_ID_1) != 'R' ||
bpi_flash_read_word(fdev, CFI_ID_2) != 'Y')
{
fprintf(stderr, "Failed to read flash ID\n");
ret = -1;
goto err;
}
fdev->protocol = bpi_flash_read_word(fdev, CFI_PRI_CMD_SET_0) | (bpi_flash_read_word(fdev, CFI_PRI_CMD_SET_1) << 8);
printf("Command set: %d\n", fdev->protocol);
switch (fdev->protocol)
{
case 0x0001:
// Intel command set (P30)
fdev->ops = &bpi_flash_intel_ops;
break;
case 0x0002:
// AMD command set (S29, MT28)
fdev->ops = &bpi_flash_amd_ops;
break;
case 0x0200:
// Micron
fdev->ops = &bpi_flash_micron_ops;
break;
default:
fprintf(stderr, "Unknown command set: %d\n", fdev->protocol);
ret = -1;
goto err;
}
uint8_t flash_size = bpi_flash_read_word(fdev, CFI_DEVICE_SIZE);
fdev->size = ((size_t)1) << flash_size;
printf("Flash size: %d MB\n", 1 << (flash_size-20));
uint16_t write_buffer_size = bpi_flash_read_word(fdev, CFI_WRITE_BUFFER_SIZE_0) | (bpi_flash_read_word(fdev, CFI_WRITE_BUFFER_SIZE_1) << 8);
fdev->write_buffer_size = ((size_t)1) << write_buffer_size;
printf("Write buffer size: %ld B\n", fdev->write_buffer_size);
fdev->erase_region_count = bpi_flash_read_word(fdev, CFI_ERASE_REGION_COUNT);
printf("Erase regions: %d\n", fdev->erase_region_count);
if (fdev->erase_region_count > 0)
{
fdev->erase_region[0].block_count = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_0) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_1) << 8)) + 1;
fdev->erase_region[0].block_size = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_2) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_1_INFO_3) << 8)) * 256;
fdev->erase_region[0].region_start = 0;
fdev->erase_region[0].region_end = fdev->erase_region[0].region_start + fdev->erase_region[0].block_count * fdev->erase_region[0].block_size;
fdev->erase_block_size = fdev->erase_region[0].block_size;
printf("Erase region 0 block count: %ld\n", fdev->erase_region[0].block_count);
printf("Erase region 0 block size: %ld B\n", fdev->erase_region[0].block_size);
printf("Erase region 0 start: 0x%08lx\n", fdev->erase_region[0].region_start);
printf("Erase region 0 end: 0x%08lx\n", fdev->erase_region[0].region_end);
}
else
{
fprintf(stderr, "No erase regions found!\n");
ret = -1;
goto err;
}
if (fdev->erase_region_count > 1)
{
fdev->erase_region[1].block_count = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_0) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_1) << 8)) + 1;
fdev->erase_region[1].block_size = (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_2) | (bpi_flash_read_word(fdev, CFI_ERASE_REGION_2_INFO_3) << 8)) * 256;
fdev->erase_region[1].region_start = fdev->erase_region[0].region_end;
fdev->erase_region[1].region_end = fdev->erase_region[1].region_start + fdev->erase_region[1].block_count * fdev->erase_region[1].block_size;
if (fdev->erase_region[1].block_size > fdev->erase_block_size)
{
fdev->erase_block_size = fdev->erase_region[1].block_size;
}
printf("Erase region 1 block count: %ld\n", fdev->erase_region[1].block_count);
printf("Erase region 1 block size: %ld B\n", fdev->erase_region[1].block_size);
printf("Erase region 1 start: 0x%08lx\n", fdev->erase_region[1].region_start);
printf("Erase region 1 end: 0x%08lx\n", fdev->erase_region[1].region_end);
}
printf("Erase block size: %ld B\n", fdev->erase_block_size);
fdev->ops->init(fdev);
err:
bpi_flash_release(fdev);
return ret;
}
int bpi_flash_read(struct flash_device *fdev, size_t addr, size_t len, void *dest)
{
char *d = dest;
bpi_flash_write_word(fdev, 0, CFI_READ_ARRAY);
if (addr & 1)
{
*d = bpi_flash_read_word(fdev, addr >> 1) >> 8;
addr++;
len--;
d++;
}
while (len > 1)
{
*((uint16_t *)d) = bpi_flash_read_word(fdev, addr >> 1);
addr += 2;
len -= 2;
d += 2;
}
if (len)
{
*d = bpi_flash_read_word(fdev, addr >> 1);
addr++;
len--;
d++;
}
bpi_flash_deselect(fdev);
return 0;
}
int bpi_flash_write(struct flash_device *fdev, size_t addr, size_t len, const void *src)
{
const char *s = src;
while (len > 0)
{
size_t seg = len;
// align to buffer size
if (seg > fdev->write_buffer_size - (addr & (fdev->write_buffer_size-1)))
{
seg = fdev->write_buffer_size - (addr & (fdev->write_buffer_size-1));
}
if (fdev->ops->buffered_program(fdev, addr >> 1, seg >> 1, s))
{
fprintf(stderr, "Buffered write failed\n");
bpi_flash_deselect(fdev);
return -1;
}
addr += seg;
len -= seg;
s += seg;
}
bpi_flash_deselect(fdev);
return 0;
}
int bpi_flash_erase(struct flash_device *fdev, size_t addr, size_t len)
{
size_t erase_block_size = fdev->erase_block_size;
while (len > 0)
{
// determine sector size
erase_block_size = 0;
for (int k = 0; k < fdev->erase_region_count; k++)
{
if (addr >= fdev->erase_region[k].region_start && addr < fdev->erase_region[k].region_end)
{
erase_block_size = fdev->erase_region[k].block_size;
break;
}
}
if (!erase_block_size)
{
fprintf(stderr, "Address does not match an erase region\n");
bpi_flash_deselect(fdev);
return -1;
}
// check size and alignment
if (addr & (erase_block_size-1) || len < erase_block_size)
{
fprintf(stderr, "Invalid erase request\n");
bpi_flash_deselect(fdev);
return -1;
}
// block erase
if (fdev->ops->sector_erase(fdev, addr >> 1))
{
fprintf(stderr, "Failed to erase sector\n");
bpi_flash_deselect(fdev);
return -1;
}
if (len <= erase_block_size)
break;
addr += erase_block_size;
len -= erase_block_size;
}
bpi_flash_deselect(fdev);
return 0;
}
const struct flash_driver bpi_flash_driver = {
.init = bpi_flash_init,
.release = bpi_flash_release,
.read = bpi_flash_read,
.write = bpi_flash_write,
.erase = bpi_flash_erase
};