/* * (C) Copyright 2010 Quantenna Communications Inc. * * See file CREDITS for list of people who contributed to this * project. * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include "ruby.h" #ifdef CONFIG_CMD_UC /****************************************************/ /* Helper macros */ #define MUC_ENTRY_CODE (&__muc_start_begin) #define MUC_ENTRY_CODE_SIZE (&__muc_start_end - &__muc_start_begin) #define DSP_ENTRY_CODE (&__dsp_start_begin) #define DSP_ENTRY_CODE_SIZE (&__dsp_start_end - &__dsp_start_begin) #define UC_BSS ((unsigned long)(&__uc_bss_begin)) #define UC_BSS_SIZE (&__uc_bss_end - &__uc_bss_begin) /* MuC/DSP sections. * MuC/DSP function can call only other MuC/DSP functions - * at least until we have memmap flip feature (unfortunately). */ #define UC_TEXT_SECTION __attribute__((section(UC_TEXT_SECTION_NAME))) #define UC_DATA_SECTION __attribute__((section(UC_DATA_SECTION_NAME))) #define UC_BSS_SECTION __attribute__((section(UC_BSS_SECTION_NAME))) /****************************************************/ /* Defined in assembler - MuC code entry point */ extern char __muc_start_begin; extern char __muc_start_end; /* Defined in assembler - DSP code entry point */ extern char __dsp_start_begin; extern char __dsp_start_end; /* Defined in assembler - MuC/DSP BSS section */ extern char __uc_bss_begin; extern char __uc_bss_end; /****************************************************/ UC_BSS_SECTION volatile unsigned long uc_trace_num = 0; UC_BSS_SECTION volatile unsigned long uc_trace_done = 0; /****************************************************/ static int trigger_irq(unsigned long mask, unsigned long reg, int sec) { int ret = -1; const unsigned long irq_num = 0x0; int i; writel(1 << irq_num, mask); writel(1 << irq_num, reg); for(i = 0; i < sec; ++i) { unsigned long stamp = get_timer(0); while(1) { if(!readl(reg)) { ret = 0; goto done; } else if (get_timer(stamp) > CONFIG_SYS_HZ) { break; } } } done: writel(0x0, mask); return ret; } static void switch_uc(int enable, unsigned long reset_val) { const unsigned long reset = reset_val; writel(reset, RUBY_SYS_CTL_CPU_VEC_MASK); writel(enable ? reset : 0, RUBY_SYS_CTL_CPU_VEC); writel(0, RUBY_SYS_CTL_CPU_VEC_MASK); } inline static void switch_muc(int enable) { switch_uc(enable, RUBY_SYS_CTL_RESET_MUC_ALL); } inline static void switch_dsp(int enable) { switch_uc(enable, RUBY_SYS_CTL_RESET_DSP_ALL); } static void prepare_uc_code(void *code_begin, unsigned long code_size) { /* Copy instructions to place from which MuC/DSP start execution */ memmove(UC_ENTRY_SLOT, code_begin, code_size); flush_cache( virt_to_phys(UC_ENTRY_SLOT), virt_to_phys(UC_ENTRY_SLOT + code_size)); /* Cleanup BSS section */ memset(bus_to_virt(UC_BSS), 0, UC_BSS_SIZE); flush_cache( virt_to_phys(bus_to_virt(UC_BSS)), virt_to_phys(bus_to_virt(UC_BSS)) + UC_BSS_SIZE); } static int set_muc_start_addr(void *addr) { /* Check that we have correct address. */ if ((unsigned long)addr & (RUBY_BIT(RUBY_SYS_CTL_MUC_REMAP_SHIFT) - 1)) { return -1; } /* Tells MuC from which address start execution */ writel(RUBY_SYS_CTL_MUC_REMAP_VAL(virt_to_bus(addr)), RUBY_SYS_CTL_MUC_REMAP); return 0; } static int prepare_muc_code(void) { prepare_uc_code(MUC_ENTRY_CODE, MUC_ENTRY_CODE_SIZE); return set_muc_start_addr(UC_ENTRY_SLOT); } static int set_dsp_start_addr(void *addr) { /* Check that we have correct address. */ if ((unsigned long)addr & (RUBY_BIT(RUBY_SYS_CTL_DSP_REMAP_SHIFT) - 1)) { return -1; } /* Tells DSP from which address start execution */ writel(RUBY_SYS_CTL_DSP_REMAP_VAL(virt_to_bus(addr)), RUBY_SYS_CTL_DSP_REMAP); return 0; } static int prepare_dsp_code(void) { prepare_uc_code(DSP_ENTRY_CODE, DSP_ENTRY_CODE_SIZE); return set_dsp_start_addr(UC_ENTRY_SLOT); } static void test_uc_trace_prepare(void) { writel(0, bus_to_virt((unsigned long)&uc_trace_num)); writel(0, bus_to_virt((unsigned long)&uc_trace_done)); } static unsigned long get_test_uc_trace_num(void) { return readl(bus_to_virt((unsigned long)&uc_trace_num)); } static unsigned long is_test_uc_trace_done(void) { return readl(bus_to_virt((unsigned long)&uc_trace_done)); } static int test_uc_wait(int sec) { int ret = -1; int i; for(i = 0; i < sec; ++i) { unsigned long stamp = get_timer(0); while(1) { if(is_test_uc_trace_done()) { ret = 0; printf("Success: execution time: %d sec, %lu ticks\n", (int)i, (unsigned long)get_timer(stamp)); goto done; } else if (get_timer(stamp) > CONFIG_SYS_HZ) { break; } } } done: printf("uC trace number: %u\n", (unsigned)get_test_uc_trace_num()); if (ret) { printf("Failure: %d\n", ret); } return ret; } static int test_uc_irq(unsigned long mask, unsigned long irq, int sec, int attempts) { int ret = 0; int i; for (i = 0; i < attempts; ++i) { ret = trigger_irq(mask, irq, sec); if (ret) { ret = -1; printf("uC irq triggering failed: ret=%d num=%u\n", ret, (unsigned)get_test_uc_trace_num()); break; } printf("uC trace number after IRQ: %u\n", (unsigned)get_test_uc_trace_num()); } return ret; } static int test_muc(int sec) { int ret = 0; switch_muc(0); udelay(100000); ret = prepare_muc_code(); if (ret) { printf("MuC code preparation failed: ret=%d\n", ret); return -1; } test_uc_trace_prepare(); switch_muc(1); ret = test_uc_wait(sec); if (ret) { printf("MuC wait failed: ret=%d\n", ret); return -2; } ret = test_uc_irq(RUBY_SYS_CTL_L2M_INT_MASK, RUBY_SYS_CTL_L2M_INT, sec, 10); if (ret) { printf("MuC L2M irq triggering failed: ret=%d\n", ret); return -3; } printf("MuC test: SUCCESS!\n"); return 0; } static int test_dsp(int sec) { int ret = 0; switch_dsp(0); udelay(100000); ret = prepare_dsp_code(); if (ret) { printf("DSP code preparation failed: ret=%d\n", ret); return -1; } test_uc_trace_prepare(); switch_dsp(1); ret = test_uc_wait(sec); if (ret) { printf("DSP wait failed: ret=%d\n", ret); return -2; } ret = test_uc_irq(RUBY_SYS_CTL_L2D_INT_MASK, RUBY_SYS_CTL_L2D_INT, sec, 10); if (ret) { printf("DSP L2D irq triggering failed: ret=%d\n", ret); return -3; } printf("DSP test: SUCCESS!\n"); return 0; } static int run_muc(void *addr) { int ret = 0; switch_muc(0); udelay(100000); ret = set_muc_start_addr(addr); if (ret) { printf("Start address is bad: ret=%d\n", ret); } else { switch_muc(1); } return 0; } static int run_dsp(void *addr) { int ret = 0; switch_dsp(0); udelay(100000); ret = set_dsp_start_addr(addr); if (ret) { printf("Start address is bad: ret=%d\n", ret); } else { switch_dsp(1); } return 0; } /****************************************************/ static int do_uc_test(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { int ret = 0; if (argc != 2) { ret = -1; } else if (!strcmp(argv[1], "dsp")) { ret = test_dsp(5/*sec timeout*/); } else if (!strcmp(argv[1], "muc")) { ret = test_muc(5/*sec timeout*/); } else { ret = -2; } return ret; } static int do_uc_run(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { int ret = 0; if (argc != 3) { ret = -1; } else { unsigned long addr = simple_strtoul(argv[2], NULL, 0); printf("Run code from 0x%lx\n", addr); if (!strcmp(argv[1], "dsp")) { ret = run_dsp((void*)addr); } else if (!strcmp(argv[1], "muc")) { ret = run_muc((void*)addr); } else { ret = -2; } } return ret; } U_BOOT_CMD(uc_test, 2, 0, do_uc_test, "MuC/DSP test", "uc_test dsp|muc - perform test (beware, it changes uboot in-RAM image!)\n" ); U_BOOT_CMD(uc_run, 3, 0, do_uc_run, "MuC/DSP code run", "uc_run dsp|muc addr - run code\n" ); /****************************************************/ #endif // #ifdef CONFIG_CMD_UC