forked from Miroirs/x49gp
18e10037dd
modified: include/x49gp_ui.h modified: s3c2410_lcd.c modified: ui.c Patches from 3298: http://www.hpmuseum.org/forum/thread-2352-post-20831.html#pid20831
639 lines
14 KiB
C
639 lines
14 KiB
C
/* $Id: flash.c,v 1.18 2008/12/11 12:18:17 ecd Exp $
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <errno.h>
|
|
|
|
#include <x49gp.h>
|
|
#include <memory.h>
|
|
#include <byteorder.h>
|
|
|
|
#define FLASH_STATE_NORMAL 0
|
|
|
|
#define FLASH_STATE_UNLOCK1 1
|
|
#define FLASH_STATE_UNLOCK2 2
|
|
|
|
#define FLASH_STATE_ERASE1 3
|
|
#define FLASH_STATE_ERASE2 4
|
|
#define FLASH_STATE_ERASE3 5
|
|
|
|
#define FLASH_STATE_SOFTWARE_EXIT1 6
|
|
#define FLASH_STATE_SOFTWARE_EXIT2 7
|
|
#define FLASH_STATE_CFI_QUERY_EXIT1 8
|
|
#define FLASH_STATE_CFI_QUERY_EXIT2 9
|
|
|
|
#define FLASH_STATE_SOFTWARE_ID 10
|
|
#define FLASH_STATE_CFI_QUERY 11
|
|
#define FLASH_STATE_WORD_PROG 12
|
|
|
|
typedef struct {
|
|
void *data;
|
|
int state;
|
|
unsigned short vendor_ID;
|
|
unsigned short device_ID;
|
|
const unsigned short *cfi_data;
|
|
uint32_t cfi_size;
|
|
uint32_t sector_size;
|
|
uint32_t block_size;
|
|
int fd;
|
|
size_t size;
|
|
|
|
uint32_t iotype;
|
|
uint32_t offset;
|
|
} x49gp_flash_t;
|
|
|
|
#define SST29VF160_VENDOR_ID 0x00bf
|
|
#define SST29VF160_DEVICE_ID 0x2782
|
|
|
|
#define SST29VF160_SECTOR_SIZE 0x00001000
|
|
#define SST29VF160_BLOCK_SIZE 0x00010000
|
|
#define SST29VF160_SIZE 0x00200000
|
|
|
|
static const unsigned short sst29vf160_cfi_data[] =
|
|
{
|
|
[0x10] = 0x0051,
|
|
[0x11] = 0x0052,
|
|
[0x12] = 0x0059,
|
|
[0x13] = 0x0001,
|
|
[0x14] = 0x0007,
|
|
[0x15] = 0x0000,
|
|
[0x16] = 0x0000,
|
|
[0x17] = 0x0000,
|
|
[0x18] = 0x0000,
|
|
[0x19] = 0x0000,
|
|
[0x1a] = 0x0000,
|
|
|
|
[0x1b] = 0x0027,
|
|
[0x1c] = 0x0036,
|
|
[0x1d] = 0x0000,
|
|
[0x1e] = 0x0000,
|
|
[0x1f] = 0x0004,
|
|
[0x20] = 0x0000,
|
|
[0x21] = 0x0004,
|
|
[0x22] = 0x0006,
|
|
[0x23] = 0x0001,
|
|
[0x24] = 0x0000,
|
|
[0x25] = 0x0001,
|
|
[0x26] = 0x0001,
|
|
|
|
[0x27] = 0x0015,
|
|
[0x28] = 0x0001,
|
|
[0x29] = 0x0000,
|
|
[0x2a] = 0x0000,
|
|
[0x2b] = 0x0000,
|
|
[0x2c] = 0x0002,
|
|
[0x2d] = 0x00ff,
|
|
[0x2e] = 0x0001,
|
|
[0x2f] = 0x0010,
|
|
[0x30] = 0x0000,
|
|
[0x31] = 0x003f,
|
|
[0x32] = 0x0000,
|
|
[0x33] = 0x0000,
|
|
[0x34] = 0x0001
|
|
};
|
|
#define SST29VF160_CFI_SIZE (sizeof(sst29vf160_cfi_data) / sizeof(sst29vf160_cfi_data[0]))
|
|
|
|
static void
|
|
flash_state_reset(x49gp_flash_t *flash)
|
|
{
|
|
if (flash->state != FLASH_STATE_NORMAL) {
|
|
cpu_register_physical_memory(0x00000000, SST29VF160_SIZE,
|
|
flash->offset | flash->iotype | IO_MEM_ROMD);
|
|
flash->state = FLASH_STATE_NORMAL;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
flash_get_halfword(x49gp_flash_t *flash, uint32_t offset)
|
|
{
|
|
uint8_t *datap = flash->data;
|
|
uint16_t data;
|
|
|
|
switch (flash->state) {
|
|
default:
|
|
flash_state_reset(flash);
|
|
/* fall through */
|
|
|
|
case FLASH_STATE_NORMAL:
|
|
data = lduw_p(datap + offset);
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_ID:
|
|
if (offset & 2) {
|
|
data = flash->device_ID;
|
|
} else {
|
|
data = flash->vendor_ID;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY:
|
|
if (offset < flash->cfi_size) {
|
|
data = flash->cfi_data[offset >> 1];
|
|
} else {
|
|
data = 0x0000;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
flash_put_halfword(x49gp_flash_t *flash, uint32_t offset, uint32_t data)
|
|
{
|
|
uint8_t *datap = flash->data;
|
|
uint16_t temp;
|
|
|
|
data &= 0xffff;
|
|
|
|
switch (flash->state) {
|
|
default:
|
|
flash_state_reset(flash);
|
|
/* fall through */
|
|
|
|
case FLASH_STATE_NORMAL:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_UNLOCK1;
|
|
cpu_register_physical_memory(0x00000000, SST29VF160_SIZE, flash->iotype);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_UNLOCK1:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_UNLOCK2;
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_UNLOCK2:
|
|
if ((offset >> 1) == 0x5555) {
|
|
switch (data & 0xff) {
|
|
case 0xa0:
|
|
flash->state = FLASH_STATE_WORD_PROG;
|
|
break;
|
|
case 0x80:
|
|
flash->state = FLASH_STATE_ERASE1;
|
|
break;
|
|
case 0x90:
|
|
flash->state = FLASH_STATE_SOFTWARE_ID;
|
|
break;
|
|
case 0x98:
|
|
flash->state = FLASH_STATE_CFI_QUERY;
|
|
break;
|
|
default:
|
|
flash_state_reset(flash);
|
|
break;
|
|
}
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_ERASE1:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_ERASE2;
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_ERASE2:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_ERASE3;
|
|
} else {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_EXIT1:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_SOFTWARE_EXIT2;
|
|
} else {
|
|
flash->state = FLASH_STATE_SOFTWARE_ID;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_EXIT2:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xf0)) {
|
|
flash_state_reset(flash);
|
|
} else {
|
|
flash->state = FLASH_STATE_SOFTWARE_ID;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY_EXIT1:
|
|
if (((offset >> 1) == 0x2aaa) && ((data & 0xff) == 0x55)) {
|
|
flash->state = FLASH_STATE_CFI_QUERY_EXIT2;
|
|
} else {
|
|
flash->state = FLASH_STATE_CFI_QUERY;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY_EXIT2:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xf0)) {
|
|
flash_state_reset(flash);
|
|
} else {
|
|
flash->state = FLASH_STATE_CFI_QUERY;
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_SOFTWARE_ID:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_SOFTWARE_EXIT1;
|
|
} else if ((data & 0xff) == 0xf0) {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_CFI_QUERY:
|
|
if (((offset >> 1) == 0x5555) && ((data & 0xff) == 0xaa)) {
|
|
flash->state = FLASH_STATE_CFI_QUERY_EXIT1;
|
|
} else if ((data & 0xff) == 0xf0) {
|
|
flash_state_reset(flash);
|
|
}
|
|
break;
|
|
|
|
case FLASH_STATE_WORD_PROG:
|
|
temp = lduw_p(datap + offset);
|
|
stw_p(datap + offset, data & temp);
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 2 (state %u) at offset %08x: %04x, result: %04x\n",
|
|
flash->state, offset, data, lduw_p(datap + offset));
|
|
#endif
|
|
|
|
flash_state_reset(flash);
|
|
break;
|
|
|
|
case FLASH_STATE_ERASE3:
|
|
switch (data & 0xff) {
|
|
case 0x10: /* Chip Erase */
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x %08x\n", 0, SST29VF160_SIZE);
|
|
#endif
|
|
memset(datap, 0xff, SST29VF160_SIZE);
|
|
break;
|
|
|
|
case 0x30: /* Sector Erase */
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x %08x\n",
|
|
(offset & ~(flash->sector_size - 1)),
|
|
flash->sector_size);
|
|
#endif
|
|
memset(datap + (offset & ~(flash->sector_size - 1)), 0xff, flash->sector_size);
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x: %04x, %08x: %04x, %08x: %04x\n",
|
|
offset, lduw_p(datap + offset),
|
|
offset + 0x800, lduw_p(datap + offset + 0x800),
|
|
offset + 0xffc, lduw_p(datap + offset + 0xffc));
|
|
#endif
|
|
break;
|
|
|
|
case 0x50: /* Block Erase */
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("erase FLASH %08x %08x\n",
|
|
(offset & ~(flash->block_size - 1)),
|
|
flash->block_size);
|
|
#endif
|
|
memset(datap + (offset & ~(flash->block_size - 1)), 0xff, flash->block_size);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
flash_state_reset(flash);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
flash_readb(void *opaque, target_phys_addr_t offset)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint8_t *datap = flash->data;
|
|
unsigned short temp;
|
|
uint32_t shift;
|
|
unsigned char data;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL) {
|
|
offset -= (target_phys_addr_t) phys_ram_base;
|
|
data = *(datap + offset);
|
|
} else {
|
|
offset -= 0;
|
|
temp = flash_get_halfword(flash, offset & ~(1));
|
|
shift = (offset & 1) << 3;
|
|
data = (temp >> shift) & 0xff;
|
|
}
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_READ
|
|
printf("read FLASH 1 (state %u) at offset %08x: %02x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
|
|
static uint32_t
|
|
flash_readw(void *opaque, target_phys_addr_t offset)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint8_t *datap = flash->data;
|
|
uint32_t data;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL) {
|
|
offset -= (target_phys_addr_t) phys_ram_base;
|
|
data = lduw_p(datap + offset);
|
|
} else {
|
|
offset -= 0;
|
|
data = flash_get_halfword(flash, offset);
|
|
}
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_READ
|
|
printf("read FLASH 2 (state %u) at offset %08x: %04x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
|
|
static uint32_t
|
|
flash_readl(void *opaque, target_phys_addr_t offset)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint8_t *datap = flash->data;
|
|
uint32_t data;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL) {
|
|
offset -= (target_phys_addr_t) phys_ram_base;
|
|
data = ldl_p(datap + offset);
|
|
} else {
|
|
offset -= 0;
|
|
data = (flash_get_halfword(flash, offset + 2) << 16) |
|
|
(flash_get_halfword(flash, offset + 0) << 0);
|
|
}
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_READ
|
|
printf("read FLASH 4 (state %u) at offset %08x: %08x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
flash_writeb(void *opaque, target_phys_addr_t offset, uint32_t data)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
uint32_t shift;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL)
|
|
offset -= (target_phys_addr_t) phys_ram_base;
|
|
else
|
|
offset -= 0;
|
|
|
|
data &= 0xff;
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 1 (state %u) at offset %08x: %02x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
/*
|
|
* This does not issue read-modify-write, i.e. you will get
|
|
* broken data in FLASH memory. This would be the case with
|
|
* real hardware, too.
|
|
*/
|
|
shift = (offset & 1) << 3;
|
|
flash_put_halfword(flash, offset & ~(1), data << shift);
|
|
}
|
|
|
|
static void
|
|
flash_writew(void *opaque, target_phys_addr_t offset, uint32_t data)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL)
|
|
offset -= (target_phys_addr_t) phys_ram_base;
|
|
else
|
|
offset -= 0;
|
|
|
|
data &= 0xffff;
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 2 (state %u) at offset %08x: %04x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
flash_put_halfword(flash, offset, data);
|
|
}
|
|
|
|
static void
|
|
flash_writel(void *opaque, target_phys_addr_t offset, uint32_t data)
|
|
{
|
|
x49gp_flash_t *flash = opaque;
|
|
|
|
if (flash->state == FLASH_STATE_NORMAL)
|
|
offset -= (target_phys_addr_t) phys_ram_base;
|
|
else
|
|
offset -= 0;
|
|
|
|
#ifdef DEBUG_X49GP_FLASH_WRITE
|
|
printf("write FLASH 4 (state %u) at offset %08x: %08x\n",
|
|
flash->state, offset, data);
|
|
#endif
|
|
|
|
flash_put_halfword(flash, offset + 2, (data >> 16) & 0xffff);
|
|
flash_put_halfword(flash, offset + 0, (data >> 0) & 0xffff);
|
|
}
|
|
|
|
static int
|
|
flash_load(x49gp_module_t *module, GKeyFile *key)
|
|
{
|
|
x49gp_flash_t *flash = module->user_data;
|
|
char *filename;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
filename = x49gp_module_get_filename(module, key, "filename");
|
|
if (NULL == filename) {
|
|
fprintf(stderr, "%s: %s:%u: key \"filename\" not found\n",
|
|
module->name, __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
flash->fd = open(filename, O_RDWR);
|
|
if (flash->fd < 0) {
|
|
fprintf(stderr, "%s: %s:%u: open %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
g_free(filename);
|
|
return -1;
|
|
}
|
|
|
|
flash->data = mmap(phys_ram_base + flash->offset, SST29VF160_SIZE,
|
|
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED,
|
|
flash->fd, 0);
|
|
if (flash->data == (void *) -1) {
|
|
fprintf(stderr, "%s: %s:%u: mmap %s: %s\n",
|
|
module->name, __FUNCTION__, __LINE__,
|
|
filename, strerror(errno));
|
|
g_free(filename);
|
|
close(flash->fd);
|
|
flash->fd = -1;
|
|
return -1;
|
|
}
|
|
flash->size = SST29VF160_SIZE;
|
|
|
|
g_free(filename);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
flash_save(x49gp_module_t *module, GKeyFile *config)
|
|
{
|
|
x49gp_flash_t *flash = module->user_data;
|
|
int error;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
error = msync(flash->data, flash->size, MS_ASYNC);
|
|
if (error) {
|
|
fprintf(stderr, "%s:%u: msync: %s\n",
|
|
__FUNCTION__, __LINE__, strerror(errno));
|
|
return error;
|
|
}
|
|
|
|
error = fsync(flash->fd);
|
|
if (error) {
|
|
fprintf(stderr, "%s:%u: fsync: %s\n",
|
|
__FUNCTION__, __LINE__, strerror(errno));
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
flash_reset(x49gp_module_t *module, x49gp_reset_t reset)
|
|
{
|
|
x49gp_flash_t *flash = module->user_data;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
flash_state_reset(flash);
|
|
return 0;
|
|
}
|
|
|
|
static CPUReadMemoryFunc *flash_readfn[] =
|
|
{
|
|
flash_readb,
|
|
flash_readw,
|
|
flash_readl
|
|
};
|
|
|
|
static CPUWriteMemoryFunc *flash_writefn[] =
|
|
{
|
|
flash_writeb,
|
|
flash_writew,
|
|
flash_writel
|
|
};
|
|
|
|
static int
|
|
flash_init(x49gp_module_t *module)
|
|
{
|
|
x49gp_flash_t *flash;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
flash = malloc(sizeof(x49gp_flash_t));
|
|
if (NULL == flash) {
|
|
fprintf(stderr, "%s: %s:%u: Out of memory\n",
|
|
module->name, __FUNCTION__, __LINE__);
|
|
return -1;
|
|
}
|
|
memset(flash, 0, sizeof(x49gp_flash_t));
|
|
|
|
flash->vendor_ID = SST29VF160_VENDOR_ID;
|
|
flash->device_ID = SST29VF160_DEVICE_ID;
|
|
flash->cfi_data = sst29vf160_cfi_data;
|
|
flash->cfi_size = SST29VF160_CFI_SIZE;
|
|
flash->sector_size = SST29VF160_SECTOR_SIZE;
|
|
flash->block_size = SST29VF160_BLOCK_SIZE;
|
|
flash->fd = -1;
|
|
|
|
module->user_data = flash;
|
|
|
|
#ifdef QEMU_OLD
|
|
flash->iotype = cpu_register_io_memory(0, flash_readfn,
|
|
flash_writefn, flash);
|
|
#else
|
|
flash->iotype = cpu_register_io_memory(flash_readfn,
|
|
flash_writefn, flash);
|
|
#endif
|
|
|
|
flash->data = (void *) -1;
|
|
flash->offset = phys_ram_size;
|
|
phys_ram_size += SST29VF160_SIZE;
|
|
|
|
cpu_register_physical_memory(0x00000000, SST29VF160_SIZE,
|
|
flash->offset | flash->iotype | IO_MEM_ROMD);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
flash_exit(x49gp_module_t *module)
|
|
{
|
|
x49gp_flash_t *flash;
|
|
|
|
#ifdef DEBUG_X49GP_MODULES
|
|
printf("%s: %s:%u\n", module->name, __FUNCTION__, __LINE__);
|
|
#endif
|
|
|
|
if (module->user_data) {
|
|
flash = module->user_data;
|
|
|
|
if (flash->data != (void *) -1) {
|
|
munmap(flash->data, flash->size);
|
|
}
|
|
if (flash->fd) {
|
|
close(flash->fd);
|
|
}
|
|
|
|
free(flash);
|
|
}
|
|
|
|
x49gp_module_unregister(module);
|
|
free(module);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
x49gp_flash_init(x49gp_t *x49gp)
|
|
{
|
|
x49gp_module_t *module;
|
|
|
|
if (x49gp_module_init(x49gp, "flash", flash_init, flash_exit,
|
|
flash_reset, flash_load, flash_save, NULL,
|
|
&module)) {
|
|
return -1;
|
|
}
|
|
|
|
return x49gp_module_register(module);
|
|
}
|