summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-05-06 00:22:56 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-05-06 08:37:26 +0100
commit15ad1c42db95b178b6125b8084148ed4463f1472 (patch)
tree4cb0a41b3dd077319a79c825d2ee666176254c7d /firmware/target/mips/ingenic_x1000
parent244aad750c0148265f03c9da8a33b4e0a9f85468 (diff)
downloadrockbox-15ad1c42db95b178b6125b8084148ed4463f1472.tar.gz
rockbox-15ad1c42db95b178b6125b8084148ed4463f1472.zip
X1000: simplify NAND driver
- Removed unnecessary layers of generic code - Software ECC is gone since we don't need it now (and maybe not ever) - Removed bounce buffering, so callers must now align buffers Change-Id: I33fbac9d9d12a4657980b8618c7d62bfa91e2ea0
Diffstat (limited to 'firmware/target/mips/ingenic_x1000')
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c33
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c53
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h37
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000-err.h9
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.c665
-rw-r--r--firmware/target/mips/ingenic_x1000/nand-x1000.h208
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c20
-rw-r--r--firmware/target/mips/ingenic_x1000/spl.lds11
8 files changed, 416 insertions, 620 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
index cdd7276bee..154785ee0b 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c
@@ -21,6 +21,7 @@
21 21
22#include "installer.h" 22#include "installer.h"
23#include "nand-x1000.h" 23#include "nand-x1000.h"
24#include "system.h"
24#include "core_alloc.h" 25#include "core_alloc.h"
25#include "file.h" 26#include "file.h"
26 27
@@ -40,22 +41,27 @@
40 41
41static int install_from_buffer(const void* buf) 42static int install_from_buffer(const void* buf)
42{ 43{
44 int status = INSTALL_SUCCESS;
45 int mf_id, dev_id;
46
43 if(nand_open()) 47 if(nand_open())
44 return ERR_FLASH_OPEN_FAILED; 48 return ERR_FLASH_OPEN_FAILED;
45 49 if(nand_identify(&mf_id, &dev_id)) {
46 int status = INSTALL_SUCCESS; 50 status = ERR_FLASH_OPEN_FAILED;
51 goto _exit;
52 }
47 53
48 if(nand_enable_writes(true)) { 54 if(nand_enable_writes(true)) {
49 status = ERR_FLASH_DISABLE_WP_FAILED; 55 status = ERR_FLASH_DISABLE_WP_FAILED;
50 goto _exit; 56 goto _exit;
51 } 57 }
52 58
53 if(nand_erase_bytes(0, BOOT_IMAGE_SIZE)) { 59 if(nand_erase(0, BOOT_IMAGE_SIZE)) {
54 status = ERR_FLASH_ERASE_FAILED; 60 status = ERR_FLASH_ERASE_FAILED;
55 goto _exit; 61 goto _exit;
56 } 62 }
57 63
58 if(nand_write_bytes(0, BOOT_IMAGE_SIZE, buf)) { 64 if(nand_write(0, BOOT_IMAGE_SIZE, (const uint8_t*)buf)) {
59 status = ERR_FLASH_WRITE_FAILED; 65 status = ERR_FLASH_WRITE_FAILED;
60 goto _exit; 66 goto _exit;
61 } 67 }
@@ -72,12 +78,17 @@ static int install_from_buffer(const void* buf)
72 78
73static int dump_to_buffer(void* buf) 79static int dump_to_buffer(void* buf)
74{ 80{
81 int status = INSTALL_SUCCESS;
82 int mf_id, dev_id;
83
75 if(nand_open()) 84 if(nand_open())
76 return ERR_FLASH_OPEN_FAILED; 85 return ERR_FLASH_OPEN_FAILED;
86 if(nand_identify(&mf_id, &dev_id)) {
87 status = ERR_FLASH_OPEN_FAILED;
88 goto _exit;
89 }
77 90
78 int status = INSTALL_SUCCESS; 91 if(nand_read(0, BOOT_IMAGE_SIZE, (uint8_t*)buf)) {
79
80 if(nand_read_bytes(0, BOOT_IMAGE_SIZE, buf)) {
81 status = ERR_FLASH_READ_FAILED; 92 status = ERR_FLASH_READ_FAILED;
82 goto _exit; 93 goto _exit;
83 } 94 }
@@ -90,12 +101,14 @@ static int dump_to_buffer(void* buf)
90int install_bootloader(const char* path) 101int install_bootloader(const char* path)
91{ 102{
92 /* Allocate memory to hold image */ 103 /* Allocate memory to hold image */
93 int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE); 104 size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1;
105 int handle = core_alloc("boot_image", bufsize);
94 if(handle < 0) 106 if(handle < 0)
95 return ERR_OUT_OF_MEMORY; 107 return ERR_OUT_OF_MEMORY;
96 108
97 int status = INSTALL_SUCCESS; 109 int status = INSTALL_SUCCESS;
98 void* buffer = core_get_data(handle); 110 void* buffer = core_get_data(handle);
111 CACHEALIGN_BUFFER(buffer, bufsize);
99 112
100 /* Open the boot image */ 113 /* Open the boot image */
101 int fd = open(path, O_RDONLY); 114 int fd = open(path, O_RDONLY);
@@ -132,13 +145,15 @@ int install_bootloader(const char* path)
132int dump_bootloader(const char* path) 145int dump_bootloader(const char* path)
133{ 146{
134 /* Allocate memory to hold image */ 147 /* Allocate memory to hold image */
135 int handle = core_alloc("boot_image", BOOT_IMAGE_SIZE); 148 size_t bufsize = BOOT_IMAGE_SIZE + CACHEALIGN_SIZE - 1;
149 int handle = core_alloc("boot_image", bufsize);
136 if(handle < 0) 150 if(handle < 0)
137 return -1; 151 return -1;
138 152
139 /* Read data from flash */ 153 /* Read data from flash */
140 int fd = -1; 154 int fd = -1;
141 void* buffer = core_get_data(handle); 155 void* buffer = core_get_data(handle);
156 CACHEALIGN_BUFFER(buffer, bufsize);
142 int status = dump_to_buffer(buffer); 157 int status = dump_to_buffer(buffer);
143 if(status) 158 if(status)
144 goto _exit; 159 goto _exit;
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
deleted file mode 100644
index 7c8a306bae..0000000000
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c
+++ /dev/null
@@ -1,53 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "nand-x1000.h"
23#include "nand-target.h"
24#include "sfc-x1000.h"
25
26/* Unbelievably FiiO has completely disabled the use of ECC for this chip
27 * in their Linux kernel, even though it has perfectly good spare areas.
28 * There's no internal ECC either.
29 *
30 * Using nanddump to read the spare areas reveals they're filled with 0xff,
31 * and the publicly released Linux source has the ecc_strength set to 0.
32 */
33static const nand_chip_data ato25d1ga = {
34 .name = "ATO25D1GA",
35 .mf_id = 0x9b,
36 .dev_id = 0x12,
37 .dev_conf = NAND_INIT_SFC_DEV_CONF,
38 /* XXX: datasheet says 104 MHz but FiiO seems to run this at 150 MHz.
39 * Didn't find any issues doing this so might as well keep the behavior.
40 */
41 .clock_freq = NAND_INIT_CLOCK_SPEED,
42 .block_size = 64,
43 .page_size = 2048,
44 .spare_size = 64,
45 .rowaddr_width = 3,
46 .coladdr_width = 2,
47 .flags = NANDCHIP_FLAG_QUAD,
48};
49
50const nand_chip_desc target_nand_chip_descs[] = {
51 {&ato25d1ga, &nand_chip_ops_std},
52 {NULL, NULL},
53};
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h
deleted file mode 100644
index 1238d7e452..0000000000
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h
+++ /dev/null
@@ -1,37 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __NAND_TARGET_H__
23#define __NAND_TARGET_H__
24
25/* The max page size (main + spare) of all NAND chips used by this target */
26#define NAND_MAX_PAGE_SIZE (2048 + 64)
27
28/* The clock speed to use for the SFC controller during chip identification */
29#define NAND_INIT_CLOCK_SPEED 150000000
30
31/* Initial value to program SFC_DEV_CONF register with */
32#define NAND_INIT_SFC_DEV_CONF \
33 jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), \
34 CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), \
35 STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1))
36
37#endif /* __NAND_TARGET_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h b/firmware/target/mips/ingenic_x1000/nand-x1000-err.h
index 252d591062..869fe73ac9 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000-err.h
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000-err.h
@@ -15,4 +15,13 @@
15#define NANDERR_COMMAND_FAILED (-9) 15#define NANDERR_COMMAND_FAILED (-9)
16#define NANDERR_OTHER (-99) 16#define NANDERR_OTHER (-99)
17 17
18/* TEMPORARY -- compatibility hack for jztool's sake.
19 * This will go away once the new bootloader gets merged */
20#define NAND_SUCCESS 0
21#define NAND_ERR_UNKNOWN_CHIP NANDERR_CHIP_UNSUPPORTED
22#define NAND_ERR_UNALIGNED NANDERR_UNALIGNED_ADDRESS
23#define NAND_ERR_WRITE_PROTECT NANDERR_WRITE_PROTECTED
24#define NAND_ERR_CONTROLLER NANDERR_OTHER
25#define NAND_ERR_COMMAND NANDERR_COMMAND_FAILED
26
18#endif /* __NAND_X1000_ERR_H__ */ 27#endif /* __NAND_X1000_ERR_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c
index fc8d471559..1770324fb3 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c
@@ -20,253 +20,350 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "nand-x1000.h" 22#include "nand-x1000.h"
23#include "nand-target.h"
24#include "sfc-x1000.h" 23#include "sfc-x1000.h"
25#include "system.h" 24#include "system.h"
26#include <string.h> 25#include <stddef.h>
27 26
28#if !defined(NAND_MAX_PAGE_SIZE) || \ 27/* NAND command numbers */
29 !defined(NAND_INIT_SFC_DEV_CONF) || \ 28#define NAND_CMD_READ_ID 0x9f
30 !defined(NAND_INIT_CLOCK_SPEED) 29#define NAND_CMD_WRITE_ENABLE 0x06
31# error "Target needs to specify NAND driver parameters" 30#define NAND_CMD_GET_FEATURE 0x0f
32#endif 31#define NAND_CMD_SET_FEATURE 0x1f
33 32#define NAND_CMD_PAGE_READ_TO_CACHE 0x13
34/* Must be at least as big as a cacheline */ 33#define NAND_CMD_READ_FROM_CACHE 0x0b
35#define NAND_AUX_BUFFER_SIZE CACHEALIGN_SIZE 34#define NAND_CMD_READ_FROM_CACHEx4 0x6b
36 35#define NAND_CMD_PROGRAM_LOAD 0x02
37/* Writes have been enabled */ 36#define NAND_CMD_PROGRAM_LOADx4 0x32
38#define NAND_DRV_FLAG_WRITEABLE 0x01 37#define NAND_CMD_PROGRAM_EXECUTE 0x10
39 38#define NAND_CMD_BLOCK_ERASE 0xd8
40/* Defined by target */ 39
41extern const nand_chip_desc target_nand_chip_descs[]; 40/* NAND device register addresses for GET_FEATURE / SET_FEATURE */
42 41#define NAND_FREG_PROTECTION 0xa0
43#ifdef BOOTLOADER_SPL 42#define NAND_FREG_FEATURE 0xb0
44# define NANDBUFFER_ATTR __attribute__((section(".sdram"))) CACHEALIGN_ATTR 43#define NAND_FREG_STATUS 0xc0
44
45/* Protection register bits */
46#define NAND_FREG_PROTECTION_BRWD 0x80
47#define NAND_FREG_PROTECTION_BP2 0x20
48#define NAND_FREG_PROTECTION_BP1 0x10
49#define NAND_FREG_PROTECTION_BP0 0x80
50/* Mask of BP bits 0-2 */
51#define NAND_FREG_PROTECTION_ALLBP (0x38)
52
53/* Feature register bits */
54#define NAND_FREG_FEATURE_QE 0x01
55
56/* Status register bits */
57#define NAND_FREG_STATUS_OIP 0x01
58#define NAND_FREG_STATUS_WEL 0x02
59#define NAND_FREG_STATUS_E_FAIL 0x04
60#define NAND_FREG_STATUS_P_FAIL 0x08
61
62/* NAND chip config */
63const nand_chip_data target_nand_chip_data[] = {
64#ifdef FIIO_M3K
65 {
66 /* ATO25D1GA */
67 .mf_id = 0x9b,
68 .dev_id = 0x12,
69 .dev_conf = jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1),
70 CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0),
71 STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)),
72 .clock_freq = 150000000,
73 .log2_page_size = 11, /* = 2048 bytes */
74 .log2_block_size = 6, /* = 64 pages */
75 .rowaddr_width = 3,
76 .coladdr_width = 2,
77 .flags = NANDCHIP_FLAG_QUAD,
78 }
45#else 79#else
46# define NANDBUFFER_ATTR CACHEALIGN_ATTR 80 /* Nobody will use this anyway if the device has no NAND flash */
81 { 0 },
47#endif 82#endif
83};
84
85const size_t target_nand_chip_count =
86 sizeof(target_nand_chip_data) / sizeof(nand_chip_data);
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};
48 110
49/* Globals for the driver */ 111static struct nand_drv nand_drv;
50static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR; 112static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR;
51static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR;
52static nand_drv nand_driver;
53 113
54static void nand_drv_reset(nand_drv* d) 114static void nand_drv_reset(void)
55{ 115{
56 d->chip_ops = NULL; 116 nand_drv.chip_data = NULL;
57 d->chip_data = NULL; 117 nand_drv.write_enabled = false;
58 d->pagebuf = &pagebuffer[0];
59 d->auxbuf = &auxbuffer[0];
60 d->raw_page_size = 0;
61 d->flags = 0;
62} 118}
63 119
64/* Driver code */
65int nand_open(void) 120int nand_open(void)
66{ 121{
67 sfc_init(); 122 sfc_init();
68 sfc_lock(); 123 sfc_lock();
69 124
70 /* Reset driver state */ 125 nand_drv_reset();
71 nand_drv_reset(&nand_driver);
72
73 /* Init hardware */
74 sfc_open(); 126 sfc_open();
75 sfc_set_dev_conf(NAND_INIT_SFC_DEV_CONF);
76 sfc_set_clock(NAND_INIT_CLOCK_SPEED);
77
78 /* Identify NAND chip */
79 int status = 0;
80 int nandid = nandcmd_read_id(&nand_driver);
81 if(nandid < 0) {
82 status = NANDERR_CHIP_UNSUPPORTED;
83 goto _err;
84 }
85 127
86 unsigned char mf_id = nandid >> 8; 128 const nand_chip_data* chip_data = &target_nand_chip_data[0];
87 unsigned char dev_id = nandid & 0xff; 129 sfc_set_dev_conf(chip_data->dev_conf);
88 const nand_chip_desc* desc = &target_nand_chip_descs[0]; 130 sfc_set_clock(chip_data->clock_freq);
89 while(1) {
90 if(desc->data == NULL || desc->ops == NULL) {
91 status = NANDERR_CHIP_UNSUPPORTED;
92 goto _err;
93 }
94
95 if(desc->data->mf_id == mf_id && desc->data->dev_id == dev_id)
96 break;
97 }
98
99 /* Fill driver parameters */
100 nand_driver.chip_ops = desc->ops;
101 nand_driver.chip_data = desc->data;
102 nand_driver.raw_page_size = desc->data->page_size + desc->data->spare_size;
103
104 /* Configure hardware and run init op */
105 sfc_set_dev_conf(desc->data->dev_conf);
106 sfc_set_clock(desc->data->clock_freq);
107 131
108 if((status = desc->ops->open(&nand_driver)) < 0) 132 return NAND_SUCCESS;
109 goto _err;
110
111 _exit:
112 sfc_unlock();
113 return status;
114 _err:
115 nand_drv_reset(&nand_driver);
116 sfc_close();
117 goto _exit;
118} 133}
119 134
120void nand_close(void) 135void nand_close(void)
121{ 136{
122 sfc_lock(); 137 sfc_lock();
123 nand_driver.chip_ops->close(&nand_driver);
124 nand_drv_reset(&nand_driver);
125 sfc_close(); 138 sfc_close();
139 nand_drv_reset();
126 sfc_unlock(); 140 sfc_unlock();
127} 141}
128 142
129int nand_enable_writes(bool en) 143int nand_identify(int* mf_id, int* dev_id)
130{ 144{
131 sfc_lock(); 145 sfc_lock();
132 146
133 int st = nand_driver.chip_ops->set_wp_enable(&nand_driver, en); 147 int status = nandcmd_read_id(mf_id, dev_id);
134 if(st >= 0) { 148 if(status < 0)
135 if(en) 149 goto error;
136 nand_driver.flags |= NAND_DRV_FLAG_WRITEABLE; 150
137 else 151 for(size_t i = 0; i < target_nand_chip_count; ++i) {
138 nand_driver.flags &= ~NAND_DRV_FLAG_WRITEABLE; 152 const nand_chip_data* data = &target_nand_chip_data[i];
153 if(data->mf_id == *mf_id && data->dev_id == *dev_id) {
154 nand_drv.chip_data = data;
155 break;
156 }
157 }
158
159 if(!nand_drv.chip_data) {
160 status = NAND_ERR_UNKNOWN_CHIP;
161 goto error;
139 } 162 }
140 163
164 /* Set parameters according to new chip data */
165 sfc_set_dev_conf(nand_drv.chip_data->dev_conf);
166 sfc_set_clock(nand_drv.chip_data->clock_freq);
167 status = NAND_SUCCESS;
168
169 error:
141 sfc_unlock(); 170 sfc_unlock();
142 return st; 171 return status;
143} 172}
144 173
145extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf) 174const nand_chip_data* nand_get_chip_data(void)
146{ 175{
147 if(count <= 0) 176 return nand_drv.chip_data;
148 return 0; 177}
149 178
150 nand_drv* d = &nand_driver; 179extern int nand_enable_writes(bool en)
151 uint32_t rowaddr = byteaddr / d->chip_data->page_size; 180{
152 uint32_t coladdr = byteaddr % d->chip_data->page_size; 181 if(en == nand_drv.write_enabled)
153 unsigned char* dstbuf = (unsigned char*)buf; 182 return NAND_SUCCESS;
154 int status = 0;
155 183
156 sfc_lock(); 184 int rc = nandop_set_write_protect(!en);
157 do { 185 if(rc == NAND_SUCCESS)
158 if(d->chip_ops->read_page(d, rowaddr, d->pagebuf) < 0) { 186 nand_drv.write_enabled = en;
159 status = NANDERR_READ_FAILED;
160 goto _end;
161 }
162 187
163 if(d->chip_ops->ecc_read(d, d->pagebuf) < 0) { 188 return rc;
164 status = NANDERR_ECC_FAILED; 189}
165 goto _end;
166 }
167 190
168 int amount = d->chip_data->page_size - coladdr; 191static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf)
169 if(amount > count) 192{
170 amount = count; 193 const uint32_t page_size = (1 << nand_drv.chip_data->log2_page_size);
194
195 if(addr & (page_size - 1))
196 return NAND_ERR_UNALIGNED;
197 if(size & (page_size - 1))
198 return NAND_ERR_UNALIGNED;
199 if(size <= 0)
200 return NAND_SUCCESS;
201 if(write && !nand_drv.write_enabled)
202 return NAND_ERR_WRITE_PROTECT;
203 /* FIXME: re-enable this check after merging new SPL+bootloader.
204 * It's only necessary for DMA, which is currently not used, but it's a
205 * good practice anyway. Disable for now due to SPL complications. */
206 /*if((uint32_t)buf & (CACHEALIGN_SIZE - 1))
207 return NAND_ERR_UNALIGNED;*/
208
209 addr >>= nand_drv.chip_data->log2_page_size;
210 size >>= nand_drv.chip_data->log2_page_size;
211
212 int rc = NAND_SUCCESS;
213 sfc_lock();
171 214
172 memcpy(dstbuf, d->pagebuf, amount); 215 for(; size > 0; --size, ++addr, buf += page_size) {
173 dstbuf += amount; 216 if(write)
174 count -= amount; 217 rc = nandop_write_page(addr, buf);
175 rowaddr += 1; 218 else
176 coladdr = 0; 219 rc = nandop_read_page(addr, buf);
177 } while(count > 0); 220
221 if(rc)
222 break;
223 }
178 224
179 _end:
180 sfc_unlock(); 225 sfc_unlock();
181 return status; 226 return rc;
182} 227}
183 228
184int nand_write_bytes(uint32_t byteaddr, int count, const void* buf) 229int nand_read(uint32_t addr, uint32_t size, uint8_t* buf)
185{ 230{
186 nand_drv* d = &nand_driver; 231 return nand_rdwr(false, addr, size, buf);
232}
187 233
188 if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) 234int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf)
189 return NANDERR_WRITE_PROTECTED; 235{
236 return nand_rdwr(true, addr, size, (uint8_t*)buf);
237}
190 238
191 if(count <= 0) 239int nand_erase(uint32_t addr, uint32_t size)
192 return 0; 240{
241 const uint32_t page_size = 1 << nand_drv.chip_data->log2_page_size;
242 const uint32_t block_size = page_size << nand_drv.chip_data->log2_block_size;
243 const uint32_t pages_per_block = 1 << nand_drv.chip_data->log2_page_size;
244
245 if(addr & (block_size - 1))
246 return NAND_ERR_UNALIGNED;
247 if(size & (block_size - 1))
248 return NAND_ERR_UNALIGNED;
249 if(size <= 0)
250 return NAND_SUCCESS;
251 if(!nand_drv.write_enabled)
252 return NAND_ERR_WRITE_PROTECT;
253
254 addr >>= nand_drv.chip_data->log2_page_size;
255 size >>= nand_drv.chip_data->log2_page_size;
256 size >>= nand_drv.chip_data->log2_block_size;
257
258 int rc = NAND_SUCCESS;
259 sfc_lock();
193 260
194 uint32_t rowaddr = byteaddr / d->chip_data->page_size; 261 for(; size > 0; --size, addr += pages_per_block)
195 uint32_t coladdr = byteaddr % d->chip_data->page_size; 262 if((rc = nandop_erase_block(addr)))
263 break;
196 264
197 /* Only support whole page writes right now */ 265 sfc_unlock();
198 if(coladdr != 0) 266 return rc;
199 return NANDERR_UNALIGNED_ADDRESS; 267}
200 if(count % d->chip_data->page_size)
201 return NANDERR_UNALIGNED_LENGTH;
202 268
203 const unsigned char* srcbuf = (const unsigned char*)buf; 269/*
204 int status = 0; 270 * NAND ops
271 */
205 272
206 sfc_lock(); 273static int nandop_wait_status(int errbit)
274{
275 int reg;
207 do { 276 do {
208 memcpy(d->pagebuf, srcbuf, d->chip_data->page_size); 277 reg = nandcmd_get_feature(NAND_FREG_STATUS);
209 d->chip_ops->ecc_write(d, d->pagebuf); 278 if(reg < 0)
210 279 return reg;
211 if(d->chip_ops->write_page(d, rowaddr, d->pagebuf) < 0) { 280 } while(reg & NAND_FREG_STATUS_OIP);
212 status = NANDERR_PROGRAM_FAILED;
213 goto _end;
214 }
215 281
216 rowaddr += 1; 282 if(reg & errbit)
217 srcbuf += d->chip_data->page_size; 283 return NAND_ERR_COMMAND;
218 count -= d->chip_data->page_size;
219 } while(count > 0);
220 284
221 _end: 285 return reg;
222 sfc_unlock();
223 return status;
224} 286}
225 287
226int nand_erase_bytes(uint32_t byteaddr, int count) 288static int nandop_read_page(uint32_t row_addr, uint8_t* buf)
227{ 289{
228 nand_drv* d = &nand_driver; 290 int status;
229 291
230 if((d->flags & NAND_DRV_FLAG_WRITEABLE) == 0) 292 if((status = nandcmd_page_read_to_cache(row_addr)) < 0)
231 return NANDERR_WRITE_PROTECTED; 293 return status;
294 if((status = nandop_wait_status(0)) < 0)
295 return status;
296 if((status = nandcmd_read_from_cache(buf)) < 0)
297 return status;
232 298
233 /* Ensure address is aligned to a block boundary */ 299 return NAND_SUCCESS;
234 if(byteaddr % d->chip_data->page_size) 300}
235 return NANDERR_UNALIGNED_ADDRESS;
236 301
237 uint32_t blockaddr = byteaddr / d->chip_data->page_size; 302static int nandop_write_page(uint32_t row_addr, const uint8_t* buf)
238 if(blockaddr % d->chip_data->block_size) 303{
239 return NANDERR_UNALIGNED_ADDRESS; 304 int status;
240 305
241 /* Ensure length is also aligned to the size of a block */ 306 if((status = nandcmd_write_enable()) < 0)
242 if(count % d->chip_data->page_size) 307 return status;
243 return NANDERR_UNALIGNED_LENGTH; 308 if((status = nandcmd_program_load(buf)) < 0)
309 return status;
310 if((status = nandcmd_program_execute(row_addr)) < 0)
311 return status;
312 if((status = nandop_wait_status(NAND_FREG_STATUS_P_FAIL)) < 0)
313 return status;
244 314
245 count /= d->chip_data->page_size; 315 return NAND_SUCCESS;
246 if(count % d->chip_data->block_size) 316}
247 return NANDERR_UNALIGNED_LENGTH; 317
318static int nandop_erase_block(uint32_t block_addr)
319{
320 int status;
248 321
249 count /= d->chip_data->block_size; 322 if((status = nandcmd_write_enable()) < 0)
323 return status;
324 if((status = nandcmd_block_erase(block_addr)) < 0)
325 return status;
326 if((status = nandop_wait_status(NAND_FREG_STATUS_E_FAIL)) < 0)
327 return status;
250 328
251 int status = 0; 329 return NAND_SUCCESS;
252 sfc_lock(); 330}
253 331
254 for(int i = 0; i < count; ++i) { 332static int nandop_set_write_protect(bool en)
255 if(d->chip_ops->erase_block(d, blockaddr)) { 333{
256 status = NANDERR_ERASE_FAILED; 334 int val = nandcmd_get_feature(NAND_FREG_PROTECTION);
257 goto _end; 335 if(val < 0)
258 } 336 return val;
259 337
260 /* Advance to next block */ 338 if(en) {
261 blockaddr += d->chip_data->block_size; 339 val &= ~NAND_FREG_PROTECTION_ALLBP;
340 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
341 val &= ~NAND_FREG_PROTECTION_BRWD;
342 } else {
343 val |= NAND_FREG_PROTECTION_ALLBP;
344 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
345 val |= NAND_FREG_PROTECTION_BRWD;
262 } 346 }
263 347
264 _end: 348 /* NOTE: The WP pin typically only protects changes to the protection
265 sfc_unlock(); 349 * register -- it doesn't actually prevent writing to the chip. That's
266 return status; 350 * why it should be re-enabled after setting the new protection status.
351 */
352 sfc_set_wp_enable(false);
353 int status = nandcmd_set_feature(NAND_FREG_PROTECTION, val);
354 sfc_set_wp_enable(true);
355
356 if(status < 0)
357 return status;
358
359 return NAND_SUCCESS;
267} 360}
268 361
269int nandcmd_read_id(nand_drv* d) 362/*
363 * Low-level NAND commands
364 */
365
366static int nandcmd_read_id(int* mf_id, int* dev_id)
270{ 367{
271 sfc_op op = {0}; 368 sfc_op op = {0};
272 op.command = NAND_CMD_READ_ID; 369 op.command = NAND_CMD_READ_ID;
@@ -274,72 +371,72 @@ int nandcmd_read_id(nand_drv* d)
274 op.addr_bytes = 1; 371 op.addr_bytes = 1;
275 op.addr_lo = 0; 372 op.addr_lo = 0;
276 op.data_bytes = 2; 373 op.data_bytes = 2;
277 op.buffer = d->auxbuf; 374 op.buffer = nand_auxbuf;
278 if(sfc_exec(&op)) 375 if(sfc_exec(&op))
279 return NANDERR_COMMAND_FAILED; 376 return NAND_ERR_CONTROLLER;
280 377
281 return (d->auxbuf[0] << 8) | d->auxbuf[1]; 378 *mf_id = nand_auxbuf[0];
379 *dev_id = nand_auxbuf[1];
380 return NAND_SUCCESS;
282} 381}
283 382
284int nandcmd_write_enable(nand_drv* d) 383static int nandcmd_write_enable(void)
285{ 384{
286 (void)d;
287
288 sfc_op op = {0}; 385 sfc_op op = {0};
289 op.command = NAND_CMD_WRITE_ENABLE; 386 op.command = NAND_CMD_WRITE_ENABLE;
290 if(sfc_exec(&op)) 387 if(sfc_exec(&op))
291 return NANDERR_COMMAND_FAILED; 388 return NAND_ERR_CONTROLLER;
292 389
293 return 0; 390 return NAND_SUCCESS;
294} 391}
295 392
296int nandcmd_get_feature(nand_drv* d, int reg) 393static int nandcmd_get_feature(uint8_t reg)
297{ 394{
298 sfc_op op = {0}; 395 sfc_op op = {0};
299 op.command = NAND_CMD_GET_FEATURE; 396 op.command = NAND_CMD_GET_FEATURE;
300 op.flags = SFC_FLAG_READ; 397 op.flags = SFC_FLAG_READ;
301 op.addr_bytes = 1; 398 op.addr_bytes = 1;
302 op.addr_lo = reg & 0xff; 399 op.addr_lo = reg;
303 op.data_bytes = 1; 400 op.data_bytes = 1;
304 op.buffer = d->auxbuf; 401 op.buffer = nand_auxbuf;
305 if(sfc_exec(&op)) 402 if(sfc_exec(&op))
306 return NANDERR_COMMAND_FAILED; 403 return NAND_ERR_CONTROLLER;
307 404
308 return d->auxbuf[0]; 405 return nand_auxbuf[0];
309} 406}
310 407
311int nandcmd_set_feature(nand_drv* d, int reg, int val) 408static int nandcmd_set_feature(uint8_t reg, uint8_t val)
312{ 409{
313 sfc_op op = {0}; 410 sfc_op op = {0};
314 op.command = NAND_CMD_SET_FEATURE; 411 op.command = NAND_CMD_SET_FEATURE;
315 op.flags = SFC_FLAG_READ; 412 op.flags = SFC_FLAG_READ;
316 op.addr_bytes = 1; 413 op.addr_bytes = 1;
317 op.addr_lo = reg & 0xff; 414 op.addr_lo = reg;
318 op.data_bytes = 1; 415 op.data_bytes = 1;
319 op.buffer = d->auxbuf; 416 op.buffer = nand_auxbuf;
320 d->auxbuf[0] = val & 0xff; 417 nand_auxbuf[0] = val;
321 if(sfc_exec(&op)) 418 if(sfc_exec(&op))
322 return NANDERR_COMMAND_FAILED; 419 return NAND_ERR_CONTROLLER;
323 420
324 return 0; 421 return NAND_SUCCESS;
325} 422}
326 423
327int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr) 424static int nandcmd_page_read_to_cache(uint32_t row_addr)
328{ 425{
329 sfc_op op = {0}; 426 sfc_op op = {0};
330 op.command = NAND_CMD_PAGE_READ_TO_CACHE; 427 op.command = NAND_CMD_PAGE_READ_TO_CACHE;
331 op.addr_bytes = d->chip_data->rowaddr_width; 428 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
332 op.addr_lo = rowaddr; 429 op.addr_lo = row_addr;
333 if(sfc_exec(&op)) 430 if(sfc_exec(&op))
334 return NANDERR_COMMAND_FAILED; 431 return NAND_ERR_CONTROLLER;
335 432
336 return 0; 433 return NAND_SUCCESS;
337} 434}
338 435
339int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf) 436static int nandcmd_read_from_cache(uint8_t* buf)
340{ 437{
341 sfc_op op = {0}; 438 sfc_op op = {0};
342 if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { 439 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) {
343 op.command = NAND_CMD_READ_FROM_CACHEx4; 440 op.command = NAND_CMD_READ_FROM_CACHEx4;
344 op.mode = SFC_MODE_QUAD_IO; 441 op.mode = SFC_MODE_QUAD_IO;
345 } else { 442 } else {
@@ -348,21 +445,21 @@ int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf)
348 } 445 }
349 446
350 op.flags = SFC_FLAG_READ; 447 op.flags = SFC_FLAG_READ;
351 op.addr_bytes = d->chip_data->coladdr_width; 448 op.addr_bytes = nand_drv.chip_data->coladdr_width;
352 op.addr_lo = 0; 449 op.addr_lo = 0;
353 op.dummy_bits = 8; 450 op.dummy_bits = 8; // NOTE: this may need a chip_data parameter
354 op.data_bytes = d->raw_page_size; 451 op.data_bytes = (1 << nand_drv.chip_data->log2_page_size);
355 op.buffer = buf; 452 op.buffer = buf;
356 if(sfc_exec(&op)) 453 if(sfc_exec(&op))
357 return NANDERR_COMMAND_FAILED; 454 return NAND_ERR_CONTROLLER;
358 455
359 return 0; 456 return NAND_SUCCESS;
360} 457}
361 458
362int nandcmd_program_load(nand_drv* d, const unsigned char* buf) 459static int nandcmd_program_load(const uint8_t* buf)
363{ 460{
364 sfc_op op = {0}; 461 sfc_op op = {0};
365 if(d->chip_data->flags & NANDCHIP_FLAG_QUAD) { 462 if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) {
366 op.command = NAND_CMD_PROGRAM_LOADx4; 463 op.command = NAND_CMD_PROGRAM_LOADx4;
367 op.mode = SFC_MODE_QUAD_IO; 464 op.mode = SFC_MODE_QUAD_IO;
368 } else { 465 } else {
@@ -371,156 +468,36 @@ int nandcmd_program_load(nand_drv* d, const unsigned char* buf)
371 } 468 }
372 469
373 op.flags = SFC_FLAG_WRITE; 470 op.flags = SFC_FLAG_WRITE;
374 op.addr_bytes = d->chip_data->coladdr_width; 471 op.addr_bytes = nand_drv.chip_data->coladdr_width;
375 op.addr_lo = 0; 472 op.addr_lo = 0;
376 op.data_bytes = d->raw_page_size; 473 op.data_bytes = (1 << nand_drv.chip_data->log2_page_size);
377 op.buffer = (void*)buf; 474 op.buffer = (void*)buf;
378 if(sfc_exec(&op)) 475 if(sfc_exec(&op))
379 return NANDERR_COMMAND_FAILED; 476 return NAND_ERR_CONTROLLER;
380 477
381 return 0; 478 return NAND_SUCCESS;
382} 479}
383 480
384int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr) 481static int nandcmd_program_execute(uint32_t row_addr)
385{ 482{
386 sfc_op op = {0}; 483 sfc_op op = {0};
387 op.command = NAND_CMD_PROGRAM_EXECUTE; 484 op.command = NAND_CMD_PROGRAM_EXECUTE;
388 op.addr_bytes = d->chip_data->rowaddr_width; 485 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
389 op.addr_lo = rowaddr; 486 op.addr_lo = row_addr;
390 if(sfc_exec(&op)) 487 if(sfc_exec(&op))
391 return NANDERR_COMMAND_FAILED; 488 return NAND_ERR_CONTROLLER;
392 489
393 return 0; 490 return NAND_SUCCESS;
394} 491}
395 492
396int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr) 493static int nandcmd_block_erase(uint32_t block_addr)
397{ 494{
398 sfc_op op = {0}; 495 sfc_op op = {0};
399 op.command = NAND_CMD_BLOCK_ERASE; 496 op.command = NAND_CMD_BLOCK_ERASE;
400 op.addr_bytes = d->chip_data->rowaddr_width; 497 op.addr_bytes = nand_drv.chip_data->rowaddr_width;
401 op.addr_lo = blockaddr; 498 op.addr_lo = block_addr;
402 if(sfc_exec(&op)) 499 if(sfc_exec(&op))
403 return NANDERR_COMMAND_FAILED; 500 return NAND_ERR_CONTROLLER;
404 501
405 return 0; 502 return NAND_SUCCESS;
406}
407
408const nand_chip_ops nand_chip_ops_std = {
409 .open = nandop_std_open,
410 .close = nandop_std_close,
411 .read_page = nandop_std_read_page,
412 .write_page = nandop_std_write_page,
413 .erase_block = nandop_std_erase_block,
414 .set_wp_enable = nandop_std_set_wp_enable,
415 .ecc_read = nandop_ecc_none_read,
416 .ecc_write = nandop_ecc_none_write,
417};
418
419/* Helper needed by other ops */
420static int nandop_std_wait_status(nand_drv* d, int errbit)
421{
422 int reg;
423 do {
424 reg = nandcmd_get_feature(d, NAND_FREG_STATUS);
425 if(reg < 0)
426 return reg;
427 } while(reg & NAND_FREG_STATUS_OIP);
428
429 if(reg & errbit)
430 return NANDERR_OTHER;
431
432 return reg;
433}
434
435int nandop_std_open(nand_drv* d)
436{
437 (void)d;
438 return 0;
439}
440
441void nandop_std_close(nand_drv* d)
442{
443 (void)d;
444}
445
446int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf)
447{
448 int status;
449
450 if((status = nandcmd_page_read_to_cache(d, rowaddr)) < 0)
451 return status;
452 if((status = nandop_std_wait_status(d, 0)) < 0)
453 return status;
454 if((status = nandcmd_read_from_cache(d, buf)) < 0)
455 return status;
456
457 return 0;
458}
459
460int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf)
461{
462 int status;
463
464 if((status = nandcmd_write_enable(d)) < 0)
465 return status;
466 if((status = nandcmd_program_load(d, buf)) < 0)
467 return status;
468 if((status = nandcmd_program_execute(d, rowaddr)) < 0)
469 return status;
470 if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_P_FAIL)) < 0)
471 return status;
472
473 return 0;
474}
475
476int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr)
477{
478 int status;
479
480 if((status = nandcmd_write_enable(d)) < 0)
481 return status;
482 if((status = nandcmd_block_erase(d, blockaddr)) < 0)
483 return status;
484 if((status = nandop_std_wait_status(d, NAND_FREG_STATUS_E_FAIL) < 0))
485 return status;
486
487 return 0;
488}
489
490int nandop_std_set_wp_enable(nand_drv* d, bool en)
491{
492 int val = nandcmd_get_feature(d, NAND_FREG_PROTECTION);
493 if(val < 0)
494 return val;
495
496 if(en) {
497 val &= ~NAND_FREG_PROTECTION_ALLBP;
498 if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
499 val &= ~NAND_FREG_PROTECTION_BRWD;
500 } else {
501 val |= NAND_FREG_PROTECTION_ALLBP;
502 if(d->chip_data->flags & NANDCHIP_FLAG_USE_BRWD)
503 val |= NAND_FREG_PROTECTION_BRWD;
504 }
505
506 sfc_set_wp_enable(false);
507 int status = nandcmd_set_feature(d, NAND_FREG_PROTECTION, val);
508 sfc_set_wp_enable(true);
509
510 if(status < 0)
511 return status;
512
513 return 0;
514}
515
516int nandop_ecc_none_read(nand_drv* d, unsigned char* buf)
517{
518 (void)d;
519 (void)buf;
520 return 0;
521}
522
523void nandop_ecc_none_write(nand_drv* d, unsigned char* buf)
524{
525 memset(&buf[d->chip_data->page_size], 0xff, d->chip_data->spare_size);
526} 503}
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.h b/firmware/target/mips/ingenic_x1000/nand-x1000.h
index f6709aaaf9..6c415b1170 100644
--- a/firmware/target/mips/ingenic_x1000/nand-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/nand-x1000.h
@@ -26,6 +26,11 @@
26 * Not suitable for general data storage. It doesn't have proper support for 26 * Not suitable for general data storage. It doesn't have proper support for
27 * partial page writes, access to spare area, etc, which are all necessary 27 * partial page writes, access to spare area, etc, which are all necessary
28 * for an effective flash translation layer. 28 * for an effective flash translation layer.
29 *
30 * There's no ECC support. This can be added if necessary, but it's unlikely
31 * the boot area on any X1000 device uses software ECC as Ingenic's SPL simply
32 * doesn't have much room for more code (theirs programmed to work on multiple
33 * hardware configurations, so it's bigger than ours).
29 */ 34 */
30 35
31#include <stdint.h> 36#include <stdint.h>
@@ -39,176 +44,57 @@
39/* Set/clear the BRWD bit when enabling/disabling write protection */ 44/* Set/clear the BRWD bit when enabling/disabling write protection */
40#define NANDCHIP_FLAG_USE_BRWD 0x02 45#define NANDCHIP_FLAG_USE_BRWD 0x02
41 46
42typedef struct nand_drv nand_drv;
43
44/* Defines some static information about a NAND chip */
45typedef struct nand_chip_data { 47typedef struct nand_chip_data {
46 const char* name; /* Name for debugging purposes */ 48 /* Chip manufacturer / device ID */
47 uint8_t mf_id; /* Manufacturer ID */ 49 uint8_t mf_id;
48 uint8_t dev_id; /* Device ID */ 50 uint8_t dev_id;
49 uint8_t rowaddr_width; /* Number of bytes in row addresses */ 51
50 uint8_t coladdr_width; /* Number of bytes in column addresses */ 52 /* Width of row/column addresses in bytes */
51 uint32_t dev_conf; /* Value to write to SFC_DEV_CONF register */ 53 uint8_t rowaddr_width;
52 uint32_t clock_freq; /* Frequency to switch to after identification */ 54 uint8_t coladdr_width;
53 uint32_t block_size; /* Number of pages per eraseblock */ 55
54 uint32_t page_size; /* Number of data bytes per page */ 56 /* SFC dev conf and clock frequency to use for this device */
55 uint32_t spare_size; /* Number of spare bytes per page */ 57 uint32_t dev_conf;
56 int flags; /* Various flags */ 58 uint32_t clock_freq;
59
60 /* Page size in bytes = 1 << log2_page_size */
61 uint32_t log2_page_size;
62
63 /* Block size in number of pages = 1 << log2_block_size */
64 uint32_t log2_block_size;
65
66 /* Chip flags */
67 uint32_t flags;
57} nand_chip_data; 68} nand_chip_data;
58 69
59/* Defines high-level operations used to implement the public API. 70/* Open or close the NAND driver. The NAND driver takes control of the SFC,
60 * Chips may need to override operations if the default ones aren't suitable. 71 * so that driver must be in the closed state before opening the NAND driver.
61 *
62 * Negative return codes return an error, while zero or positive codes are
63 * considered successful. This allows a function to return meaningful data,
64 * if applicable.
65 */ 72 */
66typedef struct nand_chip_ops {
67 /* Called once after identifying the chip */
68 int(*open)(nand_drv* d);
69
70 /* Called once when the driver is closed */
71 void(*close)(nand_drv* d);
72
73 /* Read or write a complete page including both main and spare areas. */
74 int(*read_page)(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
75 int(*write_page)(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
76
77 /* Erase a block. */
78 int(*erase_block)(nand_drv* d, uint32_t blockaddr);
79
80 /* Enable or disable the chip's write protection. */
81 int(*set_wp_enable)(nand_drv* d, bool en);
82
83 /* Perform error correction and detection on a raw page (main + spare).
84 * Return the number of errors detected and corrected, or a negative value
85 * if errors were detected but could not be corrected.
86 */
87 int(*ecc_read)(nand_drv* d, unsigned char* buf);
88
89 /* Generate ECC data for a page. The buffer main area is already filled
90 * and this function should write ECC data into the spare area.
91 */
92 void(*ecc_write)(nand_drv* d, unsigned char* buf);
93} nand_chip_ops;
94
95/* Struct used to list all supported NAND chips in an array */
96typedef struct nand_chip_desc {
97 const nand_chip_data* data;
98 const nand_chip_ops* ops;
99} nand_chip_desc;
100
101/* NAND driver structure. It can be accessed by chip ops, but they must not
102 * modify any fields except for "auxbuf", which is a small buffer that can
103 * be used for commands that need to read/write small amounts of data: often
104 * needed for polling status, etc.
105 */
106struct nand_drv {
107 const nand_chip_ops* chip_ops;
108 const nand_chip_data* chip_data;
109 unsigned char* pagebuf;
110 unsigned char* auxbuf;
111 uint32_t raw_page_size;
112 int flags;
113};
114
115/* Note: sfc_init() must be called prior to nand_open() */
116extern int nand_open(void); 73extern int nand_open(void);
117extern void nand_close(void); 74extern void nand_close(void);
118 75
119/* Controls device-side write protection registers as well as software lock. 76/* Identify the NAND chip. This must be done after opening the driver and
120 * Erase and program operations will fail unless you first enable writes. 77 * prior to any data access, in order to set the chip parameters. */
121 */ 78extern int nand_identify(int* mf_id, int* dev_id);
79
80/* Return the chip data for the identified NAND chip.
81 * Returns NULL if the chip is not identified. */
82const nand_chip_data* nand_get_chip_data(void);
83
84/* Controls the chip's write protect features. The driver also keeps track of
85 * this flag and refuses to perform write or erase operations unless you have
86 * enabled writes. Writes should be disabled again when you finish writing. */
122extern int nand_enable_writes(bool en); 87extern int nand_enable_writes(bool en);
123 88
124/* Byte-based NAND operations */ 89/* Reading and writing operates on whole pages at a time. If the address or
125extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf); 90 * size is not aligned to a multiple of the page size, no data will be read
126extern int nand_write_bytes(uint32_t byteaddr, int count, const void* buf); 91 * or written and an error code is returned. */
127extern int nand_erase_bytes(uint32_t byteaddr, int count); 92extern int nand_read(uint32_t addr, uint32_t size, uint8_t* buf);
128 93extern int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf);
129/* NAND command numbers */
130#define NAND_CMD_READ_ID 0x9f
131#define NAND_CMD_WRITE_ENABLE 0x06
132#define NAND_CMD_GET_FEATURE 0x0f
133#define NAND_CMD_SET_FEATURE 0x1f
134#define NAND_CMD_PAGE_READ_TO_CACHE 0x13
135#define NAND_CMD_READ_FROM_CACHE 0x0b
136#define NAND_CMD_READ_FROM_CACHEx4 0x6b
137#define NAND_CMD_PROGRAM_LOAD 0x02
138#define NAND_CMD_PROGRAM_LOADx4 0x32
139#define NAND_CMD_PROGRAM_EXECUTE 0x10
140#define NAND_CMD_BLOCK_ERASE 0xd8
141
142/* NAND device register addresses for GET_FEATURE / SET_FEATURE */
143#define NAND_FREG_PROTECTION 0xa0
144#define NAND_FREG_FEATURE 0xb0
145#define NAND_FREG_STATUS 0xc0
146
147/* Protection register bits */
148#define NAND_FREG_PROTECTION_BRWD 0x80
149#define NAND_FREG_PROTECTION_BP2 0x20
150#define NAND_FREG_PROTECTION_BP1 0x10
151#define NAND_FREG_PROTECTION_BP0 0x80
152/* Mask of BP bits 0-2 */
153#define NAND_FREG_PROTECTION_ALLBP (0x38)
154
155/* Feature register bits */
156#define NAND_FREG_FEATURE_QE 0x01
157
158/* Status register bits */
159#define NAND_FREG_STATUS_OIP 0x01
160#define NAND_FREG_STATUS_WEL 0x02
161#define NAND_FREG_STATUS_E_FAIL 0x04
162#define NAND_FREG_STATUS_P_FAIL 0x08
163
164/* Standard implementations for low-level NAND commands. I'm not aware of any
165 * actual standard governing these, but it seems many vendors follow the same
166 * command numbering, status bits, and behavior so these implementations should
167 * work across a wide variety of chips.
168 *
169 * If adding a new NAND chip which only has a minor deviation from these
170 * standard implementations, consider adding a flag and modifying these
171 * functions to change their behavior based on the flag, instead of writing
172 * a whole new implementation.
173 *
174 * None of these functions are directly called by the high-level driver code,
175 * except for nandcmd_std_read_id(). They can be used to implement higher-level
176 * functions in a device's "nand_chip_ops".
177 */
178extern int nandcmd_read_id(nand_drv* d);
179extern int nandcmd_write_enable(nand_drv* d);
180extern int nandcmd_get_feature(nand_drv* d, int reg);
181extern int nandcmd_set_feature(nand_drv* d, int reg, int val);
182extern int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr);
183extern int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf);
184extern int nandcmd_program_load(nand_drv* d, const unsigned char* buf);
185extern int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr);
186extern int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr);
187
188/* Table filled with all the standard operations for chips which don't
189 * need to override any operations.
190 */
191extern const nand_chip_ops nand_chip_ops_std;
192 94
193/* Standard NAND chip ops based on the standard "nandcmd" functions. 95/* Ereas eoperates on whole blocks. Like the page read/write operations,
194 * 96 * the address and size must be aligned to a multiple of the block size.
195 * Same advice applies here as there: if it's possible to support minor 97 * If not, no blocks are erased and an error code is returned. */
196 * chip variations with a flag, modify these functions to do so. 98extern int nand_erase(uint32_t addr, uint32_t size);
197 */
198extern int nandop_std_open(nand_drv* d);
199extern void nandop_std_close(nand_drv* d);
200extern int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf);
201extern int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf);
202extern int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr);
203extern int nandop_std_set_wp_enable(nand_drv* d, bool en);
204
205/* The default ECC implementation is a no-op: reads always succeed without
206 * reporting errors and writes will fill the spare area with '0xff' bytes.
207 *
208 * For chips that support internal ECC, this often works because the chip will
209 * ignore writes to the ECC areas.
210 */
211extern int nandop_ecc_none_read(nand_drv* d, unsigned char* buf);
212extern void nandop_ecc_none_write(nand_drv* d, unsigned char* buf);
213 99
214#endif /* __NAND_X1000_H__ */ 100#endif /* __NAND_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index 36ef25f60d..6505cabbef 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -229,29 +229,39 @@ static void init(void)
229static int nandread(uint32_t addr, uint32_t size, void* buffer) 229static int nandread(uint32_t addr, uint32_t size, void* buffer)
230{ 230{
231 int rc; 231 int rc;
232 int mf_id, dev_id;
232 233
233 if((rc = nand_open())) 234 if((rc = nand_open()))
234 return rc; 235 return rc;
236 if((rc = nand_identify(&mf_id, &dev_id))) {
237 nand_close();
238 return rc;
239 }
235 240
236 rc = nand_read_bytes(addr, size, buffer); 241 rc = nand_read(addr, size, (uint8_t*)buffer);
237 nand_close(); 242 nand_close();
238 return rc; 243 return rc;
239} 244}
240 245
241static int nandwrite(uint32_t addr, uint32_t size, void* buffer) 246static int nandwrite(uint32_t addr, uint32_t size, const void* buffer)
242{ 247{
243 int rc; 248 int rc;
249 int mf_id, dev_id;
244 250
245 if((rc = nand_open())) 251 if((rc = nand_open()))
246 return rc; 252 return rc;
253 if((rc = nand_identify(&mf_id, &dev_id))) {
254 nand_close();
255 return rc;
256 }
247 257
248 if((rc = nand_enable_writes(true))) 258 if((rc = nand_enable_writes(true)))
249 goto _end; 259 goto _end;
250 260
251 if((rc = nand_erase_bytes(addr, size))) 261 if((rc = nand_erase(addr, size)))
252 goto _end1; 262 goto _end1;
253 263
254 rc = nand_write_bytes(addr, size, buffer); 264 rc = nand_write(addr, size, (const uint8_t*)buffer);
255 265
256 _end1: 266 _end1:
257 /* an error here is very unlikely, so ignore it */ 267 /* an error here is very unlikely, so ignore it */
@@ -309,7 +319,7 @@ void main(void)
309 case SPL_CMD_FLASH_WRITE: 319 case SPL_CMD_FLASH_WRITE:
310 SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1, 320 SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1,
311 SPL_ARGUMENTS->param2, 321 SPL_ARGUMENTS->param2,
312 (void*)SPL_BUFFER_ADDRESS); 322 (const void*)SPL_BUFFER_ADDRESS);
313 return; 323 return;
314 } 324 }
315} 325}
diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds
index e8bf9d4700..36ae8c34d1 100644
--- a/firmware/target/mips/ingenic_x1000/spl.lds
+++ b/firmware/target/mips/ingenic_x1000/spl.lds
@@ -11,12 +11,6 @@ MEMORY {
11 * and the next 2k are occupied by SPL header */ 11 * and the next 2k are occupied by SPL header */
12 TCSM : ORIGIN = X1000_TCSM_BASE + 0x1800, 12 TCSM : ORIGIN = X1000_TCSM_BASE + 0x1800,
13 LENGTH = X1000_TCSM_SIZE - 0x1800 13 LENGTH = X1000_TCSM_SIZE - 0x1800
14
15 /* Small area of DRAM is required for NAND bounce buffers,
16 * though not strictly necessary as ECC isn't really practical
17 * this early in the boot */
18 DRAM : ORIGIN = X1000_DRAM_END - 16K,
19 LENGTH = 16K
20} 14}
21 15
22SECTIONS 16SECTIONS
@@ -52,11 +46,6 @@ SECTIONS
52 _bssend = .; 46 _bssend = .;
53 } > TCSM 47 } > TCSM
54 48
55 .sdram (NOLOAD) :
56 {
57 *(.sdram);
58 } > DRAM
59
60 /DISCARD/ : 49 /DISCARD/ :
61 { 50 {
62 *(.MIPS.abiflags); 51 *(.MIPS.abiflags);