phaser-ags / firmware /drivers /nor_flash.c
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
829b19c verified
Raw History Blame Contribute Delete
4.62 kB
/*
* Driver — OMAP3530 GPMC NOR Flash driver
* Intel JS28F256M29EWH via GPMC CS0
*/
#include "nor_flash.h"
static hw_reg32_t nor_base(int cs) {
return (hw_reg32_t)(NOR_FLASH_BASE + cs * 0x10000000UL);
}
static void nor_write_cmd(int cs, uint32_t offset, uint16_t cmd) {
hw_reg32_t base = nor_base(cs);
volatile uint16_t *flash = (volatile uint16_t *)base;
flash[offset] = cmd;
__asm__ volatile("dmb" ::: "memory");
}
static uint16_t nor_read_reg(int cs, uint32_t offset) {
hw_reg32_t base = nor_base(cs);
volatile uint16_t *flash = (volatile uint16_t *)base;
return flash[offset];
}
int nor_flash_init(int cs) {
hw_reg32_t gpmc = (hw_reg32_t)GPMC_BASE;
/* Enable GPMC */
reg_write32(gpmc + GPMC_SYSCONFIG, 0x10); /* Smart idle */
delay_cycles(100);
/* Configure CS0 for 16-bit NOR flash, async */
reg_write32(gpmc + GPMC_CONFIG1(cs),
(0x00 << 0) | /* DEVICETYPE: NOR async */
(0x02 << 12) | /* MWIDTH: 16-bit */
(0x01 << 18) /* WAITPIN: WAIT0 */
);
/* CS signal timings — conservative for initial bring-up */
reg_write32(gpmc + GPMC_CONFIG2(cs),
(0x08 << 0) | /* CS_ON: 8 cycles */
(0x1F << 8) /* CS_WR_OFF: 31 cycles */
);
reg_write32(gpmc + GPMC_CONFIG3(cs),
(0x02 << 0) | /* ADV_ON: 2 cycles */
(0x06 << 8) /* ADV_WR_OFF: 6 cycles */
);
reg_write32(gpmc + GPMC_CONFIG4(cs),
(0x03 << 0) | /* OE_ON: 3 cycles */
(0x0C << 8) | /* OE_OFF: 12 cycles */
(0x03 << 16) | /* WE_ON: 3 cycles */
(0x0C << 24) /* WE_OFF: 12 cycles */
);
reg_write32(gpmc + GPMC_CONFIG5(cs),
(0x02 << 0) | /* RD_CYCLE: 2 extra */
(0x02 << 8) | /* WR_CYCLE: 2 extra */
(0x02 << 16) | /* RD_ACCESS: 2 cycles */
(0x02 << 24) /* WR_ACCESS: 2 cycles */
);
reg_write32(gpmc + GPMC_CONFIG6(cs),
(0x00 << 0) | /* WR_DATA_MUX: 0 */
(0x02 << 8) | /* WR_ACCESS: 2 cycles */
(0x00 << 16) /* CYCLE2CYCLE: no delay */
);
/* Set base address and enable CS */
reg32_t config7 = (0x08 << 0) | /* Base address bits [31:24] = 0x08 → 0x08000000 */
(0x0F << 8) | /* Mask: 256MB region */
GPMC_CONFIG7_CSVALID;
reg_write32(gpmc + GPMC_CONFIG7(cs), config7);
/* Reset flash to read mode */
nor_flash_reset(cs);
return 0;
}
void nor_flash_read_id(int cs, uint16_t *manufacturer, uint16_t *device) {
nor_write_cmd(cs, 0, NOR_CMD_READ_ID);
delay_cycles(100);
*manufacturer = nor_read_reg(cs, 0);
*device = nor_read_reg(cs, 1);
nor_flash_reset(cs);
}
void nor_flash_reset(int cs) {
nor_write_cmd(cs, 0, NOR_CMD_RESET);
delay_cycles(500);
}
int nor_flash_erase_sector(int cs, uint32_t offset) {
nor_write_cmd(cs, 0, NOR_CMD_ERASE);
nor_write_cmd(cs, offset >> 1, NOR_CMD_CONFIRM);
/* Poll for completion (status bit 7 toggles when done) */
volatile uint16_t *flash = (volatile uint16_t *)nor_base(cs);
uint32_t timeout = 5000000;
while (timeout--) {
uint16_t status = flash[offset >> 1];
if (status & 0x0080) /* DSQ7: done */
break;
}
nor_flash_reset(cs);
/* Verify erased (should read 0xFFFF) */
if (flash[offset >> 1] != 0xFFFF)
return -1;
return 0;
}
int nor_flash_program_word(int cs, uint32_t offset, uint16_t data) {
volatile uint16_t *flash = (volatile uint16_t *)nor_base(cs);
uint32_t word_offset = offset >> 1;
nor_write_cmd(cs, 0, NOR_CMD_PROG);
flash[word_offset] = data;
__asm__ volatile("dmb" ::: "memory");
/* Poll for completion */
uint32_t timeout = 1000000;
while (timeout--) {
uint16_t status = flash[word_offset];
if ((status & 0x0080) && (status == data))
break;
}
nor_flash_reset(cs);
if (flash[word_offset] != data)
return -1;
return 0;
}
int nor_flash_program_buf(int cs, uint32_t offset, const uint16_t *buf, uint32_t count) {
for (uint32_t i = 0; i < count; i++) {
if (nor_flash_program_word(cs, offset + i * 2, buf[i]) != 0)
return -1;
}
return 0;
}
int nor_flash_read_buf(int cs, uint32_t offset, uint16_t *buf, uint32_t count) {
volatile uint16_t *flash = (volatile uint16_t *)nor_base(cs);
uint32_t word_offset = offset >> 1;
for (uint32_t i = 0; i < count; i++) {
buf[i] = flash[word_offset + i];
}
return 0;
}