mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
348 lines
8.1 KiB
C
348 lines
8.1 KiB
C
/*
|
|
* This is driver implementation for the RTL837x flash controller
|
|
* This code is in the Public Domain
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include "rtl837x_common.h"
|
|
#include "rtl837x_sfr.h"
|
|
|
|
__xdata uint8_t dio_enabled;
|
|
__xdata struct flash_region_t flash_region;
|
|
|
|
__xdata uint32_t flash_size;
|
|
__xdata uint8_t flash_capacity_code;
|
|
|
|
// For the flash commands, see e.g. Windbond W25Q32JV datasheet
|
|
#define CMD_WRITE_STATUS 0x01
|
|
#define CMD_PAGE_PROGRAM 0x02
|
|
// Don't use command `READ 0x03`, because on many device this command can't run at maximum SPI-clock speed.
|
|
// Use `Fast READ 0x0b` instead!
|
|
//#define CMD_READ 0x03
|
|
#define CMD_READ_STATUS 0x05
|
|
#define CMD_WRITE_ENABLE 0x06
|
|
#define CMD_FREAD 0x0b
|
|
#define CMD_SECTOR_ERASE 0x20
|
|
#define CMD_READ_SECURITY_REGS 0x48
|
|
#define CMD_READ_UNIQUE_ID 0x4b
|
|
#define CMD_READ_JEDEC_ID 0x9f
|
|
#define CMD_FREAD_DIO 0xbb
|
|
|
|
/*
|
|
* Configure Memory Managed IO
|
|
*/
|
|
void flash_configure_mmio(void)
|
|
{
|
|
// Set configuration for MMIO access by controller
|
|
if (dio_enabled) {
|
|
SFR_FLASH_MODEB = 0x18;
|
|
SFR_FLASH_CMD_R = CMD_FREAD_DIO; // By default we read with Dual speed
|
|
SFR_FLASH_DUMMYCYCLES = 4;
|
|
return;
|
|
}
|
|
|
|
SFR_FLASH_MODEB = 0x0;
|
|
SFR_FLASH_CMD_R = CMD_FREAD; // Default is Single IO
|
|
SFR_FLASH_DUMMYCYCLES = 8;
|
|
}
|
|
|
|
|
|
/*
|
|
* Initializes the flash controller for programmed control
|
|
* The configuration options are not really understood, the SPI speed
|
|
* seems to be directly linked to the CPU frequency
|
|
* This configures fast single IO at 20.8 MHz when the CPU clock is at 20.8MHz
|
|
* and 62.5MHz when the CPU clock is configured at 125MHz
|
|
*/
|
|
void flash_init(uint8_t enable_dio)
|
|
{
|
|
if (enable_dio) {
|
|
SFR_FLASH_CONFIG = 9; // There may be a chip-select in here
|
|
SFR_FLASH_CONF_RCMD = CMD_FREAD_DIO;
|
|
SFR_FLASH_CONF_DIV = 4;
|
|
} else {
|
|
// Configure fast read via divider = 8 and read-cmd being CMD_FREAD (for mmio)
|
|
SFR_FLASH_CONFIG = 9;
|
|
SFR_FLASH_CONF_RCMD = CMD_FREAD;
|
|
SFR_FLASH_CONF_DIV = 8;
|
|
}
|
|
// Test Controller Busy
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
// Write 0 to status register
|
|
SFR_FLASH_DUMMYCYCLES = 8;
|
|
SFR_FLASH_MODEB = 0;
|
|
SFR_FLASH_TCONF = 0x19;
|
|
SFR_FLASH_CMD = CMD_WRITE_STATUS;
|
|
SFR_FLASH_DATA0 = 0;
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
dio_enabled = enable_dio;
|
|
flash_configure_mmio();
|
|
}
|
|
|
|
|
|
uint8_t flash_read_status(void)
|
|
{
|
|
// Test Controller Busy (we might call this directly after executing a command)
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
// setup status read command
|
|
SFR_FLASH_TCONF = 0x11;
|
|
SFR_FLASH_CMD_R = CMD_READ_STATUS;
|
|
|
|
// execute and wait for controller done
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
return SFR_FLASH_DATA0;
|
|
}
|
|
|
|
|
|
void flash_read_uid(void)
|
|
{
|
|
while (flash_read_status() & 0x1);
|
|
|
|
// Set slow read mode for UID
|
|
SFR_FLASH_MODEB = 0x0;
|
|
SFR_FLASH_CMD_R = CMD_READ_UNIQUE_ID;
|
|
SFR_FLASH_DUMMYCYCLES = 8;
|
|
|
|
// Transfer 4 bytes (command + 3 dummy bytes)
|
|
SFR_FLASH_TCONF = 4;
|
|
SFR_FLASH_ADDR16 = 0;
|
|
SFR_FLASH_ADDR8 = 0;
|
|
SFR_FLASH_ADDR0 = 0;
|
|
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
print_byte(SFR_FLASH_DATA0);
|
|
print_byte(SFR_FLASH_DATA8);
|
|
print_byte(SFR_FLASH_DATA16);
|
|
print_byte(SFR_FLASH_DATA24);
|
|
write_char(' ');
|
|
|
|
SFR_FLASH_DUMMYCYCLES = 24; // Doesn't seem to work; we get the same data as for the first transfer
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
print_byte(SFR_FLASH_DATA0);
|
|
print_byte(SFR_FLASH_DATA8);
|
|
print_byte(SFR_FLASH_DATA16);
|
|
print_byte(SFR_FLASH_DATA24);
|
|
|
|
flash_configure_mmio();
|
|
}
|
|
|
|
__code char* get_flash_size_str(void)
|
|
{
|
|
switch (flash_capacity_code) {
|
|
case 0x12: return "256 KB";
|
|
case 0x13: return "512 KB";
|
|
case 0x14: return "1 MB";
|
|
case 0x15: return "2 MB";
|
|
case 0x16: return "4 MB";
|
|
case 0x17: return "8 MB";
|
|
case 0x18: return "16 MB";
|
|
default: return "unknown";
|
|
}
|
|
}
|
|
|
|
void flash_read_jedecid(void)
|
|
{
|
|
while (flash_read_status() & 0x1);
|
|
|
|
// Set read mode for JEDEC ID
|
|
SFR_FLASH_MODEB = 0x0;
|
|
SFR_FLASH_CMD_R = CMD_READ_JEDEC_ID;
|
|
SFR_FLASH_DUMMYCYCLES = 0;
|
|
|
|
// Transfer 3 bytes back
|
|
SFR_FLASH_TCONF = 0x13;
|
|
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
print_string("Flash information:\n");
|
|
print_string(" Manufacturer ID: 0x");
|
|
print_byte(SFR_FLASH_DATA0);
|
|
print_string("\n Memory Type: 0x");
|
|
print_byte(SFR_FLASH_DATA8);
|
|
print_string("\n Capacity: 0x");
|
|
flash_capacity_code = SFR_FLASH_DATA16;
|
|
flash_size = 1UL << flash_capacity_code;
|
|
print_byte(flash_capacity_code);
|
|
print_string(" = "); print_string(get_flash_size_str()); write_char('\n');
|
|
|
|
flash_configure_mmio();
|
|
}
|
|
|
|
|
|
void flash_write_enable(void)
|
|
{
|
|
short status;
|
|
|
|
// Wait until busy bit clear
|
|
do {
|
|
status = flash_read_status();
|
|
} while (status & 0x1);
|
|
|
|
SFR_FLASH_TCONF = 0x18;
|
|
SFR_FLASH_CMD = CMD_WRITE_ENABLE;
|
|
|
|
/* The following makes sure that the PAGE_PROGRAM command,
|
|
* where the data to be written follows the command word directly
|
|
* works properly
|
|
*/
|
|
SFR_FLASH_DUMMYCYCLES = 0;
|
|
SFR_FLASH_MODEB = 0;
|
|
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
// Wait for write status enabled
|
|
do {
|
|
status = flash_read_status();
|
|
} while (!(status & 0x2));
|
|
}
|
|
|
|
/*
|
|
* Reads bulk data of length len from the flash memory starging at address src
|
|
* and writes the data into a buffer pointed to by dst in XMEM
|
|
*/
|
|
void flash_read_bulk(__xdata uint8_t *dst)
|
|
{
|
|
short status;
|
|
do {
|
|
status = flash_read_status();
|
|
} while (status & 0x1);
|
|
|
|
// Set fast read mode
|
|
if (dio_enabled) {
|
|
SFR_FLASH_MODEB = 0x18;
|
|
SFR_FLASH_CMD_R = CMD_FREAD_DIO;
|
|
SFR_FLASH_DUMMYCYCLES = 4;
|
|
} else {
|
|
SFR_FLASH_MODEB = 0x0;
|
|
SFR_FLASH_CMD_R = CMD_FREAD; // Fast read
|
|
SFR_FLASH_DUMMYCYCLES = 8; // Add 8 dummy clocks
|
|
}
|
|
|
|
|
|
// Read 4 bytes
|
|
while (1) {
|
|
SFR_FLASH_ADDR16 = flash_region.addr >> 16;
|
|
SFR_FLASH_ADDR8 = flash_region.addr >> 8;
|
|
SFR_FLASH_ADDR0 = flash_region.addr;
|
|
flash_region.addr += 4;
|
|
|
|
SFR_FLASH_TCONF = 4;
|
|
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
*dst++ = SFR_FLASH_DATA0;
|
|
if (flash_region.len == 1)
|
|
break;
|
|
*dst++ = SFR_FLASH_DATA8;
|
|
if (flash_region.len == 2)
|
|
break;
|
|
*dst++ = SFR_FLASH_DATA16;
|
|
if (flash_region.len == 3)
|
|
break;
|
|
*dst++ = SFR_FLASH_DATA24;
|
|
if (flash_region.len == 4)
|
|
break;
|
|
flash_region.len -= 4;
|
|
}
|
|
}
|
|
|
|
|
|
void flash_read_security(void)
|
|
{
|
|
while (flash_read_status() & 0x1);
|
|
|
|
// Set slow read mode
|
|
SFR_FLASH_MODEB = 0x0;
|
|
SFR_FLASH_CMD_R = CMD_READ_SECURITY_REGS; // read security register
|
|
SFR_FLASH_DUMMYCYCLES = 8; // Add 8 dummy clocks as for fast read
|
|
|
|
// Transfer 4 bytes (command + 3byte address)
|
|
SFR_FLASH_TCONF = 4;
|
|
do {
|
|
SFR_FLASH_ADDR16 = flash_region.addr >> 16;
|
|
SFR_FLASH_ADDR8 = flash_region.addr >> 8;
|
|
SFR_FLASH_ADDR0 = flash_region.addr;
|
|
flash_region.addr += 4;
|
|
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while(SFR_FLASH_EXEC_BUSY);
|
|
|
|
print_byte(SFR_FLASH_DATA0);
|
|
if (flash_region.len == 1)
|
|
break;
|
|
print_byte(SFR_FLASH_DATA8);
|
|
if (flash_region.len == 2)
|
|
break;
|
|
print_byte(SFR_FLASH_DATA16);
|
|
if (flash_region.len == 3)
|
|
break;
|
|
print_byte(SFR_FLASH_DATA24);
|
|
write_char(' ');
|
|
flash_region.len -= 4;
|
|
} while(flash_region.len);
|
|
|
|
flash_configure_mmio();
|
|
}
|
|
|
|
|
|
void flash_sector_erase(void)
|
|
{
|
|
flash_write_enable();
|
|
SFR_FLASH_TCONF = 8;
|
|
SFR_FLASH_CMD = CMD_SECTOR_ERASE;
|
|
|
|
SFR_FLASH_ADDR16 = flash_region.addr >> 16;
|
|
SFR_FLASH_ADDR8 = flash_region.addr >> 8;
|
|
SFR_FLASH_ADDR0 = flash_region.addr;
|
|
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
while (flash_read_status() & 0x1);
|
|
|
|
flash_configure_mmio();
|
|
}
|
|
|
|
|
|
void flash_write_bytes(__xdata uint8_t *ptr)
|
|
{
|
|
// write_char('\n'); write_char('>'); print_long(flash_region.addr); write_char(':'); print_short(flash_region.len); write_char('-'); print_byte(*ptr); // write_char('\n');
|
|
while(1) {
|
|
flash_write_enable();
|
|
SFR_FLASH_CMD = CMD_PAGE_PROGRAM;
|
|
SFR_FLASH_TCONF = 0x40 | 8 | 4; // Bytes written is 4, 8 enables write, 0x40 is unknown
|
|
// Last transfer?
|
|
if (flash_region.len < 5) {
|
|
SFR_FLASH_TCONF = 8 | flash_region.len;
|
|
}
|
|
|
|
SFR_FLASH_ADDR16 = flash_region.addr >> 16;
|
|
SFR_FLASH_ADDR8 = flash_region.addr >> 8;
|
|
SFR_FLASH_ADDR0 = flash_region.addr;
|
|
SFR_FLASH_DATA0 = *ptr++;
|
|
SFR_FLASH_DATA8 = *ptr++;
|
|
SFR_FLASH_DATA16 = *ptr++;
|
|
SFR_FLASH_DATA24 = *ptr++;
|
|
|
|
// Execute transfer, we wait for completion at top of loop
|
|
SFR_FLASH_EXEC_GO = 1;
|
|
|
|
if (flash_region.len < 5)
|
|
break;
|
|
|
|
flash_region.len -= 4;
|
|
flash_region.addr += 4;
|
|
};
|
|
while (flash_read_status() & 0x1);
|
|
flash_configure_mmio();
|
|
}
|