/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * * Copyright (C) 2018 by Solomon Peachy * Copyright (C) 2020 by William Wilgus * * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include "system.h" #include "cpu.h" #include #include #include "lcd.h" #include "kernel.h" #include "font.h" #include "button.h" #include "timefuncs.h" #ifndef BOOTLOADER #include "action.h" #include "list.h" static bool is_done = false; static int line = 0; static int maxlines = 0; /* * Clock Generation Module */ #define DBG_FREQ_FMT "%s:%3d.%02d %cHz" /*IDSTR, 000 HZ INT, 00HZ FRAC, _/K/M */ #define TO_MHZ(x) ((x)/1000000), ((x)%1000000)/10000, 'M' #define TO_KHZ(x) ((x)/1000), ((x)%1000)/10, 'K' #define DEBUG_CANCEL ACTION_STD_CANCEL #define DEBUG_NEXT ACTION_STD_NEXT #define DEBUG_LEFT_JUSTIFY ACTION_STD_OK #define DEBUG_LEFT_SCROLL ACTION_STD_MENU /* if the possiblity exists to divide by zero protect with this macro */ #define DIV_FINITE(dividend, divisor) ((divisor == 0)? divisor : dividend/divisor) #define ON "Enabled" #define OFF "Disabled" #define INSERTED "Inserted" #define REMOVED "Removed" #define OFF "Disabled" #define STOPPED "Stopped" #define RUNNING "Running" static bool dbg_btn(bool *done, int *x) { bool cont = !*done; if (cont) { int button = get_action(CONTEXT_STD,HZ/10); switch(button) { case DEBUG_CANCEL: *done = true; case DEBUG_NEXT: cont = false; case DEBUG_LEFT_JUSTIFY: if (x){(*x) = 0;} sleep(HZ/5); break; case DEBUG_LEFT_SCROLL: if(x){(*x)--;} break; } } return cont; } static bool dbg_btn_update(bool *done, int *x) { bool cont = !*done; if (cont) { lcd_update(); cont = dbg_btn(done, x); } lcd_clear_display(); return cont; } static inline unsigned int read_cp0_15 (void) { /* CPUID Cp0 Register 15 Select 0 */ unsigned int cp_val; asm volatile("mfc0 %0, $15\n" : "=r" (cp_val)); return (cp_val); } static bool display_clocks(void) { unsigned int cppcr0 = REG_CPM_CPPCR0; /* PLL Control Register */ unsigned int cppcr1 = REG_CPM_CPPCR1; /* PLL Control Register */ unsigned int div[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; unsigned int od[4] = {1, 2, 4, 8}; int x = 0; int cur_item = -1; bool is_last_item = false; while(!is_done) { lcd_clear_display(); if (is_last_item || cur_item < 0) cur_item = 0; else if (cur_item >= 0) cur_item += maxlines; is_last_item = false; while(dbg_btn(&is_done, &x)) { lcd_clear_display(); line = 0; switch(cur_item) { case 0: lcd_putsf(x, line++, "[%s]", "Clocks"); case 1: lcd_putsf(x, line++, "CPPCR0:0x%08x", cppcr0); case 2: if (cppcr0 & CPPCR0_PLLEN) { lcd_putsf(x, line++, DBG_FREQ_FMT, "PLL0 ", TO_MHZ(__cpm_get_pllout())); lcd_putsf(x, line++, "m:n:o :%d:%d:%d", __cpm_get_pllm(), __cpm_get_plln(), od[__cpm_get_pllod()] ); } else if (cur_item == 2) cur_item++; case 3: lcd_putsf(x, line++, "CPPCR1:0x%08x", cppcr1); case 4: if (cppcr1 & CPPCR1_PLL1EN) { lcd_putsf(x, line++, DBG_FREQ_FMT, "PLL1 ", TO_MHZ(__cpm_get_pll1out())); lcd_putsf(x, line++, "m:n:o :%d:%d:%d", __cpm_get_pll1m(), __cpm_get_pll1n(), od[__cpm_get_pll1od()] ); } else if (cur_item == 3) cur_item++; case 5: lcd_putsf(x, line++, "C:H:M:P:%d:%d:%d:%d", div[__cpm_get_cdiv()], div[__cpm_get_hdiv()], div[__cpm_get_mdiv()], div[__cpm_get_pdiv()] ); case 6: lcd_putsf(x, line++, "I:P:M : %d:%d:%d", __cpm_get_i2sdiv()+1, __cpm_get_pixdiv()+1, __cpm_get_mscdiv()+1 ); case 7: lcd_putsf(x, line++, DBG_FREQ_FMT, "CCLK ", TO_MHZ(__cpm_get_cclk())); case 8: lcd_putsf(x, line++, DBG_FREQ_FMT, "HCLK ", TO_MHZ(__cpm_get_hclk())); case 9: lcd_putsf(x, line++, DBG_FREQ_FMT, "MCLK ", TO_MHZ(__cpm_get_mclk())); case 10: lcd_putsf(x, line++, DBG_FREQ_FMT, "PCLK ", TO_MHZ(__cpm_get_pclk())); case 11: lcd_putsf(x, line++, DBG_FREQ_FMT, "PIXCLK ", TO_KHZ(__cpm_get_pixclk())); case 12: lcd_putsf(x, line++, DBG_FREQ_FMT, "I2SCLK ", TO_MHZ(__cpm_get_i2sclk())); case 13: lcd_putsf(x, line++, DBG_FREQ_FMT, "MSCCLK ", TO_MHZ(__cpm_get_mscclk())); case 14: lcd_putsf(x, line++, DBG_FREQ_FMT, "XTALCLK ", TO_MHZ(__cpm_get_extalclk())); case 15: lcd_putsf(x, line++, DBG_FREQ_FMT, "RTCCLK ", TO_KHZ(__cpm_get_rtcclk())); default: if (line <= maxlines + 1) is_last_item = true; } lcd_update(); } } return true; } static bool display_enabled_clocks(void) { unsigned long lcr = REG_CPM_LCR; unsigned long clkgr0 = REG_CPM_CLKGR0; int x = 0; int cur_item = -1; bool is_last_item = false; while(!is_done) { lcd_clear_display(); if (is_last_item || cur_item < 0) cur_item = 0; else if (cur_item >= 0) cur_item += maxlines; is_last_item = false; while(dbg_btn(&is_done, &x)) { lcd_clear_display(); line = 0; switch(cur_item) { case 0: lcd_putsf(x, line++, "[%s]", "Enabled Clocks"); case 1: lcd_putsf(x, line++, "Low Power Mode : %s", ((lcr & LCR_LPM_MASK) == LCR_LPM_IDLE) ? "IDLE" : (((lcr & LCR_LPM_MASK) == LCR_LPM_SLEEP) ? "SLEEP" : "HIBERNATE") ); case 2: lcd_putsf(x, line++, "Doze Mode : %s", (lcr & LCR_DOZE) ? "ON" : "OFF"); case 3: if (lcr & LCR_DOZE) lcd_putsf(x, line++, " duty : %d", (int)((lcr & LCR_DUTY_MASK) >> LCR_DUTY_LSB)); else if (cur_item == 2) cur_item++; case 4: lcd_putsf(x, line++, "IPU : %s", (clkgr0 & CLKGR0_IPU) ? STOPPED : RUNNING); case 5: lcd_putsf(x, line++, "DMAC : %s", (clkgr0 & CLKGR0_DMAC) ? STOPPED : RUNNING); case 6: lcd_putsf(x, line++, "UHC : %s", (clkgr0 & CLKGR0_UHC) ? STOPPED : RUNNING); case 7: lcd_putsf(x, line++, "LCD : %s", (clkgr0 & CLKGR0_LCD) ? STOPPED : RUNNING); case 8: lcd_putsf(x, line++, "CIM : %s", (clkgr0 & CLKGR0_CIM) ? STOPPED : RUNNING); case 9: lcd_putsf(x, line++, "SADC : %s", (clkgr0 & CLKGR0_SADC) ? STOPPED : RUNNING); case 10: lcd_putsf(x, line++, "MSC0 : %s", (clkgr0 & CLKGR0_MSC0) ? STOPPED : RUNNING); case 11: lcd_putsf(x, line++, "MSC1 : %s", (clkgr0 & CLKGR0_MSC1) ? STOPPED : RUNNING); case 12: lcd_putsf(x, line++, "MSC2 : %s", (clkgr0 & CLKGR0_MSC2) ? STOPPED : RUNNING); case 13: lcd_putsf(x, line++, "AIC : %s", (clkgr0 & CLKGR0_AIC) ? STOPPED : RUNNING); case 14: lcd_putsf(x, line++, "SSI1 : %s", (clkgr0 & CLKGR0_SSI1) ? STOPPED : RUNNING); case 15: lcd_putsf(x, line++, "SSI2 : %s", (clkgr0 & CLKGR0_SSI2) ? STOPPED : RUNNING); case 16: lcd_putsf(x, line++, "I2C0 : %s", (clkgr0 & CLKGR0_I2C0) ? STOPPED : RUNNING); case 17: lcd_putsf(x, line++, "I2C1 : %s", (clkgr0 & CLKGR0_I2C1) ? STOPPED : RUNNING); case 18: lcd_putsf(x, line++, "UART1 : %s", (clkgr0 & CLKGR0_UART1) ? STOPPED : RUNNING); case 19: lcd_putsf(x, line++, "UART0 : %s", (clkgr0 & CLKGR0_UART0) ? STOPPED : RUNNING); default: if (line <= maxlines + 1) is_last_item = true; } lcd_update(); } } return true; } bool dbg_ports(void) { #if CONFIG_CPU == JZ4760B int line, i, j, cur; int x = 0; const int last_port = 5; bool done = false; long data, dir; long fun, intr; long lvl; lcd_clear_display(); lcd_setfont(FONT_SYSFIXED); while(!done) { i = 0; while(dbg_btn_update(&done, &x)) { i %= last_port; /*PORT: A B C D E F HEADPHONE/LINEOUT */ while(dbg_btn_update(&done, &x)) { line = 0; lcd_puts(x, line++, "[GPIO Vals and Dirs]"); for (j = i; j < i + 2; j++) { if (j < last_port) { cur = j % last_port; dir = REG_GPIO_PXDIR(cur); data = REG_GPIO_PXDAT(cur); fun = REG_GPIO_PXFUN(cur); intr = REG_GPIO_PXIM(cur); lvl = REG_GPIO_PXPIN(cur); lcd_putsf(x, line++, "[%s%c]: %8x", "GPIO", 'A' + cur, lvl); lcd_putsf(x, line++, "DIR: %8x FUN: %8x", dir, fun); lcd_putsf(x, line++, "DAT: %8x INT: %8x", data, intr); line++; } else { lcd_puts(x, line++, "[Headphone Status]"); #if defined(HAVE_HEADPHONE_DETECTION) lcd_putsf(x, line++, "HP: %s", headphones_inserted() ? INSERTED:REMOVED); #endif #if defined(HAVE_LINEOUT_DETECTION) lcd_putsf(x, line++, "LO: %s", lineout_inserted() ? INSERTED:REMOVED); #endif } } } i++; } } lcd_setfont(FONT_UI); #endif /* CONFIG_CPU ==JZ4760B */ return false; } #endif #ifdef BOOTLOADER #define WITH_SERIAL #endif #ifdef WITH_SERIAL void serial_putc (const char c) { volatile u8 *uart_lsr = (volatile u8 *)(CFG_UART_BASE + OFF_LSR); volatile u8 *uart_tdr = (volatile u8 *)(CFG_UART_BASE + OFF_TDR); if (c == '\n') serial_putc ('\r'); /* Wait for fifo to shift out some bytes */ while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) ); *uart_tdr = (u8)c; } void serial_puts (const char *s) { while (*s) { serial_putc (*s++); } } void serial_putsf(const char *format, ...) { static char printfbuf[256]; int len; unsigned char *ptr; va_list ap; va_start(ap, format); ptr = printfbuf; len = vsnprintf(ptr, sizeof(printfbuf), format, ap); va_end(ap); (void)len; serial_puts(ptr); serial_putc('\n'); } void serial_put_hex(unsigned int d) { char c[12]; int i; for(i = 0; i < 8;i++) { c[i] = (d >> ((7 - i) * 4)) & 0xf; if(c[i] < 10) c[i] += 0x30; else c[i] += (0x41 - 10); } c[8] = '\n'; c[9] = 0; serial_puts(c); } void serial_put_dec(unsigned int d) { char c[16]; int i; int j = 0; int x = d; while (x /= 10) j++; for (i = j; i >= 0; i--) { c[i] = d % 10; c[i] += 0x30; d /= 10; } c[j + 1] = '\n'; c[j + 2] = 0; serial_puts(c); } void serial_dump_data(unsigned char* data, int len) { int i; for(i=0; i>4) & 0xf; if(a < 10) a += 0x30; else a += (0x41 - 10); serial_putc( a ); a = (*data) & 0xf; if(a < 10) a += 0x30; else a += (0x41 - 10); serial_putc( a ); serial_putc( ' ' ); data++; } serial_putc( '\n' ); } #endif #ifndef BOOTLOADER static const struct { unsigned char *desc; /* string or ID */ bool (*function) (void); /* return true if USB was connected */ } hwinfo_items[] = { { "", NULL }, /*RTC*/ { "", NULL }, /*CPUID*/ { "", NULL }, /*IRQSTACKMAX*/ { "Clocks", display_clocks}, { "Enabled Clocks", display_enabled_clocks}, { "", NULL}, /*TOUCH/BTN*/ }; static int hw_info_menu_action_cb(int btn, struct gui_synclist *lists) { static long last_refresh = 0; int selection = gui_synclist_get_sel_pos(lists); if (btn == ACTION_STD_OK) { FOR_NB_SCREENS(i) viewportmanager_theme_enable(i, false, NULL); if (hwinfo_items[selection].function) { is_done = false; hwinfo_items[selection].function(); } btn = ACTION_REDRAW; FOR_NB_SCREENS(i) viewportmanager_theme_undo(i, false); } else if (btn == ACTION_STD_CONTEXT) { } else if (btn == 0 && TIME_AFTER(current_tick, last_refresh + HZ / 2)) { last_refresh = current_tick; btn = ACTION_REDRAW; /* to make menu items update */ } return btn; } static const char* hw_info_menu_get_name(int item, void * data, char *buffer, size_t buffer_len) { (void)data; struct tm *cur_time; uint32_t *stackptr; extern uint32_t _irqstackend,_irqstackbegin; int btn; #ifdef HAVE_TOUCHSCREEN int touch; #endif switch(item) { /* create your dynamic items here */ case 0:/*RTC*/ cur_time = get_time(); snprintf(buffer, buffer_len, "%02d/%02d/%04d %02d:%02d:%02d", cur_time->tm_mday,cur_time->tm_mon, cur_time->tm_year, cur_time->tm_hour, cur_time->tm_min, cur_time->tm_sec); return buffer; case 1: /*cpuID*/ snprintf(buffer, buffer_len, "CPUID %X:%d", read_cp0_15(), (REG_CPM_CLKGR0 & BIT31) >> 31); return buffer; case 2: /*IRQstack*/ stackptr = &_irqstackbegin; for ( ; stackptr < &_irqstackend && *stackptr == 0xDEADBEEF; stackptr++) {} snprintf(buffer, buffer_len, "IRQ stack max: %lu", (uint32_t)&_irqstackend - (uint32_t)stackptr); return buffer; case 5: /*Touch/BTN*/ #ifdef HAVE_TOUCHSCREEN btn = button_read_device(&touch); snprintf(buffer, buffer_len, "X: %d Y: %d BTN: 0x%X", touch>>16, touch&0xFFFF, btn); #else btn = button_read_device(); snprintf(buffer, buffer_len, "BTN: 0x%X", btn); #endif return buffer; default: /* static items -- default */ return hwinfo_items[item].desc; } return "???"; } static int hw_info_debug_menu(void) { int h; lcd_setfont(FONT_SYSFIXED); lcd_getstringsize((unsigned char *)"A", NULL, &h); maxlines = LCD_HEIGHT / h - 1; is_done = false; struct simplelist_info info; simplelist_info_init(&info, "Hw Info", ARRAYLEN(hwinfo_items), NULL); info.action_callback = hw_info_menu_action_cb; info.get_name = hw_info_menu_get_name; return (simplelist_show_list(&info)) ? 1 : 0; } bool dbg_hw_info(void) { return hw_info_debug_menu() > 0; } #endif