diff options
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c | 58 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | 44 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/nand-x1000.c | 686 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/nand-x1000.h | 207 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/sfc-x1000.c | 294 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/sfc-x1000.h | 152 |
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 | ||
35 | static 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 | |||
53 | static 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 | |||
61 | static int flash_img_read(uint8_t* buffer) | 35 | static 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 | ||
78 | static int flash_img_write(const uint8_t* buffer) | 56 | static 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 | ||
122 | nand_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 | |||
122 | void spl_target_boot(void) | 145 | void 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 | 60 | const nand_chip supported_nand_chips[] = { |
61 | 61 | #if defined(FIIO_M3K) | |
62 | /* NAND chip config */ | ||
63 | const 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 | ||
85 | const size_t target_nand_chip_count = | 86 | const 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 */ | ||
89 | static int nandop_wait_status(int errbit); | ||
90 | static int nandop_read_page(uint32_t row_addr, uint8_t* buf); | ||
91 | static int nandop_write_page(uint32_t row_addr, const uint8_t* buf); | ||
92 | static int nandop_erase_block(uint32_t block_addr); | ||
93 | static int nandop_set_write_protect(bool en); | ||
94 | |||
95 | /* NAND commands -- 1-to-1 mapping between chip commands and functions */ | ||
96 | static int nandcmd_read_id(int* mf_id, int* dev_id); | ||
97 | static int nandcmd_write_enable(void); | ||
98 | static int nandcmd_get_feature(uint8_t reg); | ||
99 | static int nandcmd_set_feature(uint8_t reg, uint8_t val); | ||
100 | static int nandcmd_page_read_to_cache(uint32_t row_addr); | ||
101 | static int nandcmd_read_from_cache(uint8_t* buf); | ||
102 | static int nandcmd_program_load(const uint8_t* buf); | ||
103 | static int nandcmd_program_execute(uint32_t row_addr); | ||
104 | static int nandcmd_block_erase(uint32_t block_addr); | ||
105 | |||
106 | struct nand_drv { | ||
107 | const nand_chip_data* chip_data; | ||
108 | bool write_enabled; | ||
109 | }; | ||
110 | 88 | ||
111 | static struct nand_drv nand_drv; | 89 | static nand_drv static_nand_drv; |
112 | static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR; | 90 | static uint8_t static_scratch_buf[NAND_DRV_SCRATCHSIZE] CACHEALIGN_ATTR; |
91 | static uint8_t static_page_buf[NAND_DRV_MAXPAGESIZE] CACHEALIGN_ATTR; | ||
113 | 92 | ||
114 | static void nand_drv_reset(void) | 93 | nand_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 | ||
120 | int nand_open(void) | 106 | static 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 | ||
136 | void nand_close(void) | 112 | static 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 | ||
144 | int nand_identify(int* mf_id, int* dev_id) | 118 | static 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) { | 126 | static 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: | 153 | static 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 | ||
175 | const nand_chip_data* nand_get_chip_data(void) | 159 | static 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 | ||
180 | extern int nand_enable_writes(bool en) | 175 | static 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 | ||
192 | static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) | 191 | int 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 | ||
227 | int 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 | ||
232 | int 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 | ||
237 | int nand_erase(uint32_t addr, uint32_t size) | 223 | void 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 | /* | 237 | static uint8_t nand_wait_busy(nand_drv* drv) |
268 | * NAND ops | ||
269 | */ | ||
270 | |||
271 | static 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 | ||
286 | static int nandop_read_page(uint32_t row_addr, uint8_t* buf) | 246 | int 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 | ||
300 | static int nandop_write_page(uint32_t row_addr, const uint8_t* buf) | 258 | int 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 | ||
316 | static int nandop_erase_block(uint32_t block_addr) | 271 | int 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 | ||
330 | static int nandop_set_write_protect(bool en) | 279 | int 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 | ||
364 | static 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 | ||
381 | static 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 | ||
391 | static int nandcmd_get_feature(uint8_t reg) | 307 | int 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 | |||
406 | static 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 | ||
422 | static 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 | ||
434 | static 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 | ||
457 | static 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 | |||
479 | static 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 | ||
491 | static 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 | */ |
59 | typedef uint32_t nand_block_t; | ||
60 | typedef uint32_t nand_page_t; | ||
35 | 61 | ||
36 | #include <stdint.h> | 62 | typedef 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 | ||
54 | typedef 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 | |||
91 | typedef 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 | |||
133 | extern const nand_chip supported_nand_chips[]; | ||
134 | extern 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 | */ | ||
141 | extern nand_drv* nand_init(void); | ||
142 | |||
143 | static 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 */ | 148 | static 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 | */ | ||
162 | extern int nand_open(nand_drv* drv); | ||
163 | extern 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 | */ | ||
171 | extern int nand_block_erase(nand_drv* drv, nand_block_t block); | ||
172 | extern int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer); | ||
173 | extern 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 | */ |
80 | extern int nand_open(void); | 179 | extern int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer); |
81 | extern void nand_close(void); | 180 | extern 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. */ | ||
85 | extern 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. */ | ||
89 | const 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. */ | ||
94 | extern 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. */ | ||
99 | extern int nand_read(uint32_t addr, uint32_t size, uint8_t* buf); | ||
100 | extern 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. */ | ||
105 | extern 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) | 31 | static void sfc_poll_wait(void); |
32 | #ifdef USE_DMA | ||
33 | static void sfc_irq_wait(void); | ||
38 | 34 | ||
39 | #ifdef NEED_SFC_DMA | ||
40 | static struct mutex sfc_mutex; | ||
41 | static struct semaphore sfc_sema; | 35 | static struct semaphore sfc_sema; |
42 | static struct timeout sfc_lockup_tmo; | ||
43 | static bool sfc_inited = false; | ||
44 | static volatile int sfc_status; | ||
45 | #else | ||
46 | # define sfc_status SFC_STATUS_OK | ||
47 | #endif | ||
48 | |||
49 | void 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 | |||
61 | void sfc_lock(void) | ||
62 | { | ||
63 | #ifdef NEED_SFC_DMA | ||
64 | mutex_lock(&sfc_mutex); | ||
65 | #endif | ||
66 | } | ||
67 | 36 | ||
68 | void 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); | 40 | static void(*sfc_wait)(void) = sfc_poll_wait; |
72 | #endif | 41 | #endif |
73 | } | ||
74 | 42 | ||
75 | void sfc_open(void) | 43 | void 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 | |||
58 | void sfc_close(void) | ||
59 | { | ||
60 | REG_SFC_CGE = 0x1f; | ||
61 | jz_writef(CPM_CLKGR, SFC(1)); | ||
62 | } | ||
63 | |||
64 | void 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 | ||
90 | void sfc_close(void) | 78 | void 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 | ||
100 | void sfc_set_clock(uint32_t freq) | 86 | void 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 |
119 | static int sfc_lockup_tmo_cb(struct timeout* tmo) | 104 | static 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 | |||
134 | static 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; | |
139 | void 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 | |||
169 | static 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 | ||
186 | static 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 | |||
203 | static 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 | |||
211 | int 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)) { | 131 | void 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(); | 178 | static 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) | 185 | static 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; | 190 | void 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 | 76 | extern void sfc_open(void); |
51 | #define SFC_STATUS_OVERFLOW 1 | 77 | extern void sfc_close(void); |
52 | #define SFC_STATUS_UNDERFLOW 2 | ||
53 | #define SFC_STATUS_LOCKUP 3 | ||
54 | 78 | ||
55 | typedef 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 */ | 83 | extern void sfc_irq_begin(void); |
60 | int dummy_bits; /* Number of dummy bits (yes: bits, not bytes) */ | 84 | extern 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 | 87 | extern void sfc_set_clock(uint32_t freq); |
69 | * actually perform the init. | 88 | |
70 | */ | 89 | /* Set the device configuration register */ |
71 | extern void sfc_init(void); | 90 | inline 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 */ |
74 | extern void sfc_lock(void); | 96 | inline void sfc_set_wp_enable(bool en) |
75 | extern 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 | */ |
84 | extern void sfc_open(void); | 114 | extern void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size); |
85 | extern 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 | |||
102 | extern 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__ */ |
105 | extern int sfc_exec(const sfc_op* op); | ||