--- /dev/null
+/***************************************************************************\r
+ * Copyright (C) 2008 by *\r
+ * Karl RobinSod <karl.robinsod@gmail.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
+* There are some things to notice\r
+*\r
+* You need to unprotect flash sectors each time you connect the OpenOCD\r
+* Dumping 1MB takes about 60 Seconds\r
+* Full erase (sectors 0-22 inclusive) takes 2-4 seconds\r
+* Writing 1MB takes 88 seconds\r
+*\r
+ ***************************************************************************/\r
+#ifdef HAVE_CONFIG_H\r
+#include "config.h"\r
+#endif\r
+\r
+#include "replacements.h"\r
+\r
+#include "lpc288x.h"\r
+\r
+#include "flash.h"\r
+#include "target.h"\r
+#include "log.h"\r
+#include "binarybuffer.h"\r
+#include "types.h"\r
+\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <unistd.h>\r
+\r
+#define LOAD_TIMER_ERASE 0\r
+#define LOAD_TIMER_WRITE 1\r
+\r
+#define FLASH_PAGE_SIZE 512\r
+\r
+/* LPC288X control registers */\r
+#define DBGU_CIDR 0x8000507C\r
+/* LPC288X flash registers */\r
+#define F_CTRL 0x80102000 /* Flash control register R/W 0x5 */\r
+#define F_STAT 0x80102004 /* Flash status register RO 0x45 */\r
+#define F_PROG_TIME 0x80102008 /* Flash program time register R/W 0 */\r
+#define F_WAIT 0x80102010 /* Flash read wait state register R/W 0xC004 */\r
+#define F_CLK_TIME 0x8010201C /* Flash clock divider for 66 kHz generation R/W 0 */\r
+#define F_INTEN_CLR 0x80102FD8 /* Clear interrupt enable bits WO - */\r
+#define F_INTEN_SET 0x80102FDC /* Set interrupt enable bits WO - */\r
+#define F_INT_STAT 0x80102FE0 /* Interrupt status bits RO 0 */\r
+#define F_INTEN 0x80102FE4 /* Interrupt enable bits RO 0 */\r
+#define F_INT_CLR 0x80102FE8 /* Clear interrupt status bits WO */\r
+#define F_INT_SET 0x80102FEC /* Set interrupt status bits WO - */\r
+#define FLASH_PD 0x80005030 /* Allows turning off the Flash memory for power savings. R/W 1*/\r
+#define FLASH_INIT 0x80005034 /* Monitors Flash readiness, such as recovery from Power Down mode. R/W -*/\r
+\r
+/* F_CTRL bits */\r
+#define FC_CS 0x0001 \r
+#define FC_FUNC 0x0002 \r
+#define FC_WEN 0x0004 \r
+#define FC_RD_LATCH 0x0020 \r
+#define FC_PROTECT 0x0080 \r
+#define FC_SET_DATA 0x0400 \r
+#define FC_RSSL 0x0800 \r
+#define FC_PROG_REQ 0x1000 \r
+#define FC_CLR_BUF 0x4000 \r
+#define FC_LOAD_REQ 0x8000 \r
+/* F_STAT bits */\r
+#define FS_DONE 0x0001 \r
+#define FS_PROGGNT 0x0002 \r
+#define FS_RDY 0x0004 \r
+#define FS_ERR 0x0020 \r
+/* F_PROG_TIME */\r
+#define FPT_TIME_MASK 0x7FFF \r
+\r
+#define FPT_ENABLE 0x8000 \r
+/* F_WAIT */\r
+#define FW_WAIT_STATES_MASK 0x00FF\r
+#define FW_SET_MASK 0xC000\r
+\r
+/* F_CLK_TIME */\r
+#define FCT_CLK_DIV_MASK 0x0FFF\r
+\r
+\r
+\r
+int lpc288x_register_commands(struct command_context_s *cmd_ctx);\r
+int lpc288x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);\r
+int lpc288x_erase(struct flash_bank_s *bank, int first, int last);\r
+int lpc288x_protect(struct flash_bank_s *bank, int set, int first, int last);\r
+int lpc288x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count);\r
+int lpc288x_probe(struct flash_bank_s *bank);\r
+int lpc288x_auto_probe(struct flash_bank_s *bank);\r
+int lpc288x_erase_check(struct flash_bank_s *bank);\r
+int lpc288x_protect_check(struct flash_bank_s *bank);\r
+int lpc288x_info(struct flash_bank_s *bank, char *buf, int buf_size);\r
+void lpc288x_set_flash_mode(flash_bank_t *bank, u8 flashplane, int mode);\r
+u32 lpc288x_wait_status_busy(flash_bank_t *bank, int timeout);\r
+void lpc288x_load_timer(int erase, struct target_s *target);\r
+void lpc288x_set_flash_clk(struct flash_bank_s *bank);\r
+u32 lpc288x_system_ready(struct flash_bank_s *bank);\r
+int lpc288x_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);\r
+\r
+flash_driver_t lpc288x_flash =\r
+{\r
+ .name = "lpc288x",\r
+ .register_commands = lpc288x_register_commands,\r
+ .flash_bank_command = lpc288x_flash_bank_command,\r
+ .erase = lpc288x_erase,\r
+ .protect = lpc288x_protect,\r
+ .write = lpc288x_write,\r
+ .probe = lpc288x_probe,\r
+ .auto_probe = lpc288x_probe,\r
+ .erase_check = lpc288x_erase_check,\r
+ .protect_check = lpc288x_protect_check,\r
+ .info = lpc288x_info\r
+};\r
+\r
+\r
+int lpc288x_register_commands(struct command_context_s *cmd_ctx)\r
+{\r
+ return ERROR_OK;\r
+}\r
+\r
+\r
+\r
+u32 lpc288x_wait_status_busy(flash_bank_t *bank, int timeout)\r
+{\r
+ u32 status;\r
+ target_t *target = bank->target;\r
+ do\r
+ {\r
+ usleep(1000);\r
+ timeout--;\r
+ target_read_u32(target, F_STAT, &status);\r
+ }while (((status & FS_DONE) == 0) && timeout);\r
+\r
+ if(timeout == 0)\r
+ {\r
+ LOG_DEBUG("Timedout!");\r
+ return ERROR_FLASH_OPERATION_FAILED;\r
+ }\r
+ return ERROR_OK;\r
+}\r
+\r
+/* Read device id register and fill in driver info structure */\r
+int lpc288x_read_part_info(struct flash_bank_s *bank)\r
+{\r
+ lpc288x_flash_bank_t *lpc288x_info = bank->driver_priv;\r
+ target_t *target = bank->target;\r
+ u32 cidr, status;\r
+ int sectornum;\r
+\r
+ int i = 0;\r
+ u32 offset;\r
+\r
+ if (lpc288x_info->cidr == 0x0102100A)\r
+ return ERROR_OK; /* already probed, multiple probes may cause memory leak, not allowed */\r
+ \r
+ /* Read and parse chip identification register */\r
+ target_read_u32(target, DBGU_CIDR, &cidr);\r
+ \r
+ if (cidr != 0x0102100A)\r
+ {\r
+ LOG_WARNING("Cannot identify target as an LPC288X (%08X)",cidr);\r
+ return ERROR_FLASH_OPERATION_FAILED;\r
+ }\r
+\r
+ lpc288x_info->cidr = cidr;\r
+ lpc288x_info->sector_size_break = 0x000F0000;\r
+ lpc288x_info->target_name = "LPC288x";\r
+\r
+ /* setup the sector info... */\r
+ offset = bank->base;\r
+ bank->num_sectors = 23;\r
+ bank->sectors = malloc(sizeof(flash_sector_t) * 23);\r
+\r
+ for (i = 0; i < 15; i++)\r
+ {\r
+ bank->sectors[i].offset = offset;\r
+ bank->sectors[i].size = 64 * 1024;\r
+ offset += bank->sectors[i].size;\r
+ bank->sectors[i].is_erased = -1;\r
+ bank->sectors[i].is_protected = 1;\r
+ }\r
+ for (i = 15; i < 23; i++)\r
+ {\r
+ bank->sectors[i].offset = offset;\r
+ bank->sectors[i].size = 8 * 1024;\r
+ offset += bank->sectors[i].size;\r
+ bank->sectors[i].is_erased = -1;\r
+ bank->sectors[i].is_protected = 1;\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int lpc288x_protect_check(struct flash_bank_s *bank)\r
+{\r
+ return ERROR_OK;\r
+}\r
+\r
+/* flash_bank LPC288x 0 0 0 0 <target#> <cclk>\r
+ */\r
+int lpc288x_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)\r
+{\r
+ lpc288x_flash_bank_t *lpc288x_info;\r
+ int i;\r
+ \r
+ if (argc < 6)\r
+ {\r
+ LOG_WARNING("incomplete flash_bank LPC288x configuration");\r
+ return ERROR_FLASH_BANK_INVALID;\r
+ }\r
+ \r
+ lpc288x_info = malloc(sizeof(lpc288x_flash_bank_t));\r
+ bank->driver_priv = lpc288x_info;\r
+ \r
+ /* part wasn't probed for info yet */\r
+ lpc288x_info->cidr = 0;\r
+ lpc288x_info->cclk = strtoul(args[6], NULL, 0);\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+/*\r
+The frequency is the AHB clock frequency divided by (CLK_DIV ×\r
+3) + 1. This must be programmed such that the Flash\r
+Programming clock frequency is 66 kHz ± 20%.\r
+ AHB = 12 MHz ?\r
+ 12000000/66000 = 182\r
+ CLK_DIV = 60 ?\r
+*/\r
+void lpc288x_set_flash_clk(struct flash_bank_s *bank)\r
+{\r
+ u32 clk_time;\r
+ lpc288x_flash_bank_t *lpc288x_info = bank->driver_priv;\r
+ clk_time = (lpc288x_info->cclk / 66000) / 3;\r
+ target_write_u32(bank->target, F_CTRL, FC_CS | FC_WEN );\r
+ target_write_u32(bank->target, F_CLK_TIME, clk_time);\r
+}\r
+\r
+/*\r
+AHB tcyc (in ns) 83 ns\r
+\r
+LOAD_TIMER_ERASE FPT_TIME = ((400,000,000 / AHB tcyc (in ns)) - 2) / 512\r
+ = 9412 (9500) (AN10548 9375)\r
+LOAD_TIMER_WRITE FPT_TIME = ((1,000,000 / AHB tcyc (in ns)) - 2) / 512\r
+ = 23 (75) (AN10548 72 - is this wrong?)\r
+ \r
+TODO: Sort out timing calcs ;)\r
+*/\r
+void lpc288x_load_timer(int erase, struct target_s *target)\r
+{\r
+ if(erase == LOAD_TIMER_ERASE)\r
+ {\r
+ target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 9500);\r
+ }\r
+ else\r
+ {\r
+ target_write_u32(target, F_PROG_TIME, FPT_ENABLE | 75);\r
+ }\r
+}\r
+\r
+\r
+\r
+u32 lpc288x_system_ready(struct flash_bank_s *bank)\r
+{\r
+ lpc288x_flash_bank_t *lpc288x_info = bank->driver_priv;\r
+ if (lpc288x_info->cidr == 0)\r
+ {\r
+ return ERROR_FLASH_BANK_NOT_PROBED;\r
+ }\r
+\r
+ if (bank->target->state != TARGET_HALTED)\r
+ {\r
+ return ERROR_TARGET_NOT_HALTED;\r
+ }\r
+ return ERROR_OK;\r
+}\r
+\r
+int lpc288x_erase_check(struct flash_bank_s *bank)\r
+{\r
+ u32 buffer, test_bytes;\r
+ u32 addr, sector, i, status = lpc288x_system_ready(bank); /* probed? halted? */\r
+ if(status != ERROR_OK)\r
+ {\r
+ LOG_INFO("Processor not halted/not probed");\r
+ return status;\r
+ }\r
+\r
+ return ERROR_OK;\r
+}\r
+\r
+int lpc288x_erase(struct flash_bank_s *bank, int first, int last)\r
+{\r
+ u32 status;\r
+ int sector;\r
+ target_t *target = bank->target;\r
+\r
+ status = lpc288x_system_ready(bank); /* probed? halted? */\r
+ if(status != ERROR_OK)\r
+ {\r
+ return status;\r
+ }\r
+ \r
+ if ((first < 0) || (last < first) || (last >= bank->num_sectors))\r
+ {\r
+ LOG_INFO("Bad sector range");\r
+ return ERROR_FLASH_SECTOR_INVALID;\r
+ }\r
+\r
+ /* Configure the flash controller timing */\r
+ lpc288x_set_flash_clk(bank);\r
+\r
+ for (sector = first; sector <= last; sector++)\r
+ {\r
+ if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK)\r
+ {\r
+ return ERROR_FLASH_OPERATION_FAILED;\r
+ }\r
+\r
+ lpc288x_load_timer(LOAD_TIMER_ERASE,target);\r
+ \r
+ target_write_u32( target,\r
+ bank->sectors[sector].offset,\r
+ 0x00);\r
+ \r
+ target_write_u32( target,\r
+ F_CTRL,\r
+ FC_PROG_REQ |\r
+ FC_PROTECT |\r
+ FC_CS);\r
+ }\r
+ if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK)\r
+ {\r
+ return ERROR_FLASH_OPERATION_FAILED;\r
+ }\r
+ return ERROR_OK;\r
+\r
+}\r
+\r
+\r
+int lpc288x_write(struct flash_bank_s *bank, u8 *buffer, u32 offset, u32 count)\r
+{\r
+ u8 page_buffer[FLASH_PAGE_SIZE];\r
+ u32 i, status, source_offset,dest_offset;\r
+ target_t *target = bank->target;\r
+ u32 bytes_remaining = count;\r
+ u32 first_sector, last_sector, sector, page;\r
+\r
+ /* probed? halted? */\r
+ status = lpc288x_system_ready(bank); \r
+ if(status != ERROR_OK)\r
+ {\r
+ return status;\r
+ }\r
+\r
+ /* Initialise search indices */\r
+ first_sector = last_sector = 0xffffffff;\r
+ \r
+ /* validate the write range... */\r
+ for(i = 0; i < bank->num_sectors; i++)\r
+ {\r
+ if((offset >= bank->sectors[i].offset) &&\r
+ (offset < (bank->sectors[i].offset + bank->sectors[i].size)) &&\r
+ (first_sector == 0xffffffff))\r
+ {\r
+ first_sector = i;\r
+ /* all writes must start on a sector boundary... */\r
+ if (offset % bank->sectors[i].size)\r
+ {\r
+ LOG_INFO("offset 0x%x breaks required alignment 0x%x", offset, bank->sectors[i].size);\r
+ return ERROR_FLASH_DST_BREAKS_ALIGNMENT;\r
+ }\r
+ }\r
+ if(((offset + count) > bank->sectors[i].offset) &&\r
+ ((offset + count) <= (bank->sectors[i].offset + bank->sectors[i].size)) &&\r
+ (last_sector == 0xffffffff))\r
+ {\r
+ last_sector = i;\r
+ }\r
+ }\r
+ \r
+ /* Range check... */\r
+ if (first_sector == 0xffffffff || last_sector == 0xffffffff)\r
+ {\r
+ LOG_INFO("Range check failed %x %x", offset, count);\r
+ return ERROR_FLASH_DST_OUT_OF_BANK;\r
+ }\r
+ \r
+ /* Configure the flash controller timing */\r
+ lpc288x_set_flash_clk(bank); \r
+\r
+ /* initialise the offsets */\r
+ source_offset = 0;\r
+ dest_offset = 0;\r
+ \r
+ for (sector=first_sector; sector<=last_sector; sector++)\r
+ {\r
+ for(page = 0; page < bank->sectors[sector].size / FLASH_PAGE_SIZE; page++)\r
+ {\r
+ if(bytes_remaining == 0)\r
+ {\r
+ count = 0;\r
+ memset(page_buffer, 0xFF, FLASH_PAGE_SIZE);\r
+ }\r
+ else if (bytes_remaining < FLASH_PAGE_SIZE)\r
+ {\r
+ count = bytes_remaining;\r
+ memset(page_buffer, 0xFF, FLASH_PAGE_SIZE);\r
+ memcpy(page_buffer, &buffer[source_offset], count);\r
+ }\r
+ else\r
+ {\r
+ count = FLASH_PAGE_SIZE;\r
+ memcpy(page_buffer, &buffer[source_offset], count);\r
+ }\r
+\r
+ /* Wait for flash to become ready */\r
+ if (lpc288x_wait_status_busy(bank, 1000) != ERROR_OK)\r
+ {\r
+ return ERROR_FLASH_OPERATION_FAILED;\r
+ }\r
+ \r
+ /* fill flash data latches with 1's */\r
+ target_write_u32(target, F_CTRL,\r
+ FC_CS |\r
+ FC_SET_DATA |\r
+ FC_WEN |\r
+ FC_FUNC );\r
+\r
+ target_write_u32(target, F_CTRL,\r
+ FC_CS |\r
+ FC_WEN |\r
+ FC_FUNC );\r
+ /*would be better to use the clean target_write_buffer() interface but\r
+ it seems not to be a LOT slower....\r
+ bulk_write_memory() is no quicker :(*/\r
+#if 1\r
+ if (target->type->write_memory(target, offset + dest_offset, 4, 128, page_buffer) != ERROR_OK)\r
+ {\r
+ LOG_ERROR("Write failed s %x p %x", sector, page);\r
+ return ERROR_FLASH_OPERATION_FAILED;\r
+ }\r
+#else\r
+ if(target_write_buffer(target, offset + dest_offset, FLASH_PAGE_SIZE, page_buffer) != ERROR_OK)\r
+ {\r
+ LOG_INFO("Write to flash buffer failed");\r
+ return ERROR_FLASH_OPERATION_FAILED;\r
+ }\r
+#endif\r
+ dest_offset += FLASH_PAGE_SIZE;\r
+ source_offset += count;\r
+ bytes_remaining -= count;\r
+\r
+ lpc288x_load_timer(LOAD_TIMER_WRITE, target);\r
+ \r
+ target_write_u32( target,\r
+ F_CTRL,\r
+ FC_PROG_REQ |\r
+ FC_PROTECT |\r
+ FC_FUNC |\r
+ FC_CS);\r
+ \r
+ \r
+ } \r
+ }\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+\r
+int lpc288x_probe(struct flash_bank_s *bank)\r
+{\r
+ /* we only deal with LPC2888 so flash config is fixed\r
+ */\r
+ lpc288x_flash_bank_t *lpc288x_info = bank->driver_priv;\r
+ int retval;\r
+\r
+ if (lpc288x_info->cidr != 0)\r
+ {\r
+ return ERROR_OK; /* already probed */\r
+ }\r
+\r
+ if (bank->target->state != TARGET_HALTED)\r
+ {\r
+ return ERROR_TARGET_NOT_HALTED;\r
+ }\r
+\r
+ retval = lpc288x_read_part_info(bank);\r
+ if (retval != ERROR_OK)\r
+ return retval;\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r
+\r
+int lpc288x_info(struct flash_bank_s *bank, char *buf, int buf_size)\r
+{\r
+ snprintf(buf, buf_size, "lpc288x flash driver");\r
+ return ERROR_OK;\r
+}\r
+\r
+int lpc288x_protect(struct flash_bank_s *bank, int set, int first, int last)\r
+{\r
+ int lockregion, status;\r
+ u32 value;\r
+ target_t *target = bank->target;\r
+ \r
+ /* probed? halted? */\r
+ status = lpc288x_system_ready(bank); \r
+ if(status != ERROR_OK)\r
+ {\r
+ return status;\r
+ }\r
+ \r
+ if ((first < 0) || (last < first) || (last >= bank->num_sectors))\r
+ {\r
+ return ERROR_FLASH_SECTOR_INVALID;\r
+ }\r
+\r
+ /* Configure the flash controller timing */\r
+ lpc288x_set_flash_clk(bank); \r
+\r
+ for (lockregion = first; lockregion <= last; lockregion++)\r
+ {\r
+ if(set)\r
+ {\r
+ /* write an odd value to base addy to protect... */\r
+ value = 0x01;\r
+ }\r
+ else\r
+ {\r
+ /* write an even value to base addy to unprotect... */\r
+ value = 0x00;\r
+ }\r
+ target_write_u32( target,\r
+ bank->sectors[lockregion].offset,\r
+ value);\r
+ \r
+ target_write_u32( target,\r
+ F_CTRL,\r
+ FC_LOAD_REQ |\r
+ FC_PROTECT |\r
+ FC_WEN |\r
+ FC_FUNC |\r
+ FC_CS);\r
+ }\r
+ \r
+ return ERROR_OK;\r
+}\r
+\r