blob: ee7eed01f81aeb600688c421b783b672ff5535a4 [file] [log] [blame]
/*
* (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
#include <topaz_reset.h>
/****************************************************/
/* 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;
}
inline static void switch_muc(int enable)
{
topaz_set_reset_vec(enable, RUBY_SYS_CTL_RESET_MUC_ALL);
}
inline static void switch_dsp(int enable)
{
topaz_set_reset_vec(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