/* * 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. */ #include #include #include #include #include "nimble_syscfg.h" #include "sysflash/sysflash.h" #include "os/os.h" #include "mcu/mcu.h" #include "mcu/cmsis_nvic.h" #include "mcu/da1469x_hal.h" #include "mcu/da1469x_lpclk.h" #include "mcu/da1469x_clock.h" #include "mcu/da1469x_trimv.h" #include "mcu/da1469x_pdc.h" #include "cmac_driver/cmac_host.h" #include "cmac_driver/cmac_shared.h" #include "cmac_driver/cmac_diag.h" #include "trng/trng.h" #if MYNEWT_VAL(CMAC_DEBUG_COREDUMP_ENABLE) #include "console/console.h" #endif /* CMAC data */ extern char _binary_cmac_rom_bin_start[]; extern char _binary_cmac_rom_bin_end; extern char _binary_cmac_ram_bin_start[]; extern char _binary_cmac_ram_bin_end; struct cmac_image_info { uint32_t magic; uint32_t size_rom; uint32_t size_ram; uint32_t offset_data; uint32_t offset_shared; }; /* PDC entry for waking up CMAC */ static int8_t g_cmac_host_pdc_sys2cmac; /* PDC entry for waking up M33 */ static int8_t g_cmac_host_pdc_cmac2sys; static void cmac_host_rand_fill(struct os_event *ev); static struct os_event g_cmac_host_rand_ev = { .ev_cb = cmac_host_rand_fill }; static void cmac_host_rand_chk_fill(void); #if MYNEWT_VAL_CHOICE(BLE_TRANSPORT_HS, uart) static void cmac_host_error_w4flush(struct os_event *ev); static struct os_event g_cmac_host_error_ev = { .ev_cb = cmac_host_error_w4flush }; #endif static void cmac2sys_isr(void) { #if MYNEWT_VAL(CMAC_DEBUG_COREDUMP_ENABLE) volatile struct cmac_coredump *cd = &g_cmac_shared_data->coredump; const char *assert_file; #endif os_trace_isr_enter(); /* Clear CMAC2SYS interrupt */ *(volatile uint32_t *)0x40002000 = 2; cmac_mbox_read(); if (*(volatile uint32_t *)0x40002000 & 0x1c00) { #if MYNEWT_VAL(CMAC_DEBUG_COREDUMP_ENABLE) console_blocking_mode(); console_printf("CMAC error (0x%08lx)\n", *(volatile uint32_t *)0x40002000); console_printf(" lr:0x%08lx pc:0x%08lx\n", cd->lr, cd->pc); if (cd->assert) { console_printf(" assert:0x%08lx\n", cd->assert); if (cd->assert_file) { /* Need to translate pointer from M0 code segment to M33 data */ assert_file = cd->assert_file + MCU_MEM_SYSRAM_START_ADDRESS + MEMCTRL->CMI_CODE_BASE_REG; console_printf(" %s:%d\n", assert_file, (unsigned)cd->assert_line); } } console_printf(" 0x%08lx CM_ERROR_REG\n", cd->CM_ERROR_REG); console_printf(" 0x%08lx CM_EXC_STAT_REG\n", cd->CM_EXC_STAT_REG); console_printf(" 0x%08lx CM_LL_TIMER1_36_10_REG\n", cd->CM_LL_TIMER1_36_10_REG); console_printf(" 0x%08lx CM_LL_TIMER1_9_0_REG\n", cd->CM_LL_TIMER1_9_0_REG); /* Spin if debugger is connected to CMAC to avoid resetting it */ if (cd->CM_STAT_REG & 0x20) { for (;;); } #endif #if MYNEWT_VAL_CHOICE(BLE_TRANSPORT_HS, uart) NVIC_DisableIRQ(CMAC2SYS_IRQn); /* Wait until UART is flushed and then assert */ cmac_host_error_w4flush(NULL); return; #endif /* XXX CMAC is in error state, need to recover */ assert(0); return; } cmac_host_rand_chk_fill(); os_trace_isr_exit(); } static void cmac_host_rand_fill(struct os_event *ev) { size_t num_bytes; struct trng_dev *trng; uint32_t *rnum; uint32_t rnums[CMAC_RAND_BUF_ELEMS]; /* Check if full */ if (!cmac_rand_is_active() || cmac_rand_is_full()) { return; } assert(ev->ev_arg != NULL); /* Fill buffer with random numbers even though we may not use all of them */ trng = ev->ev_arg; rnum = &rnums[0]; num_bytes = trng_read(trng, rnum, CMAC_RAND_BUF_ELEMS * sizeof(uint32_t)); cmac_rand_fill(rnum, num_bytes / 4); cmac_host_signal2cmac(); } static void cmac_host_rand_chk_fill(void) { if (cmac_rand_is_active() && !cmac_rand_is_full()) { os_eventq_put(os_eventq_dflt_get(), &g_cmac_host_rand_ev); } } #if MYNEWT_VAL_CHOICE(BLE_TRANSPORT_HS, uart) #if MYNEWT_VAL(BLE_TRANSPORT_UART_PORT) < 0 || MYNEWT_VAL(BLE_TRANSPORT_UART_PORT) > 2 #error Invalid BLE_HCI_UART_PORT #endif static void cmac_host_error_w4flush(struct os_event *ev) { static UART_Type * const regs[] = { (void *)UART, (void *)UART2, (void *)UART3 }; if (!ev) { /* Move to task context, we do not want to spin in interrupt */ os_eventq_put(os_eventq_dflt_get(), &g_cmac_host_error_ev); return; } do { cmac_mbox_read(); while ((regs[MYNEWT_VAL(BLE_TRANSPORT_UART_PORT)]->UART_LSR_REG & UART_UART_LSR_REG_UART_TEMT_Msk) == 0) { /* Wait until both FIFO and shift registers are empty */ } } while (cmac_mbox_has_data()); /* Reset CMAC */ CRG_TOP->CLK_RADIO_REG |= CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Msk; assert(0); } #endif void cmac_host_signal2cmac(void) { da1469x_pdc_set(g_cmac_host_pdc_sys2cmac); } static void cmac_host_lpclk_cb(uint32_t freq) { /* No need to wakeup CMAC if LP clock frequency did not change */ if (g_cmac_shared_data->lp_clock_freq == freq) { return; } cmac_shared_lock(); g_cmac_shared_data->lp_clock_freq = freq; g_cmac_shared_data->pending_ops |= CMAC_PENDING_OP_LP_CLK; cmac_shared_unlock(); cmac_host_signal2cmac(); } #if MYNEWT_VAL(CMAC_DEBUG_HOST_PRINT_ENABLE) static void cmac_host_print_trim(const char *name, const uint32_t *tv, unsigned len) { console_printf("[CMAC] Trim values for '%s'\n", name); while (len) { console_printf(" 0x%08x = 0x%08x\n", (unsigned)tv[0], (unsigned)tv[1]); len -= 2; tv += 2; } } #endif void cmac_host_rf_calibrate(void) { cmac_shared_lock(); g_cmac_shared_data->pending_ops |= CMAC_PENDING_OP_RF_CAL; cmac_shared_unlock(); cmac_host_signal2cmac(); } void cmac_host_init(void) { struct trng_dev *trng; struct cmac_image_info ii; uint32_t cmac_rom_size; uint32_t cmac_ram_size; #if !MYNEWT_VAL(CMAC_IMAGE_SINGLE) const struct flash_area *fa; int rc; #endif struct cmac_trim *trim; /* Get trng os device */ trng = (struct trng_dev *) os_dev_open("trng", OS_TIMEOUT_NEVER, NULL); assert(trng); g_cmac_host_rand_ev.ev_arg = trng; #if MYNEWT_VAL(CMAC_DEBUG_DIAG_ENABLE) cmac_diag_setup_host(); #endif #if MYNEWT_VAL(CMAC_DEBUG_SWD_ENABLE) /* Enable CMAC debugger */ CRG_TOP->SYS_CTRL_REG |= 0x40; /* CRG_TOP_SYS_CTRL_REG_CMAC_DEBUGGER_ENABLE_Msk */ #endif /* * Add PDC entry to wake up CMAC from M33 * * XXX if MCU_DEBUG_GPIO_DEEP_SLEEP is enabled on CMAC, this should also * enable PD_COM so CMAC can access GPIOs after wake up */ g_cmac_host_pdc_sys2cmac = da1469x_pdc_add(MCU_PDC_TRIGGER_MAC_TIMER, MCU_PDC_MASTER_CMAC, MCU_PDC_EN_XTAL); da1469x_pdc_set(g_cmac_host_pdc_sys2cmac); da1469x_pdc_ack(g_cmac_host_pdc_sys2cmac); /* Add PDC entry to wake up M33 from CMAC, if does not exist yet */ g_cmac_host_pdc_cmac2sys = da1469x_pdc_find(MCU_PDC_TRIGGER_COMBO, MCU_PDC_MASTER_M33, 0); if (g_cmac_host_pdc_cmac2sys < 0) { g_cmac_host_pdc_cmac2sys = da1469x_pdc_add(MCU_PDC_TRIGGER_COMBO, MCU_PDC_MASTER_M33, MCU_PDC_EN_XTAL); da1469x_pdc_set(g_cmac_host_pdc_cmac2sys); da1469x_pdc_ack(g_cmac_host_pdc_cmac2sys); } /* Setup CMAC2SYS interrupt */ NVIC_SetVector(CMAC2SYS_IRQn, (uint32_t)cmac2sys_isr); NVIC_SetPriority(CMAC2SYS_IRQn, MYNEWT_VAL(CMAC_CMAC2SYS_IRQ_PRIORITY)); NVIC_DisableIRQ(CMAC2SYS_IRQn); /* Enable Radio LDO */ CRG_TOP->POWER_CTRL_REG |= CRG_TOP_POWER_CTRL_REG_LDO_RADIO_ENABLE_Msk; /* Enable CMAC, but keep it in reset */ CRG_TOP->CLK_RADIO_REG = (1 << CRG_TOP_CLK_RADIO_REG_RFCU_ENABLE_Pos) | (1 << CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Pos) | (0 << CRG_TOP_CLK_RADIO_REG_CMAC_CLK_SEL_Pos) | (1 << CRG_TOP_CLK_RADIO_REG_CMAC_CLK_ENABLE_Pos) | (0 << CRG_TOP_CLK_RADIO_REG_CMAC_DIV_Pos); /* Calculate size of ROM and RAM area */ cmac_rom_size = &_binary_cmac_rom_bin_end - &_binary_cmac_rom_bin_start[0]; cmac_ram_size = &_binary_cmac_ram_bin_end - &_binary_cmac_ram_bin_start[0]; /* Load image header and check if image can be loaded */ #if MYNEWT_VAL(CMAC_IMAGE_SINGLE) memcpy(&ii, &_binary_cmac_rom_bin_start[128], sizeof(ii)); #else rc = flash_area_open(FLASH_AREA_IMAGE_1, &fa); assert(rc == 0); rc = flash_area_read(fa, 128, &ii, sizeof(ii)); assert(rc == 0); #endif assert(ii.magic == 0xC3ACC3AC); assert(ii.size_rom == cmac_rom_size); assert(ii.size_ram <= cmac_ram_size); /* Copy CMAC image to RAM */ #if MYNEWT_VAL(CMAC_IMAGE_SINGLE) memset(&_binary_cmac_ram_bin_start, 0xaa, cmac_ram_size); memcpy(&_binary_cmac_ram_bin_start, &_binary_cmac_rom_bin_start, ii.size_rom); #else memset(&_binary_cmac_ram_bin_start, 0xaa, cmac_ram_size); rc = flash_area_read(fa, 0, &_binary_cmac_ram_bin_start, ii.size_rom); assert(rc == 0); #endif /* Setup CMAC memory addresses */ MEMCTRL->CMI_CODE_BASE_REG = (uint32_t)&_binary_cmac_ram_bin_start; MEMCTRL->CMI_DATA_BASE_REG = MEMCTRL->CMI_CODE_BASE_REG + ii.offset_data; MEMCTRL->CMI_SHARED_BASE_REG = MEMCTRL->CMI_CODE_BASE_REG + ii.offset_shared; MEMCTRL->CMI_END_REG = MEMCTRL->CMI_CODE_BASE_REG + ii.size_ram - 1; /* Initialize shared memory */ cmac_shared_init(); trim = (struct cmac_trim *)&g_cmac_shared_data->trim; trim->rfcu_len = da1469x_trimv_group_read(6, trim->rfcu, ARRAY_SIZE(trim->rfcu)); trim->rfcu_mode1_len = da1469x_trimv_group_read(8, trim->rfcu_mode1, ARRAY_SIZE(trim->rfcu_mode1)); trim->rfcu_mode2_len = da1469x_trimv_group_read(10, trim->rfcu_mode2, ARRAY_SIZE(trim->rfcu_mode2)); trim->synth_len = da1469x_trimv_group_read(7, trim->synth, ARRAY_SIZE(trim->synth)); #if MYNEWT_VAL(CMAC_DEBUG_HOST_PRINT_ENABLE) cmac_host_print_trim("rfcu", trim->rfcu, trim->rfcu_len); cmac_host_print_trim("rfcu_mode1", trim->rfcu_mode1, trim->rfcu_mode1_len); cmac_host_print_trim("rfcu_mode2", trim->rfcu_mode2, trim->rfcu_mode2_len); cmac_host_print_trim("synth", trim->synth, trim->synth_len); #endif /* Release CMAC from reset and sync */ CRG_TOP->CLK_RADIO_REG &= ~CRG_TOP_CLK_RADIO_REG_CMAC_SYNCH_RESET_Msk; cmac_shared_sync(); da1469x_lpclk_register_cmac_cb(cmac_host_lpclk_cb); #if MYNEWT_VAL(CMAC_DEBUG_HOST_PRINT_ENABLE) && MYNEWT_VAL(CMAC_DEBUG_DATA_ENABLE) /* Trim values are calculated on RF init, so are valid after synced with CMAC */ console_printf("[CMAC] Calculated trim_val1: 1=0x%08x 2=0x%08x\n", (unsigned)g_cmac_shared_data->debug.trim_val1_tx_1, (unsigned)g_cmac_shared_data->debug.trim_val1_tx_2); console_printf("[CMAC] Calculated trim_val2: tx=0x%08x rx=0x%08x\n", (unsigned)g_cmac_shared_data->debug.trim_val2_tx, (unsigned)g_cmac_shared_data->debug.trim_val2_rx); #endif }