From 3d8b34f180eaa9bfe021186b9e20a8a551b35515 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Sat, 4 Apr 2015 19:15:03 -0700 Subject: lpc: split out common code and rewrite to use new interface. --- src/lpc_common.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 src/lpc_common.c (limited to 'src/lpc_common.c') diff --git a/src/lpc_common.c b/src/lpc_common.c new file mode 100644 index 0000000..d9abc76 --- /dev/null +++ b/src/lpc_common.c @@ -0,0 +1,135 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Gareth McMullin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "general.h" +#include "target.h" +#include "cortexm.h" +#include "lpc_common.h" + +#include + +struct flash_param { + uint16_t opcode; + uint16_t pad0; + uint32_t command; + uint32_t words[4]; + uint32_t result; +} __attribute__((aligned(4))); + + +struct lpc_flash *lpc_add_flash(target *t, uint32_t addr, size_t length) +{ + struct lpc_flash *lf = calloc(1, sizeof(*lf)); + struct target_flash *f = &lf->f; + f->start = addr; + f->length = length; + f->erase = lpc_flash_erase; + f->write = target_flash_write_buffered; + f->done = target_flash_done_buffered; + f->write_buf = lpc_flash_write; + f->erased = 0xff; + target_add_flash(t, f); + return lf; +} + +enum iap_status lpc_iap_call(struct lpc_flash *f, enum iap_cmd cmd, ...) +{ + target *t = f->f.t; + struct flash_param param = { + .opcode = ARM_THUMB_BREAKPOINT, + .command = cmd, + }; + + /* Pet WDT before each IAP call, if it is on */ + if (f->wdt_kick) + f->wdt_kick(t); + + /* fill out the remainder of the parameters */ + va_list ap; + va_start(ap, cmd); + for (int i = 0; i < 4; i++) + param.words[i] = va_arg(ap, uint32_t); + va_end(ap); + + /* copy the structure to RAM */ + target_mem_write(t, f->iap_ram, ¶m, sizeof(param)); + + /* set up for the call to the IAP ROM */ + uint32_t regs[t->regs_size / sizeof(uint32_t)]; + target_regs_read(t, regs); + regs[0] = f->iap_ram + offsetof(struct flash_param, command); + regs[1] = f->iap_ram + offsetof(struct flash_param, result); + regs[REG_MSP] = f->iap_msp; + regs[REG_LR] = f->iap_ram | 1; + regs[REG_PC] = f->iap_entry; + target_regs_write(t, regs); + + /* start the target and wait for it to halt again */ + target_halt_resume(t, false); + while (!target_halt_wait(t)); + + /* copy back just the parameters structure */ + target_mem_read(t, ¶m, f->iap_ram, sizeof(param)); + return param.result; +} + +static uint8_t lpc_sector_for_addr(struct lpc_flash *f, uint32_t addr) +{ + return f->base_sector + (addr - f->f.start) / f->f.blocksize; +} + +int lpc_flash_erase(struct target_flash *tf, uint32_t addr, size_t len) +{ + struct lpc_flash *f = (struct lpc_flash *)tf; + uint32_t start = lpc_sector_for_addr(f, addr); + uint32_t end = lpc_sector_for_addr(f, addr + len - 1); + + if (lpc_iap_call(f, IAP_CMD_PREPARE, start, end, f->bank)) + return -1; + + /* and now erase them */ + if (lpc_iap_call(f, IAP_CMD_ERASE, start, end, CPU_CLK_KHZ, f->bank)) + return -2; + + /* check erase ok */ + if (lpc_iap_call(f, IAP_CMD_BLANKCHECK, start, end, f->bank)) + return -3; + + return 0; +} + +int lpc_flash_write(struct target_flash *tf, + uint32_t dest, const void *src, size_t len) +{ + struct lpc_flash *f = (struct lpc_flash *)tf; + /* prepare... */ + uint32_t sector = lpc_sector_for_addr(f, dest); + if (lpc_iap_call(f, IAP_CMD_PREPARE, sector, sector, f->bank)) + return -1; + + /* Write payload to target ram */ + uint32_t bufaddr = ALIGN(f->iap_ram + sizeof(struct flash_param), 4); + target_mem_write(f->f.t, bufaddr, src, len); + + /* set the destination address and program */ + if (lpc_iap_call(f, IAP_CMD_PROGRAM, dest, bufaddr, len, CPU_CLK_KHZ)) + return -2; + + return 0; +} + -- cgit v1.2.3