diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-05-06 00:22:56 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-05-06 08:37:26 +0100 |
commit | 15ad1c42db95b178b6125b8084148ed4463f1472 (patch) | |
tree | 4cb0a41b3dd077319a79c825d2ee666176254c7d /firmware/target | |
parent | 244aad750c0148265f03c9da8a33b4e0a9f85468 (diff) | |
download | rockbox-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')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c | 33 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c | 53 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/nand-target.h | 37 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/nand-x1000-err.h | 9 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/nand-x1000.c | 665 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/nand-x1000.h | 208 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000.c | 20 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl.lds | 11 |
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 | ||
41 | static int install_from_buffer(const void* buf) | 42 | static 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 | ||
73 | static int dump_to_buffer(void* buf) | 79 | static 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) | |||
90 | int install_bootloader(const char* path) | 101 | int 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) | |||
132 | int dump_bootloader(const char* path) | 145 | int 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 | */ | ||
33 | static 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 | |||
50 | const 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 | |
41 | extern 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 */ | ||
63 | const 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 | |||
85 | const 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 */ | ||
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 | }; | ||
48 | 110 | ||
49 | /* Globals for the driver */ | 111 | static struct nand_drv nand_drv; |
50 | static unsigned char pagebuffer[NAND_MAX_PAGE_SIZE] NANDBUFFER_ATTR; | 112 | static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR; |
51 | static unsigned char auxbuffer[NAND_AUX_BUFFER_SIZE] NANDBUFFER_ATTR; | ||
52 | static nand_drv nand_driver; | ||
53 | 113 | ||
54 | static void nand_drv_reset(nand_drv* d) | 114 | static 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 */ | ||
65 | int nand_open(void) | 120 | int 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 | ||
120 | void nand_close(void) | 135 | void 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 | ||
129 | int nand_enable_writes(bool en) | 143 | int 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 | ||
145 | extern int nand_read_bytes(uint32_t byteaddr, int count, void* buf) | 174 | const 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; | 179 | extern 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; | 191 | static 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 | ||
184 | int nand_write_bytes(uint32_t byteaddr, int count, const void* buf) | 229 | int 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) | 234 | int 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) | 239 | int 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(); | 273 | static 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 | ||
226 | int nand_erase_bytes(uint32_t byteaddr, int count) | 288 | static 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; | 302 | static 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 | |
318 | static 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) { | 332 | static 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 | ||
269 | int nandcmd_read_id(nand_drv* d) | 362 | /* |
363 | * Low-level NAND commands | ||
364 | */ | ||
365 | |||
366 | static 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 | ||
284 | int nandcmd_write_enable(nand_drv* d) | 383 | static 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 | ||
296 | int nandcmd_get_feature(nand_drv* d, int reg) | 393 | static 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 | ||
311 | int nandcmd_set_feature(nand_drv* d, int reg, int val) | 408 | static 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 | ||
327 | int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr) | 424 | static 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 | ||
339 | int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf) | 436 | static 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 | ||
362 | int nandcmd_program_load(nand_drv* d, const unsigned char* buf) | 459 | static 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 | ||
384 | int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr) | 481 | static 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 | ||
396 | int nandcmd_block_erase(nand_drv* d, uint32_t blockaddr) | 493 | static 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 | |||
408 | const 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 */ | ||
420 | static 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 | |||
435 | int nandop_std_open(nand_drv* d) | ||
436 | { | ||
437 | (void)d; | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | void nandop_std_close(nand_drv* d) | ||
442 | { | ||
443 | (void)d; | ||
444 | } | ||
445 | |||
446 | int 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 | |||
460 | int 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 | |||
476 | int 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 | |||
490 | int 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 | |||
516 | int nandop_ecc_none_read(nand_drv* d, unsigned char* buf) | ||
517 | { | ||
518 | (void)d; | ||
519 | (void)buf; | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | void 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 | ||
42 | typedef struct nand_drv nand_drv; | ||
43 | |||
44 | /* Defines some static information about a NAND chip */ | ||
45 | typedef struct nand_chip_data { | 47 | typedef 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 | */ |
66 | typedef 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 */ | ||
96 | typedef 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 | */ | ||
106 | struct 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() */ | ||
116 | extern int nand_open(void); | 73 | extern int nand_open(void); |
117 | extern void nand_close(void); | 74 | extern 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 | */ | 78 | extern 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. */ | ||
82 | const 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. */ | ||
122 | extern int nand_enable_writes(bool en); | 87 | extern 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 |
125 | extern 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 |
126 | extern int nand_write_bytes(uint32_t byteaddr, int count, const void* buf); | 91 | * or written and an error code is returned. */ |
127 | extern int nand_erase_bytes(uint32_t byteaddr, int count); | 92 | extern int nand_read(uint32_t addr, uint32_t size, uint8_t* buf); |
128 | 93 | extern 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 | */ | ||
178 | extern int nandcmd_read_id(nand_drv* d); | ||
179 | extern int nandcmd_write_enable(nand_drv* d); | ||
180 | extern int nandcmd_get_feature(nand_drv* d, int reg); | ||
181 | extern int nandcmd_set_feature(nand_drv* d, int reg, int val); | ||
182 | extern int nandcmd_page_read_to_cache(nand_drv* d, uint32_t rowaddr); | ||
183 | extern int nandcmd_read_from_cache(nand_drv* d, unsigned char* buf); | ||
184 | extern int nandcmd_program_load(nand_drv* d, const unsigned char* buf); | ||
185 | extern int nandcmd_program_execute(nand_drv* d, uint32_t rowaddr); | ||
186 | extern 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 | */ | ||
191 | extern 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. | 98 | extern int nand_erase(uint32_t addr, uint32_t size); |
197 | */ | ||
198 | extern int nandop_std_open(nand_drv* d); | ||
199 | extern void nandop_std_close(nand_drv* d); | ||
200 | extern int nandop_std_read_page(nand_drv* d, uint32_t rowaddr, unsigned char* buf); | ||
201 | extern int nandop_std_write_page(nand_drv* d, uint32_t rowaddr, const unsigned char* buf); | ||
202 | extern int nandop_std_erase_block(nand_drv* d, uint32_t blockaddr); | ||
203 | extern 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 | */ | ||
211 | extern int nandop_ecc_none_read(nand_drv* d, unsigned char* buf); | ||
212 | extern 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) | |||
229 | static int nandread(uint32_t addr, uint32_t size, void* buffer) | 229 | static 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 | ||
241 | static int nandwrite(uint32_t addr, uint32_t size, void* buffer) | 246 | static 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 | ||
22 | SECTIONS | 16 | SECTIONS |
@@ -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); |