/* * 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; }