/* * SPDX-License-Identifier: BSD-2-Clause * * Copyright (C) 2019 Chris Johns . All rights reserved. * Copyright (C) 2015, Dutch & Dutch. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * This is a C version of the jbang code found in the jbang repo: * * https://github.com/dutchanddutch/jbang.git * * Dutch & Dutch is 'zmatt' on the #beagle IRC channel. This work is all based * on zmatt's research and solution. * * Hardware Modification * --------------------- * * The TI designers did not bring the ARM signal DBGEN to a system control * register so software can not directly control it. The BBB has a pull down * resister (R25) on the TRSTn JTAG signal which means software cannot * reconfigure the TRSTn pin and control it. As a result software access to the * ARM's debug registers is locked out. You can access the registers if you * want too but hardware breakpoints and watchpoints are ignored. ARM uses * hardware breakpoints to single step instructions. * * You need to add at least one wire for this code to work and optionally a * second wire if you need to test this code to make sure it is working. The * wires are small and require a small amount of soldering and soldering skill. * * If you add the TDO link change 'has_tdo' to '1' to enable support. This * setting defaults to '0' to disable support. * * Steps: * * 1. Locate P2 on the bottom side of the board. It is the JTAG connector * pads. If you look at the underside of the board with the SD card holder * to the right the pads are top center left. There are 20 pads in two * columns. The pads are numbered 1 at the top left then 2 top right, 3 is * second top on the left, 4 is second top to the right, then the pin * number increments as you move left then right down the pads. * * 2. Connect P2 to P5. * * 3. Optionally connect P7 to P13. * * The resulting wiring is: * * 1 === /--=== 2 * 3 === | === 4 * 5 ===--/ === 6 * 7 ===--\ === 8 * 9 === | === 10 * 11 === | === 12 * 13 ===--/ === 14 * 15 === === 16 * 17 === === 18 * 19 === === 20 */ #include #include #include #include "bspdebug.h" /* * Set to '1' to enable the traces. */ #define trace 0 #define trace_reg 0 /* * TDO provided feedback for testing via EMU0. */ #define has_tdo 0 /* * Pins of interest. */ #define AM335x_PIN_CONF_TMS 116 #define AM335x_PIN_CONF_TDI 117 #define AM335x_PIN_CONF_TDO 118 #define AM335x_PIN_CONF_TCK 119 #define AM335x_PIN_CONF_TRSTN 120 #define AM335x_PIN_CONF_EMU0 121 /* * Register base addresses. */ #define AM335x_PRCM_CM_PER (0x44E00000) #define AM335x_PRCM_WKUP (AM335x_PRCM_CM_PER + 0x400) #define AM335x_CONTROL_MODULE (0x44E10000) #define AM335x_CONF_PIN (AM335x_CONTROL_MODULE + 0x800) #define AM335x_GPIO0 (0x44e07000) #define AM335x_GPIO1 (0x4804c000) #define AM335x_GPIO2 (0x481ac000) #define AM335x_GPIO3 (0x481ae000) #define AM335x_DEBUGSS_ETM (0x4B140000) #define AM335x_DEBUGSS_DEBUG (0x4B141000) #define AM335x_DEBUGSS_CTI (0x4B142000) #define AM335x_DEBUGSS_ICE (0x4B143000) #define AM335x_DEBUGSS_DRM (0x4B160000) #define AM335x_DEBUGSS_ETB (0x4B162000) /* * Power, Reset, Clock Management (PRCM) */ #define AM335x_PRCM_CM_PER_GPIO3_CLKCTRL (0x0b4) #define AM335x_PRCM_WKUP_DEBUGSS_CLKCTRL (0x014) #define AM335x_PRCM_CM_MODULEMODE_MASK (3) #define AM335x_PRCM_CM_MODULEMODE_DISABLE (0) #define AM335x_PRCM_CM_MODULEMODE_ENABLE (2) #define AM335x_PRCM_CM_IDLEST_MASK (3 << 16) #define AM335x_PRCM_CM_IDLEST_FUNC (0 << 16) #define AM335x_PRCM_CM_IDLEST_TRANS (1 << 16) #define AM335x_PRCM_CM_IDLEST_IDLE (2 << 16) #define AM335x_PRCM_CM_IDLEST_DISABLED (3 << 16) /* * Pin configurations. */ #define AM335x_PIN_IN_OFFSET (0x138) #define AM335x_PIN_SLEW_FAST (0 << 6) #define AM335x_PIN_SLEW_SLOW (1 << 6) #define AM335x_PIN_RX_DISABLE (0 << 5) #define AM335x_PIN_RX_ENABLE (1 << 5) #define AM335x_PIN_PULL_DIS (1 << 3) #define AM335x_PIN_PULL_EN (0 << 3) #define AM335x_PIN_PULL_UP ((1 << 4) | AM335x_PIN_PULL_EN) #define AM335x_PIN_PULL_DOWN ((0 << 4) | AM335x_PIN_PULL_EN) /* * JTAG. */ typedef enum { JTAG_RST, JTAG_COMMIT, JTAG_RUN, JTAG_DATA, JTAG_PAUSE } JTAG_STATE; #define AM335x_JTAG_MASK (0x0fffffff) #define AM335x_JTAG_ID (0x0b94402f) /* * ICE Pick */ #define AM335x_ICEPICK_IR_LEN (6) #define AM335x_ICEPICK_IR_BYPASS (0x3f) /* 0b11'1111 1 bit */ #define AM335x_ICEPICK_IR_PUB_CONNECT (0x07) /* 0b00'0111 8-bit (3->4 bit indirect rw) */ #define AM335x_ICEPICK_IR_ROUTER (0x02) /* 0b00'0010 32-bit (7->24 bit indirect rw) */ /* * DAP */ #define AM335x_DAP_ID (0x3ba00477) #define AM335x_DAP_IR_LEN (4) #define AM335x_DAP_IR_ABORT (0x08) /* 0b'1000 */ #define AM335x_DAP_IR_DPACC (0x0a) /* 0b'1010 */ #define AM335x_DAP_IR_APACC (0x0b) /* 0b'1011 */ #define AM335x_DAP_IR_IDCODE (0x0e) /* 0b'1110 */ /* * */ #define AM335x_APB_DEBUG (0x80001000) /* * State of JTAG. */ static JTAG_STATE jtag_state; /* * Initialisation of icepick registers. */ static const uint32_t am335x_icepick_init_regs[2] = { 0x60002000, /* assert cortex-a8 DBGEN */ 0x2c002100, /* link DAP into chain (takes effect at run) */ }; #define AM335x_ICEPICK_INIT_REGS \ (sizeof(am335x_icepick_init_regs) / sizeof(am335x_icepick_init_regs[0])) /* * Low level trace. */ static void dbg_printk(const char* format, ...) RTEMS_PRINTFLIKE(1, 2); static void dbg_printk(const char* format, ...) { va_list ap; va_start(ap, format); if (trace) { vprintk(format, ap); } va_end(ap); } static volatile uint32_t* arm_reg_addr(uint32_t base, uint32_t offset) { return (volatile uint32_t*) (base + offset); } static uint32_t arm_reg_read(uint32_t base, uint32_t offset) { volatile uint32_t* addr = arm_reg_addr(base, offset); uint32_t val = *addr; if (trace_reg) printk("bbb: dbg: read: %08x -> %08x\n", (intptr_t) addr, val); return val; } static void arm_reg_write(uint32_t base, uint32_t offset, uint32_t val) { volatile uint32_t* addr = arm_reg_addr(base, offset); if (trace_reg) printk("bbb: dbg: read: %08x <- %08x\n", (intptr_t) addr, val); *addr = val; } static void arm_reg_set(uint32_t base, uint32_t offset, uint32_t mask, uint32_t val) { uint32_t rval = arm_reg_read(base, offset); rval &= ~mask; rval |= val; arm_reg_write(base, offset, rval); } static void am335x_pin_config(int pin, uint32_t setting) { arm_reg_write(AM335x_CONF_PIN, pin * sizeof(uint32_t), setting); } static void am335x_prcm_cm_enable(uint32_t clk) { arm_reg_set(AM335x_PRCM_CM_PER, clk, AM335x_PRCM_CM_MODULEMODE_MASK, AM335x_PRCM_CM_MODULEMODE_ENABLE); } static bool am335x_prcm_cm_ready(uint32_t clk) { uint32_t val = arm_reg_read(AM335x_PRCM_CM_PER, clk) & AM335x_PRCM_CM_IDLEST_MASK; return val == AM335x_PRCM_CM_IDLEST_FUNC; } static uint32_t bbb_sig_bank_addr(int pin) { static const uint32_t bank_addr[4] = { AM335x_GPIO0, AM335x_GPIO1, AM335x_GPIO2, AM335x_GPIO3 }; return bank_addr[(pin >> 5) & 3]; } static void bbb_sig_set(int pin, int level) { uint32_t val = (0 | AM335x_PIN_PULL_UP| AM335x_PIN_SLEW_FAST | ((level != 0) ? AM335x_PIN_RX_ENABLE : AM335x_PIN_RX_DISABLE)); am335x_pin_config(pin, val); } static uint32_t bbb_sig_get(int pin) { uint32_t in = arm_reg_read(bbb_sig_bank_addr(pin), AM335x_PIN_IN_OFFSET); return (in >> (32 - (pin & 31))) & 1; } static void bbb_tdo_init(void) { if (has_tdo) { am335x_pin_config(AM335x_PIN_CONF_EMU0, 7 | AM335x_PIN_PULL_UP | AM335x_PIN_RX_ENABLE | AM335x_PIN_SLEW_FAST); am335x_prcm_cm_enable(AM335x_PRCM_CM_PER_GPIO3_CLKCTRL); while (!am335x_prcm_cm_ready(AM335x_PRCM_CM_PER_GPIO3_CLKCTRL)) {} } } static uint32_t bbb_sig_tdo(void) { return has_tdo && bbb_sig_get(AM335x_PIN_CONF_EMU0) == 1 ? 1 : 0; } static void bbb_sig_trst(int level) { bbb_sig_set(AM335x_PIN_CONF_TRSTN, level); } static void bbb_sig_tck(int level) { bbb_sig_set(AM335x_PIN_CONF_TCK, level); } static void bbb_sig_tdi(int level) { bbb_sig_set(AM335x_PIN_CONF_TDI, level); } static void bbb_sig_tms(int level) { bbb_sig_set(AM335x_PIN_CONF_TMS, level); } static void bbb_nap(void) { volatile size_t count = 1000; while (count > 0) --count; } static void bbb_sig_tck_pulse(void) { bbb_nap(); bbb_sig_tck(1); bbb_nap(); bbb_sig_tck(0); bbb_nap(); } static void bbb_jtag_cmd(size_t nbits, uint32_t data) { size_t b; for (b = 0; b < nbits; ++b, data >>= 1) { bbb_sig_tms(data & 1); bbb_sig_tck_pulse(); } } static void bbb_hw_init(void) { dbg_printk("]] HW reset\n"); /* * Set CM_WKUP_DEBUGSS_CLKCTRL bits[1:0] = 2h, explict enable (reset does * this so leave out). */ am335x_prcm_cm_enable(AM335x_PRCM_WKUP_DEBUGSS_CLKCTRL); while (!am335x_prcm_cm_ready(AM335x_PRCM_WKUP_DEBUGSS_CLKCTRL)) {} bbb_tdo_init(); } static void bbb_jtag_reset(void) { dbg_printk("]] JTAG reset\n"); bbb_sig_trst(0); bbb_sig_tck(0); bbb_sig_tdi(1); bbb_jtag_cmd(7, 0x1f /* 0b1'1111 */); } static void bbb_jtag_commit(void) { if (jtag_state == JTAG_DATA) { dbg_printk("]] JTAG commit\n"); bbb_jtag_cmd(2, 0x3 /* 0b11 */); jtag_state = JTAG_COMMIT; } } static void bbb_jtag_run(size_t ncycles) { dbg_printk("]] JTAG run: %zu\n", ncycles); bbb_jtag_commit(); bbb_jtag_cmd(ncycles, 0); jtag_state = JTAG_RUN; } static uint32_t bbb_jtag_xfer(size_t nbits) { uint32_t in = 0; size_t b; dbg_printk("]] JTAG xfer: %zu\n", nbits); for (b = 0; b < nbits; ++b) { bbb_sig_tck_pulse(); in |= bbb_sig_tdo() << b; } dbg_printk("]] JTAG xfer: in: %08x\n", in); return in; } static uint32_t bbb_jtag_xfer_out(size_t nbits, uint32_t out) { uint32_t in = 0; size_t b; dbg_printk("]] JTAG xfer out: %zu %08x\n", nbits, out); for (b = 0; b < nbits; ++b, out >>= 1) { bbb_sig_tck_pulse(); bbb_sig_tdi(out & 1); in |= bbb_sig_tdo() << b; } dbg_printk("]] JTAG xfer out: in: %08x\n", in); return in; } static void bbb_jtag_dr(void) { dbg_printk("]] JTAG dr\n"); bbb_jtag_commit(); bbb_jtag_cmd(2, 0xb01); jtag_state = JTAG_DATA; } static uint32_t bbb_jtag_dr_out(size_t nbits, uint32_t out) { uint32_t in; dbg_printk("]] JTAG dr out\n"); bbb_jtag_dr(); in = bbb_jtag_xfer_out(nbits, out); bbb_jtag_commit(); return in; } static void bbb_jtag_ir(void) { dbg_printk("]] JTAG ir\n"); bbb_jtag_commit(); bbb_jtag_cmd(3, 0x3 /* 0b011 */); jtag_state = JTAG_DATA; } static uint32_t bbb_jtag_ir_out(size_t nbits, uint32_t out) { uint32_t in; dbg_printk("]] JTAG ir out\n"); bbb_jtag_ir(); in = bbb_jtag_xfer_out(nbits, out); bbb_jtag_commit(); return in; } static bool bbb_jtag_hw_init(void) { bool match = true; bbb_jtag_reset(); bbb_sig_trst(1); bbb_jtag_run(100); if (has_tdo) { uint32_t id = bbb_jtag_dr_out(32, 0); match = (id & AM335x_JTAG_MASK) == AM335x_JTAG_ID; printk("bbb: dbg: JTAG Id: %08x (%s)\n", id, match ? "match" : "mismatch"); } return match; } static bool bbb_icepick_init(void) { size_t r; bbb_jtag_ir_out(AM335x_ICEPICK_IR_LEN, AM335x_ICEPICK_IR_PUB_CONNECT); bbb_jtag_dr_out(8, 0x89 /* 0b'1000'1001 */); if (has_tdo && bbb_jtag_dr_out(8, 0) != 0x9 /* 0b1001 */) { printk("bbb: dbg: ICEPick connect failure\n"); return false; } bbb_jtag_ir_out(AM335x_ICEPICK_IR_LEN, AM335x_ICEPICK_IR_ROUTER); for (r = 0; r < AM335x_ICEPICK_INIT_REGS; ++r) { uint32_t val = am335x_icepick_init_regs[r]; bbb_jtag_dr_out(32, val | (1 << 31)); if (has_tdo && ((bbb_jtag_dr_out(32, 0) >> 24) != (val >> 24))) { printk("bbb: dbg: ICEPick write failure\n"); return false; } } bbb_jtag_ir_out(AM335x_ICEPICK_IR_LEN, AM335x_ICEPICK_IR_BYPASS); bbb_jtag_run(16); return true; } static void bbb_dap_ir(uint32_t reg) { static uint32_t last_ir = AM335x_DAP_IR_IDCODE; if (reg != last_ir) { last_ir = reg; bbb_jtag_ir(); bbb_jtag_xfer_out(AM335x_DAP_IR_LEN, reg); bbb_jtag_xfer_out(AM335x_ICEPICK_IR_LEN, AM335x_ICEPICK_IR_BYPASS); bbb_jtag_commit(); } } static uint32_t bbb_dap_op(uint32_t ir, uint32_t op, uint32_t arg) { uint32_t res; bbb_dap_ir(ir); bbb_jtag_dr(); res = bbb_jtag_xfer_out(3, op); if (has_tdo && res != 0x2 /* 0b010 */) { printk("bbb: dbg: DAP status code: %08x\n", res); } res = bbb_jtag_xfer_out(32, arg); /* * ICEPick in bypass. */ bbb_jtag_xfer(1); /* * No always needed, but it does not burt. */ bbb_jtag_run(1); return res; } static uint32_t bbb_dap_csw(void) { return bbb_dap_op(AM335x_DAP_IR_DPACC, 0x3 /* 0b011 */, 0); } static uint32_t bbb_dap_set_csw(uint32_t val) { return bbb_dap_op(AM335x_DAP_IR_DPACC, 0x2 /* 0b010 */, val); } static uint32_t bbb_dap_set_sel(uint32_t val) { return bbb_dap_op(AM335x_DAP_IR_DPACC, 0x4 /* 0b100 */, val); } static uint32_t bbb_dap_nop(void) { return bbb_dap_op(AM335x_DAP_IR_DPACC, 0x6 /* 0b110 */, 0); } static uint32_t bbb_ap_set_csw(uint32_t val) { return bbb_dap_op(AM335x_DAP_IR_APACC, 0x0 /* 0b000 */, val); } static uint32_t bbb_ap_set_addr(uint32_t val) { return bbb_dap_op(AM335x_DAP_IR_APACC, 0x2 /* 0b010 */, val); } static uint32_t bbb_ap_data(void) { return bbb_dap_op(AM335x_DAP_IR_APACC, 0x7 /* 0b111 */, 0); } static uint32_t bbb_ap_set_data(uint32_t val) { return bbb_dap_op(AM335x_DAP_IR_APACC, 0x6 /* 0b110 */, val); } static uint32_t bbb_dap_check(void) { uint32_t data = bbb_dap_csw(); uint32_t csw = bbb_dap_nop(); if (has_tdo && csw != 0xf0000000) { printk("bbb: dbg: DP-CSW unexpected value: %08x\n", csw); } return data; } static bool bbb_dap_init(void) { bool ok = true; if (has_tdo) { uint32_t id = bbb_jtag_dr_out(32, 0); ok = id == AM335x_DAP_ID; printk("bbb: dbg: DAP JTAG Id: %08x (%s)\n", id, ok ? "match" : "mismatch"); } if (ok) { /* * Power up and clear errors. */ bbb_dap_set_csw(0x50000032); bbb_dap_check(); /* * Select and configure APB-AP. */ bbb_dap_set_sel(1 << 24); bbb_ap_set_csw(0xe3000012); } return ok; } static uint32_t bbb_ap_read(uint32_t addr) { uint32_t data; bbb_ap_set_addr(addr); bbb_ap_data(); data = bbb_dap_check(); if (has_tdo) { dbg_printk("bbb: dbg: read 0x%08x -> 0x%08x\n", addr, data); } return data; } static uint32_t bbb_ap_write(uint32_t addr, uint32_t data) { bbb_ap_set_addr(addr); bbb_ap_set_data(data); return bbb_dap_check(); } static void bbb_show_auth_status(uint32_t addr) { if (has_tdo) { const char* privs[] = { "public invasive debug", "public non-invasive debug", "secure invasive debug", "secure non-invasive debug", }; uint32_t val = bbb_ap_read(addr + 0xfb8); size_t s; printk("bbb: dbg: auth status: %08x\n", val); for (s = 0; s < (sizeof(privs) / sizeof(privs[0])); ++s, val >>= 2) { printk("bbb: dbg: %s: ", privs[s]); if ((val & 1) != 0) printk("granted"); else if ((val & 2) != 0) printk("denied"); printk("\n"); } } } static uint32_t bbb_debug_rx(void) { /* debugger -> core */ uint32_t data; asm volatile("mrc p14, 0, %0, c0, c5, 0" : "=r" (data)); return data; } bool rtems_debugger_arm_debug_configure(void) { const uint32_t addr = (intptr_t) &rtems_debugger_arm_debug_configure; bbb_hw_init(); if (!bbb_jtag_hw_init()) return false; if (!bbb_icepick_init()) return false; if (!bbb_dap_init()) return false; bbb_show_auth_status(AM335x_APB_DEBUG); bbb_ap_read(AM335x_APB_DEBUG + 0x314); bbb_ap_read(AM335x_APB_DEBUG + 0x088); bbb_ap_write(AM335x_APB_DEBUG + 0x080, addr); usleep(1000); if (bbb_debug_rx() != addr) { printk("bbb: dbg: path test failure\n"); return false; } return true; } void* bbb_arm_debug_registers(void) { return (void*) AM335x_DEBUGSS_DEBUG; } void* rtems_debugger_arm_debug_registers(void) { return bbb_arm_debug_registers(); }