--- /dev/null
+/***************************************************************************\r
+ * Copyright (C) 2009 by Marvell Semiconductors, Inc. *\r
+ * Written by Nicolas Pitre <nico at marvell.com> *\r
+ * *\r
+ * This program is free software; you can redistribute it and/or modify *\r
+ * it under the terms of the GNU General Public License as published by *\r
+ * the Free Software Foundation; either version 2 of the License, or *\r
+ * (at your option) any later version. *\r
+ * *\r
+ * This program is distributed in the hope that it will be useful, *\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *\r
+ * GNU General Public License for more details. *\r
+ * *\r
+ * You should have received a copy of the GNU General Public License *\r
+ * along with this program; if not, write to the *\r
+ * Free Software Foundation, Inc., *\r
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *\r
+ ***************************************************************************/\r
+\r
+/*\r
+ * NAND controller interface for Marvell Orion/Kirkwood SoCs.\r
+ */\r
+\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include "replacements.h"\r
+#include "log.h"\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include "nand.h"\r
+#include "target.h"\r
+#include "armv4_5.h"\r
+#include "binarybuffer.h"\r
+\r
+typedef struct orion_nand_controller_s\r
+{\r
+ struct target_s *target;\r
+ working_area_t *copy_area;\r
+\r
+ u32 cmd;\r
+ u32 addr;\r
+ u32 data;\r
+} orion_nand_controller_t;\r
+\r
+#define CHECK_HALTED \\r
+ do { \\r
+ if (target->state != TARGET_HALTED) { \\r
+ LOG_ERROR("NAND flash access requires halted target"); \\r
+ return ERROR_NAND_OPERATION_FAILED; \\r
+ } \\r
+ } while (0)\r
+\r
+int orion_nand_command(struct nand_device_s *device, u8 command)\r
+{\r
+ orion_nand_controller_t *hw = device->controller_priv;\r
+ target_t *target = hw->target;\r
+\r
+ CHECK_HALTED;\r
+ target_write_u8(target, hw->cmd, command);\r
+ return ERROR_OK;\r
+}\r
+\r
+int orion_nand_address(struct nand_device_s *device, u8 address)\r
+{\r
+ orion_nand_controller_t *hw = device->controller_priv;\r
+ target_t *target = hw->target;\r
+\r
+ CHECK_HALTED;\r
+ target_write_u8(target, hw->addr, address);\r
+ return ERROR_OK;\r
+}\r
+\r
+int orion_nand_read(struct nand_device_s *device, void *data)\r
+{\r
+ orion_nand_controller_t *hw = device->controller_priv;\r
+ target_t *target = hw->target;\r
+\r
+ CHECK_HALTED;\r
+ target_read_u8(target, hw->data, data);\r
+ return ERROR_OK;\r
+}\r
+\r
+int orion_nand_write(struct nand_device_s *device, u16 data)\r
+{\r
+ orion_nand_controller_t *hw = device->controller_priv;\r
+ target_t *target = hw->target;\r
+\r
+ CHECK_HALTED;\r
+ target_write_u8(target, hw->data, data);\r
+ return ERROR_OK;\r
+}\r
+\r
+int orion_nand_slow_block_write(struct nand_device_s *device, u8 *data, int size)\r
+{\r
+ while (size--)\r
+ orion_nand_write(device, *data++);\r
+ return ERROR_OK;\r
+}\r
+\r
+int orion_nand_fast_block_write(struct nand_device_s *device, u8 *data, int size)\r
+{\r
+ orion_nand_controller_t *hw = device->controller_priv;\r
+ target_t *target = hw->target;\r
+ armv4_5_algorithm_t algo;\r
+ reg_param_t reg_params[3];\r
+ u32 target_buf;\r
+ int retval;\r
+\r
+ static const u32 code[] = {\r
+ 0xe4d13001, /* ldrb r3, [r1], #1 */\r
+ 0xe5c03000, /* strb r3, [r0] */\r
+ 0xe2522001, /* subs r2, r2, #1 */\r
+ 0x1afffffb, /* bne 0 */\r
+ 0xeafffffe, /* b . */\r
+ };\r
+ int code_size = sizeof(code);\r
+\r
+ if (!hw->copy_area) {\r
+ u8 code_buf[code_size];\r
+ int i;\r
+\r
+ /* make sure we have a working area */\r
+ if (target_alloc_working_area(target,\r
+ code_size + device->page_size,\r
+ &hw->copy_area) != ERROR_OK)\r
+ {\r
+ return orion_nand_slow_block_write(device, data, size);\r
+ }\r
+\r
+ /* copy target instructions to target endianness */\r
+ for (i = 0; i < code_size/4; i++)\r
+ target_buffer_set_u32(target, code_buf + i*4, code[i]);\r
+\r
+ /* write code to working area */\r
+ retval = target->type->write_memory(target,\r
+ hw->copy_area->address,\r
+ 4, code_size/4, code_buf);\r
+ if (retval != ERROR_OK)\r
+ return retval;\r
+ }\r
+\r
+ /* copy data to target's memory */\r
+ target_buf = hw->copy_area->address + code_size;\r
+ retval = target->type->bulk_write_memory(target, target_buf,\r
+ size/4, data);\r
+ if (retval == ERROR_OK && size & 3) {\r
+ retval = target->type->write_memory(target,\r
+ target_buf + (size & ~3),\r
+ 1, size & 3, data + (size & ~3));\r
+ }\r
+ if (retval != ERROR_OK)\r
+ return retval;\r
+\r
+ algo.common_magic = ARMV4_5_COMMON_MAGIC;\r
+ algo.core_mode = ARMV4_5_MODE_SVC;\r
+ algo.core_state = ARMV4_5_STATE_ARM;\r
+\r
+ init_reg_param(®_params[0], "r0", 32, PARAM_IN);\r
+ init_reg_param(®_params[1], "r1", 32, PARAM_IN);\r
+ init_reg_param(®_params[2], "r2", 32, PARAM_IN);\r
+\r
+ buf_set_u32(reg_params[0].value, 0, 32, hw->data);\r
+ buf_set_u32(reg_params[1].value, 0, 32, target_buf);\r
+ buf_set_u32(reg_params[2].value, 0, 32, size);\r
+\r
+ retval = target->type->run_algorithm(target, 0, NULL, 3, reg_params,\r
+ hw->copy_area->address,\r
+ hw->copy_area->address + code_size - 4,\r
+ 1000, &algo);\r
+ if (retval != ERROR_OK)\r
+ LOG_ERROR("error executing hosted NAND write");\r
+\r
+ destroy_reg_param(®_params[0]);\r
+ destroy_reg_param(®_params[1]);\r
+ destroy_reg_param(®_params[2]);\r
+ return retval;\r
+}\r
+\r
+int orion_nand_reset(struct nand_device_s *device)\r
+{\r
+ return orion_nand_command(device, NAND_CMD_RESET);\r
+}\r
+\r
+int orion_nand_controller_ready(struct nand_device_s *device, int timeout)\r
+{\r
+ return 1;\r
+}\r
+\r
+int orion_nand_register_commands(struct command_context_s *cmd_ctx)\r
+{\r
+ return ERROR_OK;\r
+}\r
+\r
+int orion_nand_device_command(struct command_context_s *cmd_ctx, char *cmd,\r
+ char **args, int argc,\r
+ struct nand_device_s *device)\r
+{\r
+ orion_nand_controller_t *hw;\r
+ u32 base;\r
+ u8 ale, cle;\r
+\r
+ if (argc != 3) {\r
+ LOG_ERROR("arguments must be: <target_number> <NAND_address>\n");\r
+ return ERROR_NAND_DEVICE_INVALID;\r
+ }\r
+\r
+ hw = calloc(1, sizeof(*hw));\r
+ if (!hw) {\r
+ LOG_ERROR("no memory for nand controller\n");\r
+ return ERROR_NAND_DEVICE_INVALID;\r
+ }\r
+\r
+ device->controller_priv = hw;\r
+ hw->target = get_target_by_num(strtoul(args[1], NULL, 0));\r
+ if (!hw->target) {\r
+ LOG_ERROR("no target '%s' configured", args[1]);\r
+ free(hw);\r
+ return ERROR_NAND_DEVICE_INVALID;\r
+ }\r
+\r
+ base = strtoul(args[2], NULL, 0);\r
+ cle = 0;\r
+ ale = 1;\r
+\r
+ hw->data = base;\r
+ hw->cmd = base + (1 << cle);\r
+ hw->addr = base + (1 << ale);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int orion_nand_init(struct nand_device_s *device)\r
+{\r
+ return ERROR_OK;\r
+}\r
+\r
+nand_flash_controller_t orion_nand_controller =\r
+{\r
+ .name = "orion",\r
+ .command = orion_nand_command,\r
+ .address = orion_nand_address,\r
+ .read_data = orion_nand_read,\r
+ .write_data = orion_nand_write,\r
+ .write_block_data = orion_nand_fast_block_write,\r
+ .reset = orion_nand_reset,\r
+ .controller_ready = orion_nand_controller_ready,\r
+ .nand_device_command = orion_nand_device_command,\r
+ .register_commands = orion_nand_register_commands,\r
+ .init = orion_nand_init,\r
+};\r
+\r