From bfeaa9ded2338b7b17a067bee71edab2f5638df7 Mon Sep 17 00:00:00 2001 From: Przemyslaw Marczak Date: Mon, 24 Mar 2014 10:40:12 +0100 Subject: [PATCH] trats2: add support to interactive charger This commit adds support to interactive charger api which means an implementation of listed functions: - charger_enable() - enable the charger.Charger can be enabled automatically if the device power on state is triggered by USB cable connection. - charger_type() - this function returns device type connected to usb port of device. For charger api matters only positive value. - battery_present() - check if battery is connected (DETBAT pin) - battery_state() - check battery charge level. Returns percent value. - low_power_mode() - switch SOC into low power mode which covers: - disable cores: 1, 2, 3, - disable SOC unused power domains, - decrease CPU clock to 200 MHz After low_power_mode() function call, device should be restarted, which is implemented in interactive charger. Change-Id: I7e664fab29c45c2d29dc57a2faa887d88530d8f3 Signed-off-by: Przemyslaw Marczak --- board/samsung/trats2/setup.h | 73 +++++++++++++ board/samsung/trats2/trats2.c | 247 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 board/samsung/trats2/setup.h diff --git a/board/samsung/trats2/setup.h b/board/samsung/trats2/setup.h new file mode 100644 index 0000000..24e87d0 --- /dev/null +++ b/board/samsung/trats2/setup.h @@ -0,0 +1,73 @@ +#ifndef _BOARD_TRATS2_SETUP_ +#define _BOARD_TRATS2_SETUP_ + +/* A/M PLL_CON0 */ +#define SDIV(x) ((x) & 0x7) +#define PDIV(x) ((x & 0x3f) << 8) +#define MDIV(x) ((x & 0x3ff) << 16) +#define FSEL(x) ((x & 0x1) << 27) +#define PLL_LOCKED_BIT (0x1 << 29) +#define PLL_ENABLE(x) ((x & 0x1) << 31) + +/* CLK_SRC_CPU */ +#define MUX_APLL_SEL(x) ((x) & 0x1) +#define MUX_CORE_SEL(x) ((x & 0x1) << 16) +#define MUX_HPM_SEL(x) ((x & 0x1) << 20) +#define MUX_MPLL_USER_SEL_C(x) ((x & 0x1) << 24) + +/* CLK_MUX_STAT_CPU */ +#define APLL_SEL(x) ((x) & 0x7) +#define CORE_SEL(x) ((x & 0x7) << 16) +#define HPM_SEL(x) ((x & 0x7) << 20) +#define MPLL_USER_SEL_C(x) ((x & 0x7) << 24) +#define MUX_STAT_CHANGING 0x100 +#define MUX_STAT_CPU_CHANGING (APLL_SEL(MUX_STAT_CHANGING) | \ + CORE_SEL(MUX_STAT_CHANGING) | \ + HPM_SEL(MUX_STAT_CHANGING) | \ + MPLL_USER_SEL_C(MUX_STAT_CHANGING)) + +/* CLK_DIV_CPU0 */ +#define CORE_RATIO(x) ((x) & 0x7) +#define COREM0_RATIO(x) ((x & 0x7) << 4) +#define COREM1_RATIO(x) ((x & 0x7) << 8) +#define PERIPH_RATIO(x) ((x & 0x7) << 12) +#define ATB_RATIO(x) ((x & 0x7) << 16) +#define PCLK_DBG_RATIO(x) ((x & 0x7) << 20) +#define APLL_RATIO(x) ((x & 0x7) << 24) +#define CORE2_RATIO(x) ((x & 0x7) << 28) + +/* CLK_DIV_STAT_CPU0 */ +#define DIV_CORE(x) ((x) & 0x1) +#define DIV_COREM0(x) ((x & 0x1) << 4) +#define DIV_COREM1(x) ((x & 0x1) << 8) +#define DIV_PERIPH(x) ((x & 0x1) << 12) +#define DIV_ATB(x) ((x & 0x1) << 16) +#define DIV_PCLK_DBG(x) ((x & 0x1) << 20) +#define DIV_APLL(x) ((x & 0x1) << 24) +#define DIV_CORE2(x) ((x & 0x1) << 28) + +#define DIV_STAT_CHANGING 0x1 +#define DIV_STAT_CPU0_CHANGING (DIV_CORE(DIV_STAT_CHANGING) | \ + DIV_COREM0(DIV_STAT_CHANGING) | \ + DIV_COREM1(DIV_STAT_CHANGING) | \ + DIV_PERIPH(DIV_STAT_CHANGING) | \ + DIV_ATB(DIV_STAT_CHANGING) | \ + DIV_PCLK_DBG(DIV_STAT_CHANGING) | \ + DIV_APLL(DIV_STAT_CHANGING) | \ + DIV_CORE2(DIV_STAT_CHANGING)) + +/* CLK_DIV_CPU1 */ +#define COPY_RATIO(x) ((x) & 0x7) +#define HPM_RATIO(x) ((x & 0x7) << 4) +#define CORES_RATIO(x) ((x & 0x7) << 8) + +/* CLK_DIV_STAT_CPU1 */ +#define DIV_COPY(x) ((x) & 0x7) +#define DIV_HPM(x) ((x & 0x1) << 4) +#define DIV_CORES(x) ((x & 0x1) << 8) + +#define DIV_STAT_CPU1_CHANGING (DIV_COPY(DIV_STAT_CHANGING) | \ + DIV_HPM(DIV_STAT_CHANGING) | \ + DIV_CORES(DIV_STAT_CHANGING)) + +#endif /* _BOARD_TRATS2_SETUP_ */ diff --git a/board/samsung/trats2/trats2.c b/board/samsung/trats2/trats2.c index 9858150..3def401 100644 --- a/board/samsung/trats2/trats2.c +++ b/board/samsung/trats2/trats2.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -18,13 +19,21 @@ #include #include #include +#include #include #include #include #include +#include "setup.h" -static unsigned int board_rev = -1; +DECLARE_GLOBAL_DATA_PTR; + +/* For global battery and charger functions */ +static struct power_battery *pbat; +static struct pmic *p_chrg, *p_muic, *p_fg, *p_bat; +static int power_init_done; +static unsigned int board_rev = -1; static inline u32 get_model_rev(void); static void check_hw_revision(void) @@ -168,6 +177,9 @@ int exynos_power_init(void) p_chrg->parent = p_bat; p_muic->parent = p_bat; +#ifdef CONFIG_INTERACTIVE_CHARGER + p_bat->low_power_mode = board_low_power_mode; +#endif p_bat->pbat->battery_init(p_bat, p_fg, p_chrg, p_muic); pb = p_bat->pbat; @@ -183,6 +195,9 @@ int exynos_power_init(void) if (pb->bat->state == CHARGE && chrg == CHARGER_USB) puts("CHARGE Battery !\n"); + + pbat = p_bat->pbat; + power_init_done = 1; #endif return 0; } @@ -335,6 +350,131 @@ void exynos_lcd_misc_init(vidinfo_t *vid) } #endif /* LCD */ +void low_clock_mode(void) +{ + struct exynos4x12_clock *clk = (struct exynos4x12_clock *) + samsung_get_base_clock(); + + unsigned int cfg_apll_con0; + unsigned int cfg_src_cpu; + unsigned int cfg_div_cpu0; + unsigned int cfg_div_cpu1; + unsigned int clk_gate_cfg; + + /* Turn off unnecessary clocks */ + clk_gate_cfg = 0x0; + writel(clk_gate_cfg, &clk->gate_ip_image); /* IMAGE */ + writel(clk_gate_cfg, &clk->gate_ip_cam); /* CAM */ + writel(clk_gate_cfg, &clk->gate_ip_tv); /* TV */ + writel(clk_gate_cfg, &clk->gate_ip_mfc); /* MFC */ + writel(clk_gate_cfg, &clk->gate_ip_g3d); /* G3D */ + writel(clk_gate_cfg, &clk->gate_ip_gps); /* GPS */ + writel(clk_gate_cfg, &clk->gate_ip_isp1); /* ISP1 */ + + /* + * Set CMU_CPU clocks src to MPLL + * Bit values: 0 ; 1 + * MUX_APLL_SEL: FIN_PLL; FOUT_APLL + * MUX_CORE_SEL: MOUT_APLL; SCLK_MPLL + * MUX_HPM_SEL: MOUT_APLL; SCLK_MPLL_USER_C + * MUX_MPLL_USER_SEL_C: FIN_PLL; SCLK_MPLL + */ + cfg_src_cpu = MUX_APLL_SEL(1) | MUX_CORE_SEL(1) | MUX_HPM_SEL(1) | + MUX_MPLL_USER_SEL_C(1); + writel(cfg_src_cpu, &clk->src_cpu); + + /* Disable APLL */ + cfg_apll_con0 = readl(&clk->apll_con0); + writel(cfg_apll_con0 & ~PLL_ENABLE(1), &clk->apll_con0); + + /* Set APLL to 200MHz */ + cfg_apll_con0 = SDIV(2) | PDIV(3) | MDIV(100) | FSEL(1) | PLL_ENABLE(1); + writel(cfg_apll_con0, &clk->apll_con0); + + /* Wait for PLL to be locked */ + while (!(readl(&clk->apll_con0) & PLL_LOCKED_BIT)) + continue; + + /* Set CMU_CPU clock src to APLL */ + cfg_src_cpu = MUX_APLL_SEL(1) | MUX_CORE_SEL(0) | MUX_HPM_SEL(0) | + MUX_MPLL_USER_SEL_C(0); + writel(cfg_src_cpu, &clk->src_cpu); + + /* Wait for MUX ready status */ + while (readl(&clk->src_cpu) & MUX_STAT_CPU_CHANGING) + continue; + + /* + * Set dividers for MOUTcore = 200 MHz + * coreout = MOUT / (ratio + 1) = 200 MHz + * corem0 = armclk / (ratio + 1) = 200 MHz + * corem1 = armclk / (ratio + 1) = 200 MHz + * periph = armclk / (ratio + 1) = 200 MHz + * atbout = MOUT / (ratio + 1) = 200 MHz + * pclkdbgout = atbout / (ratio + 1) = 200 MHz + * sclkapll = MOUTapll / (ratio + 1) = 50 MHz + * armclk = core_out / (ratio + 1) = 200 MHz + */ + cfg_div_cpu0 = CORE_RATIO(0) | COREM0_RATIO(0) | COREM1_RATIO(0) | + PERIPH_RATIO(0) | ATB_RATIO(0) | PCLK_DBG_RATIO(1) | + APLL_RATIO(3) | CORE2_RATIO(0); + writel(cfg_div_cpu0, &clk->div_cpu0); + + /* Wait for divider ready status */ + while (readl(&clk->div_stat_cpu0) & DIV_STAT_CPU0_CHANGING) + continue; + + /* + * For MOUThpm = 200 MHz (MOUTapll) + * doutcopy = MOUThpm / (ratio + 1) = 100 + * sclkhpm = doutcopy / (ratio + 1) = 100 + * cores_out = armclk / (ratio + 1) = 200 + */ + cfg_div_cpu1 = COPY_RATIO(1) | HPM_RATIO(0) | CORES_RATIO(0); + writel(cfg_div_cpu1, &clk->div_cpu1); /* DIV_CPU1 */ + + /* Wait for divider ready status */ + while (readl(&clk->div_stat_cpu1) & DIV_STAT_CPU1_CHANGING) + continue; +} + +#ifdef CONFIG_INTERACTIVE_CHARGER +static int low_power_mode_set; + +void board_low_power_mode(void) +{ + struct exynos4x12_power *pwr = (struct exynos4x12_power *) + samsung_get_base_power(); + + unsigned int pwr_core_cfg = 0x0; + unsigned int pwr_cfg = 0x0; + + /* Set low power mode only once */ + if (low_power_mode_set) + return; + + /* Power down CORES: 1, 2, 3 */ + /* LOCAL_PWR_CFG [1:0] 0x3 EN, 0x0 DIS */ + writel(pwr_core_cfg, &pwr->arm_core1_configuration); + writel(pwr_core_cfg, &pwr->arm_core2_configuration); + writel(pwr_core_cfg, &pwr->arm_core3_configuration); + + /* Turn off unnecessary power domains */ + writel(pwr_cfg, &pwr->xxti_configuration); /* XXTI */ + writel(pwr_cfg, &pwr->cam_configuration); /* CAM */ + writel(pwr_cfg, &pwr->tv_configuration); /* TV */ + writel(pwr_cfg, &pwr->mfc_configuration); /* MFC */ + writel(pwr_cfg, &pwr->g3d_configuration); /* G3D */ + writel(pwr_cfg, &pwr->gps_configuration); /* GPS */ + writel(pwr_cfg, &pwr->gps_alive_configuration); /* GPS_ALIVE */ + + /* Set CPU clock to 200MHz */ + low_clock_mode(); + + low_power_mode_set = 1; +} +#endif + #ifdef CONFIG_CMD_POWEROFF void board_poweroff(void) { @@ -351,3 +491,108 @@ void board_poweroff(void) /* Should not reach here */ } #endif + +/* Functions for interactive charger in board/samsung/common/misc.c */ +#ifdef CONFIG_INTERACTIVE_CHARGER +int charger_enable(void) +{ +#ifndef CONFIG_DM_I2C /* TODO(maintainer): Convert to driver model */ + if (!power_init_done) { + if (exynos_power_init()) { + puts("Can't init board power subsystem"); + return -1; + } + } + + if (!pbat->battery_charge) { + puts("Can't enable charger\n"); + return -1; + } + + /* Enable charger */ + if (pbat->battery_charge(p_bat)) { + puts("Charger enable error\n"); + return -1; + } +#endif + + return 0; +} + +int charger_type(void) +{ +#ifndef CONFIG_DM_I2C /* TODO(maintainer): Convert to driver model */ + if (!power_init_done) { + if (exynos_power_init()) { + puts("Can't init board power subsystem"); + return -1; + } + } + + if (!p_muic->chrg->chrg_type) { + puts("Can't get charger type\n"); + return -1; + } + + return p_muic->chrg->chrg_type(p_muic); +#else + return 0; +#endif +} + +int battery_present(void) +{ +#ifndef CONFIG_DM_I2C /* TODO(maintainer): Convert to driver model */ + if (!power_init_done) { + if (exynos_power_init()) { + puts("Can't init board power subsystem"); + return -1; + } + } + + if (!p_chrg->chrg->chrg_bat_present) { + puts("Can't get battery state\n"); + return -1; + } + + if (!p_chrg->chrg->chrg_bat_present(p_chrg)) { + puts("Battery not present.\n"); + return 0; + } +#endif + + return 1; +} + +int battery_state(unsigned int *soc) +{ +#ifndef CONFIG_DM_I2C /* TODO(maintainer): Convert to driver model */ + struct battery *bat = pbat->bat; + + if (!power_init_done) { + if (exynos_power_init()) { + printf("Can't init board power subsystem"); + return -1; + } + } + + if (!p_fg->fg->fg_battery_update) { + puts("Can't update battery state\n"); + return -1; + } + + /* Check battery state */ + if (p_fg->fg->fg_battery_update(p_fg, p_bat)) { + puts("Battery update error\n"); + return -1; + } + + debug("[BAT]:\n#state:%u\n#soc:%3.1u\n#vcell:%u\n", bat->state, + bat->state_of_chrg, + bat->voltage_uV); + + *soc = bat->state_of_chrg; +#endif + return 0; +} +#endif -- 2.7.4