summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-06-19 17:48:13 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-06-27 19:09:03 +0100
commit9f950d8bbf25995d0ba71ce2cd6cc9c0d3b91a9c (patch)
tree4946622b4bb3d8862237ae6eeddb39e1c2bfcf67
parent9246cbc65e0d71428f7ac8662efdb47f7b9cc174 (diff)
downloadrockbox-9f950d8bbf25995d0ba71ce2cd6cc9c0d3b91a9c.tar.gz
rockbox-9f950d8bbf25995d0ba71ce2cd6cc9c0d3b91a9c.zip
x1000: NAND rewrite
This new design saves on binary size and stack usage. The API is also block- and page-based, requiring awareness of the chip layout to use properly. Out-of-band areas are also exposed for reading and writing. The byte-oriented routines are kept for compatibility with the existing installer and SPL. Change-Id: Iaacc694d2f651ab74d45330e0434ee778a0d91bc
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c58
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c44
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c686
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.h207
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.c294
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.h152
6 files changed, 675 insertions, 766 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
index 10a58ace38..8ce73bf09e 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
@@ -32,75 +32,45 @@
32#define IMAGE_SIZE (128 * 1024) 32#define IMAGE_SIZE (128 * 1024)
33#define TAR_SIZE (256 * 1024) 33#define TAR_SIZE (256 * 1024)
34 34
35static int flash_prepare(void)
36{
37 int mf_id, dev_id;
38 int rc;
39
40 rc = nand_open();
41 if(rc < 0)
42 return INSTALL_ERR_FLASH(NAND_OPEN, rc);
43
44 rc = nand_identify(&mf_id, &dev_id);
45 if(rc < 0) {
46 nand_close();
47 return INSTALL_ERR_FLASH(NAND_IDENTIFY, rc);
48 }
49
50 return INSTALL_SUCCESS;
51}
52
53static void flash_finish(void)
54{
55 /* Ensure writes are always disabled when we finish.
56 * Errors are safe to ignore here, there's nothing we could do anyway. */
57 nand_enable_writes(false);
58 nand_close();
59}
60
61static int flash_img_read(uint8_t* buffer) 35static int flash_img_read(uint8_t* buffer)
62{ 36{
63 int rc = flash_prepare(); 37 nand_drv* drv = nand_init();
38 nand_lock(drv);
39
40 int rc = nand_open(drv);
64 if(rc < 0) 41 if(rc < 0)
65 goto error; 42 goto error;
66 43
67 rc = nand_read(0, IMAGE_SIZE, buffer); 44 rc = nand_read_bytes(drv, 0, IMAGE_SIZE, buffer);
68 if(rc < 0) { 45 if(rc < 0) {
69 rc = INSTALL_ERR_FLASH(NAND_READ, rc); 46 rc = INSTALL_ERR_FLASH(NAND_READ, rc);
70 goto error; 47 goto error;
71 } 48 }
72 49
73 error: 50 error:
74 flash_finish(); 51 nand_close(drv);
52 nand_unlock(drv);
75 return rc; 53 return rc;
76} 54}
77 55
78static int flash_img_write(const uint8_t* buffer) 56static int flash_img_write(const uint8_t* buffer)
79{ 57{
80 int rc = flash_prepare(); 58 nand_drv* drv = nand_init();
81 if(rc < 0) 59 nand_lock(drv);
82 goto error;
83
84 rc = nand_enable_writes(true);
85 if(rc < 0) {
86 rc = INSTALL_ERR_FLASH(NAND_ENABLE_WRITES, rc);
87 goto error;
88 }
89 60
90 rc = nand_erase(0, IMAGE_SIZE); 61 int rc = nand_open(drv);
91 if(rc < 0) { 62 if(rc < 0)
92 rc = INSTALL_ERR_FLASH(NAND_ERASE, rc);
93 goto error; 63 goto error;
94 }
95 64
96 rc = nand_write(0, IMAGE_SIZE, buffer); 65 rc = nand_write_bytes(drv, 0, IMAGE_SIZE, buffer);
97 if(rc < 0) { 66 if(rc < 0) {
98 rc = INSTALL_ERR_FLASH(NAND_WRITE, rc); 67 rc = INSTALL_ERR_FLASH(NAND_WRITE, rc);
99 goto error; 68 goto error;
100 } 69 }
101 70
102 error: 71 error:
103 flash_finish(); 72 nand_close(drv);
73 nand_unlock(drv);
104 return rc; 74 return rc;
105} 75}
106 76
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
index 7c56e4ac19..5680b1e548 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
@@ -119,6 +119,29 @@ void spl_error(void)
119 } 119 }
120} 120}
121 121
122nand_drv* alloc_nand_drv(uint32_t laddr, uint32_t lsize)
123{
124 size_t need_size = sizeof(nand_drv) +
125 NAND_DRV_SCRATCHSIZE +
126 NAND_DRV_MAXPAGESIZE;
127
128 /* find a hole to keep the buffers */
129 uintptr_t addr;
130 if(X1000_SDRAM_BASE + need_size <= laddr)
131 addr = X1000_SDRAM_BASE;
132 else
133 addr = CACHEALIGN_UP(X1000_SDRAM_BASE + laddr + lsize);
134
135 uint8_t* page_buf = (uint8_t*)addr;
136 uint8_t* scratch_buf = page_buf + NAND_DRV_MAXPAGESIZE;
137 nand_drv* drv = (nand_drv*)(scratch_buf + NAND_DRV_SCRATCHSIZE);
138
139 drv->page_buf = page_buf;
140 drv->scratch_buf = scratch_buf;
141 drv->refcount = 0;
142 return drv;
143}
144
122void spl_target_boot(void) 145void spl_target_boot(void)
123{ 146{
124 int opt_index = spl_get_boot_option(); 147 int opt_index = spl_get_boot_option();
@@ -134,33 +157,26 @@ void spl_target_boot(void)
134 gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1)); 157 gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
135 158
136 /* Open NAND chip */ 159 /* Open NAND chip */
137 int rc = nand_open(); 160 nand_drv* ndrv = alloc_nand_drv(opt->load_addr, opt->nand_size);
161 int rc = nand_open(ndrv);
138 if(rc) 162 if(rc)
139 spl_error(); 163 spl_error();
140 164
141 int mf_id, dev_id;
142 rc = nand_identify(&mf_id, &dev_id);
143 if(rc)
144 goto nand_err;
145
146 /* For OF only: load DMA coprocessor's firmware from flash */ 165 /* For OF only: load DMA coprocessor's firmware from flash */
147 if(opt_index != BOOTOPTION_ROCKBOX) { 166 if(opt_index != BOOTOPTION_ROCKBOX) {
148 rc = nand_read(0x4000, 0x2000, (uint8_t*)0xb3422000); 167 rc = nand_read_bytes(ndrv, 0x4000, 0x2000, (uint8_t*)0xb3422000);
149 if(rc) 168 if(rc)
150 goto nand_err; 169 goto nand_err;
151 } 170 }
152 171
153 /* Read the firmware */ 172 /* Read the firmware */
154 rc = nand_read(opt->nand_addr, opt->nand_size, load_addr); 173 rc = nand_read_bytes(ndrv, opt->nand_addr, opt->nand_size, load_addr);
155 if(rc) 174 if(rc)
156 goto nand_err; 175 goto nand_err;
157 176
158 /* Rockbox doesn't need the NAND; for the OF, we should leave it open 177 /* Rockbox doesn't need the NAND; for the OF, we should leave it open */
159 * and also make sure to turn off the write protect bits. */
160 if(opt_index == BOOTOPTION_ROCKBOX) 178 if(opt_index == BOOTOPTION_ROCKBOX)
161 nand_close(); 179 nand_close(ndrv);
162 else
163 nand_enable_writes(true);
164 180
165 /* Kernel arguments pointer, for Linux only */ 181 /* Kernel arguments pointer, for Linux only */
166 char** kargv = (char**)0x80004000; 182 char** kargv = (char**)0x80004000;
@@ -184,7 +200,7 @@ void spl_target_boot(void)
184 __builtin_unreachable(); 200 __builtin_unreachable();
185 201
186 nand_err: 202 nand_err:
187 nand_close(); 203 nand_close(ndrv);
188 spl_error(); 204 spl_error();
189} 205}
190 206
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index 5a21b1f8c2..b76efe65e5 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -22,480 +22,394 @@
22#include "nand-x1000.h" 22#include "nand-x1000.h"
23#include "sfc-x1000.h" 23#include "sfc-x1000.h"
24#include "system.h" 24#include "system.h"
25#include <stddef.h> 25#include <string.h>
26 26
27/* NAND command numbers */ 27/* cmd mode a d phase format has data */
28#define NAND_CMD_READ_ID 0x9f 28#define NANDCMD_RESET SFC_CMD(0xff, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
29#define NAND_CMD_WRITE_ENABLE 0x06 29#define NANDCMD_READID(x,y) SFC_CMD(0x9f, SFC_TMODE_1_1_1, x, y, SFC_PFMT_ADDR_FIRST, 1)
30#define NAND_CMD_GET_FEATURE 0x0f 30#define NANDCMD_WR_EN SFC_CMD(0x06, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
31#define NAND_CMD_SET_FEATURE 0x1f 31#define NANDCMD_GET_FEATURE SFC_CMD(0x0f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
32#define NAND_CMD_PAGE_READ_TO_CACHE 0x13 32#define NANDCMD_SET_FEATURE SFC_CMD(0x1f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
33#define NAND_CMD_READ_FROM_CACHE 0x0b 33#define NANDCMD_PAGE_READ(x) SFC_CMD(0x13, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0)
34#define NAND_CMD_READ_FROM_CACHEx4 0x6b 34#define NANDCMD_READ_CACHE(x) SFC_CMD(0x0b, SFC_TMODE_1_1_1, x, 8, SFC_PFMT_ADDR_FIRST, 1)
35#define NAND_CMD_PROGRAM_LOAD 0x02 35#define NANDCMD_READ_CACHE_x4(x) SFC_CMD(0x6b, SFC_TMODE_1_1_4, x, 8, SFC_PFMT_ADDR_FIRST, 1)
36#define NAND_CMD_PROGRAM_LOADx4 0x32 36#define NANDCMD_PROGRAM_LOAD(x) SFC_CMD(0x02, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 1)
37#define NAND_CMD_PROGRAM_EXECUTE 0x10 37#define NANDCMD_PROGRAM_LOAD_x4(x) SFC_CMD(0x32, SFC_TMODE_1_1_4, x, 0, SFC_PFMT_ADDR_FIRST, 1)
38#define NAND_CMD_BLOCK_ERASE 0xd8 38#define NANDCMD_PROGRAM_EXECUTE(x) SFC_CMD(0x10, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0)
39 39#define NANDCMD_BLOCK_ERASE(x) SFC_CMD(0xd8, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0)
40/* NAND device register addresses for GET_FEATURE / SET_FEATURE */ 40
41#define NAND_FREG_PROTECTION 0xa0 41/* Feature registers are found in linux/mtd/spinand.h,
42#define NAND_FREG_FEATURE 0xb0 42 * apparently these are pretty standardized */
43#define NAND_FREG_STATUS 0xc0 43#define FREG_PROT 0xa0
44 44#define FREG_PROT_UNLOCK 0x00
45/* Protection register bits */ 45
46#define NAND_FREG_PROTECTION_BRWD 0x80 46#define FREG_CFG 0xb0
47#define NAND_FREG_PROTECTION_BP2 0x20 47#define FREG_CFG_OTP_ENABLE (1 << 6)
48#define NAND_FREG_PROTECTION_BP1 0x10 48#define FREG_CFG_ECC_ENABLE (1 << 4)
49#define NAND_FREG_PROTECTION_BP0 0x08 49#define FREG_CFG_QUAD_ENABLE (1 << 0)
50/* Mask of BP bits 0-2 */ 50
51#define NAND_FREG_PROTECTION_ALLBP 0x38 51#define FREG_STATUS 0xc0
52 52#define FREG_STATUS_BUSY (1 << 0)
53/* Feature register bits */ 53#define FREG_STATUS_EFAIL (1 << 2)
54#define NAND_FREG_FEATURE_QE 0x01 54#define FREG_STATUS_PFAIL (1 << 3)
55 55#define FREG_STATUS_ECC_MASK (3 << 4)
56/* Status register bits */ 56#define FREG_STATUS_ECC_NO_FLIPS (0 << 4)
57#define NAND_FREG_STATUS_OIP 0x01 57#define FREG_STATUS_ECC_HAS_FLIPS (1 << 4)
58#define NAND_FREG_STATUS_WEL 0x02 58#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
59#define NAND_FREG_STATUS_E_FAIL 0x04 59
60#define NAND_FREG_STATUS_P_FAIL 0x08 60const nand_chip supported_nand_chips[] = {
61 61#if defined(FIIO_M3K)
62/* NAND chip config */
63const nand_chip_data target_nand_chip_data[] = {
64#ifdef FIIO_M3K
65 { 62 {
66 /* ATO25D1GA */ 63 /* ATO25D1GA */
67 .mf_id = 0x9b, 64 .mf_id = 0x9b,
68 .dev_id = 0x12, 65 .dev_id = 0x12,
69 .dev_conf = jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), 66 .row_cycles = 3,
70 CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), 67 .col_cycles = 2,
71 STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)), 68 .log2_ppb = 6, /* 64 pages */
69 .page_size = 2048,
70 .oob_size = 64,
71 .nr_blocks = 1024,
72 .clock_freq = 150000000, 72 .clock_freq = 150000000,
73 .log2_page_size = 11, /* = 2048 bytes */ 73 .dev_conf = jz_orf(SFC_DEV_CONF,
74 .log2_block_size = 6, /* = 64 pages */ 74 CE_DL(1), HOLD_DL(1), WP_DL(1),
75 .rowaddr_width = 3, 75 CPHA(0), CPOL(0),
76 .coladdr_width = 2, 76 TSH(7), TSETUP(0), THOLD(0),
77 .flags = NANDCHIP_FLAG_QUAD, 77 STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS),
78 } 78 SMP_DELAY(1)),
79 .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT,
80 },
79#else 81#else
80 /* Nobody will use this anyway if the device has no NAND flash */
81 { 0 }, 82 { 0 },
82#endif 83#endif
83}; 84};
84 85
85const size_t target_nand_chip_count = 86const size_t nr_supported_nand_chips =
86 sizeof(target_nand_chip_data) / sizeof(nand_chip_data); 87 sizeof(supported_nand_chips) / sizeof(nand_chip);
87
88/* NAND ops -- high level primitives used by the driver */
89static int nandop_wait_status(int errbit);
90static int nandop_read_page(uint32_t row_addr, uint8_t* buf);
91static int nandop_write_page(uint32_t row_addr, const uint8_t* buf);
92static int nandop_erase_block(uint32_t block_addr);
93static int nandop_set_write_protect(bool en);
94
95/* NAND commands -- 1-to-1 mapping between chip commands and functions */
96static int nandcmd_read_id(int* mf_id, int* dev_id);
97static int nandcmd_write_enable(void);
98static int nandcmd_get_feature(uint8_t reg);
99static int nandcmd_set_feature(uint8_t reg, uint8_t val);
100static int nandcmd_page_read_to_cache(uint32_t row_addr);
101static int nandcmd_read_from_cache(uint8_t* buf);
102static int nandcmd_program_load(const uint8_t* buf);
103static int nandcmd_program_execute(uint32_t row_addr);
104static int nandcmd_block_erase(uint32_t block_addr);
105
106struct nand_drv {
107 const nand_chip_data* chip_data;
108 bool write_enabled;
109};
110 88
111static struct nand_drv nand_drv; 89static nand_drv static_nand_drv;
112static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR; 90static uint8_t static_scratch_buf[NAND_DRV_SCRATCHSIZE] CACHEALIGN_ATTR;
91static uint8_t static_page_buf[NAND_DRV_MAXPAGESIZE] CACHEALIGN_ATTR;
113 92
114static void nand_drv_reset(void) 93nand_drv* nand_init(void)
115{ 94{
116 nand_drv.chip_data = NULL; 95 static bool inited = false;
117 nand_drv.write_enabled = false; 96 if(!inited) {
97 mutex_init(&static_nand_drv.mutex);
98 static_nand_drv.scratch_buf = static_scratch_buf;
99 static_nand_drv.page_buf = static_page_buf;
100 static_nand_drv.refcount = 0;
101 }
102
103 return &static_nand_drv;
118} 104}
119 105
120int nand_open(void) 106static uint8_t nand_get_reg(nand_drv* drv, uint8_t reg)
121{ 107{
122 sfc_init(); 108 sfc_exec(NANDCMD_GET_FEATURE, reg, drv->scratch_buf, 1|SFC_READ);
123 sfc_lock(); 109 return drv->scratch_buf[0];
124
125 nand_drv_reset();
126 sfc_open();
127
128 const nand_chip_data* chip_data = &target_nand_chip_data[0];
129 sfc_set_dev_conf(chip_data->dev_conf);
130 sfc_set_clock(chip_data->clock_freq);
131
132 sfc_unlock();
133 return NAND_SUCCESS;
134} 110}
135 111
136void nand_close(void) 112static void nand_set_reg(nand_drv* drv, uint8_t reg, uint8_t val)
137{ 113{
138 sfc_lock(); 114 drv->scratch_buf[0] = val;
139 sfc_close(); 115 sfc_exec(NANDCMD_SET_FEATURE, reg, drv->scratch_buf, 1|SFC_WRITE);
140 nand_drv_reset();
141 sfc_unlock();
142} 116}
143 117
144int nand_identify(int* mf_id, int* dev_id) 118static void nand_upd_reg(nand_drv* drv, uint8_t reg, uint8_t msk, uint8_t val)
145{ 119{
146 sfc_lock(); 120 uint8_t x = nand_get_reg(drv, reg);
147 121 x &= ~msk;
148 int status = nandcmd_read_id(mf_id, dev_id); 122 x |= val;
149 if(status < 0) 123 nand_set_reg(drv, reg, x);
150 goto error; 124}
151 125
152 for(size_t i = 0; i < target_nand_chip_count; ++i) { 126static bool identify_chip(nand_drv* drv)
153 const nand_chip_data* data = &target_nand_chip_data[i]; 127{
154 if(data->mf_id == *mf_id && data->dev_id == *dev_id) { 128 /* Read ID command has some variations; Linux handles these 3:
155 nand_drv.chip_data = data; 129 * - no address or dummy bytes
156 break; 130 * - 1 byte address, no dummy byte
131 * - no address byte, 1 byte dummy
132 *
133 * Right now there is only a need for the 2nd variation, as that is
134 * the method used by the ATO25D1GA.
135 *
136 * Some chips also output more than 2 ID bytes.
137 */
138 sfc_exec(NANDCMD_READID(1, 0), 0, drv->scratch_buf, 2|SFC_READ);
139 drv->mf_id = drv->scratch_buf[0];
140 drv->dev_id = drv->scratch_buf[1];
141
142 for(size_t i = 0; i < nr_supported_nand_chips; ++i) {
143 const nand_chip* chip = &supported_nand_chips[i];
144 if(chip->mf_id == drv->mf_id && chip->dev_id == drv->dev_id) {
145 drv->chip = chip;
146 return true;
157 } 147 }
158 } 148 }
159 149
160 if(!nand_drv.chip_data) { 150 return false;
161 status = NAND_ERR_UNKNOWN_CHIP; 151}
162 goto error;
163 }
164
165 /* Set parameters according to new chip data */
166 sfc_set_dev_conf(nand_drv.chip_data->dev_conf);
167 sfc_set_clock(nand_drv.chip_data->clock_freq);
168 status = NAND_SUCCESS;
169 152
170 error: 153static void setup_chip_data(nand_drv* drv)
171 sfc_unlock(); 154{
172 return status; 155 drv->ppb = 1 << drv->chip->log2_ppb;
156 drv->fpage_size = drv->chip->page_size + drv->chip->oob_size;
173} 157}
174 158
175const nand_chip_data* nand_get_chip_data(void) 159static void setup_chip_commands(nand_drv* drv)
176{ 160{
177 return nand_drv.chip_data; 161 /* Select commands appropriate for the chip */
162 drv->cmd_page_read = NANDCMD_PAGE_READ(drv->chip->row_cycles);
163 drv->cmd_program_execute = NANDCMD_PROGRAM_EXECUTE(drv->chip->row_cycles);
164 drv->cmd_block_erase = NANDCMD_BLOCK_ERASE(drv->chip->row_cycles);
165
166 if(drv->chip->flags & NAND_CHIPFLAG_QUAD) {
167 drv->cmd_read_cache = NANDCMD_READ_CACHE_x4(drv->chip->col_cycles);
168 drv->cmd_program_load = NANDCMD_PROGRAM_LOAD_x4(drv->chip->col_cycles);
169 } else {
170 drv->cmd_read_cache = NANDCMD_READ_CACHE(drv->chip->col_cycles);
171 drv->cmd_program_load = NANDCMD_PROGRAM_LOAD(drv->chip->col_cycles);
172 }
178} 173}
179 174
180extern int nand_enable_writes(bool en) 175static void setup_chip_registers(nand_drv* drv)
181{ 176{
182 if(en == nand_drv.write_enabled) 177 /* Set chip registers to enter normal operation */
183 return NAND_SUCCESS; 178 if(drv->chip->flags & NAND_CHIPFLAG_HAS_QE_BIT) {
179 bool en = (drv->chip->flags & NAND_CHIPFLAG_QUAD) != 0;
180 nand_upd_reg(drv, FREG_CFG, FREG_CFG_QUAD_ENABLE,
181 en ? FREG_CFG_QUAD_ENABLE : 0);
182 }
184 183
185 int rc = nandop_set_write_protect(!en); 184 /* Clear OTP bit to access the main data array */
186 if(rc == NAND_SUCCESS) 185 nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE, 0);
187 nand_drv.write_enabled = en;
188 186
189 return rc; 187 /* Clear write protection bits */
188 nand_set_reg(drv, FREG_PROT, FREG_PROT_UNLOCK);
190} 189}
191 190
192static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) 191int nand_open(nand_drv* drv)
193{ 192{
194 const uint32_t page_size = (1 << nand_drv.chip_data->log2_page_size); 193 if(drv->refcount > 0)
195
196 if(addr & (page_size - 1))
197 return NAND_ERR_UNALIGNED;
198 if(size & (page_size - 1))
199 return NAND_ERR_UNALIGNED;
200 if(size <= 0)
201 return NAND_SUCCESS; 194 return NAND_SUCCESS;
202 if(write && !nand_drv.write_enabled)
203 return NAND_ERR_WRITE_PROTECT;
204 if((uint32_t)buf & (CACHEALIGN_SIZE - 1))
205 return NAND_ERR_UNALIGNED;
206 195
207 addr >>= nand_drv.chip_data->log2_page_size; 196 /* Initialize the controller */
208 size >>= nand_drv.chip_data->log2_page_size; 197 sfc_open();
198 sfc_set_dev_conf(supported_nand_chips[0].dev_conf);
199 sfc_set_clock(supported_nand_chips[0].clock_freq);
209 200
210 int rc = NAND_SUCCESS; 201 /* Send the software reset command */
211 sfc_lock(); 202 sfc_exec(NANDCMD_RESET, 0, NULL, 0);
203 mdelay(10);
212 204
213 for(; size > 0; --size, ++addr, buf += page_size) { 205 /* Chip identification and setup */
214 if(write) 206 if(!identify_chip(drv))
215 rc = nandop_write_page(addr, buf); 207 return NAND_ERR_UNKNOWN_CHIP;
216 else
217 rc = nandop_read_page(addr, buf);
218 208
219 if(rc) 209 setup_chip_data(drv);
220 break; 210 setup_chip_commands(drv);
221 }
222 211
223 sfc_unlock(); 212 /* Set new SFC parameters */
224 return rc; 213 sfc_set_dev_conf(drv->chip->dev_conf);
225} 214 sfc_set_clock(drv->chip->clock_freq);
226 215
227int nand_read(uint32_t addr, uint32_t size, uint8_t* buf) 216 /* Enter normal operating mode */
228{ 217 setup_chip_registers(drv);
229 return nand_rdwr(false, addr, size, buf);
230}
231 218
232int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf) 219 drv->refcount++;
233{ 220 return NAND_SUCCESS;
234 return nand_rdwr(true, addr, size, (uint8_t*)buf);
235} 221}
236 222
237int nand_erase(uint32_t addr, uint32_t size) 223void nand_close(nand_drv* drv)
238{ 224{
239 const uint32_t page_size = 1 << nand_drv.chip_data->log2_page_size; 225 if(drv->refcount == 0)
240 const uint32_t block_size = page_size << nand_drv.chip_data->log2_block_size; 226 return;
241 const uint32_t pages_per_block = 1 << nand_drv.chip_data->log2_block_size;
242 227
243 if(addr & (block_size - 1)) 228 /* Let's reset the chip... the idea is to restore the registers
244 return NAND_ERR_UNALIGNED; 229 * to whatever they should "normally" be */
245 if(size & (block_size - 1)) 230 sfc_exec(NANDCMD_RESET, 0, NULL, 0);
246 return NAND_ERR_UNALIGNED; 231 mdelay(10);
247 if(size <= 0)
248 return NAND_SUCCESS;
249 if(!nand_drv.write_enabled)
250 return NAND_ERR_WRITE_PROTECT;
251
252 addr >>= nand_drv.chip_data->log2_page_size;
253 size >>= nand_drv.chip_data->log2_page_size;
254 size >>= nand_drv.chip_data->log2_block_size;
255 232
256 int rc = NAND_SUCCESS; 233 sfc_close();
257 sfc_lock(); 234 drv->refcount--;
258
259 for(; size > 0; --size, addr += pages_per_block)
260 if((rc = nandop_erase_block(addr)))
261 break;
262
263 sfc_unlock();
264 return rc;
265} 235}
266 236
267/* 237static uint8_t nand_wait_busy(nand_drv* drv)
268 * NAND ops
269 */
270
271static int nandop_wait_status(int errbit)
272{ 238{
273 int reg; 239 uint8_t reg;
274 do { 240 do {
275 reg = nandcmd_get_feature(NAND_FREG_STATUS); 241 reg = nand_get_reg(drv, FREG_STATUS);
276 if(reg < 0) 242 } while(reg & FREG_STATUS_BUSY);
277 return reg;
278 } while(reg & NAND_FREG_STATUS_OIP);
279
280 if(reg & errbit)
281 return NAND_ERR_COMMAND;
282
283 return reg; 243 return reg;
284} 244}
285 245
286static int nandop_read_page(uint32_t row_addr, uint8_t* buf) 246int nand_block_erase(nand_drv* drv, nand_block_t block)
287{ 247{
288 int status; 248 sfc_exec(NANDCMD_WR_EN, 0, NULL, 0);
289 249 sfc_exec(drv->cmd_block_erase, block, NULL, 0);
290 if((status = nandcmd_page_read_to_cache(row_addr)) < 0)
291 return status;
292 if((status = nandop_wait_status(0)) < 0)
293 return status;
294 if((status = nandcmd_read_from_cache(buf)) < 0)
295 return status;
296 250
297 return NAND_SUCCESS; 251 uint8_t status = nand_wait_busy(drv);
252 if(status & FREG_STATUS_EFAIL)
253 return NAND_ERR_ERASE_FAIL;
254 else
255 return NAND_SUCCESS;
298} 256}
299 257
300static int nandop_write_page(uint32_t row_addr, const uint8_t* buf) 258int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer)
301{ 259{
302 int status; 260 sfc_exec(NANDCMD_WR_EN, 0, NULL, 0);
303 261 sfc_exec(drv->cmd_program_load, 0, (void*)buffer, drv->fpage_size|SFC_WRITE);
304 if((status = nandcmd_write_enable()) < 0) 262 sfc_exec(drv->cmd_program_execute, page, NULL, 0);
305 return status; 263
306 if((status = nandcmd_program_load(buf)) < 0) 264 uint8_t status = nand_wait_busy(drv);
307 return status; 265 if(status & FREG_STATUS_PFAIL)
308 if((status = nandcmd_program_execute(row_addr)) < 0) 266 return NAND_ERR_PROGRAM_FAIL;
309 return status; 267 else
310 if((status = nandop_wait_status(NAND_FREG_STATUS_P_FAIL)) < 0) 268 return NAND_SUCCESS;
311 return status;
312
313 return NAND_SUCCESS;
314} 269}
315 270
316static int nandop_erase_block(uint32_t block_addr) 271int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer)
317{ 272{
318 int status; 273 sfc_exec(drv->cmd_page_read, page, NULL, 0);
319 274 nand_wait_busy(drv);
320 if((status = nandcmd_write_enable()) < 0) 275 sfc_exec(drv->cmd_read_cache, 0, buffer, drv->fpage_size|SFC_READ);
321 return status;
322 if((status = nandcmd_block_erase(block_addr)) < 0)
323 return status;
324 if((status = nandop_wait_status(NAND_FREG_STATUS_E_FAIL)) < 0)
325 return status;
326
327 return NAND_SUCCESS; 276 return NAND_SUCCESS;
328} 277}
329 278
330static int nandop_set_write_protect(bool en) 279int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer)
331{ 280{
332 int val = nandcmd_get_feature(NAND_FREG_PROTECTION); 281 if(byte_len == 0)
333 if(val < 0) 282 return NAND_SUCCESS;
334 return val;
335
336 if(en) {
337 val |= NAND_FREG_PROTECTION_ALLBP;
338 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
339 val |= NAND_FREG_PROTECTION_BRWD;
340 } else {
341 val &= ~NAND_FREG_PROTECTION_ALLBP;
342 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
343 val &= ~NAND_FREG_PROTECTION_BRWD;
344 }
345
346 /* NOTE: The WP pin typically only protects changes to the protection
347 * register -- it doesn't actually prevent writing to the chip. That's
348 * why it should be re-enabled after setting the new protection status.
349 */
350 sfc_set_wp_enable(false);
351 int status = nandcmd_set_feature(NAND_FREG_PROTECTION, val);
352 sfc_set_wp_enable(true);
353
354 if(status < 0)
355 return status;
356 283
357 return NAND_SUCCESS; 284 int rc;
358} 285 unsigned pg_size = drv->chip->page_size;
286 nand_page_t page = byte_addr / pg_size;
287 unsigned offset = byte_addr % pg_size;
288 while(1) {
289 rc = nand_page_read(drv, page, drv->page_buf);
290 if(rc < 0)
291 return rc;
359 292
360/* 293 memcpy(buffer, &drv->page_buf[offset], MIN(pg_size, byte_len));
361 * Low-level NAND commands
362 */
363 294
364static int nandcmd_read_id(int* mf_id, int* dev_id) 295 if(byte_len <= pg_size)
365{ 296 break;
366 sfc_op op = {0};
367 op.command = NAND_CMD_READ_ID;
368 op.flags = SFC_FLAG_READ;
369 op.addr_bytes = 1;
370 op.addr_lo = 0;
371 op.data_bytes = 2;
372 op.buffer = nand_auxbuf;
373 if(sfc_exec(&op))
374 return NAND_ERR_CONTROLLER;
375
376 *mf_id = nand_auxbuf[0];
377 *dev_id = nand_auxbuf[1];
378 return NAND_SUCCESS;
379}
380 297
381static int nandcmd_write_enable(void) 298 offset = 0;
382{ 299 byte_len -= pg_size;
383 sfc_op op = {0}; 300 buffer += pg_size;
384 op.command = NAND_CMD_WRITE_ENABLE; 301 page++;
385 if(sfc_exec(&op)) 302 }
386 return NAND_ERR_CONTROLLER;
387 303
388 return NAND_SUCCESS; 304 return NAND_SUCCESS;
389} 305}
390 306
391static int nandcmd_get_feature(uint8_t reg) 307int nand_write_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer)
392{ 308{
393 sfc_op op = {0}; 309 if(byte_len == 0)
394 op.command = NAND_CMD_GET_FEATURE; 310 return NAND_SUCCESS;
395 op.flags = SFC_FLAG_READ;
396 op.addr_bytes = 1;
397 op.addr_lo = reg;
398 op.data_bytes = 1;
399 op.buffer = nand_auxbuf;
400 if(sfc_exec(&op))
401 return NAND_ERR_CONTROLLER;
402
403 return nand_auxbuf[0];
404}
405
406static int nandcmd_set_feature(uint8_t reg, uint8_t val)
407{
408 sfc_op op = {0};
409 op.command = NAND_CMD_SET_FEATURE;
410 op.flags = SFC_FLAG_WRITE;
411 op.addr_bytes = 1;
412 op.addr_lo = reg;
413 op.data_bytes = 1;
414 op.buffer = nand_auxbuf;
415 nand_auxbuf[0] = val;
416 if(sfc_exec(&op))
417 return NAND_ERR_CONTROLLER;
418 311
419 return NAND_SUCCESS; 312 int rc;
420} 313 unsigned pg_size = drv->chip->page_size;
314 unsigned blk_size = pg_size << drv->chip->log2_ppb;
421 315
422static int nandcmd_page_read_to_cache(uint32_t row_addr) 316 if(byte_addr % blk_size != 0)
423{ 317 return NAND_ERR_UNALIGNED;
424 sfc_op op = {0}; 318 if(byte_len % blk_size != 0)
425 op.command = NAND_CMD_PAGE_READ_TO_CACHE; 319 return NAND_ERR_UNALIGNED;
426 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
427 op.addr_lo = row_addr;
428 if(sfc_exec(&op))
429 return NAND_ERR_CONTROLLER;
430 320
431 return NAND_SUCCESS; 321 nand_page_t page = byte_addr / pg_size;
432} 322 nand_page_t end_page = page + (byte_len / pg_size);
433 323
434static int nandcmd_read_from_cache(uint8_t* buf) 324 for(nand_block_t blk = page; blk < end_page; blk += drv->ppb) {
435{ 325 rc = nand_block_erase(drv, blk);
436 sfc_op op = {0}; 326 if(rc < 0)
437 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { 327 return rc;
438 op.command = NAND_CMD_READ_FROM_CACHEx4;
439 op.mode = SFC_MODE_QUAD_IO;
440 } else {
441 op.command = NAND_CMD_READ_FROM_CACHE;
442 op.mode = SFC_MODE_STANDARD;
443 } 328 }
444 329
445 op.flags = SFC_FLAG_READ; 330 for(; page != end_page; ++page) {
446 op.addr_bytes = nand_drv.chip_data->coladdr_width; 331 memcpy(drv->page_buf, buffer, pg_size);
447 op.addr_lo = 0; 332 memset(&drv->page_buf[pg_size], 0xff, drv->chip->oob_size);
448 op.dummy_bits = 8; // NOTE: this may need a chip_data parameter 333 buffer += pg_size;
449 op.data_bytes = (1 << nand_drv.chip_data->log2_page_size);
450 op.buffer = buf;
451 if(sfc_exec(&op))
452 return NAND_ERR_CONTROLLER;
453
454 return NAND_SUCCESS;
455}
456 334
457static int nandcmd_program_load(const uint8_t* buf) 335 rc = nand_page_program(drv, page, drv->page_buf);
458{ 336 if(rc < 0)
459 sfc_op op = {0}; 337 return rc;
460 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) {
461 op.command = NAND_CMD_PROGRAM_LOADx4;
462 op.mode = SFC_MODE_QUAD_IO;
463 } else {
464 op.command = NAND_CMD_PROGRAM_LOAD;
465 op.mode = SFC_MODE_STANDARD;
466 } 338 }
467 339
468 op.flags = SFC_FLAG_WRITE;
469 op.addr_bytes = nand_drv.chip_data->coladdr_width;
470 op.addr_lo = 0;
471 op.data_bytes = (1 << nand_drv.chip_data->log2_page_size);
472 op.buffer = (void*)buf;
473 if(sfc_exec(&op))
474 return NAND_ERR_CONTROLLER;
475
476 return NAND_SUCCESS;
477}
478
479static int nandcmd_program_execute(uint32_t row_addr)
480{
481 sfc_op op = {0};
482 op.command = NAND_CMD_PROGRAM_EXECUTE;
483 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
484 op.addr_lo = row_addr;
485 if(sfc_exec(&op))
486 return NAND_ERR_CONTROLLER;
487
488 return NAND_SUCCESS; 340 return NAND_SUCCESS;
489} 341}
490 342
491static int nandcmd_block_erase(uint32_t block_addr) 343/* TODO - NAND driver future improvements
492{ 344 *
493 sfc_op op = {0}; 345 * 1. Support sofware or on-die ECC transparently. Support debug ECC bypass.
494 op.command = NAND_CMD_BLOCK_ERASE; 346 *
495 op.addr_bytes = nand_drv.chip_data->rowaddr_width; 347 * It's probably best to add an API call to turn ECC on or off. Software
496 op.addr_lo = block_addr; 348 * ECC and most or all on-die ECC implementations require some OOB bytes
497 if(sfc_exec(&op)) 349 * to function; which leads us to the next problem...
498 return NAND_ERR_CONTROLLER; 350 *
499 351 * 2. Allow safe access to OOB areas
500 return NAND_SUCCESS; 352 *
501} 353 * The OOB data area is not fully available to users; it is also occupied
354 * by ECC data and bad block markings. The NAND driver needs to provide a
355 * mapping which allows OOB data users to map around those reserved areas,
356 * otherwise it's not really possible to use OOB data.
357 *
358 * 3. Support partial page programming.
359 *
360 * This might already work. My understanding of NAND flash is that bits are
361 * represented by charge deposited on flash cells. In the case of SLC flash,
362 * cells are one bit. For MLC flash, cells can store more than one bit; but
363 * MLC flash is much less reliable than SLC. We probably don't have to be
364 * concerned about MLC flash, and its does not support partial programming
365 * anyway due to the cell characteristics, so I will only consider SLC here.
366 *
367 * For SLC there are two cell states -- an uncharged cell represents a "1"
368 * and a charged cell represents "0". Programming can only deposit charge
369 * on a cell and erasing can only remove charge. Therefore, "programming" a
370 * cell to 1 is actually a no-op.
371 *
372 * So, there's no datasheet which spells this out, but I suspect you just
373 * set the areas you're not interested in programming to 0xff. Programming
374 * can never change a written 0 back to a 1, so programming a 1 bit works
375 * more like a "don't care" (= keep whatever value is already there).
376 *
377 * What _is_ given by the datasheets is limits on how many times you can
378 * reprogram the same page without erasing it. This is an overall limit
379 * called NOP (number of programs) in many datasheets. In addition to this,
380 * sub-regions of the page have further limits: it's common for a 2048+64
381 * byte page to be split into 8 regions, with four 512-byte main areas and
382 * four 16-byte OOB areas. Usually, each subregion can only be programmed
383 * once. However, you can write multiple subregions with a single program.
384 *
385 * Violating programming constraints could cause data loss, so we need to
386 * communicate to upper layers what the limitations are here if they want
387 * to use partial programming safely.
388 *
389 * Programming the same page more than once increases the overall stress
390 * on the flash cells and can cause bitflips. For this reason, it's best
391 * to keep the number of programs as low as possible. Some sources suggest
392 * that programming the pages in a block in linear order is also better to
393 * reduce stress, although I don't know why this would be.
394 *
395 * These program/read stresses can flip bits, but it's only due to residual
396 * charge building up on uncharged cells; cells are not permanently damaged
397 * by these kind of stresses. Erasing the block will remove the charge and
398 * restore all the cells to a clean state.
399 *
400 * These slides are fairly informative on this subject:
401 * - https://cushychicken.github.io/assets/cooke_inconvenient_truths.pdf
402 *
403 * 4. Bad block management
404 *
405 * This probably doesn't belong in the NAND layer but it seems wise to keep
406 * at least a bad block table at the level of the NAND driver. Factory bad
407 * block marks are usually some non-0xFF byte in the OOB area, but bad blocks
408 * which develop over the device lifetime usually won't be marked; after all
409 * they are unreliable, so we can't program a marking on them and expect it
410 * to stick. So, most FTL systems keep a bad block table somewhere in flash
411 * and update it whenever a block goes bad.
412 *
413 * So, in addition to a bad block marker scan, we should try to gather bad
414 * block information from such tables.
415 */
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h
index cc56b836f8..711bf190b5 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h
@@ -22,86 +22,161 @@
22#ifndef __NAND_X1000_H__ 22#ifndef __NAND_X1000_H__
23#define __NAND_X1000_H__ 23#define __NAND_X1000_H__
24 24
25/* NOTE: this is a very minimal API designed only to support a bootloader. 25#include <stdint.h>
26 * Not suitable for general data storage. It doesn't have proper support for 26#include <stddef.h>
27 * partial page writes, access to spare area, etc, which are all necessary 27#include <stdbool.h>
28 * for an effective flash translation layer. 28#include "kernel.h"
29
30#define NAND_SUCCESS 0
31#define NAND_ERR_UNKNOWN_CHIP (-1)
32#define NAND_ERR_PROGRAM_FAIL (-2)
33#define NAND_ERR_ERASE_FAIL (-3)
34#define NAND_ERR_UNALIGNED (-4)
35
36/* keep max page size in sync with the NAND chip table in the .c file */
37#define NAND_DRV_SCRATCHSIZE 32
38#define NAND_DRV_MAXPAGESIZE 2112
39
40/* Quad I/O support bit */
41#define NAND_CHIPFLAG_QUAD 0x0001
42/* Chip requires QE bit set to enable quad I/O mode */
43#define NAND_CHIPFLAG_HAS_QE_BIT 0x0002
44
45/* Types to distinguish between block & page addresses in the API.
29 * 46 *
30 * There's no ECC support. This can be added if necessary, but it's unlikely 47 * BIT 31 log2_ppb bits
31 * the boot area on any X1000 device uses software ECC as Ingenic's SPL simply 48 * +-------------------------------+---------------+
32 * doesn't have much room for more code (theirs programmed to work on multiple 49 * nand_page_t = | block nr | page nr |
33 * hardware configurations, so it's bigger than ours). 50 * +-------------------------------+---------------+
51 * BIT 0
52 *
53 * The page address is split into block and page numbers. Page numbers occupy
54 * the lower log2_ppb bits, and the block number occupies the upper bits.
55 *
56 * Block addresses are structured the same as page addresses, but with a page
57 * number of 0. So block number N has address N << log2_ppb.
34 */ 58 */
59typedef uint32_t nand_block_t;
60typedef uint32_t nand_page_t;
35 61
36#include <stdint.h> 62typedef struct nand_chip {
37#include <stdbool.h> 63 /* Manufacturer and device ID bytes */
38#include <stddef.h> 64 uint8_t mf_id;
65 uint8_t dev_id;
39 66
40/* Error codes which can be returned by the NAND API */ 67 /* Row/column address width */
41#define NAND_SUCCESS 0 68 uint8_t row_cycles;
42#define NAND_ERR_UNKNOWN_CHIP (-1) 69 uint8_t col_cycles;
43#define NAND_ERR_UNALIGNED (-2)
44#define NAND_ERR_WRITE_PROTECT (-3)
45#define NAND_ERR_CONTROLLER (-4)
46#define NAND_ERR_COMMAND (-5)
47 70
48/* Chip supports quad I/O for page read/write */ 71 /* Base2 logarithm of the number of pages per block */
49#define NANDCHIP_FLAG_QUAD 0x01 72 unsigned log2_ppb;
50 73
51/* Set/clear the BRWD bit when enabling/disabling write protection */ 74 /* Size of a page's main / oob areas, in bytes. */
52#define NANDCHIP_FLAG_USE_BRWD 0x02 75 unsigned page_size;
76 unsigned oob_size;
53 77
54typedef struct nand_chip_data { 78 /* Total number of blocks in the chip */
55 /* Chip manufacturer / device ID */ 79 unsigned nr_blocks;
56 uint8_t mf_id;
57 uint8_t dev_id;
58 80
59 /* Width of row/column addresses in bytes */ 81 /* Clock frequency to use */
60 uint8_t rowaddr_width; 82 uint32_t clock_freq;
61 uint8_t coladdr_width;
62 83
63 /* SFC dev conf and clock frequency to use for this device */ 84 /* Value of sfc_dev_conf */
64 uint32_t dev_conf; 85 uint32_t dev_conf;
65 uint32_t clock_freq;
66 86
67 /* Page size in bytes = 1 << log2_page_size */ 87 /* Chip specific flags */
68 uint32_t log2_page_size; 88 uint32_t flags;
89} nand_chip;
90
91typedef struct nand_drv {
92 /* NAND access lock. Needs to be held during any operations. */
93 struct mutex mutex;
94
95 /* Reference count for open/close operations */
96 unsigned refcount;
97
98 /* Scratch and page buffers. Both need to be cacheline-aligned and are
99 * provided externally by the caller prior to nand_open().
100 *
101 * - The scratch buffer is NAND_DRV_SCRATCHSIZE bytes long and is used
102 * for small data transfers associated with commands. It must not be
103 * disturbed while any NAND operation is in progress.
104 *
105 * - The page buffer is used by certain functions like nand_read_bytes(),
106 * but it's main purpose is to provide a common temporary buffer for
107 * driver users to perform I/O with. Must be fpage_size bytes long.
108 */
109 uint8_t* scratch_buf;
110 uint8_t* page_buf;
111
112 /* Pointer to the chip data. */
113 const nand_chip* chip;
114
115 /* Pages per block = 1 << chip->log2_ppb */
116 unsigned ppb;
117
118 /* Full page size = chip->page_size + chip->oob_size */
119 unsigned fpage_size;
120
121 /* Probed mf_id / dev_id for debugging, in case identification fails. */
122 uint8_t mf_id;
123 uint8_t dev_id;
124
125 /* SFC commands used for I/O, these are set based on chip data */
126 uint32_t cmd_page_read;
127 uint32_t cmd_read_cache;
128 uint32_t cmd_program_load;
129 uint32_t cmd_program_execute;
130 uint32_t cmd_block_erase;
131} nand_drv;
132
133extern const nand_chip supported_nand_chips[];
134extern const size_t nr_supported_nand_chips;
135
136/* Return the static NAND driver instance.
137 *
138 * ALL normal Rockbox code should use this instance. The SPL does not
139 * use it, because it needs to manually place buffers in external RAM.
140 */
141extern nand_drv* nand_init(void);
142
143static inline void nand_lock(nand_drv* drv)
144{
145 mutex_lock(&drv->mutex);
146}
69 147
70 /* Block size in number of pages = 1 << log2_block_size */ 148static inline void nand_unlock(nand_drv* drv)
71 uint32_t log2_block_size; 149{
150 mutex_unlock(&drv->mutex);
151}
72 152
73 /* Chip flags */ 153/* Open or close the NAND driver
74 uint32_t flags; 154 *
75} nand_chip_data; 155 * The NAND driver is reference counted, and opening / closing it will
156 * increment and decrement the reference count. The hardware is only
157 * controlled when the reference count rises above or falls to 0, else
158 * these functions are no-ops which always succeed.
159 *
160 * These functions require the lock to be held.
161 */
162extern int nand_open(nand_drv* drv);
163extern void nand_close(nand_drv* drv);
164
165/* Read / program / erase operations. Buffer needs to be cache-aligned for DMA.
166 * Read and program operate on full page data, ie. including OOB data areas.
167 *
168 * NOTE: ECC is not implemented. If it ever needs to be, these functions will
169 * probably use ECC transparently. All code should be written to expect this.
170 */
171extern int nand_block_erase(nand_drv* drv, nand_block_t block);
172extern int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer);
173extern int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer);
76 174
77/* Open or close the NAND driver. The NAND driver takes control of the SFC, 175/* Wrappers to read/write bytes. For simple access to the main data area only.
78 * so that driver must be in the closed state before opening the NAND driver. 176 * The write address / length must align to a block boundary. Reads do not have
177 * any alignment requirement. OOB data is never read, and is written as 0xff.
79 */ 178 */
80extern int nand_open(void); 179extern int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer);
81extern void nand_close(void); 180extern int nand_write_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer);
82
83/* Identify the NAND chip. This must be done after opening the driver and
84 * prior to any data access, in order to set the chip parameters. */
85extern int nand_identify(int* mf_id, int* dev_id);
86
87/* Return the chip data for the identified NAND chip.
88 * Returns NULL if the chip is not identified. */
89const nand_chip_data* nand_get_chip_data(void);
90
91/* Controls the chip's write protect features. The driver also keeps track of
92 * this flag and refuses to perform write or erase operations unless you have
93 * enabled writes. Writes should be disabled again when you finish writing. */
94extern int nand_enable_writes(bool en);
95
96/* Reading and writing operates on whole pages at a time. If the address or
97 * size is not aligned to a multiple of the page size, no data will be read
98 * or written and an error code is returned. */
99extern int nand_read(uint32_t addr, uint32_t size, uint8_t* buf);
100extern int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf);
101
102/* Erase operates on whole blocks. Like the page read/write operations,
103 * the address and size must be aligned to a multiple of the block size.
104 * If not, no blocks are erased and an error code is returned. */
105extern int nand_erase(uint32_t addr, uint32_t size);
106 181
107#endif /* __NAND_X1000_H__ */ 182#endif /* __NAND_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.c b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
index c1fde89b70..5ade6bcc64 100644
--- a/firmware/target/mips/ingenic_x1000/sfc-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
@@ -21,86 +21,71 @@
21 21
22#include "system.h" 22#include "system.h"
23#include "kernel.h" 23#include "kernel.h"
24#include "panic.h"
25#include "sfc-x1000.h" 24#include "sfc-x1000.h"
25#include "clk-x1000.h"
26#include "irq-x1000.h" 26#include "irq-x1000.h"
27#include "x1000/sfc.h"
28#include "x1000/cpm.h"
29
30/* DMA works, but not in the SPL due to some hardware not being set up right.
31 * Only the SPL and bootloader actually require flash access, so to keep it
32 * simple, DMA is unconditionally disabled. */
33//#define NEED_SFC_DMA
34 27
28/* #define USE_DMA */
35#define FIFO_THRESH 31 29#define FIFO_THRESH 31
36 30
37#define SFC_STATUS_PENDING (-1) 31static void sfc_poll_wait(void);
32#ifdef USE_DMA
33static void sfc_irq_wait(void);
38 34
39#ifdef NEED_SFC_DMA
40static struct mutex sfc_mutex;
41static struct semaphore sfc_sema; 35static struct semaphore sfc_sema;
42static struct timeout sfc_lockup_tmo;
43static bool sfc_inited = false;
44static volatile int sfc_status;
45#else
46# define sfc_status SFC_STATUS_OK
47#endif
48
49void sfc_init(void)
50{
51#ifdef NEED_SFC_DMA
52 if(sfc_inited)
53 return;
54
55 mutex_init(&sfc_mutex);
56 semaphore_init(&sfc_sema, 1, 0);
57 sfc_inited = true;
58#endif
59}
60
61void sfc_lock(void)
62{
63#ifdef NEED_SFC_DMA
64 mutex_lock(&sfc_mutex);
65#endif
66}
67 36
68void sfc_unlock(void) 37/* This function pointer thing is a hack for the SPL, since it has to use
69{ 38 * the NAND driver directly and we can't afford to drag in the whole kernel
70#ifdef NEED_SFC_DMA 39 * just to wait on a semaphore. */
71 mutex_unlock(&sfc_mutex); 40static void(*sfc_wait)(void) = sfc_poll_wait;
72#endif 41#endif
73}
74 42
75void sfc_open(void) 43void sfc_open(void)
76{ 44{
77 jz_writef(CPM_CLKGR, SFC(0)); 45 jz_writef(CPM_CLKGR, SFC(0));
46#ifdef USE_DMA
47 jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32),
48 PHASE_NUM(1), THRESHOLD(FIFO_THRESH), WP_EN(1));
49#else
78 jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1), 50 jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1),
79 THRESHOLD(FIFO_THRESH), WP_EN(1)); 51 THRESHOLD(FIFO_THRESH), WP_EN(1));
52#endif
80 REG_SFC_CGE = 0; 53 REG_SFC_CGE = 0;
81 REG_SFC_INTC = 0x1f; 54 REG_SFC_INTC = 0x1f;
82 REG_SFC_MEM_ADDR = 0; 55 REG_SFC_MEM_ADDR = 0;
56}
57
58void sfc_close(void)
59{
60 REG_SFC_CGE = 0x1f;
61 jz_writef(CPM_CLKGR, SFC(1));
62}
63
64void sfc_irq_begin(void)
65{
66#ifdef USE_DMA
67 static bool inited = false;
68 if(!inited) {
69 semaphore_init(&sfc_sema, 1, 0);
70 inited = true;
71 }
83 72
84#ifdef NEED_SFC_DMA
85 jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32));
86 system_enable_irq(IRQ_SFC); 73 system_enable_irq(IRQ_SFC);
74 sfc_wait = sfc_irq_wait;
87#endif 75#endif
88} 76}
89 77
90void sfc_close(void) 78void sfc_irq_end(void)
91{ 79{
92#ifdef NEED_SFC_DMA 80#ifdef USE_DMA
93 system_disable_irq(IRQ_SFC); 81 system_disable_irq(IRQ_SFC);
82 sfc_wait = sfc_poll_wait;
94#endif 83#endif
95
96 REG_SFC_CGE = 0x1f;
97 jz_writef(CPM_CLKGR, SFC(1));
98} 84}
99 85
100void sfc_set_clock(uint32_t freq) 86void sfc_set_clock(uint32_t freq)
101{ 87{
102 /* TODO: This is a hack so we can use MPLL in the SPL. 88 /* FIXME: Get rid of this hack & allow defining a real clock tree... */
103 * There must be a better way to do this... */
104 x1000_clk_t clksrc = X1000_CLK_MPLL; 89 x1000_clk_t clksrc = X1000_CLK_MPLL;
105 uint32_t in_freq = clk_get(clksrc); 90 uint32_t in_freq = clk_get(clksrc);
106 if(in_freq < freq) { 91 if(in_freq < freq) {
@@ -115,170 +100,99 @@ void sfc_set_clock(uint32_t freq)
115 jz_writef(CPM_SSICDR, CE(0)); 100 jz_writef(CPM_SSICDR, CE(0));
116} 101}
117 102
118#ifdef NEED_SFC_DMA 103#ifndef USE_DMA
119static int sfc_lockup_tmo_cb(struct timeout* tmo) 104static void sfc_fifo_rdwr(bool write, void* buffer, uint32_t data_bytes)
120{
121 (void)tmo;
122
123 int irq = disable_irq_save();
124 if(sfc_status == SFC_STATUS_PENDING) {
125 sfc_status = SFC_STATUS_LOCKUP;
126 jz_overwritef(SFC_TRIG, STOP(1));
127 semaphore_release(&sfc_sema);
128 }
129
130 restore_irq(irq);
131 return 0;
132}
133
134static void sfc_wait_end(void)
135{ 105{
136 semaphore_wait(&sfc_sema, TIMEOUT_BLOCK); 106 uint32_t* word_buf = (uint32_t*)buffer;
137} 107 uint32_t sr_bit = write ? BM_SFC_SR_TREQ : BM_SFC_SR_RREQ;
138 108 uint32_t clr_bit = write ? BM_SFC_SCR_CLR_TREQ : BM_SFC_SCR_CLR_RREQ;
139void SFC(void) 109 uint32_t data_words = (data_bytes + 3) / 4;
140{
141 unsigned sr = REG_SFC_SR & ~REG_SFC_INTC;
142
143 if(jz_vreadf(sr, SFC_SR, OVER)) {
144 jz_overwritef(SFC_SCR, CLR_OVER(1));
145 sfc_status = SFC_STATUS_OVERFLOW;
146 } else if(jz_vreadf(sr, SFC_SR, UNDER)) {
147 jz_overwritef(SFC_SCR, CLR_UNDER(1));
148 sfc_status = SFC_STATUS_UNDERFLOW;
149 } else if(jz_vreadf(sr, SFC_SR, END)) {
150 jz_overwritef(SFC_SCR, CLR_END(1));
151 sfc_status = SFC_STATUS_OK;
152 } else {
153 panicf("SFC IRQ bug");
154 return;
155 }
156
157 /* Not sure this is wholly correct */
158 if(sfc_status != SFC_STATUS_OK)
159 jz_overwritef(SFC_TRIG, STOP(1));
160
161 REG_SFC_INTC = 0x1f;
162 semaphore_release(&sfc_sema);
163}
164#else
165/* Note the X1000 is *very* picky about how the SFC FIFOs are accessed
166 * so please do NOT try to rearrange the code without testing it first!
167 */
168
169static void sfc_fifo_read(unsigned* buffer, int data_bytes)
170{
171 int data_words = (data_bytes + 3) / 4;
172 while(data_words > 0) { 110 while(data_words > 0) {
173 if(jz_readf(SFC_SR, RREQ)) { 111 if(REG_SFC_SR & sr_bit) {
174 jz_overwritef(SFC_SCR, CLR_RREQ(1)); 112 REG_SFC_SCR = clr_bit;
175 113
176 int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; 114 /* We need to read/write in bursts equal to FIFO threshold amount
115 * X1000 PM, 10.8.5, SFC > software guidelines > slave mode */
116 uint32_t amount = MIN(data_words, FIFO_THRESH);
177 data_words -= amount; 117 data_words -= amount;
178 while(amount > 0) {
179 *buffer++ = REG_SFC_DATA;
180 amount -= 1;
181 }
182 }
183 }
184}
185 118
186static void sfc_fifo_write(const unsigned* buffer, int data_bytes) 119 uint32_t* endptr = word_buf + amount;
187{ 120 for(; word_buf != endptr; ++word_buf) {
188 int data_words = (data_bytes + 3) / 4; 121 if(write)
189 while(data_words > 0) { 122 REG_SFC_DATA = *word_buf;
190 if(jz_readf(SFC_SR, TREQ)) { 123 else
191 jz_overwritef(SFC_SCR, CLR_TREQ(1)); 124 *word_buf = REG_SFC_DATA;
192
193 int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words;
194 data_words -= amount;
195 while(amount > 0) {
196 REG_SFC_DATA = *buffer++;
197 amount -= 1;
198 } 125 }
199 } 126 }
200 } 127 }
201} 128}
202
203static void sfc_wait_end(void)
204{
205 while(jz_readf(SFC_SR, END) == 0);
206 jz_overwritef(SFC_SCR, CLR_TREQ(1));
207}
208
209#endif /* NEED_SFC_DMA */
210
211int sfc_exec(const sfc_op* op)
212{
213#ifdef NEED_SFC_DMA
214 uint32_t intc_clear = jz_orm(SFC_INTC, MSK_END);
215#endif 129#endif
216 130
217 if(op->flags & (SFC_FLAG_READ|SFC_FLAG_WRITE)) { 131void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size)
218 jz_writef(SFC_TRAN_CONF(0), DATA_EN(1)); 132{
219 REG_SFC_TRAN_LENGTH = op->data_bytes; 133 /* Deal with transfer direction */
220#ifdef NEED_SFC_DMA 134 bool write = (size & SFC_WRITE) != 0;
221 REG_SFC_MEM_ADDR = PHYSADDR(op->buffer); 135 uint32_t glb = REG_SFC_GLB;
222#endif 136 if(data) {
223 137 if(write) {
224 if(op->flags & SFC_FLAG_READ) 138 jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(WRITE));
225 { 139 size &= ~SFC_WRITE;
226 jz_writef(SFC_GLB, TRAN_DIR_V(READ)); 140#ifdef USE_DMA
227#ifdef NEED_SFC_DMA 141 commit_dcache_range(data, size);
228 discard_dcache_range(op->buffer, op->data_bytes);
229 intc_clear |= jz_orm(SFC_INTC, MSK_OVER);
230#endif 142#endif
231 } 143 } else {
232 else 144 jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(READ));
233 { 145#ifdef USE_DMA
234 jz_writef(SFC_GLB, TRAN_DIR_V(WRITE)); 146 discard_dcache_range(data, size);
235#ifdef NEED_SFC_DMA
236 commit_dcache_range(op->buffer, op->data_bytes);
237 intc_clear |= jz_orm(SFC_INTC, MSK_UNDER);
238#endif 147#endif
239 } 148 }
240 } else {
241 jz_writef(SFC_TRAN_CONF(0), DATA_EN(0));
242 REG_SFC_TRAN_LENGTH = 0;
243#ifdef NEED_SFC_DMA
244 REG_SFC_MEM_ADDR = 0;
245#endif
246 } 149 }
247 150
248 bool dummy_first = (op->flags & SFC_FLAG_DUMMYFIRST) != 0; 151 /* Program transfer configuration */
249 jz_writef(SFC_TRAN_CONF(0), 152 REG_SFC_GLB = glb;
250 MODE(op->mode), POLL_EN(0), 153 REG_SFC_TRAN_LENGTH = size;
251 ADDR_WIDTH(op->addr_bytes), 154#ifdef USE_DMA
252 PHASE_FMT(dummy_first ? 1 : 0), 155 REG_SFC_MEM_ADDR = PHYSADDR(data);
253 DUMMY_BITS(op->dummy_bits), 156#endif
254 COMMAND(op->command), CMD_EN(1)); 157 REG_SFC_TRAN_CONF(0) = cmd;
255 158 REG_SFC_DEV_ADDR(0) = addr;
256 REG_SFC_DEV_ADDR(0) = op->addr_lo; 159 REG_SFC_DEV_PLUS(0) = 0;
257 REG_SFC_DEV_PLUS(0) = op->addr_hi;
258 160
259#ifdef NEED_SFC_DMA 161 /* Clear old interrupts */
260 sfc_status = SFC_STATUS_PENDING;
261 timeout_register(&sfc_lockup_tmo, sfc_lockup_tmo_cb, 10*HZ, 0);
262 REG_SFC_SCR = 0x1f; 162 REG_SFC_SCR = 0x1f;
263 REG_SFC_INTC &= ~intc_clear; 163 jz_writef(SFC_INTC, MSK_END(0));
264#endif
265 164
165 /* Start the command */
266 jz_overwritef(SFC_TRIG, FLUSH(1)); 166 jz_overwritef(SFC_TRIG, FLUSH(1));
267 jz_overwritef(SFC_TRIG, START(1)); 167 jz_overwritef(SFC_TRIG, START(1));
268 168
269#ifndef NEED_SFC_DMA 169 /* Data transfer by PIO or DMA, and wait for completion */
270 if(op->flags & SFC_FLAG_READ) 170#ifndef USE_DMA
271 sfc_fifo_read((unsigned*)op->buffer, op->data_bytes); 171 sfc_fifo_rdwr(write, data, size);
272 if(op->flags & SFC_FLAG_WRITE) 172 sfc_poll_wait();
273 sfc_fifo_write((const unsigned*)op->buffer, op->data_bytes); 173#else
174 sfc_wait();
274#endif 175#endif
176}
275 177
276 sfc_wait_end(); 178static void sfc_poll_wait(void)
179{
180 while(jz_readf(SFC_SR, END) == 0);
181 jz_overwritef(SFC_SCR, CLR_END(1));
182}
277 183
278#ifdef NEED_SFC_DMA 184#ifdef USE_DMA
279 if(op->flags & SFC_FLAG_READ) 185static void sfc_irq_wait(void)
280 discard_dcache_range(op->buffer, op->data_bytes); 186{
281#endif 187 semaphore_wait(&sfc_sema, TIMEOUT_BLOCK);
188}
282 189
283 return sfc_status; 190void SFC(void)
191{
192 /* the only interrupt we use is END; errors are basically not
193 * possible with the SPI interface... */
194 semaphore_release(&sfc_sema);
195 jz_overwritef(SFC_SCR, CLR_END(1));
196 jz_writef(SFC_INTC, MSK_END(1));
284} 197}
198#endif
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.h b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
index 5784198b93..d28bcb6740 100644
--- a/firmware/target/mips/ingenic_x1000/sfc-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.h
@@ -19,87 +19,107 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#ifndef __SFC_X1000_H__
23#define __SFC_X1000_H__
24
25#include "x1000/sfc.h"
22#include <stdint.h> 26#include <stdint.h>
23#include <stdbool.h> 27#include <stdbool.h>
24#include "clk-x1000.h"
25#include "x1000/sfc.h"
26 28
27/* SPI flash controller interface -- this is a low-level driver upon which 29/* SPI transfer mode. SFC_TMODE_X_Y_Z means:
28 * you can build NAND/NOR flash drivers. The main function is sfc_exec(), 30 *
29 * used to issue commands, transfer data, etc. 31 * - X lines for command phase
32 * - Y lines for address+dummy phase
33 * - Z lines for data phase
30 */ 34 */
35#define SFC_TMODE_1_1_1 0
36#define SFC_TMODE_1_1_2 1
37#define SFC_TMODE_1_2_2 2
38#define SFC_TMODE_2_2_2 3
39#define SFC_TMODE_1_1_4 4
40#define SFC_TMODE_1_4_4 5
41#define SFC_TMODE_4_4_4 6
31 42
32#define SFC_FLAG_READ 0x01 /* Read data */ 43/* Phase format
33#define SFC_FLAG_WRITE 0x02 /* Write data */ 44 * _____________________
34#define SFC_FLAG_DUMMYFIRST 0x04 /* Do dummy bits before sending address. 45 * / SFC_PFMT_ADDR_FIRST \
35 * Default is dummy bits after address. 46 * +-----+-------+-------+------+
36 */ 47 * | cmd | addr | dummy | data |
48 * +-----+-------+-------+------+
49 * ______________________
50 * / SFC_PFMT_DUMMY_FIRST \
51 * +-----+-------+-------+------+
52 * | cmd | dummy | addr | data |
53 * +-----+-------+-------+------+
54 */
55#define SFC_PFMT_ADDR_FIRST 0
56#define SFC_PFMT_DUMMY_FIRST 1
37 57
38/* SPI transfer mode. If in doubt, check with the X1000 manual and confirm 58/* Direction of transfer flag */
39 * the transfer format is what you expect. 59#define SFC_READ 0
60#define SFC_WRITE (1 << 31)
61
62/** \brief Macro to generate an SFC command for use with sfc_exec()
63 * \param cmd Command number (up to 16 bits)
64 * \param tmode SPI transfer mode
65 * \param awidth Number of address bytes
66 * \param dwidth Number of dummy cycles (1 cycle = 1 bit)
67 * \param pfmt Phase format (address first or dummy first)
68 * \param data_en 1 to enable data phase, 0 to omit it
40 */ 69 */
41#define SFC_MODE_STANDARD 0 70#define SFC_CMD(cmd, tmode, awidth, dwidth, pfmt, data_en) \
42#define SFC_MODE_DUAL_IN_DUAL_OUT 1 71 jz_orf(SFC_TRAN_CONF, COMMAND(cmd), CMD_EN(1), \
43#define SFC_MODE_DUAL_IO 2 72 MODE(tmode), ADDR_WIDTH(awidth), DUMMY_BITS(dwidth), \
44#define SFC_MODE_FULL_DUAL_IO 3 73 PHASE_FMT(pfmt), DATA_EN(data_en))
45#define SFC_MODE_QUAD_IN_QUAD_OUT 4
46#define SFC_MODE_QUAD_IO 5
47#define SFC_MODE_FULL_QUAD_IO 6
48 74
49/* Return status codes for sfc_exec() */ 75/* Open/close SFC hardware */
50#define SFC_STATUS_OK 0 76extern void sfc_open(void);
51#define SFC_STATUS_OVERFLOW 1 77extern void sfc_close(void);
52#define SFC_STATUS_UNDERFLOW 2
53#define SFC_STATUS_LOCKUP 3
54 78
55typedef struct sfc_op { 79/* Enable IRQ mode, instead of busy waiting for operations to complete.
56 int command; /* Command number */ 80 * Needs to be called separately after sfc_open(), because the SPL has to
57 int mode; /* SPI transfer mode */ 81 * use busy waiting, but we cannot #ifdef it for the SPL due to limitations
58 int flags; /* Flags for this op */ 82 * of the build system. */
59 int addr_bytes; /* Number of address bytes */ 83extern void sfc_irq_begin(void);
60 int dummy_bits; /* Number of dummy bits (yes: bits, not bytes) */ 84extern void sfc_irq_end(void);
61 uint32_t addr_lo; /* Lower 32 bits of address */
62 uint32_t addr_hi; /* Upper 32 bits of address */
63 int data_bytes; /* Number of data bytes to read/write */
64 void* buffer; /* Data buffer -- MUST be word-aligned */
65} sfc_op;
66 85
67/* One-time driver init for mutexes/etc needed for handling interrupts. 86/* Change the SFC clock frequency */
68 * This can be safely called multiple times; only the first call will 87extern void sfc_set_clock(uint32_t freq);
69 * actually perform the init. 88
70 */ 89/* Set the device configuration register */
71extern void sfc_init(void); 90inline void sfc_set_dev_conf(uint32_t conf)
91{
92 REG_SFC_DEV_CONF = conf;
93}
72 94
73/* Controller mutex -- lock before touching the driver */ 95/* Control the state of the write protect pin */
74extern void sfc_lock(void); 96inline void sfc_set_wp_enable(bool en)
75extern void sfc_unlock(void); 97{
98 jz_writef(SFC_GLB, WP_EN(en ? 1 : 0));
99}
76 100
77/* Open/close the driver. The driver must be open in order to do operations. 101/** \brief Execute a command
78 * Closing the driver shuts off the hardware; the driver can be re-opened at 102 * \param cmd Command encoded by `SFC_CMD` macro.
79 * a later time when it's needed again. 103 * \param addr Address up to 32 bits; pass 0 if the command doesn't need it
104 * \param data Buffer for data transfer commands, must be cache-aligned
105 * \param size Number of data bytes / direction of transfer flag
106 * \returns SFC status code: 0 on success and < 0 on failure.
80 * 107 *
81 * After opening the driver, you must also program a valid device configuration 108 * - Non-data commands must pass `data = NULL` and `size = 0` in order to
82 * and clock rate using sfc_set_dev_conf() and sfc_set_clock(). 109 * get correct results.
110 *
111 * - Data commands must specify a direction of transfer using the high bit
112 * of the `size` argument by OR'ing in `SFC_READ` or `SFC_WRITE`.
83 */ 113 */
84extern void sfc_open(void); 114extern void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size);
85extern void sfc_close(void);
86 115
87/* These functions can be called at any time while the driver is open, but 116/* NOTE: the above will need to be changed if we need better performance
88 * must not be called while there is an operation in progress. It's the 117 * The hardware can do multiple commands in a sequence, including polling,
89 * caller's job to ensure the configuration will work with the device and 118 * and emit an interrupt only at the end.
90 * be capable of reading back data correctly.
91 * 119 *
92 * - sfc_set_dev_conf() writes its argument to the SFC_DEV_CONF register. 120 * Also, some chips need more than 4 address bytes even though the block
93 * - sfc_set_wp_enable() sets the state of the write-protect pin (WP). 121 * and page numbers would still fit in 32 bits; the current API cannot
94 * - sfc_set_clock() sets the controller clock frequency (in Hz). 122 * handle this.
95 */ 123 */
96#define sfc_set_dev_conf(dev_conf) \
97 do { REG_SFC_DEV_CONF = (dev_conf); } while(0)
98
99#define sfc_set_wp_enable(en) \
100 jz_writef(SFC_GLB, WP_EN((en) ? 1 : 0))
101
102extern void sfc_set_clock(uint32_t freq);
103 124
104/* Execute an operation. Returns zero on success, nonzero on failure. */ 125#endif /* __SFC_X1000_H__ */
105extern int sfc_exec(const sfc_op* op);