diff --git a/flashstub/efm32.c b/flashstub/efm32.c new file mode 100644 index 0000000..26f19a3 --- /dev/null +++ b/flashstub/efm32.c @@ -0,0 +1,68 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Richard Meadows + * + * 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 +#include "stub.h" + +#define EFM32_MSC ((volatile uint32_t *)0x400c0000) +#define EFM32_MSC_WRITECTRL EFM32_MSC[2] +#define EFM32_MSC_WRITECMD EFM32_MSC[3] +#define EFM32_MSC_ADDRB EFM32_MSC[4] +#define EFM32_MSC_WDATA EFM32_MSC[6] +#define EFM32_MSC_STATUS EFM32_MSC[7] +#define EFM32_MSC_LOCK EFM32_MSC[15] + +#define EFM32_MSC_LOCK_LOCKKEY 0x1b71 + +#define EFM32_MSC_WRITECMD_LADDRIM (1<<0) +#define EFM32_MSC_WRITECMD_ERASEPAGE (1<<1) +#define EFM32_MSC_WRITECMD_WRITEEND (1<<2) +#define EFM32_MSC_WRITECMD_WRITEONCE (1<<3) +#define EFM32_MSC_WRITECMD_WRITETRIG (1<<4) +#define EFM32_MSC_WRITECMD_ERASEABORT (1<<5) + +#define EFM32_MSC_STATUS_BUSY (1<<0) +#define EFM32_MSC_STATUS_LOCKED (1<<1) +#define EFM32_MSC_STATUS_INVADDR (1<<2) +#define EFM32_MSC_STATUS_WDATAREADY (1<<3) +#define EFM32_MSC_STATUS_WORDTIMEOUT (1<<4) + +void __attribute__((naked)) +efm32_flash_write_stub(uint32_t *dest, uint32_t *src, uint32_t size) +{ + uint32_t i; + + EFM32_MSC_LOCK = EFM32_MSC_LOCK_LOCKKEY; + EFM32_MSC_WRITECTRL = 1; + + for (i = 0; i < size/4; i++) { + EFM32_MSC_ADDRB = (uint32_t)&dest[i];; + EFM32_MSC_WRITECMD = EFM32_MSC_WRITECMD_LADDRIM; + + /* Wait for WDATAREADY */ + while ((EFM32_MSC_STATUS & EFM32_MSC_STATUS_WDATAREADY) == 0); + + EFM32_MSC_WDATA = src[i]; + EFM32_MSC_WRITECMD = EFM32_MSC_WRITECMD_WRITEONCE; + + /* Wait for BUSY */ + while ((EFM32_MSC_STATUS & EFM32_MSC_STATUS_BUSY)); + } + + stub_exit(0); +} diff --git a/flashstub/efm32.stub b/flashstub/efm32.stub new file mode 100644 index 0000000..01fe5db --- /dev/null +++ b/flashstub/efm32.stub @@ -0,0 +1,41 @@ + [0x0/2] = 0x4c10, + [0x2/2] = 0x4b11, + [0x4/2] = 0x0892, + [0x6/2] = 0x601c, + [0x8/2] = 0x2401, + [0xa/2] = 0x4b10, + [0xc/2] = 0x0092, + [0xe/2] = 0x601c, + [0x10/2] = 0x2400, + [0x12/2] = 0x4294, + [0x14/2] = 0xd015, + [0x16/2] = 0x4d0e, + [0x18/2] = 0x1903, + [0x1a/2] = 0x602b, + [0x1c/2] = 0x2501, + [0x1e/2] = 0x4b0d, + [0x20/2] = 0x601d, + [0x22/2] = 0x2608, + [0x24/2] = 0x4d0c, + [0x26/2] = 0x682f, + [0x28/2] = 0x46ac, + [0x2a/2] = 0x4237, + [0x2c/2] = 0xd0f9, + [0x2e/2] = 0x590d, + [0x30/2] = 0x4f0a, + [0x32/2] = 0x603d, + [0x34/2] = 0x601e, + [0x36/2] = 0x4663, + [0x38/2] = 0x681b, + [0x3a/2] = 0x07db, + [0x3c/2] = 0xd4fb, + [0x3e/2] = 0x3404, + [0x40/2] = 0xe7e7, + [0x42/2] = 0xbe00, + [0x44/2] = 0x1b71, 0x0000, + [0x48/2] = 0x003c, 0x400c, + [0x4c/2] = 0x0008, 0x400c, + [0x50/2] = 0x0010, 0x400c, + [0x54/2] = 0x000c, 0x400c, + [0x58/2] = 0x001c, 0x400c, + [0x5c/2] = 0x0018, 0x400c, diff --git a/src/Makefile b/src/Makefile index 1ef3486..b39326c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,6 +20,7 @@ SRC = \ command.c \ cortexm.c \ crc32.c \ + efm32.c \ exception.c \ gdb_if.c \ gdb_main.c \ @@ -91,4 +92,3 @@ include/version.h: FORCE $(Q)echo "#define FIRMWARE_VERSION \"`git describe --dirty`\"" > $@ -include *.d - diff --git a/src/cortexm.c b/src/cortexm.c index d459cce..93cf2e8 100644 --- a/src/cortexm.c +++ b/src/cortexm.c @@ -251,6 +251,7 @@ bool cortexm_probe(target *t) PROBE(samd_probe); PROBE(lmi_probe); PROBE(kinetis_probe); + PROBE(efm32_probe); #undef PROBE return true; @@ -974,4 +975,3 @@ static void cortexm_hostio_reply(target *t, int32_t retcode, uint32_t errcode) target_regs_write(t, arm_regs); priv->errno = errcode; } - diff --git a/src/efm32.c b/src/efm32.c new file mode 100644 index 0000000..ea5cd64 --- /dev/null +++ b/src/efm32.c @@ -0,0 +1,426 @@ +/* + * This file is part of the Black Magic Debug project. + * + * Copyright (C) 2015 Richard Meadows + * + * 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 EFM32 target specific functions for + * detecting the device, providing the XML memory map and Flash memory + * programming. + * + * Both EFM32 (microcontroller only) and EZR32 (microcontroller+radio) + * devices should be supported through this driver. + * + * Tested with: + * * EZR32LG230 (EZR Leopard Gecko M3) + * * + */ + +/* Refer to the family reference manuals: + * + * + * Also refer to AN0062 "Programming Internal Flash Over the Serial Wire Debug Interface" + * http://www.silabs.com/Support%20Documents/TechnicalDocs/an0062.pdf + */ + +#include "general.h" +#include "jtagtap.h" +#include "adiv5.h" +#include "target.h" +#include "command.h" +#include "gdb_packet.h" +#include "cortexm.h" + +#define SRAM_BASE 0x20000000 +#define STUB_BUFFER_BASE ALIGN(SRAM_BASE + sizeof(efm32_flash_write_stub), 4) + +static int efm32_flash_erase(struct target_flash *t, uint32_t addr, size_t len); +static int efm32_flash_write(struct target_flash *f, + uint32_t dest, const void *src, size_t len); + +static const uint16_t efm32_flash_write_stub[] = { +#include "../flashstub/efm32.stub" +}; + +static bool efm32_cmd_erase_all(target *t); +static bool efm32_cmd_serial(target *t); + +const struct command_s efm32_cmd_list[] = { + {"erase_mass", (cmd_handler)efm32_cmd_erase_all, "Erase entire flash memory"}, + {"serial", (cmd_handler)efm32_cmd_serial, "Prints unique number"}, + {NULL, NULL, NULL} +}; + + + +/* -------------------------------------------------------------------------- */ +/* Memory System Controller (MSC) Registers */ +/* -------------------------------------------------------------------------- */ + +#define EFM32_MSC 0x400c0000 +#define EFM32_MSC_WRITECTRL (EFM32_MSC+0x008) +#define EFM32_MSC_WRITECMD (EFM32_MSC+0x00c) +#define EFM32_MSC_ADDRB (EFM32_MSC+0x010) +#define EFM32_MSC_WDATA (EFM32_MSC+0x018) +#define EFM32_MSC_STATUS (EFM32_MSC+0x01c) +#define EFM32_MSC_LOCK (EFM32_MSC+0x03c) +#define EFM32_MSC_CMD (EFM32_MSC+0x040) +#define EFM32_MSC_TIMEBASE (EFM32_MSC+0x050) +#define EFM32_MSC_MASSLOCK (EFM32_MSC+0x054) + +#define EFM32_MSC_LOCK_LOCKKEY 0x1b71 +#define EFM32_MSC_MASSLOCK_LOCKKEY 0x631a + +#define EFM32_MSC_WRITECMD_LADDRIM (1<<0) +#define EFM32_MSC_WRITECMD_ERASEPAGE (1<<1) +#define EFM32_MSC_WRITECMD_WRITEEND (1<<2) +#define EFM32_MSC_WRITECMD_WRITEONCE (1<<3) +#define EFM32_MSC_WRITECMD_WRITETRIG (1<<4) +#define EFM32_MSC_WRITECMD_ERASEABORT (1<<5) +#define EFM32_MSC_WRITECMD_ERASEMAIN0 (1<<8) + +#define EFM32_MSC_STATUS_BUSY (1<<0) +#define EFM32_MSC_STATUS_LOCKED (1<<1) +#define EFM32_MSC_STATUS_INVADDR (1<<2) +#define EFM32_MSC_STATUS_WDATAREADY (1<<3) + + +/* -------------------------------------------------------------------------- */ +/* Flash Infomation Area */ +/* -------------------------------------------------------------------------- */ + +#define EFM32_INFO 0x0fe00000 +#define EFM32_USER_DATA (EFM32_INFO+0x0000) +#define EFM32_LOCK_BITS (EFM32_INFO+0x4000) +#define EFM32_DI (EFM32_INFO+0x8000) + + +/* -------------------------------------------------------------------------- */ +/* Device Information (DI) Area */ +/* -------------------------------------------------------------------------- */ + +#define EFM32_DI_CMU_LFRCOCTRL (EFM32_DI+0x020) +#define EFM32_DI_CMU_HFRCOCTRL (EFM32_DI+0x028) +#define EFM32_DI_CMU_AUXHFRCOCTRL (EFM32_DI+0x030) +#define EFM32_DI_ADC0_CAL (EFM32_DI+0x040) +#define EFM32_DI_ADC0_BIASPROG (EFM32_DI+0x048) +#define EFM32_DI_DAC0_CAL (EFM32_DI+0x050) +#define EFM32_DI_DAC0_BIASPROG (EFM32_DI+0x058) +#define EFM32_DI_ACMP0_CTRL (EFM32_DI+0x060) +#define EFM32_DI_ACMP1_CTRL (EFM32_DI+0x068) +#define EFM32_DI_CMU_LCDCTRL (EFM32_DI+0x078) +#define EFM32_DI_DAC0_OPACTRL (EFM32_DI+0x0A0) +#define EFM32_DI_DAC0_OPAOFFSET (EFM32_DI+0x0A8) +#define EFM32_DI_EMU_BUINACT (EFM32_DI+0x0B0) +#define EFM32_DI_EMU_BUACT (EFM32_DI+0x0B8) +#define EFM32_DI_EMU_BUBODBUVINCAL (EFM32_DI+0x0C0) +#define EFM32_DI_EMU_BUBODUNREGCAL (EFM32_DI+0x0C8) +#define EFM32_DI_MCM_REV_MIN (EFM32_DI+0x1AA) +#define EFM32_DI_MCM_REV_MAJ (EFM32_DI+0x1AB) +#define EFM32_DI_RADIO_REV_MIN (EFM32_DI+0x1AC) +#define EFM32_DI_RADIO_REV_MAJ (EFM32_DI+0x1AD) +#define EFM32_DI_RADIO_OPN (EFM32_DI+0x1AE) +#define EFM32_DI_DI_CRC (EFM32_DI+0x1B0) +#define EFM32_DI_CAL_TEMP_0 (EFM32_DI+0x1B2) +#define EFM32_DI_ADC0_CAL_1V25 (EFM32_DI+0x1B4) +#define EFM32_DI_ADC0_CAL_2V5 (EFM32_DI+0x1B6) +#define EFM32_DI_ADC0_CAL_VDD (EFM32_DI+0x1B8) +#define EFM32_DI_ADC0_CAL_5VDIFF (EFM32_DI+0x1BA) +#define EFM32_DI_ADC0_CAL_2XVDD (EFM32_DI+0x1BC) +#define EFM32_DI_ADC0_TEMP_0_READ_1V25 (EFM32_DI+0x1BE) +#define EFM32_DI_DAC0_CAL_1V25 (EFM32_DI+0x1C8) +#define EFM32_DI_DAC0_CAL_2V5 (EFM32_DI+0x1CC) +#define EFM32_DI_DAC0_CAL_VDD (EFM32_DI+0x1D0) +#define EFM32_DI_AUXHFRCO_CALIB_BAND_1 (EFM32_DI+0x1D4) +#define EFM32_DI_AUXHFRCO_CALIB_BAND_7 (EFM32_DI+0x1D5) +#define EFM32_DI_AUXHFRCO_CALIB_BAND_11 (EFM32_DI+0x1D6) +#define EFM32_DI_AUXHFRCO_CALIB_BAND_14 (EFM32_DI+0x1D7) +#define EFM32_DI_AUXHFRCO_CALIB_BAND_21 (EFM32_DI+0x1D8) +#define EFM32_DI_AUXHFRCO_CALIB_BAND_28 (EFM32_DI+0x1D9) +#define EFM32_DI_HFRCO_CALIB_BAND_1 (EFM32_DI+0x1DC) +#define EFM32_DI_HFRCO_CALIB_BAND_7 (EFM32_DI+0x1DD) +#define EFM32_DI_HFRCO_CALIB_BAND_11 (EFM32_DI+0x1DE) +#define EFM32_DI_HFRCO_CALIB_BAND_14 (EFM32_DI+0x1DF) +#define EFM32_DI_HFRCO_CALIB_BAND_21 (EFM32_DI+0x1E0) +#define EFM32_DI_HFRCO_CALIB_BAND_28 (EFM32_DI+0x1E1) +#define EFM32_DI_MEM_INFO_PAGE_SIZE (EFM32_DI+0x1E7) +#define EFM32_DI_RADIO_ID (EFM32_DI+0x1EE) +#define EFM32_DI_EUI64_0 (EFM32_DI+0x1F0) +#define EFM32_DI_EUI64_1 (EFM32_DI+0x1F4) +#define EFM32_DI_MEM_INFO_FLASH (EFM32_DI+0x1F8) +#define EFM32_DI_MEM_INFO_RAM (EFM32_DI+0x1FA) +#define EFM32_DI_PART_NUMBER (EFM32_DI+0x1FC) +#define EFM32_DI_PART_FAMILY (EFM32_DI+0x1FE) +#define EFM32_DI_PROD_REV (EFM32_DI+0x1FF) + +/* top 24 bits of eui */ +#define EFM32_DI_EUI_SILABS 0x000b57 + +#define EFM32_DI_PART_FAMILY_GECKO 71 +#define EFM32_DI_PART_FAMILY_GIANT_GECKO 72 +#define EFM32_DI_PART_FAMILY_TINY_GECKO 73 +#define EFM32_DI_PART_FAMILY_LEOPARD_GECKO 74 +#define EFM32_DI_PART_FAMILY_WONDER_GECKO 75 +#define EFM32_DI_PART_FAMILY_ZERO_GECKO 76 +#define EFM32_DI_PART_FAMILY_EZR_WONDER_GECKO 120 +#define EFM32_DI_PART_FAMILY_EZR_LEOPARD_GECKO 121 + +/* -------------------------------------------------------------------------- */ +/* Helper functions */ +/* -------------------------------------------------------------------------- */ + +/** + * Reads the EFM32 Extended Unique Identifier + */ +uint64_t efm32_read_eui(target *t) +{ + uint64_t eui; + + eui = (uint64_t)target_mem_read32(t, EFM32_DI_EUI64_1) << 32; + eui |= (uint64_t)target_mem_read32(t, EFM32_DI_EUI64_0) << 0; + + return eui; +} +/** + * Reads the EFM32 flash size in kiB + */ +uint16_t efm32_read_flash_size(target *t) +{ + return target_mem_read16(t, EFM32_DI_MEM_INFO_FLASH); +} +/** + * Reads the EFM32 RAM size in kiB + */ +uint16_t efm32_read_ram_size(target *t) +{ + return target_mem_read16(t, EFM32_DI_MEM_INFO_RAM); +} +/** + * Reads the EFM32 Part Number + */ +uint16_t efm32_read_part_number(target *t) +{ + return target_mem_read16(t, EFM32_DI_PART_NUMBER); +} +/** + * Reads the EFM32 Part Family + */ +uint8_t efm32_read_part_family(target *t) +{ + return target_mem_read8(t, EFM32_DI_PART_FAMILY); +} +/** + * Reads the EFM32 Radio part number (EZR parts only) + */ +uint16_t efm32_read_radio_part_number(target *t) +{ + return target_mem_read16(t, EFM32_DI_RADIO_OPN); +} + + + + +static void efm32_add_flash(target *t, uint32_t addr, size_t length, + size_t page_size) +{ + struct target_flash *f = calloc(1, sizeof(*f)); + f->start = addr; + f->length = length; + f->blocksize = page_size; + f->erase = efm32_flash_erase; + f->write = target_flash_write_buffered; + f->done = target_flash_done_buffered; + f->write_buf = efm32_flash_write; + f->buf_size = page_size; + target_add_flash(t, f); +} + +char variant_string[40]; +bool efm32_probe(target *t) +{ + /* Read the extended unique identifier */ + uint64_t eui = efm32_read_eui(t); + + /* /\* Check top 24 bits of eui are silabs *\/ */ + if (((eui >> 40) & 0xFFFFFF) != EFM32_DI_EUI_SILABS) + return false; + + /* Read the part number and family */ + uint16_t part_number = efm32_read_part_number(t); + uint8_t part_family = efm32_read_part_family(t); + uint16_t radio_number; /* optional, for ezr parts */ + uint32_t flash_page_size; + + switch(part_family) { + case EFM32_DI_PART_FAMILY_GECKO: + sprintf(variant_string, + "EFM32 Gecko"); + flash_page_size = 512; + break; + case EFM32_DI_PART_FAMILY_GIANT_GECKO: + sprintf(variant_string, + "EFM32 Giant Gecko"); + flash_page_size = 2048; /* Could be 2048 or 4096, assume 2048 */ + break; + case EFM32_DI_PART_FAMILY_TINY_GECKO: + sprintf(variant_string, + "EFM32 Tiny Gecko"); + flash_page_size = 512; + break; + case EFM32_DI_PART_FAMILY_LEOPARD_GECKO: + sprintf(variant_string, + "EFM32 Leopard Gecko"); + flash_page_size = 2048; /* Could be 2048 or 4096, assume 2048 */ + break; + case EFM32_DI_PART_FAMILY_WONDER_GECKO: + sprintf(variant_string, + "EFM32 Wonder Gecko"); + flash_page_size = 2048; + break; + case EFM32_DI_PART_FAMILY_ZERO_GECKO: + sprintf(variant_string, + "EFM32 Zero Gecko"); + flash_page_size = 1024; + break; + case EFM32_DI_PART_FAMILY_EZR_WONDER_GECKO: + radio_number = efm32_read_radio_part_number(t); /* on-chip radio */ + + sprintf(variant_string, + "EZR32WG%d (radio si%d)", + part_number, radio_number); + + flash_page_size = 2048; + break; + case EFM32_DI_PART_FAMILY_EZR_LEOPARD_GECKO: + radio_number = efm32_read_radio_part_number(t); /* on-chip radio */ + + sprintf(variant_string, + "EZR32LG%d (radio si%d)", + part_number, radio_number); + + flash_page_size = 2048; + break; + default: /* Unknown family */ + return false; + } + + /* Read memory sizes, convert to bytes */ + uint32_t flash_size = efm32_read_flash_size(t) * 0x400; + uint32_t ram_size = efm32_read_ram_size(t) * 0x400; + + /* Setup Target */ + t->driver = variant_string; + gdb_outf("flash size %d page size %d\n", flash_size, flash_page_size); + target_add_ram (t, SRAM_BASE, ram_size); + efm32_add_flash(t, 0x00000000, flash_size, flash_page_size); + target_add_commands(t, efm32_cmd_list, "EFM32"); + + return true; +} + +/** + * Erase flash row by row + */ +static int efm32_flash_erase(struct target_flash *f, uint32_t addr, size_t len) +{ + target *t = f->t; + + /* Set WREN bit to enabel MSC write and erase functionality */ + target_mem_write32(t, EFM32_MSC_WRITECTRL, 1); + + while (len) { + /* Write address of first word in row to erase it */ + target_mem_write32(t, EFM32_MSC_ADDRB, addr); + target_mem_write32(t, EFM32_MSC_WRITECMD, EFM32_MSC_WRITECMD_LADDRIM); + + /* Issue the erase command */ + target_mem_write32(t, EFM32_MSC_WRITECMD, EFM32_MSC_WRITECMD_ERASEPAGE ); + + /* Poll MSC Busy */ + while ((target_mem_read32(t, EFM32_MSC_STATUS) & EFM32_MSC_STATUS_BUSY)) { + if (target_check_error(t)) + return -1; + } + + addr += f->blocksize; + len -= f->blocksize; + } + + return 0; +} + +/** + * Write flash page by page + */ +static int efm32_flash_write(struct target_flash *f, + uint32_t dest, const void *src, size_t len) +{ + (void)len; + target *t = f->t; + + /* Write flashloader */ + target_mem_write(t, SRAM_BASE, efm32_flash_write_stub, + sizeof(efm32_flash_write_stub)); + /* Write Buffer */ + target_mem_write(t, STUB_BUFFER_BASE, src, len); + /* Run flashloader */ + return cortexm_run_stub(t, SRAM_BASE, dest, STUB_BUFFER_BASE, len, 0); + + return 0; +} + +/** + * Uses the MSC ERASEMAIN0 command to erase the entire flash + */ +static bool efm32_cmd_erase_all(target *t) +{ + /* Set WREN bit to enabel MSC write and erase functionality */ + target_mem_write32(t, EFM32_MSC_WRITECTRL, 1); + + + /* Unlock mass erase */ + target_mem_write32(t, EFM32_MSC_MASSLOCK, EFM32_MSC_MASSLOCK_LOCKKEY); + + /* Erase operation */ + target_mem_write32(t, EFM32_MSC_WRITECMD, EFM32_MSC_WRITECMD_ERASEMAIN0); + + /* Poll MSC Busy */ + while ((target_mem_read32(t, EFM32_MSC_STATUS) & EFM32_MSC_STATUS_BUSY)) { + if (target_check_error(t)) + return false; + } + + /* Relock mass erase */ + target_mem_write32(t, EFM32_MSC_MASSLOCK, 0); + + gdb_outf("Erase successful!\n"); + + return true; +} + +/** + * Reads the 40-bit unique number + */ +static bool efm32_cmd_serial(target *t) +{ + /* Read the extended unique identifier */ + uint64_t eui = efm32_read_eui(t) & 0xFFFFFFFFFF; + + /* Bottom 40 bits are unique number */ + gdb_outf("Unique Number: 0x%010llx\n", eui); + + return true; +} diff --git a/src/include/target.h b/src/include/target.h index bff556d..dcc83dd 100644 --- a/src/include/target.h +++ b/src/include/target.h @@ -271,6 +271,6 @@ bool sam3x_probe(target *t); bool nrf51_probe(target *t); bool samd_probe(target *t); bool kinetis_probe(target *t); +bool efm32_probe(target *t); #endif -