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