/**************************************************************************** * drivers/coresight/coresight_etm3.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include "coresight_common.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Device registers: * 0x000 - 0x2FC: Trace registers * 0x300 - 0x314: Management registers * 0x318 - 0xEFC: Trace registers * * Coresight registers * 0xF00 - 0xF9C: Management registers * 0xFA0 - 0xFA4: Management registers in PFTv1.0 * Trace registers in PFTv1.1 * 0xFA8 - 0xFFC: Management registers */ #define ETM_CR 0x000 #define ETM_CCR 0x004 #define ETM_TRIGGER 0x008 #define ETM_SR 0x010 #define ETM_SCR 0x014 #define ETM_TSSCR 0x018 #define ETM_TECR2 0x01c #define ETM_TEEVR 0x020 #define ETM_TECR1 0x024 #define ETM_FFLR 0x02c #define ETM_ACVR(n) (0x040 + (n * 4)) #define ETM_ACTR(n) (0x080 + (n * 4)) #define ETM_CNTRLDVR(n) (0x140 + (n * 4)) #define ETM_CNTENR(n) (0x150 + (n * 4)) #define ETM_CNTRLDEVR(n) (0x160 + (n * 4)) #define ETM_CNTVR(n) (0x170 + (n * 4)) #define ETM_SQ12EVR 0x180 #define ETM_SQ21EVR 0x184 #define ETM_SQ23EVR 0x188 #define ETM_SQ31EVR 0x18c #define ETM_SQ32EVR 0x190 #define ETM_SQ13EVR 0x194 #define ETM_SQR 0x19c #define ETM_EXTOUTEVR(n) (0x1a0 + (n * 4)) #define ETM_CIDCVR(n) (0x1b0 + (n * 4)) #define ETM_CIDCMR 0x1bc #define ETM_IMPSPEC0 0x1c0 #define ETM_IMPSPEC1 0x1c4 #define ETM_IMPSPEC2 0x1c8 #define ETM_IMPSPEC3 0x1cc #define ETM_IMPSPEC4 0x1d0 #define ETM_IMPSPEC5 0x1d4 #define ETM_IMPSPEC6 0x1d8 #define ETM_IMPSPEC7 0x1dc #define ETM_SYNCFR 0x1e0 #define ETM_IDR 0x1e4 #define ETM_CCER 0x1e8 #define ETM_EXTINSELR 0x1ec #define ETM_TESSEICR 0x1f0 #define ETM_EIBCR 0x1f4 #define ETM_TSEVR 0x1f8 #define ETM_AUXCR 0x1fc #define ETM_TRACEIDR 0x200 #define ETM_VMIDCVR 0x240 /* Management registers (0x300-0x314) */ #define ETM_OSLAR 0x300 #define ETM_OSLSR 0x304 #define ETM_OSSRR 0x308 #define ETM_PDCR 0x310 #define ETM_PDSR 0x314 /* Register definition */ /* ETMCR - 0x00 */ #define ETM_CR_PWD_DWN BIT(0) #define ETM_CR_STALL_MODE BIT(7) #define ETM_CR_BRANCH_BROADCAST BIT(8) #define ETM_CR_ETM_PRG BIT(10) #define ETM_CR_ETM_EN BIT(11) #define ETM_CR_CYC_ACC BIT(12) #define ETM_CR_CTXID_SIZE (BIT(14) | BIT(15)) #define ETM_CR_TIMESTAMP_EN BIT(28) /* ETM_CR_RETURN_STACK is supported by PTM, ETM not support it */ #define ETM_CR_RETURN_STACK BIT(29) /* ETMCCR - 0x04 */ #define ETM_CCR_FIFOFULL BIT(23) /* ETMPDCR - 0x310 */ #define ETM_PDCR_PWD_UP BIT(3) /* ETMTECR1 - 0x024 */ #define ETM_TECR1_ADDR_COMP_1 BIT(0) #define ETM_TECR1_INC_EXC BIT(24) #define ETM_TECR1_START_STOP BIT(25) /* ETMCCER - 0x1E8 */ #define ETM_CCER_TIMESTAMP BIT(22) #define ETM_CCER_RETSTACK BIT(23) #define ETM_MODE_EXCLUDE BIT(0) #define ETM_MODE_CYCACC BIT(1) #define ETM_MODE_STALL BIT(2) #define ETM_MODE_TIMESTAMP BIT(3) #define ETM_MODE_CTXID BIT(4) #define ETM_MODE_BBROAD BIT(5) #define ETM_MODE_RET_STACK BIT(6) #define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_USER BIT(31) #define ETM_MODE_ALL \ (ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | ETM_MODE_STALL | \ ETM_MODE_TIMESTAMP | ETM_MODE_CTXID | ETM_MODE_BBROAD | \ ETM_MODE_RET_STACK | ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER) #define ETM_SQR_MASK 0x3 #define ETM_TRACEID_MASK 0x3f #define ETM_EVENT_MASK 0x1ffff #define ETM_SYNC_MASK 0xfff #define ETM_ALL_MASK 0xffffffff #define ETM_SR_PROGRAM BIT(1) #define ETM_SEQ_STATE_MAX_VAL 0x2 #define ETM_PORT_SIZE_MASK (GENMASK(21, 21) | GENMASK(6, 4)) #define ETM_ARCH_V3_3 0x23 #define ETM_ARCH_V3_5 0x25 #define PFT_ARCH_V1_0 0x30 #define PFT_ARCH_V1_1 0x31 /* Hard wired, always true */ #define ETM_HARD_WIRE_RES_A ((0x0f << 0) | (0x06 << 4)) /* Single addr comparator 1 */ #define ETM_ADD_COMP_0 ((0x00 << 7) | (0x00 << 11)) /* NOT(A) */ #define ETM_EVENT_NOT_A BIT(14) #define ETM_DEFAULT_EVENT_VAL \ (ETM_HARD_WIRE_RES_A | ETM_ADD_COMP_0 | ETM_EVENT_NOT_A) #define ETM3X_SUPPORTED_OPTIONS \ (ETM_CR_CYC_ACC | ETM_CR_TIMESTAMP_EN | ETM_CR_RETURN_STACK) /**************************************************************************** * Private Functions Prototypes ****************************************************************************/ static int etm_enable(FAR struct coresight_dev_s *csdev); static void etm_disable(FAR struct coresight_dev_s *csdev); /**************************************************************************** * Private Data ****************************************************************************/ static const struct coresight_source_ops_s g_etm_source_ops = { .enable = etm_enable, .disable = etm_disable, }; static const struct coresight_ops_s g_etm_ops = { .source_ops = &g_etm_source_ops, }; /**************************************************************************** * Private Functions ****************************************************************************/ #ifndef CONFIG_CORESIGHT_ETM_USE_COPROCESSOR static inline void etm_write_reg(FAR struct coresight_etm_dev_s *etmdev, uint32_t val, uint32_t off) { coresight_put32(val, etmdev->csdev.addr + off); } static inline uint32_t etm_read_reg(FAR struct coresight_etm_dev_s *etmdev, uint32_t off) { return coresight_get32(etmdev->csdev.addr + off); } #endif /**************************************************************************** * Name: etm_modify_reg32 * * Description: * Set a bitmask of register to specific value. * ****************************************************************************/ static void etm_modify_reg32(FAR struct coresight_etm_dev_s *etmdev, uint32_t val, uint32_t mask, uint32_t off) { uint32_t temp = etm_read_reg(etmdev, off); etm_write_reg(etmdev, (temp & ~mask) | (val & mask), off); } /**************************************************************************** * Name: etm_os_unlock * * Description: * When the ETM trace registers are locked, any attempt to access the * locked registers returns a slave-generated error response.In ETMv3.5, * the OS Lock is always set from an ETM reset. ARM recommends that, after * programming the ETM registers, you always execute an ISB instruction to * ensure that all updates are committed to the ETM before you restart * normal code execution. * ****************************************************************************/ static void etm_os_unlock(FAR struct coresight_etm_dev_s *etmdev) { etm_write_reg(etmdev, 0x0, ETM_OSLAR); } /**************************************************************************** * Name: etm_clr_pwrdwn * * Description: * When pwrdown bit is set to 1, the ETM must be powered down and disabled, * and then operated in a low power mode with all clocks stopped. When this * bit is set to 1, writes to some registers and fields might be ignored. * ARM recommends that you use a read-modify-write procedure when modifying * the ETMCR. * ****************************************************************************/ static void etm_clr_pwrdwn(FAR struct coresight_etm_dev_s *etmdev) { etm_modify_reg32(etmdev, 0, ETM_CR_PWD_DWN, ETM_CR); } /**************************************************************************** * Name: etm_set_pwrdwn ****************************************************************************/ static void etm_set_pwrdwn(FAR struct coresight_etm_dev_s *etmdev) { etm_modify_reg32(etmdev, ETM_CR_PWD_DWN, ETM_CR_PWD_DWN, ETM_CR); } /**************************************************************************** * Name: etm_set_pwrup * * Description: * Power is provided to the ETM trace registers. This register can only be * accessed using a memory-mapped interface or from an external debugger. * ****************************************************************************/ static void etm_set_pwrup(FAR struct coresight_etm_dev_s *etmdev) { coresight_modify32(ETM_PDCR_PWD_UP, ETM_PDCR_PWD_UP, etmdev->csdev.addr + ETM_PDCR); } /**************************************************************************** * Name: etm_clr_pwrup ****************************************************************************/ static void etm_clr_pwrup(FAR struct coresight_etm_dev_s *etmdev) { coresight_modify32(0, ETM_PDCR_PWD_UP, etmdev->csdev.addr + ETM_PDCR); } /**************************************************************************** * Name: etm_timeout * * Description: * Loop until a bitmask of register has changed to a specific value. * ****************************************************************************/ static int etm_timeout(FAR struct coresight_etm_dev_s *etmdev, uint32_t val, uint32_t mask, uint32_t off) { int i; for (i = CONFIG_CORESIGHT_TIMEOUT; i > 0; i--) { uint32_t value = etm_read_reg(etmdev, off); if ((value & mask) == val) { return 0; } up_udelay(1); } return -EAGAIN; } /**************************************************************************** * Name: etm_set_program * * Description: * Set ETM Programming bit to disable all operations during programming. * When programming the ETM registers you must enable all the changes at * the same time. * ****************************************************************************/ static void etm_set_program(FAR struct coresight_etm_dev_s *etmdev) { etm_modify_reg32(etmdev, ETM_CR_ETM_PRG, ETM_CR_ETM_PRG, ETM_CR); if (etm_timeout(etmdev, ETM_SR_PROGRAM, ETM_SR_PROGRAM, ETM_SR) < 0) { cserr("timeout observed at setting ETM_SR_PROGRAM\n"); } } /**************************************************************************** * Name: etm_clr_program ****************************************************************************/ static void etm_clr_program(FAR struct coresight_etm_dev_s *etmdev) { etm_modify_reg32(etmdev, 0, ETM_CR_ETM_PRG, ETM_CR); if (etm_timeout(etmdev, 0, ETM_SR_PROGRAM, ETM_SR) < 0) { cserr("timeout observed at clearing ETM_SR_PROGRAM\n"); } } /**************************************************************************** * Name: etm_init_arch_data * * Description: * Get capabilities of current ETM architecture version. * ****************************************************************************/ static void etm_init_arch_data(FAR struct coresight_etm_dev_s *etmdev) { uint32_t etmccr; coresight_unlock(etmdev->csdev.addr); etm_os_unlock(etmdev); etm_clr_pwrdwn(etmdev); etm_set_pwrup(etmdev); etm_set_program(etmdev); etmdev->arch = BMVAL(etm_read_reg(etmdev, ETM_IDR), 4, 11); etmdev->port_size = etm_read_reg(etmdev, ETM_CR) & ETM_PORT_SIZE_MASK; etmccr = etm_read_reg(etmdev, ETM_CCR); etmdev->nr_addr_cmp = BMVAL(etmccr, 0, 3) * 2; etmdev->nr_cntr = BMVAL(etmccr, 13, 15); etmdev->nr_ext_inp = BMVAL(etmccr, 17, 19); etmdev->nr_ext_out = BMVAL(etmccr, 20, 22); etmdev->nr_ctxid_cmp = BMVAL(etmccr, 24, 25); etm_clr_program(etmdev); etm_clr_pwrup(etmdev); etm_set_pwrdwn(etmdev); coresight_lock(etmdev->csdev.addr); } /**************************************************************************** * Name: etm_arch_supported * * Description: * If ETM/PTM implement is supported by current driver. * ****************************************************************************/ static bool etm_arch_supported(uint8_t arch) { switch (arch) { case ETM_ARCH_V3_3: case ETM_ARCH_V3_5: case PFT_ARCH_V1_0: case PFT_ARCH_V1_1: return true; default: return false; } } /**************************************************************************** * Name: etm_set_default * * Description: * Setup default ETM config. * ****************************************************************************/ static void etm_set_default(struct etm_config_s *config) { int i; /* Trace all memory, set according to spec */ config->enable_ctrl1 = BIT(24); config->enable_ctrl2 = 0x0; config->enable_event = ETM_HARD_WIRE_RES_A; /* Disable all other event */ config->trigger_event = ETM_DEFAULT_EVENT_VAL; config->seq_12_event = ETM_DEFAULT_EVENT_VAL; config->seq_21_event = ETM_DEFAULT_EVENT_VAL; config->seq_23_event = ETM_DEFAULT_EVENT_VAL; config->seq_31_event = ETM_DEFAULT_EVENT_VAL; config->seq_32_event = ETM_DEFAULT_EVENT_VAL; config->seq_13_event = ETM_DEFAULT_EVENT_VAL; config->timestamp_event = ETM_DEFAULT_EVENT_VAL; for (i = 0; i < ETM_MAX_CNTR; i++) { config->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; config->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; } config->sync_freq = 0x400; } /**************************************************************************** * Name: etm_hw_enable ****************************************************************************/ static void etm_hw_enable(FAR struct coresight_etm_dev_s *etmdev) { FAR struct etm_config_s *config = &etmdev->cfg; uint32_t etmcr; int i; coresight_unlock(etmdev->csdev.addr); etm_os_unlock(etmdev); etm_set_pwrup(etmdev); etm_clr_pwrdwn(etmdev); etm_set_program(etmdev); etmcr = etm_read_reg(etmdev, ETM_CR); etmcr &= ~ETM3X_SUPPORTED_OPTIONS; etmcr |= ETM_CR_ETM_EN; etm_write_reg(etmdev, config->ctrl | etmcr, ETM_CR); etm_write_reg(etmdev, config->trigger_event, ETM_TRIGGER); etm_write_reg(etmdev, config->startstop_ctrl, ETM_TSSCR); etm_write_reg(etmdev, config->enable_event, ETM_TEEVR); etm_write_reg(etmdev, config->enable_ctrl1, ETM_TECR1); etm_write_reg(etmdev, config->fifofull_level, ETM_FFLR); for (i = 0; i < etmdev->nr_addr_cmp; i++) { etm_write_reg(etmdev, config->addr_val[i], ETM_ACVR(i)); etm_write_reg(etmdev, config->addr_acctype[i], ETM_ACTR(i)); } for (i = 0; i < etmdev->nr_cntr; i++) { etm_write_reg(etmdev, config->cntr_rld_val[i], ETM_CNTRLDVR(i)); etm_write_reg(etmdev, config->cntr_event[i], ETM_CNTENR(i)); etm_write_reg(etmdev, config->cntr_rld_event[i], ETM_CNTRLDEVR(i)); etm_write_reg(etmdev, config->cntr_val[i], ETM_CNTVR(i)); } etm_write_reg(etmdev, config->seq_12_event, ETM_SQ12EVR); etm_write_reg(etmdev, config->seq_21_event, ETM_SQ21EVR); etm_write_reg(etmdev, config->seq_23_event, ETM_SQ23EVR); etm_write_reg(etmdev, config->seq_31_event, ETM_SQ31EVR); etm_write_reg(etmdev, config->seq_32_event, ETM_SQ32EVR); etm_write_reg(etmdev, config->seq_13_event, ETM_SQ13EVR); etm_write_reg(etmdev, config->seq_curr_state, ETM_SQR); for (i = 0; i < etmdev->nr_ext_out; i++) { etm_write_reg(etmdev, ETM_DEFAULT_EVENT_VAL, ETM_EXTOUTEVR(i)); } for (i = 0; i < etmdev->nr_ctxid_cmp; i++) { etm_write_reg(etmdev, config->ctxid_pid[i], ETM_CIDCVR(i)); } etm_write_reg(etmdev, config->ctxid_mask, ETM_CIDCMR); etm_write_reg(etmdev, config->sync_freq, ETM_SYNCFR); /* No external input selected */ etm_write_reg(etmdev, 0x0, ETM_EXTINSELR); etm_write_reg(etmdev, config->timestamp_event, ETM_TSEVR); /* No auxiliary control selected */ etm_write_reg(etmdev, 0x0, ETM_AUXCR); etm_write_reg(etmdev, etmdev->traceid, ETM_TRACEIDR); /* No VMID comparator value selected */ etm_write_reg(etmdev, 0x0, ETM_VMIDCVR); etm_clr_program(etmdev); coresight_lock(etmdev->csdev.addr); } /**************************************************************************** * Name: etm_hw_disable ****************************************************************************/ static void etm_hw_disable(FAR struct coresight_etm_dev_s *etmdev) { FAR struct etm_config_s *config = &etmdev->cfg; int i; coresight_unlock(etmdev->csdev.addr); etm_set_program(etmdev); /* Read back sequencer and counters for post trace analysis */ config->seq_curr_state = etm_read_reg(etmdev, ETM_SQR) & ETM_SQR_MASK; for (i = 0; i < etmdev->nr_cntr; i++) { config->cntr_val[i] = etm_read_reg(etmdev, ETM_CNTVR(i)); } etm_set_pwrdwn(etmdev); coresight_lock(etmdev->csdev.addr); } /**************************************************************************** * Name: etm_enable ****************************************************************************/ static int etm_enable(FAR struct coresight_dev_s *csdev) { FAR struct coresight_etm_dev_s *etmdev = (FAR struct coresight_etm_dev_s *)csdev; int ret; ret = coresight_claim_device(etmdev->csdev.addr); if (ret < 0) { return ret; } etm_hw_enable(etmdev); return ret; } /**************************************************************************** * Name: etm_enable ****************************************************************************/ static void etm_disable(FAR struct coresight_dev_s *csdev) { FAR struct coresight_etm_dev_s *etmdev = (FAR struct coresight_etm_dev_s *)csdev; etm_hw_disable(etmdev); coresight_disclaim_device(etmdev->csdev.addr); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: etm_config * * Description: * Configure the etm device. * * Input Parameters: * etmdev - Pointer to the ETM device to config. * config - Configuration need to be set to ETM device. * * Returned Value: * Zero on success; a negative value on failure. * ****************************************************************************/ int etm_config(FAR struct coresight_etm_dev_s *etmdev, FAR const struct etm_config_s *config) { coresight_unlock(etmdev->csdev.addr); etm_os_unlock(etmdev); etm_clr_pwrdwn(etmdev); etm_set_pwrup(etmdev); etm_set_program(etmdev); memcpy(&etmdev->cfg, config, sizeof(struct etm_config_s)); if ((etmdev->cfg.ctrl & ETM_CR_RETURN_STACK) && !(etm_read_reg(etmdev, ETM_CCER) & ETM_CCER_RETSTACK)) { etmdev->cfg.ctrl &= ~ETM_CR_RETURN_STACK; } etm_clr_program(etmdev); etm_clr_pwrup(etmdev); etm_set_pwrdwn(etmdev); coresight_lock(etmdev->csdev.addr); return 0; } /**************************************************************************** * Name: etm_register * * Description: * Register an ETM/PTM devices. * * Input Parameters: * desc - A description of this coresight device. * * Returned Value: * Pointer to an ETM device on success; NULL on failure. * ****************************************************************************/ FAR struct coresight_etm_dev_s * etm_register(FAR const struct coresight_desc_s *desc) { FAR struct coresight_etm_dev_s *etmdev; FAR struct coresight_dev_s *csdev; int ret; etmdev = kmm_zalloc(sizeof(struct coresight_etm_dev_s)); if (etmdev == NULL) { cserr("%s:malloc failed!\n", desc->name); return NULL; } etmdev->cpu = desc->cpu; etmdev->csdev.addr = desc->addr; etm_init_arch_data(etmdev); if (!etm_arch_supported(etmdev->arch)) { kmm_free(etmdev); cserr("%s:current implement version is not supported\n", desc->name); return NULL; } etmdev->traceid = coresight_get_cpu_trace_id(etmdev->cpu); etm_set_default(&etmdev->cfg); csdev = &etmdev->csdev; csdev->ops = &g_etm_ops; ret = coresight_register(csdev, desc); if (ret < 0) { kmm_free(etmdev); cserr("%s:register failed\n", desc->name); return NULL; } return etmdev; } /**************************************************************************** * Name: etm_unregister * * Description: * Unregister an EMT/PTM device. * ****************************************************************************/ void etm_unregister(FAR struct coresight_etm_dev_s *etmdev) { coresight_unregister(&etmdev->csdev); kmm_free(etmdev); }