1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * Copyright (C) 2009 by Marvell Semiconductors, Inc.
5 * Written by Nicolas Pitre <nico at marvell.com>
7 * Copyright (C) 2009 by David Brownell
16 #include <helper/binarybuffer.h>
17 #include <target/arm.h>
18 #include <target/armv7m.h>
19 #include <target/algorithm.h>
22 * Copies code to a working area. This will allocate room for the code plus the
23 * additional amount requested if the working area pointer is null.
25 * @param target Pointer to the target to copy code to
26 * @param code Pointer to the code area to be copied
27 * @param code_size Size of the code being copied
28 * @param additional Size of the additional area to be allocated in addition to
30 * @param area Pointer to a pointer to a working area to copy code to
31 * @return Success or failure of the operation
33 static int arm_code_to_working_area(struct target
*target
,
34 const uint32_t *code
, unsigned code_size
,
35 unsigned additional
, struct working_area
**area
)
37 uint8_t code_buf
[code_size
];
39 unsigned size
= code_size
+ additional
;
41 /* REVISIT this assumes size doesn't ever change.
42 * That's usually correct; but there are boards with
43 * both large and small page chips, where it won't be...
46 /* make sure we have a working area */
48 retval
= target_alloc_working_area(target
, size
, area
);
49 if (retval
!= ERROR_OK
) {
50 LOG_DEBUG("%s: no %d byte buffer", __func__
, (int) size
);
51 return ERROR_NAND_NO_BUFFER
;
55 /* buffer code in target endianness */
56 target_buffer_set_u32_array(target
, code_buf
, code_size
/ 4, code
);
58 /* copy code to work area */
59 retval
= target_write_memory(target
, (*area
)->address
,
60 4, code_size
/ 4, code_buf
);
66 * ARM-specific bulk write from buffer to address of 8-bit wide NAND.
67 * For now this supports ARMv4,ARMv5 and ARMv7-M cores.
69 * Enhancements to target_run_algorithm() could enable:
70 * - ARMv6 and ARMv7 cores in ARM mode
72 * Different code fragments could handle:
73 * - 16-bit wide data (needs different setup)
75 * @param nand Pointer to the arm_nand_data struct that defines the I/O
76 * @param data Pointer to the data to be copied to flash
77 * @param size Size of the data being copied
78 * @return Success or failure of the operation
80 int arm_nandwrite(struct arm_nand_data
*nand
, uint8_t *data
, int size
)
82 struct target
*target
= nand
->target
;
83 struct arm_algorithm armv4_5_algo
;
84 struct armv7m_algorithm armv7m_algo
;
86 struct arm
*arm
= target
->arch_info
;
87 struct reg_param reg_params
[3];
89 uint32_t exit_var
= 0;
93 * r0 NAND data address (byte wide)
97 static const uint32_t code_armv4_5
[] = {
98 0xe4d13001, /* s: ldrb r3, [r1], #1 */
99 0xe5c03000, /* strb r3, [r0] */
100 0xe2522001, /* subs r2, r2, #1 */
101 0x1afffffb, /* bne s */
103 /* exit: ARMv4 needs hardware breakpoint */
104 0xe1200070, /* e: bkpt #0 */
108 * r0 NAND data address (byte wide)
112 * see contrib/loaders/flash/armv7m_io.s for src
114 static const uint32_t code_armv7m
[] = {
121 int target_code_size
= 0;
122 const uint32_t *target_code_src
= NULL
;
124 /* set up algorithm */
125 if (is_armv7m(target_to_armv7m(target
))) { /* armv7m target */
126 armv7m_algo
.common_magic
= ARMV7M_COMMON_MAGIC
;
127 armv7m_algo
.core_mode
= ARM_MODE_THREAD
;
128 arm_algo
= &armv7m_algo
;
129 target_code_size
= sizeof(code_armv7m
);
130 target_code_src
= code_armv7m
;
132 armv4_5_algo
.common_magic
= ARM_COMMON_MAGIC
;
133 armv4_5_algo
.core_mode
= ARM_MODE_SVC
;
134 armv4_5_algo
.core_state
= ARM_STATE_ARM
;
135 arm_algo
= &armv4_5_algo
;
136 target_code_size
= sizeof(code_armv4_5
);
137 target_code_src
= code_armv4_5
;
140 if (nand
->op
!= ARM_NAND_WRITE
|| !nand
->copy_area
) {
141 retval
= arm_code_to_working_area(target
, target_code_src
, target_code_size
,
142 nand
->chunk_size
, &nand
->copy_area
);
143 if (retval
!= ERROR_OK
)
147 nand
->op
= ARM_NAND_WRITE
;
149 /* copy data to work area */
150 target_buf
= nand
->copy_area
->address
+ target_code_size
;
151 retval
= target_write_buffer(target
, target_buf
, size
, data
);
152 if (retval
!= ERROR_OK
)
155 /* set up parameters */
156 init_reg_param(®_params
[0], "r0", 32, PARAM_IN
);
157 init_reg_param(®_params
[1], "r1", 32, PARAM_IN
);
158 init_reg_param(®_params
[2], "r2", 32, PARAM_IN
);
160 buf_set_u32(reg_params
[0].value
, 0, 32, nand
->data
);
161 buf_set_u32(reg_params
[1].value
, 0, 32, target_buf
);
162 buf_set_u32(reg_params
[2].value
, 0, 32, size
);
164 /* armv4 must exit using a hardware breakpoint */
165 if (arm
->arch
== ARM_ARCH_V4
)
166 exit_var
= nand
->copy_area
->address
+ target_code_size
- 4;
168 /* use alg to write data from work area to NAND chip */
169 retval
= target_run_algorithm(target
, 0, NULL
, 3, reg_params
,
170 nand
->copy_area
->address
, exit_var
, 1000, arm_algo
);
171 if (retval
!= ERROR_OK
)
172 LOG_ERROR("error executing hosted NAND write");
174 destroy_reg_param(®_params
[0]);
175 destroy_reg_param(®_params
[1]);
176 destroy_reg_param(®_params
[2]);
182 * Uses an on-chip algorithm for an ARM device to read from a NAND device and
183 * store the data into the host machine's memory.
185 * @param nand Pointer to the arm_nand_data struct that defines the I/O
186 * @param data Pointer to the data buffer to store the read data
187 * @param size Amount of data to be stored to the buffer.
188 * @return Success or failure of the operation
190 int arm_nandread(struct arm_nand_data
*nand
, uint8_t *data
, uint32_t size
)
192 struct target
*target
= nand
->target
;
193 struct arm_algorithm armv4_5_algo
;
194 struct armv7m_algorithm armv7m_algo
;
196 struct arm
*arm
= target
->arch_info
;
197 struct reg_param reg_params
[3];
199 uint32_t exit_var
= 0;
204 * r1 NAND data address (byte wide)
207 static const uint32_t code_armv4_5
[] = {
208 0xe5d13000, /* s: ldrb r3, [r1] */
209 0xe4c03001, /* strb r3, [r0], #1 */
210 0xe2522001, /* subs r2, r2, #1 */
211 0x1afffffb, /* bne s */
213 /* exit: ARMv4 needs hardware breakpoint */
214 0xe1200070, /* e: bkpt #0 */
219 * r1 NAND data address (byte wide)
222 * see contrib/loaders/flash/armv7m_io.s for src
224 static const uint32_t code_armv7m
[] = {
231 int target_code_size
= 0;
232 const uint32_t *target_code_src
= NULL
;
234 /* set up algorithm */
235 if (is_armv7m(target_to_armv7m(target
))) { /* armv7m target */
236 armv7m_algo
.common_magic
= ARMV7M_COMMON_MAGIC
;
237 armv7m_algo
.core_mode
= ARM_MODE_THREAD
;
238 arm_algo
= &armv7m_algo
;
239 target_code_size
= sizeof(code_armv7m
);
240 target_code_src
= code_armv7m
;
242 armv4_5_algo
.common_magic
= ARM_COMMON_MAGIC
;
243 armv4_5_algo
.core_mode
= ARM_MODE_SVC
;
244 armv4_5_algo
.core_state
= ARM_STATE_ARM
;
245 arm_algo
= &armv4_5_algo
;
246 target_code_size
= sizeof(code_armv4_5
);
247 target_code_src
= code_armv4_5
;
250 /* create the copy area if not yet available */
251 if (nand
->op
!= ARM_NAND_READ
|| !nand
->copy_area
) {
252 retval
= arm_code_to_working_area(target
, target_code_src
, target_code_size
,
253 nand
->chunk_size
, &nand
->copy_area
);
254 if (retval
!= ERROR_OK
)
258 nand
->op
= ARM_NAND_READ
;
259 target_buf
= nand
->copy_area
->address
+ target_code_size
;
261 /* set up parameters */
262 init_reg_param(®_params
[0], "r0", 32, PARAM_IN
);
263 init_reg_param(®_params
[1], "r1", 32, PARAM_IN
);
264 init_reg_param(®_params
[2], "r2", 32, PARAM_IN
);
266 buf_set_u32(reg_params
[0].value
, 0, 32, target_buf
);
267 buf_set_u32(reg_params
[1].value
, 0, 32, nand
->data
);
268 buf_set_u32(reg_params
[2].value
, 0, 32, size
);
270 /* armv4 must exit using a hardware breakpoint */
271 if (arm
->arch
== ARM_ARCH_V4
)
272 exit_var
= nand
->copy_area
->address
+ target_code_size
- 4;
274 /* use alg to write data from NAND chip to work area */
275 retval
= target_run_algorithm(target
, 0, NULL
, 3, reg_params
,
276 nand
->copy_area
->address
, exit_var
, 1000, arm_algo
);
277 if (retval
!= ERROR_OK
)
278 LOG_ERROR("error executing hosted NAND read");
280 destroy_reg_param(®_params
[0]);
281 destroy_reg_param(®_params
[1]);
282 destroy_reg_param(®_params
[2]);
284 /* read from work area to the host's memory */
285 retval
= target_read_buffer(target
, target_buf
, size
, data
);