/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2011 by Amaury Pouly * * 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 "system.h" #include "dma-imx233.h" #include "action.h" #include "lcd.h" #include "font.h" #include "adc.h" #include "adc-imx233.h" #include "power-imx233.h" #include "clkctrl-imx233.h" #include "powermgmt-imx233.h" #include "rtc-imx233.h" #include "dcp-imx233.h" #include "pinctrl-imx233.h" #include "ocotp-imx233.h" #include "pwm-imx233.h" #include "string.h" #include "stdio.h" #define DEBUG_CANCEL BUTTON_BACK static struct { const char *name; unsigned chan; } dbg_channels[] = { { "i2c", APB_I2C }, { "dac", APB_AUDIO_DAC }, { "ssp1", APB_SSP(1) }, { "ssp2", APB_SSP(2) }, }; static struct { const char *name; unsigned src; } dbg_irqs[] = { { "vdd5v", INT_SRC_VDD5V }, { "dac_dma", INT_SRC_DAC_DMA }, { "dac_err", INT_SRC_DAC_ERROR }, { "adc_dma", INT_SRC_ADC_DMA }, { "adc_err", INT_SRC_ADC_ERROR }, { "usbctrl", INT_SRC_USB_CTRL }, { "ssp1_dma", INT_SRC_SSP1_DMA }, { "ssp1_err", INT_SRC_SSP1_ERROR }, { "gpio0", INT_SRC_GPIO0 }, { "gpio1", INT_SRC_GPIO1 }, { "gpio2", INT_SRC_GPIO2 }, #if IMX233_SUBTARGET >= 3780 { "ssp2_dma", INT_SRC_SSP2_DMA }, { "ssp2_err", INT_SRC_SSP2_ERROR }, { "lcdif_dma", INT_SRC_LCDIF_DMA }, { "lcdif_err", INT_SRC_LCDIF_ERROR }, { "dcp", INT_SRC_DCP }, #endif { "i2c_dma", INT_SRC_I2C_DMA }, { "i2c_err", INT_SRC_I2C_ERROR }, { "timer0", INT_SRC_TIMER(0) }, { "timer1", INT_SRC_TIMER(1) }, { "timer2", INT_SRC_TIMER(2) }, { "timer3", INT_SRC_TIMER(3) }, { "touch_det", INT_SRC_TOUCH_DETECT }, { "lradc_ch0", INT_SRC_LRADC_CHx(0) }, { "lradc_ch1", INT_SRC_LRADC_CHx(1) }, { "lradc_ch2", INT_SRC_LRADC_CHx(2) }, { "lradc_ch3", INT_SRC_LRADC_CHx(3) }, { "lradc_ch4", INT_SRC_LRADC_CHx(4) }, { "lradc_ch5", INT_SRC_LRADC_CHx(5) }, { "lradc_ch6", INT_SRC_LRADC_CHx(6) }, { "lradc_ch7", INT_SRC_LRADC_CHx(7) }, { "rtc_1msec", INT_SRC_RTC_1MSEC }, }; bool dbg_hw_info_dma(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 25); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); lcd_putsf(0, 0, "S C name bar apb ahb una"); for(unsigned i = 0; i < ARRAYLEN(dbg_channels); i++) { struct imx233_dma_info_t info = imx233_dma_get_info(dbg_channels[i].chan, DMA_INFO_ALL); lcd_putsf(0, i + 1, "%c %c %4s %8x %3x %3x %3x", info.gated ? 'g' : info.freezed ? 'f' : ' ', !info.int_enabled ? '-' : info.int_error ? 'e' : info.int_cmdcomplt ? 'c' : ' ', dbg_channels[i].name, info.bar, info.apb_bytes, info.ahb_bytes, info.nr_unaligned); } lcd_update(); yield(); } } bool dbg_hw_info_power(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); struct imx233_power_info_t info = imx233_power_get_info(POWER_INFO_ALL); int line = 0; unsigned trg, bo; bool en; int linreg; char buf[16]; lcd_putsf(0, line++, "name value bo linreg"); #define DISP_REGULATOR(name) \ imx233_power_get_regulator(REGULATOR_##name, &trg, &bo); \ imx233_power_get_regulator_linreg(REGULATOR_##name, &en, &linreg); \ if(en) snprintf(buf, sizeof(buf), "%d", linreg); \ else snprintf(buf, sizeof(buf), " "); \ lcd_putsf(0, line++, "%6s %4d %4d %s", #name, trg, bo, buf); \ DISP_REGULATOR(VDDD); #if IMX233_SUBTARGET >= 3700 DISP_REGULATOR(VDDA); DISP_REGULATOR(VDDIO); #endif #if IMX233_SUBTARGET >= 3780 DISP_REGULATOR(VDDMEM); #endif lcd_putsf(0, line++, "DC-DC: pll: %d freq: %d", info.dcdc_sel_pllclk, info.dcdc_freqsel); lcd_putsf(0, line++, "charge: %d mA stop: %d mA", info.charge_current, info.stop_current); lcd_putsf(0, line++, "charging: %d bat_adj: %d", info.charging, info.batt_adj); lcd_putsf(0, line++, "4.2: en: %d dcdc: %d", info._4p2_enable, info._4p2_dcdc); lcd_putsf(0, line++, "4.2: cmptrip: %d dropout: %d", info._4p2_cmptrip, info._4p2_dropout); lcd_putsf(0, line++, "5V: pwd_4.2_charge: %d", info._5v_pwd_charge_4p2); lcd_putsf(0, line++, "5V: chargelim: %d mA", info._5v_charge_4p2_limit); lcd_putsf(0, line++, "5V: dcdc: %d xfer: %d", info._5v_enable_dcdc, info._5v_dcdc_xfer); lcd_putsf(0, line++, "5V: thr: %d mV use: %d cmps: %d", info._5v_vbusvalid_thr, info._5v_vbusvalid_detect, info._5v_vbus_cmps); lcd_update(); yield(); } } bool dbg_hw_info_adc(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 25); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); /* add battery readout in mV, this it is not the direct output of a channel */ lcd_putsf(0, 0, "Battery(mV) %d", _battery_voltage()); for(unsigned i = 0; i < NUM_ADC_CHANNELS; i++) { lcd_putsf(0, i + 1, "%s %d", imx233_adc_channel_name[i], adc_read(i)); } lcd_update(); yield(); } } static struct { enum imx233_clock_t clk; const char *name; bool has_enable; bool has_bypass; bool has_idiv; bool has_fdiv; bool has_freq; } dbg_clk[] = { { CLK_PLL, "pll", true, false, false, false, true}, { CLK_XTAL, "xtal", false, false, false, false, true}, { CLK_PIX, "pix", true, true, true, true, true }, { CLK_SSP, "ssp", true, true, true, false, true }, { CLK_IO, "io", false, false, false, true, true }, { CLK_CPU, "cpu", false, true, true, true, true }, { CLK_HBUS, "hbus", false, false, true, true, true }, { CLK_EMI, "emi", false, true, true, true, true }, { CLK_XBUS, "xbus", false, false, true, false, true } }; bool dbg_hw_info_clkctrl(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); /* 012345678901234567890123456789 */ lcd_putsf(0, 0, "name en by idiv fdiv frequency"); for(unsigned i = 0; i < ARRAYLEN(dbg_clk); i++) { #define c dbg_clk[i] lcd_putsf(0, i + 1, "%4s", c.name); if(c.has_enable) lcd_putsf(5, i + 1, "%2d", imx233_clkctrl_is_enabled(c.clk)); if(c.has_bypass) lcd_putsf(8, i + 1, "%2d", imx233_clkctrl_get_bypass(c.clk)); if(c.has_idiv && imx233_clkctrl_get_div(c.clk) != 0) lcd_putsf(10, i + 1, "%4d", imx233_clkctrl_get_div(c.clk)); if(c.has_fdiv && imx233_clkctrl_get_frac_div(c.clk) != 0) lcd_putsf(16, i + 1, "%4d", imx233_clkctrl_get_frac_div(c.clk)); if(c.has_freq) lcd_putsf(21, i + 1, "%9d", imx233_clkctrl_get_freq(c.clk)); #undef c } int line = ARRAYLEN(dbg_clk) + 1; if(!imx233_clkctrl_is_auto_slow_enabled()) lcd_putsf(0, line++, "auto-slow: disabled"); else lcd_putsf(0, line++, "auto-slow: 1/%d", 1 << imx233_clkctrl_get_auto_slow_div()); lcd_update(); yield(); } } bool dbg_hw_info_powermgmt(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); struct imx233_powermgmt_info_t info = imx233_powermgmt_get_info(); lcd_putsf(0, 0, "state: %s", info.state == DISCHARGING ? "discharging" : #if CONFIG_CHARGING >= CHARGING_MONITOR info.state == CHARGE_STATE_DISABLED ? "disabled" : info.state == CHARGE_STATE_ERROR ? "error" : #endif #if CONFIG_CHARGING >= CHARGING_MONITOR info.state == TRICKLE ? "trickle" : info.state == TOPOFF ? "topoff" : info.state == CHARGING ? "charging" : #endif ""); lcd_putsf(0, 1, "charging tmo: %d", info.charging_timeout); lcd_putsf(0, 2, "topoff tmo: %d", info.topoff_timeout); lcd_putsf(0, 3, "4p2ilimit tmo: %d", info.incr_4p2_ilimit_timeout); lcd_update(); yield(); } } bool dbg_hw_info_rtc(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); struct imx233_rtc_info_t info = imx233_rtc_get_info(); lcd_putsf(0, 0, "seconds: %lu", info.seconds); for(int i = 0; i < 6; i++) lcd_putsf(0, i + 1, "persistent%d: 0x%lx", i, info.persistent[i]); lcd_update(); yield(); } } #if IMX233_SUBTARGET >= 3780 bool dbg_hw_info_dcp(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); struct imx233_dcp_info_t info = imx233_dcp_get_info(DCP_INFO_ALL); lcd_putsf(0, 0, "crypto: %d csc: %d", info.has_crypto, info.has_csc); lcd_putsf(0, 1, "keys: %d channels: %d", info.num_keys, info.num_channels); lcd_putsf(0, 2, "ciphers: 0x%lx hash: 0x%lx", info.ciphers, info.hashs); lcd_putsf(0, 3, "gather wr: %d otp rdy: %d ch0merged: %d", info.gather_writes, info.otp_key_ready, info.ch0_merged); lcd_putsf(0, 4, "ctx switching: %d caching: %d", info.context_switching, info.context_caching); lcd_putsf(0, 5, "ch irq ien en rdy pri sem cmdptr a"); int nr = HW_DCP_NUM_CHANNELS; for(int i = 0; i < nr; i++) { lcd_putsf(0, 6 + i, "%d %d %d %d %d %d %d 0x%08lx %d", i, info.channel[i].irq, info.channel[i].irq_en, info.channel[i].enable, info.channel[i].ready, info.channel[i].high_priority, info.channel[i].sema, info.channel[i].cmdptr, info.channel[i].acquired); } lcd_putsf(0, 6 + nr, "csc %d %d %d %d", info.csc.irq, info.csc.irq_en, info.csc.enable, info.csc.priority); lcd_update(); yield(); } } #else bool dbg_hw_info_dcp(void) { return true; } #endif bool dbg_hw_info_icoll(void) { lcd_setfont(FONT_SYSFIXED); int first_irq = 0; int dbg_irqs_count = sizeof(dbg_irqs) / sizeof(dbg_irqs[0]); int line_count = lcd_getheight() / font_get(lcd_getfont())->height; while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: first_irq++; if(first_irq >= dbg_irqs_count) first_irq = dbg_irqs_count - 1; break; case ACTION_STD_PREV: first_irq--; if(first_irq < 0) first_irq = 0; break; case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); for(int i = first_irq, j = 0; i < dbg_irqs_count && j < line_count; i++, j++) { struct imx233_icoll_irq_info_t info = imx233_icoll_get_irq_info(dbg_irqs[i].src); lcd_putsf(0, j, "%s", dbg_irqs[i].name); if(info.enabled || info.freq > 0) lcd_putsf(10, j, "%d", info.freq); } lcd_update(); yield(); } } bool dbg_hw_info_pinctrl(void) { lcd_setfont(FONT_SYSFIXED); #ifdef IMX233_PINCTRL_DEBUG unsigned top_user = 0; #endif while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: #ifdef IMX233_PINCTRL_DEBUG top_user++; break; #endif case ACTION_STD_PREV: #ifdef IMX233_PINCTRL_DEBUG if(top_user > 0) top_user--; break; #endif case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); for(int i = 0; i < 4; i++) lcd_putsf(0, i, "DIN%d = 0x%08x", i, HW_PINCTRL_DINn(i)); #ifdef IMX233_PINCTRL_DEBUG unsigned cur_line = 6; unsigned last_line = lcd_getheight() / font_get(lcd_getfont())->height; unsigned cur_idx = 0; for(int bank = 0; bank < 4; bank++) for(int pin = 0; pin < 32; pin++) { const char *owner = imx233_pinctrl_blame(bank, pin); if(owner == NULL) continue; if(cur_idx++ >= top_user && cur_line < last_line) lcd_putsf(0, cur_line++, "B%dP%02d %s", bank, pin, owner); } if(cur_idx < top_user) top_user = cur_idx - 1; #endif lcd_update(); yield(); } } #if IMX233_SUBTARGET >= 3700 struct { const char *name; volatile uint32_t *addr; } dbg_ocotp[] = { #define E(n,v) { .name = n, .addr = &v } E("CUST0", HW_OCOTP_CUSTn(0)), E("CUST1", HW_OCOTP_CUSTn(1)), E("CUST2", HW_OCOTP_CUSTn(2)), E("CUST0", HW_OCOTP_CUSTn(3)), E("HWCAP0", HW_OCOTP_HWCAPn(0)), E("HWCAP1", HW_OCOTP_HWCAPn(1)), E("HWCAP2", HW_OCOTP_HWCAPn(2)), E("HWCAP3", HW_OCOTP_HWCAPn(3)), E("HWCAP4", HW_OCOTP_HWCAPn(4)), E("HWCAP5", HW_OCOTP_HWCAPn(5)), E("SWCAP", HW_OCOTP_SWCAP), E("CUSTCAP", HW_OCOTP_CUSTCAP), E("OPS0", HW_OCOTP_OPSn(0)), E("OPS1", HW_OCOTP_OPSn(1)), E("OPS2", HW_OCOTP_OPSn(2)), E("OPS2", HW_OCOTP_OPSn(3)), E("UN0", HW_OCOTP_UNn(0)), E("UN1", HW_OCOTP_UNn(1)), E("UN2", HW_OCOTP_UNn(2)), E("ROM0", HW_OCOTP_ROMn(0)), E("ROM1", HW_OCOTP_ROMn(1)), E("ROM2", HW_OCOTP_ROMn(2)), E("ROM3", HW_OCOTP_ROMn(3)), E("ROM4", HW_OCOTP_ROMn(4)), E("ROM5", HW_OCOTP_ROMn(5)), E("ROM6", HW_OCOTP_ROMn(6)), E("ROM7", HW_OCOTP_ROMn(7)), }; bool dbg_hw_info_ocotp(void) { lcd_setfont(FONT_SYSFIXED); unsigned top_user = 0; while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: top_user++; break; case ACTION_STD_PREV: if(top_user > 0) top_user--; break; case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); unsigned cur_line = 0; unsigned last_line = lcd_getheight() / font_get(lcd_getfont())->height; unsigned i = 0; for(i = 0; i < ARRAYLEN(dbg_ocotp); i++) { if(i >= top_user && cur_line < last_line) { lcd_putsf(0, cur_line, "%s", dbg_ocotp[i].name); lcd_putsf(8, cur_line++, "%x", imx233_ocotp_read(dbg_ocotp[i].addr)); } } if(i < top_user) top_user = i - 1; lcd_update(); yield(); } } #else bool dbg_hw_info_ocotp(void) { return true; } #endif bool dbg_hw_info_pwm(void) { lcd_setfont(FONT_SYSFIXED); bool detailled_view = false; while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: case ACTION_STD_PREV: detailled_view = !detailled_view; break; case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); int line = 0; if(detailled_view) lcd_putsf(0, line++, "c cdiv period active/x inactive"); else lcd_putsf(0, line++, "pwm"); for(int i = 0; i < IMX233_PWM_NR_CHANNELS; i++) { struct imx233_pwm_info_t info = imx233_pwm_get_info(i); if(!info.enabled) continue; if(detailled_view) { lcd_putsf(0, line++, "%d %4d %6d %6d/%c %6d/%c", i, info.cdiv, info.period, info.active, info.active_state, info.inactive, info.inactive_state); } else { char *prefix = ""; int freq = imx233_clkctrl_get_freq(CLK_XTAL) * 1000 / info.cdiv / info.period; if(freq > 1000) { prefix = "K"; freq /= 1000; } int duty = (info.inactive - info.active) * 100 / info.period; lcd_putsf(0, line++, "%d @%d %sHz, %d%% %c/%c", i, freq, prefix, duty, info.active_state, info.inactive_state); } } lcd_update(); yield(); } } bool dbg_hw_info_usb(void) { lcd_setfont(FONT_SYSFIXED); while(1) { int button = get_action(CONTEXT_STD, HZ / 10); switch(button) { case ACTION_STD_NEXT: BF_SET(USBPHY_CTRL, ENOTGIDDETECT); BF_SET(USBPHY_CTRL, ENDEVPLUGINDETECT); break; case ACTION_STD_PREV: BF_CLR(USBPHY_CTRL, ENOTGIDDETECT); BF_CLR(USBPHY_CTRL, ENDEVPLUGINDETECT); break; case ACTION_STD_OK: case ACTION_STD_MENU: lcd_setfont(FONT_UI); return true; case ACTION_STD_CANCEL: lcd_setfont(FONT_UI); return false; } lcd_clear_display(); int line = 0; lcd_putsf(0, line++, "VBUS valid: %d/%d", BF_RD(POWER_STS, VBUSVALID), BF_RD(POWER_STS, VBUSVALID_STATUS)); lcd_putsf(0, line++, "A valid: %d/%d", BF_RD(POWER_STS, AVALID), BF_RD(POWER_STS, AVALID_STATUS)); lcd_putsf(0, line++, "B valid: %d/%d", BF_RD(POWER_STS, BVALID), BF_RD(POWER_STS, BVALID_STATUS)); lcd_putsf(0, line++, "session end: %d", BF_RD(POWER_STS, SESSEND)); lcd_putsf(0, line++, "dev plugin: %d", BF_RD(USBPHY_STATUS, DEVPLUGIN_STATUS)); lcd_putsf(0, line++, "OTG ID: %d", BF_RD(USBPHY_STATUS, OTGID_STATUS)); lcd_update(); yield(); } } bool dbg_hw_info(void) { return dbg_hw_info_clkctrl() && dbg_hw_info_dma() && dbg_hw_info_adc() && dbg_hw_info_power() && dbg_hw_info_powermgmt() && dbg_hw_info_rtc() && dbg_hw_info_dcp() && dbg_hw_info_pinctrl() && dbg_hw_info_icoll() && dbg_hw_info_ocotp() && dbg_hw_info_pwm() && dbg_hw_info_usb() && dbg_hw_target_info(); } bool dbg_ports(void) { return false; }