/* * Copyright (c) 1997-2015, Actions Semi Co., Inc. * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include "dsp_inner.h" #include #include #include static struct dsp_acts_data dsp_data __in_section_unique(DSP_SHARE_RAM); #include struct acts_ringbuf *dsp_dev_get_print_buffer(void); static void dsp_print_work(struct k_work *work); static struct acts_ringbuf debug_buff __in_section_unique(DSP_SHARE_RAM); static u8_t debug_data_buff[0x200] __in_section_unique(DSP_SHARE_RAM); K_DELAYED_WORK_DEFINE(print_work, dsp_print_work); #ifdef CONFIG_SOC_NO_PSRAM __in_section_unique(media.noinit.heap) #endif char hex_buf[0x200]; int dsp_print_all_buff(void) { int i = 0; int length; struct acts_ringbuf *debug_buf = &debug_buff; if(!debug_buf) return 0; length = acts_ringbuf_length(debug_buf); if(length > acts_ringbuf_size(debug_buf)) { acts_ringbuf_drop_all(&debug_buff); length = 0; } if(length >= sizeof(hex_buf)){ length = sizeof(hex_buf) - 2; } if(length){ //int index = 0; acts_ringbuf_get(debug_buf, &hex_buf[0], length); for(i = 0; i < length / 2; i++){ hex_buf[i] = hex_buf[2 * i]; } hex_buf[i] = 0; #ifdef CONFIG_DSP_DEBUG_PRINT printk("%s\n",hex_buf); #endif return acts_ringbuf_length(debug_buf); } return 0; } static void dsp_print_work(struct k_work *work) { while(dsp_print_all_buff() > 0) { ; } if (dsp_data.pm_status != DSP_STATUS_POWEROFF){ k_delayed_work_submit(&print_work, K_MSEC(2)); } } static void dsp_acts_isr(void *arg); static void dsp_acts_irq_enable(void); static void dsp_acts_irq_disable(void); static void dsp_acts_irq1_disable(void); void dsp_acts_set_irq1_callback( dsp_acts_irq_callback cb, void *cb_data); DEVICE_DECLARE(dsp_acts); int wait_hw_idle_timeout(int usec_to_wait) { do { if (dsp_check_hw_idle()) return 0; if (usec_to_wait-- <= 0) break; k_busy_wait(1); } while (1); return -EAGAIN; } static int dsp_acts_suspend(struct device *dev) { struct dsp_acts_data *dsp_data = dev->data; if (dsp_data->pm_status != DSP_STATUS_POWERON) return 0; /* already idle ? */ if (!get_hw_idle()) { struct dsp_message message = { .id = DSP_MSG_SUSPEND, }; k_sem_reset(&dsp_data->msg_sem); if (dsp_acts_send_message(dev, &message)) return -EBUSY; /* wait message DSP_MSG_BOOTUP */ if (k_sem_take(&dsp_data->msg_sem, SYS_TIMEOUT_MS(20))) { SYS_LOG_ERR("dsp image <%s> busy\n", dsp_data->images[DSP_IMAGE_MAIN].name); return -EBUSY; } /* make sure hardware is really idle */ if (wait_hw_idle_timeout(10)) SYS_LOG_ERR("DSP wait idle signal timeout\n"); } dsp_do_wait(); acts_clock_peripheral_disable(CLOCK_ID_DSP); acts_clock_peripheral_disable(CLOCK_ID_AUDDSPTIMER); dsp_data->pm_status = DSP_STATUS_SUSPENDED; SYS_LOG_ERR("DSP suspended\n"); return 0; } static int dsp_acts_resume(struct device *dev) { struct dsp_acts_data *dsp_data = dev->data; struct dsp_message message = { .id = DSP_MSG_RESUME, }; if (dsp_data->pm_status != DSP_STATUS_SUSPENDED) return 0; acts_clock_peripheral_enable(CLOCK_ID_DSP); acts_clock_peripheral_enable(CLOCK_ID_AUDDSPTIMER); dsp_undo_wait(); if (dsp_acts_send_message(dev, &message)) { SYS_LOG_ERR("DSP resume failed\n"); return -EFAULT; } dsp_data->pm_status = DSP_STATUS_POWERON; SYS_LOG_ERR("DSP resumed\n"); return 0; } static int dsp_acts_kick(struct device *dev, uint32_t owner, uint32_t event, uint32_t params) { struct dsp_acts_data *dsp_data = dev->data; const struct dsp_acts_config *dsp_cfg = dev->config; struct dsp_protocol_userinfo *dsp_userinfo = dsp_cfg->dsp_userinfo; struct dsp_message message = { .id = DSP_MSG_KICK, .owner = owner, .param1 = event, .param2 = params, }; if (dsp_data->pm_status != DSP_STATUS_POWERON) return -EACCES; if (dsp_userinfo->task_state != DSP_TASK_SUSPENDED) return -EINPROGRESS; return dsp_acts_send_message(dev, &message); } static int dsp_acts_request_mem(struct device *dev, int type) { return dsp_soc_request_mem(type); } static int dsp_acts_release_mem(struct device *dev, int type) { return dsp_soc_release_mem(type); } /* synchronize cpu and dsp clocks, both base hw timer */ static int dsp_acts_synchronize_clock(void) { uint32_t flags; uint16_t sync_clock_status, sync_clock_status_last; uint32_t sync_clock_cycles_cpu, sync_clock_cycles_cpu_end; uint32_t sync_clock_cycles_cpu_diff, sync_clock_cycles_cpu_diff_last = 0xffff0000; uint32_t sync_clock_cycles_dsp; uint32_t sync_clock_cycles_offset; uint32_t try_count = 0, match_count = 0; uint32_t jitter_cycles_threshold = 16; printk("synchronize cpu and dsp clocks start\n"); flags = irq_lock(); sync_clock_cycles_cpu = k_cycle_get_32(); *(volatile uint32_t *)(DSP_SYNC_CLOCK_CYCLES_BASE) = sync_clock_cycles_cpu; *(volatile uint16_t *)(DSP_SYNC_CLOCK_STATUS_BASE) = DSP_SYNC_CLOCK_START; sync_clock_status_last = DSP_SYNC_CLOCK_START; while (1) { sync_clock_status = *(volatile uint16_t *)(DSP_SYNC_CLOCK_STATUS_BASE); if (sync_clock_status_last == DSP_SYNC_CLOCK_START) { if (sync_clock_status == DSP_SYNC_CLOCK_REPLY) { sync_clock_cycles_dsp = *(volatile uint32_t *)(DSP_SYNC_CLOCK_CYCLES_BASE); sync_clock_cycles_cpu_end = k_cycle_get_32(); sync_clock_cycles_cpu_diff = sync_clock_cycles_cpu_end - sync_clock_cycles_cpu; if ((sync_clock_cycles_cpu_diff >= sync_clock_cycles_cpu_diff_last && sync_clock_cycles_cpu_diff <= sync_clock_cycles_cpu_diff_last + jitter_cycles_threshold) || (sync_clock_cycles_cpu_diff <= sync_clock_cycles_cpu_diff_last && sync_clock_cycles_cpu_diff + jitter_cycles_threshold >= sync_clock_cycles_cpu_diff_last)) { match_count++; } else { match_count = 0; } try_count++; sync_clock_cycles_cpu_diff_last = sync_clock_cycles_cpu_diff; if (match_count >= 5) { sync_clock_cycles_offset = sync_clock_cycles_cpu - sync_clock_cycles_dsp; *(volatile uint32_t *)(DSP_SYNC_CLOCK_CYCLES_BASE) = sync_clock_cycles_offset; *(volatile uint16_t *)(DSP_SYNC_CLOCK_STATUS_BASE) = DSP_SYNC_CLOCK_OFFSET; sync_clock_status_last = DSP_SYNC_CLOCK_OFFSET; } else { sync_clock_cycles_cpu = k_cycle_get_32(); *(volatile uint32_t *)(DSP_SYNC_CLOCK_CYCLES_BASE) = sync_clock_cycles_cpu; *(volatile uint16_t *)(DSP_SYNC_CLOCK_STATUS_BASE) = DSP_SYNC_CLOCK_START; } if (try_count > 20) { try_count = 0; match_count = 0; jitter_cycles_threshold += 16; } } } else { if (sync_clock_status == DSP_SYNC_CLOCK_NULL) { break; } } } irq_unlock(flags); printk("sync clock : 0x%x,%d,%d\n", k_cycle_get_32(), sync_clock_cycles_cpu_diff_last, jitter_cycles_threshold); return 0; } static int dsp_acts_power_on(struct device *dev, void *cmdbuf) { struct dsp_acts_data *dsp_data = dev->data; const struct dsp_acts_config *dsp_cfg = dev->config; struct dsp_protocol_userinfo *dsp_userinfo = dsp_cfg->dsp_userinfo; int i; if (dsp_data->pm_status != DSP_STATUS_POWEROFF) return 0; if (dsp_data->images[DSP_IMAGE_MAIN].size == 0) { SYS_LOG_ERR("%s: no image loaded\n", __func__); return -EFAULT; } if (cmdbuf == NULL) { SYS_LOG_ERR("%s: must assign a command buffer\n", __func__); return -EINVAL; } memset((void *)DSP_MAILBOX_REG_BASE, 0, 0x80); dsp_init_clk(); #ifdef CONFIG_DSP_DEBUG_PRINT acts_ringbuf_init(&debug_buff, debug_data_buff, sizeof(debug_data_buff)); debug_buff.dsp_ptr = mcu_to_dsp_address(debug_buff.cpu_ptr, 0); k_delayed_work_submit(&print_work, K_MSEC(2)); #endif /* enable dsp clock*/ acts_clock_peripheral_enable(CLOCK_ID_DSP); acts_clock_peripheral_enable(CLOCK_ID_AUDDSPTIMER); /* assert reset */ acts_reset_peripheral_assert(RESET_ID_DSP); /* set bootargs */ dsp_data->bootargs.command_buffer = mcu_to_dsp_address((uint32_t)cmdbuf, DATA_ADDR); dsp_data->bootargs.sub_entry_point = dsp_data->images[1].entry_point; /* assert dsp wait signal */ dsp_do_wait(); dsp_acts_request_mem(dev, 0); /* intialize shared registers */ dsp_cfg->dsp_mailbox->msg = MAILBOX_MSG(DSP_MSG_NULL, MSG_FLAG_ACK) ; dsp_cfg->dsp_mailbox->owner = 0; dsp_cfg->cpu_mailbox->msg = MAILBOX_MSG(DSP_MSG_NULL, MSG_FLAG_ACK) ; dsp_cfg->cpu_mailbox->owner = 0; dsp_userinfo->task_state = DSP_TASK_PRESTART; dsp_userinfo->error_code = DSP_NO_ERROR; /* set dsp vector_address */ //set_dsp_vector_addr((unsigned int)DSP_ADDR); dsp_data->pm_status = DSP_STATUS_POWERON; /* clear all pending */ clear_dsp_all_irq_pending(); /* enable dsp irq */ dsp_acts_irq_enable(); /* deassert dsp wait signal */ dsp_undo_wait(); k_sem_reset(&dsp_data->msg_sem); /* deassert reset */ acts_reset_peripheral_deassert(RESET_ID_DSP); /* wait message DSP_MSG_STATE_CHANGED.DSP_TASK_STARTED */ if (k_sem_take(&dsp_data->msg_sem, SYS_TIMEOUT_MS(100))) { SYS_LOG_ERR("dsp image <%s> cannot boot up\n", dsp_data->images[DSP_IMAGE_MAIN].name); dsp_dump_info(); goto cleanup; } if (dsp_data->need_sync_clock == 'C') { dsp_acts_synchronize_clock(); } /* FIXME: convert userinfo address from dsp to cpu here, since * dsp will never touch it again. */ dsp_userinfo->func_table = dsp_to_mcu_address(dsp_userinfo->func_table, DATA_ADDR); for (i = 0; i < dsp_userinfo->func_size; i++) { volatile uint32_t addr = dsp_userinfo->func_table + i * 4; if (addr > 0) /* NULL, no information provided */ sys_write32(dsp_to_mcu_address(sys_read32(addr), DATA_ADDR), addr); } //SYS_LOG_ERR("DSP power on %d %p\n", dsp_userinfo->func_size, dsp_userinfo->func_table); return 0; cleanup: dsp_acts_irq_disable(); acts_clock_peripheral_disable(CLOCK_ID_DSP); acts_clock_peripheral_disable(CLOCK_ID_AUDDSPTIMER); acts_reset_peripheral_assert(RESET_ID_DSP); return -ETIMEDOUT; } static int dsp_acts_power_off(struct device *dev) { const struct dsp_acts_config *dsp_cfg = dev->config; struct dsp_acts_data *dsp_data = dev->data; if (dsp_data->pm_status == DSP_STATUS_POWEROFF) return 0; /* assert dsp wait signal */ dsp_do_wait(); /* disable dsp irq */ dsp_acts_irq_disable(); /* disable dsp irq 1 */ dsp_acts_irq1_disable(); /* assert reset dsp module */ acts_reset_peripheral_assert(RESET_ID_DSP); /* disable dsp clock*/ acts_clock_peripheral_disable(CLOCK_ID_DSP); acts_clock_peripheral_disable(CLOCK_ID_AUDDSPTIMER); /* deassert dsp wait signal */ dsp_undo_wait(); /* clear page mapping */ clear_dsp_pageaddr(); dsp_cfg->dsp_userinfo->task_state = DSP_TASK_DEAD; dsp_data->pm_status = DSP_STATUS_POWEROFF; dsp_print_all_buff(); acts_ringbuf_drop_all(&debug_buff); #ifdef CONFIG_DSP_DEBUG_PRINT k_delayed_work_cancel(&print_work); #endif SYS_LOG_INF("DSP power off\n"); return 0; } static int acts_request_userinfo(struct device *dev, int request, void *info) { const struct dsp_acts_config *dsp_cfg = dev->config; struct dsp_protocol_userinfo *dsp_userinfo = dsp_cfg->dsp_userinfo; union { struct dsp_request_session *ssn; struct dsp_request_function *func; } req_info; switch (request) { case DSP_REQUEST_TASK_STATE: *(int *)info = dsp_userinfo->task_state; break; case DSP_REQUEST_ERROR_CODE: *(int *)info = dsp_userinfo->error_code; break; case DSP_REQUEST_SESSION_INFO: req_info.ssn = info; req_info.ssn->func_enabled = dsp_userinfo->func_enabled; req_info.ssn->func_runnable = dsp_userinfo->func_runnable; req_info.ssn->func_counter = dsp_userinfo->func_counter; req_info.ssn->info = (void *)dsp_to_mcu_address(dsp_userinfo->ssn_info, DATA_ADDR); break; case DSP_REQUEST_FUNCTION_INFO: req_info.func = info; if (req_info.func->id < dsp_userinfo->func_size) req_info.func->info = (void *)sys_read32(dsp_userinfo->func_table + req_info.func->id * 4); break; case DSP_REQUEST_USER_DEFINED: { uint32_t index = *(uint32_t*)info; uint32_t debug_info = *(volatile uint32_t*)(DSP_DEBUG_REGION_REGISTER_BASE + 4 * index); *(uint32_t*)info = debug_info; break; } default: return -EINVAL; } return 0; } static void dsp_acts_isr(void *arg) { /* clear irq pending bits */ clear_dsp_irq_pending(IRQ_ID_DSP); dsp_acts_recv_message(arg); } static void dsp_acts_isr1(void *arg) { struct device *dev = (struct device *)arg; struct dsp_acts_data *dsp_data = dev->data; /* clear irq pending bits */ clear_dsp_irq1_pending(IRQ_ID_DSP1); if (dsp_data->cb) { dsp_data->cb(dsp_data->cb_data, 1, NULL); } } void dsp_acts_set_irq1_callback(dsp_acts_irq_callback cb, void *cb_data) { struct device *dev = (struct device *)DEVICE_GET(dsp_acts); if (!dev) { SYS_LOG_ERR("dev handle NULL"); return; } struct dsp_acts_data *dsp_data = dev->data; if (dsp_data) { dsp_data->cb = cb; dsp_data->cb_data = cb_data; } } static int dsp_acts_init(const struct device *dev) { const struct dsp_acts_config *dsp_cfg = dev->config; struct dsp_acts_data *dsp_data = dev->data; acts_ringbuf_init(&debug_buff, debug_data_buff, sizeof(debug_data_buff)); debug_buff.dsp_ptr = mcu_to_dsp_address(debug_buff.cpu_ptr, 0); dsp_data->bootargs.debug_buf = mcu_to_dsp_address(POINTER_TO_UINT(&debug_buff), DATA_ADDR); dsp_cfg->dsp_userinfo->task_state = DSP_TASK_DEAD; k_sem_init(&dsp_data->msg_sem, 0, 1); k_mutex_init(&dsp_data->msg_mutex); dsp_data->pm_status = DSP_STATUS_POWEROFF; memset(dsp_cfg->dsp_mailbox, 0, sizeof(struct dsp_protocol_mailbox)); memset(dsp_cfg->cpu_mailbox, 0, sizeof(struct dsp_protocol_mailbox)); memset(dsp_cfg->dsp_userinfo, 0, sizeof(struct dsp_protocol_userinfo)); return 0; } static const struct dsp_acts_config dsp_acts_config = { .dsp_mailbox = (struct dsp_protocol_mailbox *)DSP_M2D_MAILBOX_REGISTER_BASE, .cpu_mailbox = (struct dsp_protocol_mailbox *)DSP_D2M_MAILBOX_REGISTER_BASE, .dsp_userinfo = (struct dsp_protocol_userinfo *)DSP_USER_REGION_REGISTER_BASE, }; const struct dsp_driver_api dsp_drv_api = { .poweron = dsp_acts_power_on, .poweroff = dsp_acts_power_off, .suspend = dsp_acts_suspend, .resume = dsp_acts_resume, .kick = dsp_acts_kick, .register_message_handler = dsp_acts_register_message_handler, .unregister_message_handler = dsp_acts_unregister_message_handler, .request_image = dsp_acts_request_image, .release_image = dsp_acts_release_image, .request_mem = dsp_acts_request_mem, .release_mem = dsp_acts_release_mem, .send_message = dsp_acts_send_message, .request_userinfo = acts_request_userinfo, }; DEVICE_DEFINE(dsp_acts, CONFIG_DSP_NAME, dsp_acts_init, NULL, &dsp_data, &dsp_acts_config, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &dsp_drv_api); static void dsp_acts_irq_enable(void) { IRQ_CONNECT(IRQ_ID_DSP, CONFIG_DSP_IRQ_PRI, dsp_acts_isr, DEVICE_GET(dsp_acts), 0); irq_enable(IRQ_ID_DSP); IRQ_CONNECT(IRQ_ID_DSP1, CONFIG_DSP_IRQ_PRI, dsp_acts_isr1, DEVICE_GET(dsp_acts), 0); irq_enable(IRQ_ID_DSP1); } static void dsp_acts_irq_disable(void) { irq_disable(IRQ_ID_DSP); } static void dsp_acts_irq1_disable(void) { irq_disable(IRQ_ID_DSP1); } #define DEV_DATA(dev) \ ((struct dsp_acts_data * const)(dev)->data) struct acts_ringbuf *dsp_dev_get_print_buffer(void) { struct dsp_acts_data *data = DEV_DATA(DEVICE_GET(dsp_acts)); if(!data->bootargs.debug_buf){ return NULL; } return (struct acts_ringbuf *)dsp_to_mcu_address(data->bootargs.debug_buf,DATA_ADDR); }