From 9c5ffd61f7d654418c76015f0073e4accbf9e004 Mon Sep 17 00:00:00 2001 From: Gareth McMullin Date: Tue, 6 Jan 2015 22:26:00 +1300 Subject: [PATCH] First cut at Freescale Kinetis support. --- src/Makefile | 1 + src/adiv5.c | 20 ++++++ src/cortexm.c | 1 + src/include/adiv5.h | 2 + src/include/target.h | 1 + src/kinetis.c | 154 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 src/kinetis.c diff --git a/src/Makefile b/src/Makefile index 62cc617..b30922c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -27,6 +27,7 @@ SRC = \ lmi.c \ lpc11xx.c \ lpc43xx.c \ + kinetis.c \ main.c \ nrf51.c \ platform.c \ diff --git a/src/adiv5.c b/src/adiv5.c index e7f45c9..6dcc919 100644 --- a/src/adiv5.c +++ b/src/adiv5.c @@ -414,6 +414,26 @@ void adiv5_ap_mem_write_halfword(ADIv5_AP_t *ap, uint32_t addr, uint16_t value) adiv5_ap_write(ap, ADIV5_AP_DRW, v); } +uint8_t adiv5_ap_mem_read_byte(ADIv5_AP_t *ap, uint32_t addr) +{ + adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw | + ADIV5_AP_CSW_SIZE_BYTE | ADIV5_AP_CSW_ADDRINC_SINGLE); + adiv5_ap_write(ap, ADIV5_AP_TAR, addr); + uint32_t v = adiv5_ap_read(ap, ADIV5_AP_DRW); + + return v >> ((addr & 3) * 8); +} + +void adiv5_ap_mem_write_byte(ADIv5_AP_t *ap, uint32_t addr, uint8_t value) +{ + uint32_t v = value << ((addr & 3) * 8); + + adiv5_ap_write(ap, ADIV5_AP_CSW, ap->csw | + ADIV5_AP_CSW_SIZE_BYTE | ADIV5_AP_CSW_ADDRINC_SINGLE); + adiv5_ap_write(ap, ADIV5_AP_TAR, addr); + adiv5_ap_write(ap, ADIV5_AP_DRW, v); +} + void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value) { adiv5_dp_write(ap->dp, ADIV5_DP_SELECT, diff --git a/src/cortexm.c b/src/cortexm.c index 3ac960c..731632e 100644 --- a/src/cortexm.c +++ b/src/cortexm.c @@ -386,6 +386,7 @@ cortexm_probe(struct target_s *target) PROBE(nrf51_probe); PROBE(samd20_probe); PROBE(lmi_probe); + PROBE(kinetis_probe); #undef PROBE return true; diff --git a/src/include/adiv5.h b/src/include/adiv5.h index 64b6a28..0f17119 100644 --- a/src/include/adiv5.h +++ b/src/include/adiv5.h @@ -172,6 +172,8 @@ uint32_t adiv5_ap_mem_read(ADIv5_AP_t *ap, uint32_t addr); void adiv5_ap_mem_write(ADIv5_AP_t *ap, uint32_t addr, uint32_t value); uint16_t adiv5_ap_mem_read_halfword(ADIv5_AP_t *ap, uint32_t addr); void adiv5_ap_mem_write_halfword(ADIv5_AP_t *ap, uint32_t addr, uint16_t value); +uint8_t adiv5_ap_mem_read_byte(ADIv5_AP_t *ap, uint32_t addr); +void adiv5_ap_mem_write_byte(ADIv5_AP_t *ap, uint32_t addr, uint8_t value); void adiv5_ap_write(ADIv5_AP_t *ap, uint8_t addr, uint32_t value); uint32_t adiv5_ap_read(ADIv5_AP_t *ap, uint8_t addr); diff --git a/src/include/target.h b/src/include/target.h index f28f2f8..100c4a0 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -225,6 +225,7 @@ bool lpc43xx_probe(struct target_s *target); bool sam3x_probe(struct target_s *target); bool nrf51_probe(struct target_s *target); bool samd20_probe(struct target_s *target); +bool kinetis_probe(struct target_s *target); #endif diff --git a/src/kinetis.c b/src/kinetis.c new file mode 100644 index 0000000..393bb18 --- /dev/null +++ b/src/kinetis.c @@ -0,0 +1,154 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Black Sphere Technologies Ltd. + * Written by 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 . + */ + +/* This file implements KL25 target specific functions providing + * the XML memory map and Flash memory programming. + */ + +#include +#include + +#include "general.h" +#include "adiv5.h" +#include "target.h" + +#define SIM_SDID 0x40048024 + +#define FTFA_BASE 0x40020000 +#define FTFA_FSTAT (FTFA_BASE + 0x00) +#define FTFA_FCNFG (FTFA_BASE + 0x01) +#define FTFA_FSEC (FTFA_BASE + 0x02) +#define FTFA_FOPT (FTFA_BASE + 0x03) +#define FTFA_FCCOB(x) (FTFA_BASE + 0x04 + ((x) ^ 3)) + +#define FTFA_FSTAT_CCIF (1 << 7) +#define FTFA_FSTAT_RDCOLERR (1 << 6) +#define FTFA_FSTAT_ACCERR (1 << 5) +#define FTFA_FSTAT_FPVIOL (1 << 4) +#define FTFA_FSTAT_MGSTAT0 (1 << 0) + +#define FTFA_CMD_CHECK_ERASE 0x01 +#define FTFA_CMD_PROGRAM_CHECK 0x02 +#define FTFA_CMD_READ_RESOURCE 0x03 +#define FTFA_CMD_PROGRAM_LONGWORD 0x06 +#define FTFA_CMD_ERASE_SECTOR 0x09 +#define FTFA_CMD_CHECK_ERASE_ALL 0x40 +#define FTFA_CMD_READ_ONCE 0x41 +#define FTFA_CMD_PROGRAM_ONCE 0x43 +#define FTFA_CMD_ERASE_ALL 0x44 +#define FTFA_CMD_BACKDOOR_ACCESS 0x45 + +#define KL25_PAGESIZE 0x400 + +static int kl25_flash_erase(struct target_s *target, uint32_t addr, int len); +static int kl25_flash_write(struct target_s *target, uint32_t dest, + const uint8_t *src, int len); + +static const char kl25_xml_memory_map[] = "" +/* ""*/ + "" + " " + " 0x400" + " " + " " + " " + ""; + +bool kinetis_probe(struct target_s *t) +{ + uint32_t sdid = adiv5_ap_mem_read(adiv5_target_ap(t), SIM_SDID); + switch (sdid >> 20) { + case 0x251: + t->driver = "KL25"; + t->xml_mem_map = kl25_xml_memory_map; + t->flash_erase = kl25_flash_erase; + t->flash_write = kl25_flash_write; + return true; + } + return false; +} + +static bool +kl25_command(struct target_s *t, uint8_t cmd, uint32_t addr, const uint8_t data[8]) +{ + ADIv5_AP_t *ap = adiv5_target_ap(t); + uint8_t fstat; + + /* Wait for CCIF to be high */ + do { + fstat = adiv5_ap_mem_read_byte(ap, FTFA_FSTAT); + /* Check ACCERR and FPVIOL are zero in FSTAT */ + if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL)) + return false; + } while (!(fstat & FTFA_FSTAT_CCIF)); + + /* Write command to FCCOB */ + addr &= 0xffffff; + addr |= (uint32_t)cmd << 24; + adiv5_ap_mem_write(ap, FTFA_FCCOB(0), addr); + if (data) { + adiv5_ap_mem_write(ap, FTFA_FCCOB(4), *(uint32_t*)&data[0]); + adiv5_ap_mem_write(ap, FTFA_FCCOB(8), *(uint32_t*)&data[4]); + } + + /* Enable execution by clearing CCIF */ + adiv5_ap_mem_write_byte(ap, FTFA_FSTAT, FTFA_FSTAT_CCIF); + + /* Wait for execution to complete */ + do { + fstat = adiv5_ap_mem_read_byte(ap, FTFA_FSTAT); + /* Check ACCERR and FPVIOL are zero in FSTAT */ + if (fstat & (FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL)) + return false; + } while (!(fstat & FTFA_FSTAT_CCIF)); + + return true; +} + +static int kl25_flash_erase(struct target_s *t, uint32_t addr, int len) +{ + addr &= ~(KL25_PAGESIZE - 1); + len = (len + KL25_PAGESIZE - 1) & ~(KL25_PAGESIZE - 1); + + while (len) { + kl25_command(t, FTFA_CMD_ERASE_SECTOR, addr, NULL); + len -= KL25_PAGESIZE; + addr += KL25_PAGESIZE; + } + return 0; +} + +static int kl25_flash_write(struct target_s *t, uint32_t dest, + const uint8_t *src, int len) +{ + /* FIXME handle misaligned start and end of sections */ + if ((dest & 3) || (len & 3)) + return -1; + + while (len) { + kl25_command(t, FTFA_CMD_PROGRAM_LONGWORD, dest, src); + len -= 4; + dest += 4; + src += 4; + } + return 0; +}