From d36ac3a0a076db7c857d1b4b037a44cbbf248943 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Mon, 14 Nov 2011 15:19:15 +0100 Subject: pm: ARM: u8500: power: idle test Add power module test. This test, in debugfs/pwr_test/idle shall pass when the screen is black, but suspend is not yet reached. This module test checks clocks, prcmu-regulators, db8500 regulators and ab8500 regulators. The reason why not adding this to LTP is that later this module test will contain tests for voicecall and low-power-audio, which require Android to run. ST-Ericsson ID: 367599 Signed-off-by: Jonas Aaberg --- arch/arm/mach-ux500/test/Kconfig | 6 + arch/arm/mach-ux500/test/Makefile | 1 + arch/arm/mach-ux500/test/pwr.c | 828 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 835 insertions(+) create mode 100644 arch/arm/mach-ux500/test/Kconfig create mode 100644 arch/arm/mach-ux500/test/Makefile create mode 100644 arch/arm/mach-ux500/test/pwr.c diff --git a/arch/arm/mach-ux500/test/Kconfig b/arch/arm/mach-ux500/test/Kconfig new file mode 100644 index 00000000000..a071166d092 --- /dev/null +++ b/arch/arm/mach-ux500/test/Kconfig @@ -0,0 +1,6 @@ + +config DB8500_PWR_TEST + bool "Power usage module test" + depends on (UX500_SOC_DB8500 && DEBUG_FS && REGULATOR_AB8500_DEBUG) + help + Add power module tests for idle diff --git a/arch/arm/mach-ux500/test/Makefile b/arch/arm/mach-ux500/test/Makefile new file mode 100644 index 00000000000..58f1f2f3be6 --- /dev/null +++ b/arch/arm/mach-ux500/test/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DB8500_PWR_TEST) += pwr.o diff --git a/arch/arm/mach-ux500/test/pwr.c b/arch/arm/mach-ux500/test/pwr.c new file mode 100644 index 00000000000..5d5d24a38ab --- /dev/null +++ b/arch/arm/mach-ux500/test/pwr.c @@ -0,0 +1,828 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Author: Jonas Aaberg for ST-Ericsson + * + * License terms: GNU General Public License (GPL) version 2 + * + * This is a module test for clocks and regulators. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../../../drivers/regulator/dbx500-prcmu.h" +#include "../../../drivers/regulator/ab8500-debug.h" + +#define PRCC_PCKSR 0x010 +#define PRCC_KCKSR 0x014 + +#define PRCM_PLLSOC0_ENABLE 0x090 +#define PRCM_PLLSOC1_ENABLE 0x094 +#define PRCM_PLL32K_ENABLE 0x10C +#define PRCM_PLLARM_ENABLE 0x098 +#define PRCM_PLLDDR_ENABLE 0x09C +#define PRCM_RNG_ENABLE 0x50C +#define PRCM_PLLDSI_ENABLE 0x504 + +#define PRCM_CLKOCR 0x1CC + +#define PRCM_ARMCLKFIX_MGT 0x000 +#define PRCM_ACLK_MGT 0x004 +#define PRCM_SGACLK_MGT 0x014 +#define PRCM_UARTCLK_MGT 0x018 +#define PRCM_MSP02CLK_MGT 0x01C +#define PRCM_MSP1CLK_MGT 0x288 +#define PRCM_I2CCLK_MGT 0x020 +#define PRCM_SDMMCCLK_MGT 0x024 +#define PRCM_SLIMCLK_MGT 0x028 +#define PRCM_PER1CLK_MGT 0x02C +#define PRCM_PER2CLK_MGT 0x030 +#define PRCM_PER3CLK_MGT 0x034 +#define PRCM_PER5CLK_MGT 0x038 +#define PRCM_PER6CLK_MGT 0x03C +#define PRCM_PER7CLK_MGT 0x040 +#define PRCM_LCDCLK_MGT 0x044 +#define PRCM_BMLCLK_MGT 0x04C +#define PRCM_HSITXCLK_MGT 0x050 +#define PRCM_HSIRXCLK_MGT 0x054 +#define PRCM_HDMICLK_MGT 0x058 +#define PRCM_APEATCLK_MGT 0x05C +#define PRCM_APETRACECLK_MGT 0x060 +#define PRCM_MCDECLK_MGT 0x064 +#define PRCM_IPI2CCLK_MGT 0x068 +#define PRCM_DSIALTCLK_MGT 0x06C +#define PRCM_SPARE2CLK_MGT 0x070 +#define PRCM_SPARE1CLK_MGT 0x048 +#define PRCM_DMACLK_MGT 0x074 +#define PRCM_B2R2CLK_MGT 0x078 +#define PRCM_TVCLK_MGT 0x07C +#define PRCM_SSPCLK_MGT 0x280 +#define PRCM_RNGCLK_MGT 0x284 +#define PRCM_UICCCLK_MGT 0x27C + +#define PRCM_DSITVCLK_DIV 0x52C +#define PRCM_DSI_PLLOUT_SEL 0x530 + +#define PRCM_YYCLKEN0_MGT_VAL 0x520 + +enum acc_type { + RAW = 0, + CLK_PRCMU, + CLK_PAR_PRCMU, + CLK_ABB_SYS, + REG_DB8500, + REG_AB8500, +}; + +struct pwr_test { + enum acc_type type; + u32 base; + u32 off; + u32 mask; + u32 val; + + u32 par_off; + u32 par_mask; + u32 par_val; + + char *txt; + char *txt2; + + /* For AB8500 */ + enum ab8500_regulator_mode mode; + enum ab8500_regulator_hwmode hwmode; + enum hwmode_auto hwmode_auto[4]; + int volt_selected; + int alt_volt_selected; + int volt_len; + int volt[4]; + int alt_volt[4]; +}; + +#define RAW_TEST(_base, _off, _mask, _val) \ + { \ + .type = RAW, \ + .base = _base, \ + .off = _off, \ + .mask = _mask, \ + .val = _val, \ + .txt = #_base, \ + .txt2 = #_off \ + } + +#define CLK_TEST_PAR_PRCMU(_base, _off, _mask, _val, _par_off, \ + _par_mask, _par_val) \ + { \ + .type = CLK_PAR_PRCMU, \ + .base = _base, \ + .off = _off, \ + .mask = _mask, \ + .val = _val, \ + .par_off = _par_off, \ + .par_mask = _par_mask, \ + .par_val = _par_val, \ + .txt = #_base, \ + .txt2 = #_off \ + } + +#define CLK_TEST_PRCMU(_off, _mask, _val) \ + { \ + .type = CLK_PRCMU, \ + .off = _off, \ + .mask = _mask, \ + .val = _val, \ + .txt = #_off, \ + } + +#define CLK_TEST_ABB_SYS(_off, _mask, _val) \ + { \ + .type = CLK_ABB_SYS, \ + .off = _off, \ + .mask = _mask, \ + .val = _val, \ + .txt = #_off, \ + } +#define REG_TEST_DB8500(_reg, _val) \ + { \ + .type = REG_DB8500, \ + .off = _reg, \ + .val = _val, \ + } + + +static struct u8500_regulators +{ + struct dbx500_regulator_info *db8500_reg; + int db8500_num; +} u8500_reg; + +/* + * Idle - Note: this is not suspend. + * This test shall pass when the screen is black, before + * suspend is executed. + */ + +static struct pwr_test idle_test[] = { + + /* Test all periph kernel and bus clocks */ + /* bus: gpioctrl */ + CLK_TEST_PAR_PRCMU(U8500_CLKRST1_BASE, PRCC_PCKSR, 0x07ff, BIT(9), + PRCM_PER1CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PAR_PRCMU(U8500_CLKRST1_BASE, PRCC_KCKSR, 0x037f, 0x0000, + PRCM_PER1CLK_MGT, BIT(8), BIT(8)), + /* bus: gpioctrl */ + CLK_TEST_PAR_PRCMU(U8500_CLKRST2_BASE, PRCC_PCKSR, 0x0fff, BIT(11), + PRCM_PER2CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PAR_PRCMU(U8500_CLKRST2_BASE, PRCC_KCKSR, 0x00ff, 0x0000, + PRCM_PER2CLK_MGT, BIT(8), BIT(8)), + /* bus: uart2, gpioctrl - users??, kernel: uart2 */ + CLK_TEST_PAR_PRCMU(U8500_CLKRST3_BASE, PRCC_PCKSR, 0x01ff, BIT(8) | BIT(6), + PRCM_PER3CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PAR_PRCMU(U8500_CLKRST3_BASE, PRCC_KCKSR, 0x00fe, BIT(6), + PRCM_PER3CLK_MGT, BIT(8), BIT(8)), + /* No user on periph5 */ + CLK_TEST_PAR_PRCMU(U8500_CLKRST5_BASE, PRCC_PCKSR, 0x0003, 0x0000, + PRCM_PER5CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PAR_PRCMU(U8500_CLKRST5_BASE, PRCC_KCKSR, 0x0000, 0x0000, + PRCM_PER5CLK_MGT, BIT(8), BIT(8)), + /* bus: MTU0 used for scheduling ApIdle */ + CLK_TEST_PAR_PRCMU(U8500_CLKRST6_BASE, PRCC_PCKSR, 0x00ff, BIT(6), + PRCM_PER6CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PAR_PRCMU(U8500_CLKRST6_BASE, PRCC_KCKSR, 0x0001, 0x0000, + PRCM_PER6CLK_MGT, BIT(8), BIT(8)), + + /* Test that clkout 1/2 is off */ + CLK_TEST_PRCMU(PRCM_CLKOCR, 0x3f003f, 0x0000), + + /* Test that ulp clock is off */ + CLK_TEST_ABB_SYS(AB8500_SYSULPCLKCTRL1, 0xfc, 0x00), + + /* Test that prcm clks are in proper state */ + CLK_TEST_PRCMU(PRCM_ARMCLKFIX_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_ACLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_SGACLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_UARTCLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_MSP02CLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_MSP1CLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_I2CCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_SDMMCCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_SLIMCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_PER1CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_PER2CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_PER3CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_PER5CLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_PER6CLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_PER7CLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_LCDCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_BMLCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_HSITXCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_HSIRXCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_HDMICLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_APEATCLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_APETRACECLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_MCDECLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_IPI2CCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_DSIALTCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_DMACLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_B2R2CLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_TVCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_SSPCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_RNGCLK_MGT, BIT(8), BIT(8)), + CLK_TEST_PRCMU(PRCM_UICCCLK_MGT, BIT(8), 0x000), + CLK_TEST_PRCMU(PRCM_DSITVCLK_DIV, BIT(24) | BIT(25) | BIT(26), + 0x000), + CLK_TEST_PRCMU(PRCM_DSI_PLLOUT_SEL, (BIT(0) | BIT(1) | BIT(2) | + BIT(8) | BIT(9) | BIT(10)), + 0x200), /* Will change */ + CLK_TEST_PRCMU(PRCM_YYCLKEN0_MGT_VAL, 0xBFFFFFFF, + (BIT(27) | BIT(23) | BIT(22) | BIT(15) | + BIT(13) | BIT(12) | BIT(11) | BIT(5) | + BIT(1) | BIT(0))), + + /* Check db8500 regulator settings - enable */ + REG_TEST_DB8500(DB8500_REGULATOR_VAPE, 0), + REG_TEST_DB8500(DB8500_REGULATOR_VARM, 0), + REG_TEST_DB8500(DB8500_REGULATOR_VMODEM, 0), + REG_TEST_DB8500(DB8500_REGULATOR_VPLL, 0), + REG_TEST_DB8500(DB8500_REGULATOR_VSMPS1, 0), + REG_TEST_DB8500(DB8500_REGULATOR_VSMPS2, 1), + REG_TEST_DB8500(DB8500_REGULATOR_VSMPS3, 0), + REG_TEST_DB8500(DB8500_REGULATOR_VRF1, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAMMDSP, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAMMDSPRET, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SVAPIPE, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAMMDSP, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAMMDSPRET, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SIAPIPE, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_SGA, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_B2R2_MCDE, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM12, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM12RET, 0), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM34, 1), + REG_TEST_DB8500(DB8500_REGULATOR_SWITCH_ESRAM34RET, 0), + + /* ab8500 regulators */ + { + .type = REG_AB8500, + .off = AB8500_VARM, + .mode = AB8500_MODE_ON, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_OFF}, + .volt_selected = 2, + /* Voltage and voltage selection depends on ARM_OPP */ + .alt_volt_selected = 1, + .volt_len = 3, + .volt = {1350000, 1025000, 750000}, + .alt_volt = {1250000, 1025000, 750000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VBBP, + .mode = AB8500_MODE_ON, + .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_INVAL}, + .volt_selected = 1, + .volt_len = 2, + .volt = {-300000, 0}, + }, + { + .type = REG_AB8500, + .off = AB8500_VBBN, + .mode = AB8500_MODE_ON, + .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_INVAL}, + .volt_selected = 1, + .volt_len = 2, + .volt = {300000, 0}, + }, + { + .type = REG_AB8500, + .off = AB8500_VAPE, + .mode = AB8500_MODE_ON, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_INVAL, HWM_INVAL, HWM_OFF}, + .volt_selected = 2, + /* APE_OPP can get changes by cpufreq */ + .alt_volt_selected = 1, + .volt_len = 3, + .volt = {1225000, 1000000, 1200000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VSMPS1, + .mode = AB8500_MODE_HW, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF}, + .volt_selected = 2, + .volt_len = 3, + .volt = {1200000, 1200000, 1200000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VSMPS2, + .mode = AB8500_MODE_HW, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_ON, HWM_ON, HWM_ON, HWM_OFF}, + .volt_selected = 2, + .volt_len = 3, + .volt = {1800000, 1800000, 1800000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VSMPS3, + .mode = AB8500_MODE_HW, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF}, + .volt_selected = 2, + .volt_len = 3, + .volt = {925000, 1212500, 1200000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VPLL, + .mode = AB8500_MODE_HW, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_ON, HWM_OFF, HWM_OFF, HWM_OFF}, + }, + { + .type = REG_AB8500, + .off = AB8500_VREFDDR, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VMOD, + .mode = AB8500_MODE_OFF, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF}, + .volt_selected = 2, + .volt_len = 2, + .volt = {1125000, 1025000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VEXTSUPPLY1, + .mode = AB8500_MODE_LP, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF}, + }, + { + .type = REG_AB8500, + .off = AB8500_VEXTSUPPLY2, + .mode = AB8500_MODE_OFF, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF}, + }, + { + .type = REG_AB8500, + .off = AB8500_VEXTSUPPLY3, + .mode = AB8500_MODE_ON, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_ON, HWM_OFF, HWM_ON, HWM_OFF}, + }, + { + .type = REG_AB8500, + .off = AB8500_VRF1, + .mode = AB8500_MODE_HW, + .volt_len = 1, + .volt = {2150000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VANA, + .mode = AB8500_MODE_OFF, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF}, + }, + { + .type = REG_AB8500, + .off = AB8500_VAUX1, + .mode = AB8500_MODE_ON, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF}, + .volt_len = 1, + .volt = {2800000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VAUX2, + .mode = AB8500_MODE_ON, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF}, + .volt_len = 1, + .volt = {3300000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VAUX3, + .mode = AB8500_MODE_OFF, + .hwmode = AB8500_HWMODE_HPLP, + .hwmode_auto = {HWM_OFF, HWM_OFF, HWM_OFF, HWM_OFF}, + .volt_len = 1, + .volt = {2910000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VINTCORE, + .mode = AB8500_MODE_OFF, + .volt_len = 1, + .volt = {1250000}, + }, + { + .type = REG_AB8500, + .off = AB8500_VTVOUT, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VAUDIO, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VANAMIC1, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VANAMIC2, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VDMIC, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VUSB, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VOTG, + .mode = AB8500_MODE_OFF, + }, + { + .type = REG_AB8500, + .off = AB8500_VBUSBIS, + .mode = AB8500_MODE_OFF, + }, +}; + +static int read_raw(struct pwr_test *t) +{ + u32 res; + int i; + + for (i = 0 ; i < 50; i++) { + res = readl(__io_address(t->base) + t->off); + if ((res & t->mask) == t->val) + return 0; + msleep(10); + } + + pr_err("pwr_test: ERROR: %s %s 0x%x, 0x%x reads 0x%x (masked: 0x%x), " + "expected 0x%x (mask: 0x%x)\n", + t->txt, t->txt2, t->base, t->off, res, + res & t->mask, t->val, t->mask); + return -EINVAL; +} + +static int clk_read_par_prcmu(struct pwr_test *t) + { + u32 res; + int i; + bool parent_enabled = false; + + /* check if parent clock is in expected configuration (usually PRCC) */ + for (i = 0 ; i < 50; i++) { + res = prcmu_read(t->par_off); + if ((res & t->par_mask) == t->par_val) { + parent_enabled = true; + break; + } + msleep(10); + } + + /* If parent clock is off, but clock is expected to be on --> fail */ + if (!parent_enabled && t->val != 0) { + pr_err("pwr_test: ERROR: PRCMU parent clk of %s, " + "0x%x reads 0x%x (masked: 0x%x), " + "expected to be on 0x%x (mask: 0x%x)\n", + t->txt, t->par_off, res, res & t->par_mask, + t->par_val, t->par_mask); + return -EINVAL; + } + + /* It's ok if parent clock is off and clock is off as well */ + if (!parent_enabled && t->val == 0) + return 0; + + return read_raw(t); +} + +static int clk_read_prcmu(struct pwr_test *t) +{ + u32 res; + int i; + + for (i = 0 ; i < 50; i++) { + res = prcmu_read(t->off); + if ((res & t->mask) == t->val) + return 0; + msleep(10); + } + + pr_err("pwr_test: ERROR: %s PRCMU, 0x%x reads 0x%x (masked: 0x%x), " + "expected 0x%x (mask: 0x%x)\n", + t->txt, t->off, res, res & t->mask, t->val, t->mask); + return -EINVAL; +} + +static int clk_read_abb_sys(struct pwr_test *t) +{ + int ret; + u8 val = 0; + int i; + + for (i = 0; i < 50; i++) { + ret = ab8500_sysctrl_read(t->off, &val); + if (ret < 0) { + pr_err("pwr_test: AB8500 access error: %d of " + "reg: 0x%x\n", ret, t->off); + return ret; + } + if ((val & (u8)t->mask) == t->val) + return 0; + msleep(10); + } + + pr_err("pwr_test: ERROR: AB8500 register 0x%x %s, reads 0x%x," + " (masked 0x%x) expected 0x%x (mask: 0x%x)\n", + t->base, t->txt, val, val & (u8)t->mask, t->val, t->mask); + + return -EINVAL; +} + +static int reg_read_db8500(struct pwr_test *t) +{ + int i, j; + + for (j = 0 ; j < u8500_reg.db8500_num ; j++) + if (u8500_reg.db8500_reg[j].desc.id == t->off) + break; + + if (j == u8500_reg.db8500_num) { + pr_err("pwr_test: Invalid db8500 regulator\n"); + return -EINVAL; + } + + for (i = 0; i < 50; i++) { + + /* powerstate is a special case */ + if (t->off == DB8500_REGULATOR_VAPE) { + if ((u8500_reg.db8500_reg[j].is_enabled || + power_state_active_is_enabled()) == t->val) + return 0; + } else { + if (u8500_reg.db8500_reg[j].is_enabled == t->val) + return 0; + } + msleep(10); + } + + pr_err("pwr_test: ERROR: DB8500 regulator %s is 0x%x expected 0x%x\n", + u8500_reg.db8500_reg[j].desc.name, + t->off != DB8500_REGULATOR_VAPE ? + u8500_reg.db8500_reg[j].is_enabled : + u8500_reg.db8500_reg[j].is_enabled || + power_state_active_is_enabled(), + t->val); + + return -EINVAL; +} + +static int reg_read_ab8500(struct pwr_test *t) +{ + int i, j; + int ret; + struct ab8500_debug_regulator_status s; + + for (i = 0; i < 50; i++) { + ret = ab8500_regulator_debug_read(t->off, &s); + + if (ret) { + pr_err("pwr_test: Fail to read ab8500 regulator\n"); + return -EINVAL; + } + + if (t->mode != s.mode) + goto repeat; + + if (t->hwmode != s.hwmode) + goto repeat; + + if (t->volt_selected != s.volt_selected && + t->alt_volt_selected != s.volt_selected) + goto repeat; + + if (t->volt_len != s.volt_len) + goto repeat; + + for (j = 0; j < 4 && s.hwmode != AB8500_HWMODE_NONE; j++) + if (s.hwmode_auto[j] != t->hwmode_auto[j]) + goto repeat; + + for (j = 0; j < s.volt_len; j++) + if (s.volt[j] != t->volt[j] && + s.volt[j] != t->alt_volt[j]) + goto repeat; + return 0; + +repeat: + msleep(10); + } + + pr_err("pwr test: ERROR: AB8500 regulator %s ", s.name); + + if (t->mode != s.mode) + printk("mode is: %d expected: %d ", + s.mode, t->mode); + + if (t->hwmode != s.hwmode) + printk("hwmode is: %d expected: %d ", + s.hwmode, t->hwmode); + + if (t->volt_selected != s.volt_selected && + t->alt_volt_selected != s.volt_selected) { + printk("volt selected is: %d expected: %d ", + s.volt_selected, t->volt_selected); + if (t->alt_volt_selected) + printk("(alt volt: %d) ", t->alt_volt_selected); + } + + if (t->volt_len != s.volt_len) + printk("volt len is: %d expected: %d ", + s.volt_len, t->volt_len); + + for (j = 0; j < 4; j++) + if (s.hwmode_auto[j] != t->hwmode_auto[j]) + break; + if (j != 4 && s.hwmode != AB8500_HWMODE_NONE) { + + printk("hwmode auto:: {"); + for (j = 0; j < 4; j++) { + switch(s.hwmode_auto[j]) { + case HWM_OFF: { printk("OFF "); break; } + case HWM_ON: { printk("ON "); break; } + case HWM_INVAL: { printk("INVAL "); break; } + } + } + printk("}, expected: {"); + for (j = 0; j < 4; j++) { + switch(t->hwmode_auto[j]) { + case HWM_OFF: { printk("OFF "); break; } + case HWM_ON: { printk("ON "); break; } + case HWM_INVAL: { printk("INVAL "); break; } + } + } + printk("} "); + } + + if (s.volt_len == t->volt_len) { + for (j = 0; j < s.volt_len; j++) + if (s.volt[j] != t->volt[j] && + t->alt_volt[j] != s.volt[j]) + break; + + } else { + j = 0; + } + + if (j != s.volt_len) { + printk("voltage: {"); + for (j = 0; j < s.volt_len; j++) + printk("%d ", s.volt[j]); + + if (t->alt_volt) { + printk("} alt voltage: {"); + for (j = 0; j < s.volt_len; j++) + printk("%d ", t->alt_volt[j]); + } + printk("} expected: {"); + for (j = 0; j < t->volt_len; j++) + printk("%d ", t->volt[j]); + printk("} "); + } + + printk("\n"); + + return -EINVAL; +} + + +static int test_execute(struct pwr_test *t, int len) +{ + int err = 0; + int i; + + for (i = 0 ; i < len ; i++) { + switch (t[i].type) { + case CLK_ABB_SYS: + err |= clk_read_abb_sys(&t[i]); + break; + case CLK_PRCMU: + err |= clk_read_prcmu(&t[i]); + break; + case CLK_PAR_PRCMU: + err |= clk_read_par_prcmu(&t[i]); + break; + case RAW: + err |= read_raw(&t[i]); + break; + case REG_DB8500: + err |= reg_read_db8500(&t[i]); + break; + case REG_AB8500: + err |= reg_read_ab8500(&t[i]); + break; + default: + break; + } + + } + return err; +} + +static int pwr_test_idle(struct seq_file *s, void *data) +{ + int err; + + err = test_execute(idle_test, ARRAY_SIZE(idle_test)); + + seq_printf(s, "%s\n", err == 0 ? "PASS" : "FAIL"); + + return 0; +} + + +int dbx500_regulator_testcase(struct dbx500_regulator_info *regulator_info, + int num_regulators) +{ + u8500_reg.db8500_reg = regulator_info; + u8500_reg.db8500_num = num_regulators; + return 0; +} + +static int pwr_test_debugfs_open(struct inode *inode, + struct file *file) +{ + return single_open(file, + inode->i_private, + NULL); +} + +static const struct file_operations pwr_test_debugfs_ops = { + .open = pwr_test_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct dentry *debugfs_dir; + +static int __init pwr_test_init(void) +{ + int err = 0; + void *err_ptr; + + debugfs_dir = debugfs_create_dir("pwr_test", NULL); + if (IS_ERR(debugfs_dir)) + return PTR_ERR(debugfs_dir); + + err_ptr = debugfs_create_file("idle", + S_IFREG | S_IRUGO, + debugfs_dir, (void *)pwr_test_idle, + &pwr_test_debugfs_ops); + if (IS_ERR(err_ptr)) { + err = PTR_ERR(err_ptr); + goto out; + } + return 0; +out: + debugfs_remove_recursive(debugfs_dir); + return err; +} +late_initcall(pwr_test_init); -- cgit v1.2.3