aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGareth McMullin2015-01-06 22:26:00 +1300
committerGareth McMullin2015-01-06 22:26:00 +1300
commit9c5ffd61f7d654418c76015f0073e4accbf9e004 (patch)
treef33ae3b2cb0e35ff2fb93deb9955970066ce1928
parentd162e75ceb92de346951b979cee5941bdd6ef651 (diff)
First cut at Freescale Kinetis support.
-rw-r--r--src/Makefile1
-rw-r--r--src/adiv5.c20
-rw-r--r--src/cortexm.c1
-rw-r--r--src/include/adiv5.h2
-rw-r--r--src/include/target.h1
-rw-r--r--src/kinetis.c154
6 files changed, 179 insertions, 0 deletions
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 <gareth@blacksphere.co.nz>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* This file implements KL25 target specific functions providing
+ * the XML memory map and Flash memory programming.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#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[] = "<?xml version=\"1.0\"?>"
+/* "<!DOCTYPE memory-map "
+ " PUBLIC \"+//IDN gnu.org//DTD GDB Memory Map V1.0//EN\""
+ " \"http://sourceware.org/gdb/gdb-memory-map.dtd\">"*/
+ "<memory-map>"
+ " <memory type=\"flash\" start=\"0\" length=\"0x20000\">"
+ " <property name=\"blocksize\">0x400</property>"
+ " </memory>"
+ " <memory type=\"ram\" start=\"0x1ffff000\" length=\"0x1000\"/>"
+ " <memory type=\"ram\" start=\"0x20000000\" length=\"0x3000\"/>"
+ "</memory-map>";
+
+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;
+}