diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/u-boot/drivers/power | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/u-boot/drivers/power')
29 files changed, 3213 insertions, 0 deletions
diff --git a/qemu/roms/u-boot/drivers/power/Makefile b/qemu/roms/u-boot/drivers/power/Makefile new file mode 100644 index 000000000..53ff97d74 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/Makefile @@ -0,0 +1,19 @@ +# +# Copyright (c) 2009 Wind River Systems, Inc. +# Tom Rix <Tom.Rix at windriver.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_EXYNOS_TMU) += exynos-tmu.o +obj-$(CONFIG_FTPMU010_POWER) += ftpmu010.o +obj-$(CONFIG_TPS6586X_POWER) += tps6586x.o +obj-$(CONFIG_TWL4030_POWER) += twl4030.o +obj-$(CONFIG_TWL6030_POWER) += twl6030.o +obj-$(CONFIG_PALMAS_POWER) += palmas.o + +obj-$(CONFIG_POWER) += power_core.o +obj-$(CONFIG_DIALOG_POWER) += power_dialog.o +obj-$(CONFIG_POWER_FSL) += power_fsl.o +obj-$(CONFIG_POWER_I2C) += power_i2c.o +obj-$(CONFIG_POWER_SPI) += power_spi.o diff --git a/qemu/roms/u-boot/drivers/power/battery/Makefile b/qemu/roms/u-boot/drivers/power/battery/Makefile new file mode 100644 index 000000000..f864f0439 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/battery/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski <l.majewski@samsung.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_BATTERY_TRATS) += bat_trats.o +obj-$(CONFIG_POWER_BATTERY_TRATS2) += bat_trats2.o diff --git a/qemu/roms/u-boot/drivers/power/battery/bat_trats.c b/qemu/roms/u-boot/drivers/power/battery/bat_trats.c new file mode 100644 index 000000000..41b179fc5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/battery/bat_trats.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/battery.h> +#include <power/max8997_pmic.h> +#include <errno.h> + +static struct battery battery_trats; + +static int power_battery_charge(struct pmic *bat) +{ + struct power_battery *p_bat = bat->pbat; + struct battery *battery = p_bat->bat; + int k; + + if (bat->chrg->chrg_state(p_bat->chrg, CHARGER_ENABLE, 450)) + return -1; + + for (k = 0; bat->chrg->chrg_bat_present(p_bat->chrg) && + bat->chrg->chrg_type(p_bat->muic) && + battery->state_of_chrg < 100; k++) { + udelay(2000000); + if (!(k % 5)) + puts("."); + bat->fg->fg_battery_update(p_bat->fg, bat); + + if (k == 200) { + debug(" %d [V]", battery->voltage_uV); + puts("\n"); + k = 0; + } + + if (ctrlc()) { + printf("\nCharging disabled on request.\n"); + goto exit; + } + } + exit: + bat->chrg->chrg_state(p_bat->chrg, CHARGER_DISABLE, 0); + + return 0; +} + +static int power_battery_init_trats(struct pmic *bat_, + struct pmic *fg_, + struct pmic *chrg_, + struct pmic *muic_) +{ + bat_->pbat->fg = fg_; + bat_->pbat->chrg = chrg_; + bat_->pbat->muic = muic_; + + bat_->fg = fg_->fg; + bat_->chrg = chrg_->chrg; + bat_->chrg->chrg_type = muic_->chrg->chrg_type; + return 0; +} + +static struct power_battery power_bat_trats = { + .bat = &battery_trats, + .battery_init = power_battery_init_trats, + .battery_charge = power_battery_charge, +}; + +int power_bat_init(unsigned char bus) +{ + static const char name[] = "BAT_TRATS"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board BAT init\n"); + + p->interface = PMIC_NONE; + p->name = name; + p->bus = bus; + + p->pbat = &power_bat_trats; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/battery/bat_trats2.c b/qemu/roms/u-boot/drivers/power/battery/bat_trats2.c new file mode 100644 index 000000000..94015aa41 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/battery/bat_trats2.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek <p.wilczek@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/battery.h> +#include <power/max77693_pmic.h> +#include <errno.h> + +static struct battery battery_trats; + +static int power_battery_charge(struct pmic *bat) +{ + struct power_battery *p_bat = bat->pbat; + + if (bat->chrg->chrg_state(p_bat->chrg, CHARGER_ENABLE, 450)) + return -1; + + return 0; +} + +static int power_battery_init_trats2(struct pmic *bat_, + struct pmic *fg_, + struct pmic *chrg_, + struct pmic *muic_) +{ + bat_->pbat->fg = fg_; + bat_->pbat->chrg = chrg_; + bat_->pbat->muic = muic_; + + bat_->fg = fg_->fg; + bat_->chrg = chrg_->chrg; + bat_->chrg->chrg_type = muic_->chrg->chrg_type; + return 0; +} + +static struct power_battery power_bat_trats2 = { + .bat = &battery_trats, + .battery_init = power_battery_init_trats2, + .battery_charge = power_battery_charge, +}; + +int power_bat_init(unsigned char bus) +{ + static const char name[] = "BAT_TRATS2"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board BAT init\n"); + + p->interface = PMIC_NONE; + p->name = name; + p->bus = bus; + + p->pbat = &power_bat_trats2; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/exynos-tmu.c b/qemu/roms/u-boot/drivers/power/exynos-tmu.c new file mode 100644 index 000000000..9a093a5bd --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/exynos-tmu.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Akshay Saraswat <akshay.s@samsung.com> + * + * EXYNOS - Thermal Management Unit + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <tmu.h> +#include <asm/arch/tmu.h> +#include <asm/arch/power.h> + +#define TRIMINFO_RELOAD 1 +#define CORE_EN 1 +#define THERM_TRIP_EN (1 << 12) + +#define INTEN_RISE0 1 +#define INTEN_RISE1 (1 << 4) +#define INTEN_RISE2 (1 << 8) +#define INTEN_FALL0 (1 << 16) +#define INTEN_FALL1 (1 << 20) +#define INTEN_FALL2 (1 << 24) + +#define TRIM_INFO_MASK 0xff + +#define INTCLEAR_RISE0 1 +#define INTCLEAR_RISE1 (1 << 4) +#define INTCLEAR_RISE2 (1 << 8) +#define INTCLEAR_FALL0 (1 << 16) +#define INTCLEAR_FALL1 (1 << 20) +#define INTCLEAR_FALL2 (1 << 24) +#define INTCLEARALL (INTCLEAR_RISE0 | INTCLEAR_RISE1 | \ + INTCLEAR_RISE2 | INTCLEAR_FALL0 | \ + INTCLEAR_FALL1 | INTCLEAR_FALL2) + +/* Tmeperature threshold values for various thermal events */ +struct temperature_params { + /* minimum value in temperature code range */ + unsigned min_val; + /* maximum value in temperature code range */ + unsigned max_val; + /* temperature threshold to start warning */ + unsigned start_warning; + /* temperature threshold CPU tripping */ + unsigned start_tripping; + /* temperature threshold for HW tripping */ + unsigned hardware_tripping; +}; + +/* Pre-defined values and thresholds for calibration of current temperature */ +struct tmu_data { + /* pre-defined temperature thresholds */ + struct temperature_params ts; + /* pre-defined efuse range minimum value */ + unsigned efuse_min_value; + /* pre-defined efuse value for temperature calibration */ + unsigned efuse_value; + /* pre-defined efuse range maximum value */ + unsigned efuse_max_value; + /* current temperature sensing slope */ + unsigned slope; +}; + +/* TMU device specific details and status */ +struct tmu_info { + /* base Address for the TMU */ + struct exynos5_tmu_reg *tmu_base; + /* mux Address for the TMU */ + int tmu_mux; + /* pre-defined values for calibration and thresholds */ + struct tmu_data data; + /* value required for triminfo_25 calibration */ + unsigned te1; + /* value required for triminfo_85 calibration */ + unsigned te2; + /* Value for measured data calibration */ + int dc_value; + /* enum value indicating status of the TMU */ + int tmu_state; +}; + +/* Global struct tmu_info variable to store init values */ +static struct tmu_info gbl_info; + +/* + * Get current temperature code from register, + * then calculate and calibrate it's value + * in degree celsius. + * + * @return current temperature of the chip as sensed by TMU + */ +static int get_cur_temp(struct tmu_info *info) +{ + struct exynos5_tmu_reg *reg = info->tmu_base; + ulong start; + int cur_temp = 0; + + /* + * Temperature code range between min 25 and max 125. + * May run more than once for first call as initial sensing + * has not yet happened. + */ + if (info->tmu_state == TMU_STATUS_NORMAL) { + start = get_timer(0); + do { + cur_temp = readl(®->current_temp) & 0xff; + } while ((cur_temp == 0) || (get_timer(start) > 100)); + } + + if (cur_temp == 0) + return cur_temp; + + /* Calibrate current temperature */ + cur_temp = cur_temp - info->te1 + info->dc_value; + + return cur_temp; +} + +/* + * Monitors status of the TMU device and exynos temperature + * + * @param temp pointer to the current temperature value + * @return enum tmu_status_t value, code indicating event to execute + */ +enum tmu_status_t tmu_monitor(int *temp) +{ + int cur_temp; + struct tmu_data *data = &gbl_info.data; + + if (gbl_info.tmu_state == TMU_STATUS_INIT) + return TMU_STATUS_INIT; + + /* Read current temperature of the SOC */ + cur_temp = get_cur_temp(&gbl_info); + + if (!cur_temp) + goto out; + + *temp = cur_temp; + + /* Temperature code lies between min 25 and max 125 */ + if ((cur_temp >= data->ts.start_tripping) && + (cur_temp <= data->ts.max_val)) + return TMU_STATUS_TRIPPED; + + if (cur_temp >= data->ts.start_warning) + return TMU_STATUS_WARNING; + + if ((cur_temp < data->ts.start_warning) && + (cur_temp >= data->ts.min_val)) + return TMU_STATUS_NORMAL; + + out: + /* Temperature code does not lie between min 25 and max 125 */ + gbl_info.tmu_state = TMU_STATUS_INIT; + debug("EXYNOS_TMU: Thermal reading failed\n"); + return TMU_STATUS_INIT; +} + +/* + * Get TMU specific pre-defined values from FDT + * + * @param info pointer to the tmu_info struct + * @param blob FDT blob + * @return int value, 0 for success + */ +static int get_tmu_fdt_values(struct tmu_info *info, const void *blob) +{ +#ifdef CONFIG_OF_CONTROL + fdt_addr_t addr; + int node; + int error = 0; + + /* Get the node from FDT for TMU */ + node = fdtdec_next_compatible(blob, 0, + COMPAT_SAMSUNG_EXYNOS_TMU); + if (node < 0) { + debug("EXYNOS_TMU: No node for tmu in device tree\n"); + return -1; + } + + /* + * Get the pre-defined TMU specific values from FDT. + * All of these are expected to be correct otherwise + * miscalculation of register values in tmu_setup_parameters + * may result in misleading current temperature. + */ + addr = fdtdec_get_addr(blob, node, "reg"); + if (addr == FDT_ADDR_T_NONE) { + debug("%s: Missing tmu-base\n", __func__); + return -1; + } + info->tmu_base = (struct exynos5_tmu_reg *)addr; + + /* Optional field. */ + info->tmu_mux = fdtdec_get_int(blob, + node, "samsung,mux", -1); + /* Take default value as per the user manual b(110) */ + if (info->tmu_mux == -1) + info->tmu_mux = 0x6; + + info->data.ts.min_val = fdtdec_get_int(blob, + node, "samsung,min-temp", -1); + error |= (info->data.ts.min_val == -1); + info->data.ts.max_val = fdtdec_get_int(blob, + node, "samsung,max-temp", -1); + error |= (info->data.ts.max_val == -1); + info->data.ts.start_warning = fdtdec_get_int(blob, + node, "samsung,start-warning", -1); + error |= (info->data.ts.start_warning == -1); + info->data.ts.start_tripping = fdtdec_get_int(blob, + node, "samsung,start-tripping", -1); + error |= (info->data.ts.start_tripping == -1); + info->data.ts.hardware_tripping = fdtdec_get_int(blob, + node, "samsung,hw-tripping", -1); + error |= (info->data.ts.hardware_tripping == -1); + info->data.efuse_min_value = fdtdec_get_int(blob, + node, "samsung,efuse-min-value", -1); + error |= (info->data.efuse_min_value == -1); + info->data.efuse_value = fdtdec_get_int(blob, + node, "samsung,efuse-value", -1); + error |= (info->data.efuse_value == -1); + info->data.efuse_max_value = fdtdec_get_int(blob, + node, "samsung,efuse-max-value", -1); + error |= (info->data.efuse_max_value == -1); + info->data.slope = fdtdec_get_int(blob, + node, "samsung,slope", -1); + error |= (info->data.slope == -1); + info->dc_value = fdtdec_get_int(blob, + node, "samsung,dc-value", -1); + error |= (info->dc_value == -1); + + if (error) { + debug("fail to get tmu node properties\n"); + return -1; + } +#else + /* Non DT support may never be added. Just in case */ + return -1; +#endif + + return 0; +} + +/* + * Calibrate and calculate threshold values and + * enable interrupt levels + * + * @param info pointer to the tmu_info struct + */ +static void tmu_setup_parameters(struct tmu_info *info) +{ + unsigned te_code, con; + unsigned warning_code, trip_code, hwtrip_code; + unsigned cooling_temp; + unsigned rising_value; + struct tmu_data *data = &info->data; + struct exynos5_tmu_reg *reg = info->tmu_base; + + /* Must reload for reading efuse value from triminfo register */ + writel(TRIMINFO_RELOAD, ®->triminfo_control); + + /* Get the compensation parameter */ + te_code = readl(®->triminfo); + info->te1 = te_code & TRIM_INFO_MASK; + info->te2 = ((te_code >> 8) & TRIM_INFO_MASK); + + if ((data->efuse_min_value > info->te1) || + (info->te1 > data->efuse_max_value) + || (info->te2 != 0)) + info->te1 = data->efuse_value; + + /* Get RISING & FALLING Threshold value */ + warning_code = data->ts.start_warning + + info->te1 - info->dc_value; + trip_code = data->ts.start_tripping + + info->te1 - info->dc_value; + hwtrip_code = data->ts.hardware_tripping + + info->te1 - info->dc_value; + + cooling_temp = 0; + + rising_value = ((warning_code << 8) | + (trip_code << 16) | + (hwtrip_code << 24)); + + /* Set interrupt level */ + writel(rising_value, ®->threshold_temp_rise); + writel(cooling_temp, ®->threshold_temp_fall); + + /* + * Init TMU control tuning parameters + * [28:24] VREF - Voltage reference + * [15:13] THERM_TRIP_MODE - Tripping mode + * [12] THERM_TRIP_EN - Thermal tripping enable + * [11:8] BUF_SLOPE_SEL - Gain of amplifier + * [6] THERM_TRIP_BY_TQ_EN - Tripping by TQ pin + */ + writel(data->slope, ®->tmu_control); + + writel(INTCLEARALL, ®->intclear); + + /* TMU core enable */ + con = readl(®->tmu_control); + con |= THERM_TRIP_EN | CORE_EN | (info->tmu_mux << 20); + + writel(con, ®->tmu_control); + + /* Enable HW thermal trip */ + set_hw_thermal_trip(); + + /* LEV1 LEV2 interrupt enable */ + writel(INTEN_RISE1 | INTEN_RISE2, ®->inten); +} + +/* + * Initialize TMU device + * + * @param blob FDT blob + * @return int value, 0 for success + */ +int tmu_init(const void *blob) +{ + gbl_info.tmu_state = TMU_STATUS_INIT; + if (get_tmu_fdt_values(&gbl_info, blob) < 0) + goto ret; + + tmu_setup_parameters(&gbl_info); + gbl_info.tmu_state = TMU_STATUS_NORMAL; +ret: + return gbl_info.tmu_state; +} diff --git a/qemu/roms/u-boot/drivers/power/ftpmu010.c b/qemu/roms/u-boot/drivers/power/ftpmu010.c new file mode 100644 index 000000000..78128c641 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/ftpmu010.c @@ -0,0 +1,88 @@ +/* + * (C) Copyright 2009 Faraday Technology + * Po-Yu Chuang <ratbert@faraday-tech.com> + * + * Copyright (C) 2010 Andes Technology Corporation + * Shawn Lin, Andes Technology Corporation <nobuhiro@andestech.com> + * Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <faraday/ftpmu010.h> + +/* OSCC: OSC Control Register */ +void ftpmu010_32768osc_enable(void) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int oscc; + + /* enable the 32768Hz oscillator */ + oscc = readl(&pmu->OSCC); + oscc &= ~(FTPMU010_OSCC_OSCL_OFF | FTPMU010_OSCC_OSCL_TRI); + writel(oscc, &pmu->OSCC); + + /* wait until ready */ + while (!(readl(&pmu->OSCC) & FTPMU010_OSCC_OSCL_STABLE)) + ; + + /* select 32768Hz oscillator */ + oscc = readl(&pmu->OSCC); + oscc |= FTPMU010_OSCC_OSCL_RTCLSEL; + writel(oscc, &pmu->OSCC); +} + +/* MFPSR: Multi-Function Port Setting Register */ +void ftpmu010_mfpsr_select_dev(unsigned int dev) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int mfpsr; + + mfpsr = readl(&pmu->MFPSR); + mfpsr |= dev; + writel(mfpsr, &pmu->MFPSR); +} + +void ftpmu010_mfpsr_diselect_dev(unsigned int dev) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int mfpsr; + + mfpsr = readl(&pmu->MFPSR); + mfpsr &= ~dev; + writel(mfpsr, &pmu->MFPSR); +} + +/* PDLLCR0: PLL/DLL Control Register 0 */ +void ftpmu010_dlldis_disable(void) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int pdllcr0; + + pdllcr0 = readl(&pmu->PDLLCR0); + pdllcr0 |= FTPMU010_PDLLCR0_DLLDIS; + writel(pdllcr0, &pmu->PDLLCR0); +} + +void ftpmu010_sdram_clk_disable(unsigned int cr0) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int pdllcr0; + + pdllcr0 = readl(&pmu->PDLLCR0); + pdllcr0 |= FTPMU010_PDLLCR0_HCLKOUTDIS(cr0); + writel(pdllcr0, &pmu->PDLLCR0); +} + +/* SDRAMHTC: SDRAM Signal Hold Time Control */ +void ftpmu010_sdramhtc_set(unsigned int val) +{ + static struct ftpmu010 *pmu = (struct ftpmu010 *)CONFIG_FTPMU010_BASE; + unsigned int sdramhtc; + + sdramhtc = readl(&pmu->SDRAMHTC); + sdramhtc |= val; + writel(sdramhtc, &pmu->SDRAMHTC); +} diff --git a/qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile b/qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile new file mode 100644 index 000000000..3b349f939 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/fuel_gauge/Makefile @@ -0,0 +1,8 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski <l.majewski@samsung.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_FG_MAX17042) += fg_max17042.o diff --git a/qemu/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c b/qemu/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c new file mode 100644 index 000000000..154ca6a69 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/max17042_fg.h> +#include <i2c.h> +#include <power/max8997_pmic.h> +#include <power/power_chrg.h> +#include <power/battery.h> +#include <power/fg_battery_cell_params.h> +#include <errno.h> + +static int fg_write_regs(struct pmic *p, u8 addr, u16 *data, int num) +{ + int ret = 0; + int i; + + for (i = 0; i < num; i++, addr++) { + ret = pmic_reg_write(p, addr, *(data + i)); + if (ret) + return ret; + } + + return 0; +} + +static int fg_read_regs(struct pmic *p, u8 addr, u16 *data, int num) +{ + unsigned int dat; + int ret = 0; + int i; + + for (i = 0; i < num; i++, addr++) { + ret = pmic_reg_read(p, addr, &dat); + if (ret) + return ret; + + *(data + i) = (u16)dat; + } + + return 0; +} + +static int fg_write_and_verify(struct pmic *p, u8 addr, u16 data) +{ + unsigned int val = data; + int ret = 0; + + ret |= pmic_reg_write(p, addr, val); + ret |= pmic_reg_read(p, addr, &val); + + if (ret) + return ret; + + if (((u16) val) == data) + return 0; + + return -1; +} + +static void por_fuelgauge_init(struct pmic *p) +{ + u16 r_data0[16], r_data1[16], r_data2[16]; + u32 rewrite_count = 5; + u32 check_count; + u32 lock_count; + u32 i = 0; + u32 val; + s32 ret = 0; + char *status_msg; + + /* Delay 500 ms */ + mdelay(500); + /* Initilize Configuration */ + pmic_reg_write(p, MAX17042_CONFIG, 0x2310); + +rewrite_model: + check_count = 5; + lock_count = 5; + + if (!rewrite_count--) { + status_msg = "init failed!"; + goto error; + } + + /* Unlock Model Access */ + pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_UNLOCK1); + pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_UNLOCK2); + + /* Write/Read/Verify the Custom Model */ + ret = fg_write_regs(p, MAX17042_MODEL1, cell_character0, + ARRAY_SIZE(cell_character0)); + if (ret) + goto rewrite_model; + + ret = fg_write_regs(p, MAX17042_MODEL2, cell_character1, + ARRAY_SIZE(cell_character1)); + if (ret) + goto rewrite_model; + + ret = fg_write_regs(p, MAX17042_MODEL3, cell_character2, + ARRAY_SIZE(cell_character2)); + if (ret) + goto rewrite_model; + +check_model: + if (!check_count--) { + if (rewrite_count) + goto rewrite_model; + else + status_msg = "check failed!"; + + goto error; + } + + ret = fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); + if (ret) + goto check_model; + + ret = fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); + if (ret) + goto check_model; + + ret = fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); + if (ret) + goto check_model; + + for (i = 0; i < 16; i++) { + if ((cell_character0[i] != r_data0[i]) + || (cell_character1[i] != r_data1[i]) + || (cell_character2[i] != r_data2[i])) + goto rewrite_model; + } + +lock_model: + if (!lock_count--) { + if (rewrite_count) + goto rewrite_model; + else + status_msg = "lock failed!"; + + goto error; + } + + /* Lock model access */ + pmic_reg_write(p, MAX17042_MLOCKReg1, MODEL_LOCK1); + pmic_reg_write(p, MAX17042_MLOCKReg2, MODEL_LOCK2); + + /* Verify the model access is locked */ + ret = fg_read_regs(p, MAX17042_MODEL1, r_data0, ARRAY_SIZE(r_data0)); + if (ret) + goto lock_model; + + ret = fg_read_regs(p, MAX17042_MODEL2, r_data1, ARRAY_SIZE(r_data1)); + if (ret) + goto lock_model; + + ret = fg_read_regs(p, MAX17042_MODEL3, r_data2, ARRAY_SIZE(r_data2)); + if (ret) + goto lock_model; + + for (i = 0; i < ARRAY_SIZE(r_data0); i++) { + /* Check if model locked */ + if (r_data0[i] || r_data1[i] || r_data2[i]) + goto lock_model; + } + + /* Write Custom Parameters */ + fg_write_and_verify(p, MAX17042_RCOMP0, RCOMP0); + fg_write_and_verify(p, MAX17042_TEMPCO, TempCo); + + /* Delay at least 350mS */ + mdelay(350); + + /* Initialization Complete */ + pmic_reg_read(p, MAX17042_STATUS, &val); + /* Write and Verify Status with POR bit Cleared */ + fg_write_and_verify(p, MAX17042_STATUS, val & ~MAX17042_POR); + + /* Delay at least 350 ms */ + mdelay(350); + + status_msg = "OK!"; +error: + debug("%s: model init status: %s\n", p->name, status_msg); + return; +} + +static int power_update_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + unsigned int val; + int ret = 0; + + if (pmic_probe(p)) { + puts("Can't find max17042 fuel gauge\n"); + return -1; + } + + ret |= pmic_reg_read(p, MAX17042_VFSOC, &val); + pb->bat->state_of_chrg = (val >> 8); + + pmic_reg_read(p, MAX17042_VCELL, &val); + debug("vfsoc: 0x%x\n", val); + pb->bat->voltage_uV = ((val & 0xFFUL) >> 3) + ((val & 0xFF00) >> 3); + pb->bat->voltage_uV = (pb->bat->voltage_uV * 625); + + pmic_reg_read(p, 0x05, &val); + pb->bat->capacity = val >> 2; + + return ret; +} + +static int power_check_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + unsigned int val; + int ret = 0; + + if (pmic_probe(p)) { + puts("Can't find max17042 fuel gauge\n"); + return -1; + } + + ret |= pmic_reg_read(p, MAX17042_STATUS, &val); + debug("fg status: 0x%x\n", val); + + if (val & MAX17042_POR) + por_fuelgauge_init(p); + + ret |= pmic_reg_read(p, MAX17042_VERSION, &val); + pb->bat->version = val; + + power_update_battery(p, bat); + debug("fg ver: 0x%x\n", pb->bat->version); + printf("BAT: state_of_charge(SOC):%d%%\n", + pb->bat->state_of_chrg); + + printf(" voltage: %d.%6.6d [V] (expected to be %d [mAh])\n", + pb->bat->voltage_uV / 1000000, + pb->bat->voltage_uV % 1000000, + pb->bat->capacity); + + if (pb->bat->voltage_uV > 3850000) + pb->bat->state = EXT_SOURCE; + else if (pb->bat->voltage_uV < 3600000 || pb->bat->state_of_chrg < 5) + pb->bat->state = CHARGE; + else + pb->bat->state = NORMAL; + + return ret; +} + +static struct power_fg power_fg_ops = { + .fg_battery_check = power_check_battery, + .fg_battery_update = power_update_battery, +}; + +int power_fg_init(unsigned char bus) +{ + static const char name[] = "MAX17042_FG"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Fuel Gauge init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = FG_NUM_OF_REGS; + p->hw.i2c.addr = MAX17042_I2C_ADDR; + p->hw.i2c.tx_num = 2; + p->sensor_byte_order = PMIC_SENSOR_BYTE_ORDER_BIG; + p->bus = bus; + + p->fg = &power_fg_ops; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/mfd/Makefile b/qemu/roms/u-boot/drivers/power/mfd/Makefile new file mode 100644 index 000000000..43afe842a --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/Makefile @@ -0,0 +1,10 @@ +# +# Copyright (C) 2013 Samsung Electronics +# Piotr Wilczek <p.wilczek@samsung.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_PMIC_MAX77693) += pmic_max77693.o +obj-$(CONFIG_POWER_MUIC_MAX77693) += muic_max77693.o +obj-$(CONFIG_POWER_FG_MAX77693) += fg_max77693.o diff --git a/qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c b/qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c new file mode 100644 index 000000000..4519fed49 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/fg_max77693.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek <p.wilczek@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/max77693_fg.h> +#include <i2c.h> +#include <power/power_chrg.h> +#include <power/battery.h> +#include <power/fg_battery_cell_params.h> +#include <errno.h> + +static int max77693_get_vcell(u32 *vcell) +{ + u16 value; + u8 ret; + + ret = i2c_read(MAX77693_FUEL_I2C_ADDR, MAX77693_VCELL, 1, + (u8 *)&value, 2); + if (ret) + return ret; + + *vcell = (u32)(value >> 3); + *vcell = *vcell * 625; + + return 0; +} + +static int max77693_get_soc(u32 *soc) +{ + u16 value; + u8 ret; + + ret = i2c_read(MAX77693_FUEL_I2C_ADDR, MAX77693_VFSOC, 1, + (u8 *)&value, 2); + if (ret) + return ret; + + *soc = (u32)(value >> 8); + + return 0; +} + +static int power_update_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + int ret; + + if (pmic_probe(p)) { + puts("Can't find max77693 fuel gauge\n"); + return -1; + } + + ret = max77693_get_soc(&pb->bat->state_of_chrg); + if (ret) + return ret; + + max77693_get_vcell(&pb->bat->voltage_uV); + if (ret) + return ret; + + return 0; +} + +static int power_check_battery(struct pmic *p, struct pmic *bat) +{ + struct power_battery *pb = bat->pbat; + unsigned int val; + int ret = 0; + + if (pmic_probe(p)) { + puts("Can't find max77693 fuel gauge\n"); + return -1; + } + + ret = pmic_reg_read(p, MAX77693_STATUS, &val); + if (ret) + return ret; + debug("fg status: 0x%x\n", val); + + ret = pmic_reg_read(p, MAX77693_VERSION, &pb->bat->version); + if (ret) + return ret; + + ret = power_update_battery(p, bat); + if (ret) + return ret; + debug("fg ver: 0x%x\n", pb->bat->version); + printf("BAT: state_of_charge(SOC):%d%%\n", + pb->bat->state_of_chrg); + + printf(" voltage: %d.%6.6d [V] (expected to be %d [mAh])\n", + pb->bat->voltage_uV / 1000000, + pb->bat->voltage_uV % 1000000, + pb->bat->capacity); + + if (pb->bat->voltage_uV > 3850000) + pb->bat->state = EXT_SOURCE; + else if (pb->bat->voltage_uV < 3600000 || pb->bat->state_of_chrg < 5) + pb->bat->state = CHARGE; + else + pb->bat->state = NORMAL; + + return 0; +} + +static struct power_fg power_fg_ops = { + .fg_battery_check = power_check_battery, + .fg_battery_update = power_update_battery, +}; + +int power_fg_init(unsigned char bus) +{ + static const char name[] = "MAX77693_FG"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Fuel Gauge init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = FG_NUM_OF_REGS; + p->hw.i2c.addr = MAX77693_FUEL_I2C_ADDR; + p->hw.i2c.tx_num = 2; + p->sensor_byte_order = PMIC_SENSOR_BYTE_ORDER_BIG; + p->bus = bus; + + p->fg = &power_fg_ops; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c b/qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c new file mode 100644 index 000000000..e71012de1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/muic_max77693.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek <p.wilczek@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/power_chrg.h> +#include <power/max77693_muic.h> +#include <i2c.h> +#include <errno.h> + +static int power_chrg_get_type(struct pmic *p) +{ + unsigned int val; + unsigned int charge_type, charger; + + /* if probe failed, return cable none */ + if (pmic_probe(p)) + return CHARGER_NO; + + pmic_reg_read(p, MAX77693_MUIC_STATUS2, &val); + + charge_type = val & MAX77693_MUIC_CHG_MASK; + + switch (charge_type) { + case MAX77693_MUIC_CHG_NO: + charger = CHARGER_NO; + break; + case MAX77693_MUIC_CHG_USB: + case MAX77693_MUIC_CHG_USB_D: + charger = CHARGER_USB; + break; + case MAX77693_MUIC_CHG_TA: + case MAX77693_MUIC_CHG_TA_1A: + charger = CHARGER_TA; + break; + case MAX77693_MUIC_CHG_TA_500: + charger = CHARGER_TA_500; + break; + default: + charger = CHARGER_UNKNOWN; + break; + } + + return charger; +} + +static struct power_chrg power_chrg_muic_ops = { + .chrg_type = power_chrg_get_type, +}; + +int power_muic_init(unsigned int bus) +{ + static const char name[] = "MAX77693_MUIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Micro USB Interface Controller init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = MUIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX77693_MUIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_muic_ops; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c b/qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c new file mode 100644 index 000000000..1a4416b54 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/mfd/pmic_max77693.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013 Samsung Electronics + * Piotr Wilczek <p.wilczek@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/max77693_pmic.h> +#include <i2c.h> +#include <errno.h> + +static int max77693_charger_state(struct pmic *p, int state, int current) +{ + unsigned int val; + + if (pmic_probe(p)) + return -1; + + /* unlock write capability */ + val = MAX77693_CHG_UNLOCK; + pmic_reg_write(p, MAX77693_CHG_CNFG_06, val); + + if (state == CHARGER_DISABLE) { + puts("Disable the charger.\n"); + pmic_reg_read(p, MAX77693_CHG_CNFG_00, &val); + val &= ~0x01; + pmic_reg_write(p, MAX77693_CHG_CNFG_00, val); + return -1; + } + + if (current < CHARGER_MIN_CURRENT || current > CHARGER_MAX_CURRENT) { + printf("%s: Wrong charge current: %d [mA]\n", + __func__, current); + return -1; + } + + /* set charging current */ + pmic_reg_read(p, MAX77693_CHG_CNFG_02, &val); + val &= ~MAX77693_CHG_CC; + val |= current * 10 / 333; /* 0.1A/3 steps */ + pmic_reg_write(p, MAX77693_CHG_CNFG_02, val); + + /* enable charging */ + val = MAX77693_CHG_MODE_ON; + pmic_reg_write(p, MAX77693_CHG_CNFG_00, val); + + /* check charging current */ + pmic_reg_read(p, MAX77693_CHG_CNFG_02, &val); + val &= 0x3f; + printf("Enable the charger @ %d [mA]\n", val * 333 / 10); + + return 0; +} + +static int max77693_charger_bat_present(struct pmic *p) +{ + unsigned int val; + + if (pmic_probe(p)) + return -1; + + pmic_reg_read(p, MAX77693_CHG_INT_OK, &val); + + return !(val & MAX77693_CHG_DETBAT); +} + +static struct power_chrg power_chrg_pmic_ops = { + .chrg_bat_present = max77693_charger_bat_present, + .chrg_state = max77693_charger_state, +}; + +int pmic_init_max77693(unsigned char bus) +{ + static const char name[] = "MAX77693_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board PMIC init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX77693_PMIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_pmic_ops; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/palmas.c b/qemu/roms/u-boot/drivers/power/palmas.c new file mode 100644 index 000000000..cfbc9dc52 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/palmas.c @@ -0,0 +1,160 @@ +/* + * (C) Copyright 2012-2013 + * Texas Instruments, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <config.h> +#include <palmas.h> + +void palmas_init_settings(void) +{ +#ifdef CONFIG_PALMAS_SMPS7_FPWM + int err; + /* + * Set SMPS7 (1.8 V I/O supply on platforms with TWL6035/37) to + * forced PWM mode. This reduces noise (but affects efficiency). + */ + u8 val = SMPS_MODE_SLP_FPWM | SMPS_MODE_ACT_FPWM; + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS7_CTRL, val); + if (err) + printf("palmas: could not force PWM for SMPS7: err = %d\n", + err); +#endif +} + +int palmas_mmc1_poweron_ldo(void) +{ + u8 val = 0; + +#if defined(CONFIG_DRA7XX) + /* + * Currently valid for the dra7xx_evm board: + * Set TPS659038 LDO1 to 3.0 V + */ + val = LDO_VOLT_3V0; + if (palmas_i2c_write_u8(TPS65903X_CHIP_P1, LDO1_VOLTAGE, val)) { + printf("tps65903x: could not set LDO1 voltage.\n"); + return 1; + } + /* TURN ON LDO1 */ + val = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + if (palmas_i2c_write_u8(TPS65903X_CHIP_P1, LDO1_CTRL, val)) { + printf("tps65903x: could not turn on LDO1.\n"); + return 1; + } + return 0; +#else + /* + * We assume that this is a OMAP543X + TWL603X board: + * Set TWL6035/37 LDO9 to 3.0 V + */ + val = LDO_VOLT_3V0; + return twl603x_mmc1_set_ldo9(val); +#endif +} + +/* + * On some OMAP5 + TWL603X hardware the SD card socket and LDO9_IN are + * powered by an external 3.3 V regulator, while the output of LDO9 + * supplies VDDS_SDCARD for the OMAP5 interface only. This implies that + * LDO9 could be set to 'bypass' mode when required (e.g. for 3.3 V cards). + */ +int twl603x_mmc1_set_ldo9(u8 vsel) +{ + u8 cval = 0, vval = 0; /* Off by default */ + int err; + + if (vsel) { + /* Turn on */ + if (vsel > LDO_VOLT_3V3) { + /* Put LDO9 in bypass */ + cval = LDO9_BYP_EN | RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + vval = LDO_VOLT_3V3; + } else { + cval = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + vval = vsel & 0x3f; + } + } + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, LDO9_VOLTAGE, vval); + if (err) { + printf("twl603x: could not set LDO9 %s: err = %d\n", + vsel > LDO_VOLT_3V3 ? "bypass" : "voltage", err); + return err; + } + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, LDO9_CTRL, cval); + if (err) + printf("twl603x: could not turn %s LDO9: err = %d\n", + cval ? "on" : "off", err); + return err; +} + +#ifdef CONFIG_PALMAS_AUDPWR +/* + * Turn audio codec power and 32 kHz clock on/off. Use for + * testing OMAP543X + TWL603X + TWL604X boards only. + */ +int twl603x_audio_power(u8 on) +{ + u8 cval = 0, vval = 0, c32k = 0; + int err; + + if (on) { + vval = SMPS_VOLT_2V1; + cval = SMPS_MODE_SLP_AUTO | SMPS_MODE_ACT_AUTO; + c32k = RSC_MODE_SLEEP | RSC_MODE_ACTIVE; + } + /* Set SMPS9 to 2.1 V (for TWL604x), or to 0 (off) */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS9_VOLTAGE, vval); + if (err) { + printf("twl603x: could not set SMPS9 voltage: err = %d\n", + err); + return err; + } + /* Turn on or off SMPS9 */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS9_CTRL, cval); + if (err) { + printf("twl603x: could not turn SMPS9 %s: err = %d\n", + cval ? "on" : "off", err); + return err; + } + /* Output 32 kHz clock on or off */ + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, CLK32KGAUDIO_CTRL, c32k); + if (err) + printf("twl603x: could not turn CLK32KGAUDIO %s: err = %d\n", + c32k ? "on" : "off", err); + return err; +} +#endif + +#ifdef CONFIG_PALMAS_USB_SS_PWR +/** + * @brief palmas_enable_ss_ldo - Configure EVM board specific configurations + * for the USB Super speed SMPS10 regulator. + * + * @return 0 + */ +int palmas_enable_ss_ldo(void) +{ + /* Enable smps10 regulator */ + return palmas_i2c_write_u8(TWL603X_CHIP_P1, SMPS10_CTRL, + SMPS10_MODE_ACTIVE_D); +} +#endif + +/* + * Enable/disable back-up battery (or super cap) charging on TWL6035/37. + * Please use defined BB_xxx values. + */ +int twl603x_enable_bb_charge(u8 bb_fields) +{ + u8 val = bb_fields & 0x0f; + int err; + + val |= (VRTC_EN_SLP | VRTC_EN_OFF | VRTC_PWEN); + err = palmas_i2c_write_u8(TWL603X_CHIP_P1, BB_VRTC_CTRL, val); + if (err) + printf("twl603x: could not set BB_VRTC_CTRL to 0x%02x: err = %d\n", + val, err); + return err; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/Makefile b/qemu/roms/u-boot/drivers/power/pmic/Makefile new file mode 100644 index 000000000..4129bdabf --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2012 Samsung Electronics +# Lukasz Majewski <l.majewski@samsung.com> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_POWER_MAX8998) += pmic_max8998.o +obj-$(CONFIG_POWER_MAX8997) += pmic_max8997.o +obj-$(CONFIG_POWER_MUIC_MAX8997) += muic_max8997.o +obj-$(CONFIG_POWER_MAX77686) += pmic_max77686.o +obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o +obj-$(CONFIG_POWER_TPS65217) += pmic_tps65217.o +obj-$(CONFIG_POWER_TPS65910) += pmic_tps65910.o diff --git a/qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c b/qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c new file mode 100644 index 000000000..720342e5e --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/muic_max8997.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/power_chrg.h> +#include <power/max8997_muic.h> +#include <i2c.h> +#include <errno.h> + +static int power_chrg_get_type(struct pmic *p) +{ + unsigned int val; + unsigned char charge_type, charger; + + if (pmic_probe(p)) + return CHARGER_NO; + + pmic_reg_read(p, MAX8997_MUIC_STATUS2, &val); + charge_type = val & MAX8997_MUIC_CHG_MASK; + + switch (charge_type) { + case MAX8997_MUIC_CHG_NO: + charger = CHARGER_NO; + break; + case MAX8997_MUIC_CHG_USB: + case MAX8997_MUIC_CHG_USB_D: + charger = CHARGER_USB; + break; + case MAX8997_MUIC_CHG_TA: + case MAX8997_MUIC_CHG_TA_1A: + charger = CHARGER_TA; + break; + case MAX8997_MUIC_CHG_TA_500: + charger = CHARGER_TA_500; + break; + default: + charger = CHARGER_UNKNOWN; + break; + } + + return charger; +} + +static struct power_chrg power_chrg_muic_ops = { + .chrg_type = power_chrg_get_type, +}; + +int power_muic_init(unsigned int bus) +{ + static const char name[] = "MAX8997_MUIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board Micro USB Interface Controller init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = MUIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX8997_MUIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_muic_ops; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_max77686.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_max77686.c new file mode 100644 index 000000000..d4c430e22 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_max77686.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Rajeshwari Shinde <rajeshwari.s@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/max77686_pmic.h> +#include <errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +static const char max77686_buck_addr[] = { + 0xff, 0x10, 0x12, 0x1c, 0x26, 0x30, 0x32, 0x34, 0x36, 0x38 +}; + +static unsigned int max77686_ldo_volt2hex(int ldo, ulong uV) +{ + unsigned int hex = 0; + + switch (ldo) { + case 1: + case 2: + case 6: + case 7: + case 8: + case 15: + hex = (uV - 800000) / 25000; + break; + default: + hex = (uV - 800000) / 50000; + } + + if (hex >= 0 && hex <= MAX77686_LDO_VOLT_MAX_HEX) + return hex; + + debug("%s: %ld is wrong voltage value for LDO%d\n", __func__, uV, ldo); + return 0; +} + +int max77686_set_ldo_voltage(struct pmic *p, int ldo, ulong uV) +{ + unsigned int val, ret, hex, adr; + + if (ldo < 1 && ldo > 26) { + printf("%s: %d is wrong ldo number\n", __func__, ldo); + return -1; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + hex = max77686_ldo_volt2hex(ldo, uV); + + if (!hex) + return -1; + + ret = pmic_reg_read(p, adr, &val); + if (ret) + return ret; + + val &= ~MAX77686_LDO_VOLT_MASK; + val |= hex; + ret |= pmic_reg_write(p, adr, val); + + return ret; +} + +int max77686_set_ldo_mode(struct pmic *p, int ldo, char opmode) +{ + unsigned int val, ret, adr, mode; + + if (ldo < 1 && 26 < ldo) { + printf("%s: %d is wrong ldo number\n", __func__, ldo); + return -1; + } + + adr = MAX77686_REG_PMIC_LDO1CTRL1 + ldo - 1; + + /* mode */ + switch (opmode) { + case OPMODE_OFF: + mode = MAX77686_LDO_MODE_OFF; + break; + case OPMODE_STANDBY: + switch (ldo) { + case 2: + case 6: + case 7: + case 8: + case 10: + case 11: + case 12: + case 14: + case 15: + case 16: + mode = MAX77686_LDO_MODE_STANDBY; + break; + default: + mode = 0xff; + } + break; + case OPMODE_LPM: + mode = MAX77686_LDO_MODE_LPM; + break; + case OPMODE_ON: + mode = MAX77686_LDO_MODE_ON; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + printf("%s: %d is not supported on LDO%d\n", + __func__, opmode, ldo); + return -1; + } + + ret = pmic_reg_read(p, adr, &val); + if (ret) + return ret; + + val &= ~MAX77686_LDO_MODE_MASK; + val |= mode; + ret |= pmic_reg_write(p, adr, val); + + return ret; +} + +int max77686_set_buck_mode(struct pmic *p, int buck, char opmode) +{ + unsigned int val, ret, mask, adr, size, mode, mode_shift; + + size = ARRAY_SIZE(max77686_buck_addr); + if (buck >= size) { + printf("%s: %d is wrong buck number\n", __func__, buck); + return -1; + } + + adr = max77686_buck_addr[buck]; + + /* mask */ + switch (buck) { + case 2: + case 3: + case 4: + mode_shift = MAX77686_BUCK_MODE_SHIFT_2; + break; + default: + mode_shift = MAX77686_BUCK_MODE_SHIFT_1; + } + + mask = MAX77686_BUCK_MODE_MASK << mode_shift; + + /* mode */ + switch (opmode) { + case OPMODE_OFF: + mode = MAX77686_BUCK_MODE_OFF; + break; + case OPMODE_STANDBY: + switch (buck) { + case 1: + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_STANDBY << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_LPM: + switch (buck) { + case 2: + case 3: + case 4: + mode = MAX77686_BUCK_MODE_LPM << mode_shift; + break; + default: + mode = 0xff; + } + break; + case OPMODE_ON: + mode = MAX77686_BUCK_MODE_ON << mode_shift; + break; + default: + mode = 0xff; + } + + if (mode == 0xff) { + printf("%s: %d is not supported on BUCK%d\n", + __func__, opmode, buck); + return -1; + } + + ret = pmic_reg_read(p, adr, &val); + if (ret) + return ret; + + val &= ~mask; + val |= mode; + ret |= pmic_reg_write(p, adr, val); + + return ret; +} + +int pmic_init(unsigned char bus) +{ + static const char name[] = "MAX77686_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + +#ifdef CONFIG_OF_CONTROL + const void *blob = gd->fdt_blob; + int node, parent; + + node = fdtdec_next_compatible(blob, 0, COMPAT_MAXIM_MAX77686_PMIC); + if (node < 0) { + debug("PMIC: No node for PMIC Chip in device tree\n"); + debug("node = %d\n", node); + return -1; + } + + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + p->bus = i2c_get_bus_num_fdt(parent); + if (p->bus < 0) { + debug("%s: Cannot find I2C bus\n", __func__); + return -1; + } + p->hw.i2c.addr = fdtdec_get_int(blob, node, "reg", 9); +#else + p->bus = bus; + p->hw.i2c.addr = MAX77686_I2C_ADDR; +#endif + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.tx_num = 1; + + puts("Board PMIC init\n"); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c new file mode 100644 index 000000000..ba0169232 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8997.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/max8997_pmic.h> +#include <i2c.h> +#include <errno.h> + +unsigned char max8997_reg_ldo(int uV) +{ + unsigned char ret; + if (uV <= 800000) + return 0; + if (uV >= 3950000) + return MAX8997_LDO_MAX_VAL; + ret = (uV - 800000) / 50000; + if (ret > MAX8997_LDO_MAX_VAL) { + printf("MAX8997 LDO SETTING ERROR (%duV) -> %u\n", uV, ret); + ret = MAX8997_LDO_MAX_VAL; + } + + return ret; +} + +static int pmic_charger_state(struct pmic *p, int state, int current) +{ + unsigned char fc; + u32 val = 0; + + if (pmic_probe(p)) + return -1; + + if (state == CHARGER_DISABLE) { + puts("Disable the charger.\n"); + pmic_reg_read(p, MAX8997_REG_MBCCTRL2, &val); + val &= ~(MBCHOSTEN | VCHGR_FC); + pmic_reg_write(p, MAX8997_REG_MBCCTRL2, val); + + return -1; + } + + if (current < CHARGER_MIN_CURRENT || current > CHARGER_MAX_CURRENT) { + printf("%s: Wrong charge current: %d [mA]\n", + __func__, current); + return -1; + } + + fc = (current - CHARGER_MIN_CURRENT) / CHARGER_CURRENT_RESOLUTION; + fc = fc & 0xf; /* up to 950 mA */ + + printf("Enable the charger @ %d [mA]\n", fc * CHARGER_CURRENT_RESOLUTION + + CHARGER_MIN_CURRENT); + + val = fc | MBCICHFCSET; + pmic_reg_write(p, MAX8997_REG_MBCCTRL4, val); + + pmic_reg_read(p, MAX8997_REG_MBCCTRL2, &val); + val = MBCHOSTEN | VCHGR_FC; /* enable charger & fast charge */ + pmic_reg_write(p, MAX8997_REG_MBCCTRL2, val); + + return 0; +} + +static int pmic_charger_bat_present(struct pmic *p) +{ + u32 val; + + if (pmic_probe(p)) + return -1; + + pmic_reg_read(p, MAX8997_REG_STATUS4, &val); + + return !(val & DETBAT); +} + +static struct power_chrg power_chrg_pmic_ops = { + .chrg_bat_present = pmic_charger_bat_present, + .chrg_state = pmic_charger_state, +}; + +int pmic_init(unsigned char bus) +{ + static const char name[] = "MAX8997_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + debug("Board PMIC init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX8997_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + p->chrg = &power_chrg_pmic_ops; + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c new file mode 100644 index 000000000..ca2f504cc --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_max8998.c @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <power/max8998_pmic.h> +#include <errno.h> + +int pmic_init(unsigned char bus) +{ + static const char name[] = "MAX8998_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + puts("Board PMIC init\n"); + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = MAX8998_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c new file mode 100644 index 000000000..22c1f15ee --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2014 Gateworks Corporation + * Tim Harvey <tharvey@gateworks.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <i2c.h> +#include <power/pmic.h> +#include <power/pfuze100_pmic.h> + +int pmic_init(unsigned char bus) +{ + static const char name[] = "PFUZE100_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->interface = PMIC_I2C; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->hw.i2c.addr = CONFIG_POWER_PFUZE100_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65217.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65217.c new file mode 100644 index 000000000..36e9024bf --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65217.c @@ -0,0 +1,109 @@ +/* + * (C) Copyright 2011-2013 + * Texas Instruments, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <power/tps65217.h> + +/** + * tps65217_reg_read() - Generic function that can read a TPS65217 register + * @src_reg: Source register address + * @src_val: Address of destination variable + * @return: 0 for success, not 0 on failure. + */ +int tps65217_reg_read(uchar src_reg, uchar *src_val) +{ + return i2c_read(TPS65217_CHIP_PM, src_reg, 1, src_val, 1); +} + +/** + * tps65217_reg_write() - Generic function that can write a TPS65217 PMIC + * register or bit field regardless of protection + * level. + * + * @prot_level: Register password protection. Use + * TPS65217_PROT_LEVEL_NONE, + * TPS65217_PROT_LEVEL_1 or TPS65217_PROT_LEVEL_2 + * @dest_reg: Register address to write. + * @dest_val: Value to write. + * @mask: Bit mask (8 bits) to be applied. Function will only + * change bits that are set in the bit mask. + * + * @return: 0 for success, not 0 on failure, as per the i2c API + */ +int tps65217_reg_write(uchar prot_level, uchar dest_reg, uchar dest_val, + uchar mask) +{ + uchar read_val; + uchar xor_reg; + int ret; + + /* + * If we are affecting only a bit field, read dest_reg and apply the + * mask + */ + if (mask != TPS65217_MASK_ALL_BITS) { + ret = i2c_read(TPS65217_CHIP_PM, dest_reg, 1, &read_val, 1); + if (ret) + return ret; + read_val &= (~mask); + read_val |= (dest_val & mask); + dest_val = read_val; + } + + if (prot_level > 0) { + xor_reg = dest_reg ^ TPS65217_PASSWORD_UNLOCK; + ret = i2c_write(TPS65217_CHIP_PM, TPS65217_PASSWORD, 1, + &xor_reg, 1); + if (ret) + return ret; + } + + ret = i2c_write(TPS65217_CHIP_PM, dest_reg, 1, &dest_val, 1); + if (ret) + return ret; + + if (prot_level == TPS65217_PROT_LEVEL_2) { + ret = i2c_write(TPS65217_CHIP_PM, TPS65217_PASSWORD, 1, + &xor_reg, 1); + if (ret) + return ret; + + ret = i2c_write(TPS65217_CHIP_PM, dest_reg, 1, &dest_val, 1); + if (ret) + return ret; + } + + return 0; +} + +/** + * tps65217_voltage_update() - Function to change a voltage level, as this + * is a multi-step process. + * @dc_cntrl_reg: DC voltage control register to change. + * @volt_sel: New value for the voltage register + * @return: 0 for success, not 0 on failure. + */ +int tps65217_voltage_update(uchar dc_cntrl_reg, uchar volt_sel) +{ + if ((dc_cntrl_reg != TPS65217_DEFDCDC1) && + (dc_cntrl_reg != TPS65217_DEFDCDC2) && + (dc_cntrl_reg != TPS65217_DEFDCDC3)) + return 1; + + /* set voltage level */ + if (tps65217_reg_write(TPS65217_PROT_LEVEL_2, dc_cntrl_reg, volt_sel, + TPS65217_MASK_ALL_BITS)) + return 1; + + /* set GO bit to initiate voltage transition */ + if (tps65217_reg_write(TPS65217_PROT_LEVEL_2, TPS65217_DEFSLEW, + TPS65217_DCDC_GO, TPS65217_DCDC_GO)) + return 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65910.c b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65910.c new file mode 100644 index 000000000..7ee1160e0 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/pmic/pmic_tps65910.c @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2011-2013 + * Texas Instruments, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <power/tps65910.h> + +/* + * tps65910_set_i2c_control() - Set the TPS65910 to be controlled via the I2C + * interface. + * @return: 0 on success, not 0 on failure + */ +int tps65910_set_i2c_control(void) +{ + int ret; + uchar buf; + + /* VDD1/2 voltage selection register access by control i/f */ + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, TPS65910_DEVCTRL_REG, 1, + &buf, 1); + + if (ret) + return ret; + + buf |= TPS65910_DEVCTRL_REG_SR_CTL_I2C_SEL_CTL_I2C; + + return i2c_write(TPS65910_CTRL_I2C_ADDR, TPS65910_DEVCTRL_REG, 1, + &buf, 1); +} + +/* + * tps65910_voltage_update() - Voltage switching for MPU frequency switching. + * @module: mpu - 0, core - 1 + * @vddx_op_vol_sel: vdd voltage to set + * @return: 0 on success, not 0 on failure + */ +int tps65910_voltage_update(unsigned int module, unsigned char vddx_op_vol_sel) +{ + uchar buf; + unsigned int reg_offset; + int ret; + + if (module == MPU) + reg_offset = TPS65910_VDD1_OP_REG; + else + reg_offset = TPS65910_VDD2_OP_REG; + + /* Select VDDx OP */ + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + buf &= ~TPS65910_OP_REG_CMD_MASK; + + ret = i2c_write(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + /* Configure VDDx OP Voltage */ + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + buf &= ~TPS65910_OP_REG_SEL_MASK; + buf |= vddx_op_vol_sel; + + ret = i2c_write(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + ret = i2c_read(TPS65910_CTRL_I2C_ADDR, reg_offset, 1, &buf, 1); + if (ret) + return ret; + + if ((buf & TPS65910_OP_REG_SEL_MASK) != vddx_op_vol_sel) + return 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_core.c b/qemu/roms/u-boot/drivers/power/power_core.c new file mode 100644 index 000000000..fe1f31602 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_core.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <linux/types.h> +#include <linux/list.h> +#include <power/pmic.h> + +static LIST_HEAD(pmic_list); + +int check_reg(struct pmic *p, u32 reg) +{ + if (reg >= p->number_of_regs) { + printf("<reg num> = %d is invalid. Should be less than %d\n", + reg, p->number_of_regs); + return -1; + } + + return 0; +} + +int pmic_set_output(struct pmic *p, u32 reg, int out, int on) +{ + u32 val; + + if (pmic_reg_read(p, reg, &val)) + return -1; + + if (on) + val |= out; + else + val &= ~out; + + if (pmic_reg_write(p, reg, val)) + return -1; + + return 0; +} + +static void pmic_show_info(struct pmic *p) +{ + printf("PMIC: %s\n", p->name); +} + +static int pmic_dump(struct pmic *p) +{ + int i, ret; + u32 val; + + if (!p) { + puts("Wrong PMIC name!\n"); + return -1; + } + + pmic_show_info(p); + for (i = 0; i < p->number_of_regs; i++) { + ret = pmic_reg_read(p, i, &val); + if (ret) + puts("PMIC: Registers dump failed\n"); + + if (!(i % 8)) + printf("\n0x%02x: ", i); + + printf("%08x ", val); + } + puts("\n"); + return 0; +} + +struct pmic *pmic_alloc(void) +{ + struct pmic *p; + + p = calloc(sizeof(*p), 1); + if (!p) { + printf("%s: No available memory for allocation!\n", __func__); + return NULL; + } + + list_add_tail(&p->list, &pmic_list); + + debug("%s: new pmic struct: 0x%p\n", __func__, p); + + return p; +} + +struct pmic *pmic_get(const char *s) +{ + struct pmic *p; + + list_for_each_entry(p, &pmic_list, list) { + if (strcmp(p->name, s) == 0) { + debug("%s: pmic %s -> 0x%p\n", __func__, p->name, p); + return p; + } + } + + return NULL; +} + +const char *power_get_interface(int interface) +{ + const char *power_interface[] = {"I2C", "SPI", "|+|-|"}; + return power_interface[interface]; +} + +static void pmic_list_names(void) +{ + struct pmic *p; + + puts("PMIC devices:\n"); + list_for_each_entry(p, &pmic_list, list) { + printf("name: %s bus: %s_%d\n", p->name, + power_get_interface(p->interface), p->bus); + } +} + +int do_pmic(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u32 ret, reg, val; + char *cmd, *name; + struct pmic *p; + + /* at least two arguments please */ + if (argc < 2) + return CMD_RET_USAGE; + + if (strcmp(argv[1], "list") == 0) { + pmic_list_names(); + return CMD_RET_SUCCESS; + } + + if (argc < 3) + return CMD_RET_USAGE; + + name = argv[1]; + cmd = argv[2]; + + debug("%s: name: %s cmd: %s\n", __func__, name, cmd); + p = pmic_get(name); + if (!p) + return CMD_RET_FAILURE; + + if (strcmp(cmd, "dump") == 0) { + if (pmic_dump(p)) + return CMD_RET_FAILURE; + return CMD_RET_SUCCESS; + } + + if (strcmp(cmd, "read") == 0) { + if (argc < 4) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[3], NULL, 16); + ret = pmic_reg_read(p, reg, &val); + + if (ret) + puts("PMIC: Register read failed\n"); + + printf("\n0x%02x: 0x%08x\n", reg, val); + + return CMD_RET_SUCCESS; + } + + if (strcmp(cmd, "write") == 0) { + if (argc < 5) + return CMD_RET_USAGE; + + reg = simple_strtoul(argv[3], NULL, 16); + val = simple_strtoul(argv[4], NULL, 16); + pmic_reg_write(p, reg, val); + + return CMD_RET_SUCCESS; + } + + if (strcmp(cmd, "bat") == 0) { + if (argc < 4) + return CMD_RET_USAGE; + + if (!p->pbat) { + printf("%s is not a battery\n", p->name); + return CMD_RET_FAILURE; + } + + if (strcmp(argv[3], "state") == 0) + p->fg->fg_battery_check(p->pbat->fg, p); + + if (strcmp(argv[3], "charge") == 0) { + printf("BAT: %s charging (ctrl+c to break)\n", + p->name); + if (p->low_power_mode) + p->low_power_mode(); + if (p->pbat->battery_charge) + p->pbat->battery_charge(p); + } + + return CMD_RET_SUCCESS; + } + + /* No subcommand found */ + return CMD_RET_SUCCESS; +} + +U_BOOT_CMD( + pmic, CONFIG_SYS_MAXARGS, 1, do_pmic, + "PMIC", + "list - list available PMICs\n" + "pmic name dump - dump named PMIC registers\n" + "pmic name read <reg> - read register\n" + "pmic name write <reg> <value> - write register\n" + "pmic name bat state - write register\n" + "pmic name bat charge - write register\n" +); diff --git a/qemu/roms/u-boot/drivers/power/power_dialog.c b/qemu/roms/u-boot/drivers/power/power_dialog.c new file mode 100644 index 000000000..fc95d5a10 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_dialog.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <power/pmic.h> +#include <dialog_pmic.h> +#include <errno.h> + +int pmic_dialog_init(unsigned char bus) +{ + static const char name[] = "DIALOG_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->number_of_regs = DIALOG_NUM_OF_REGS; + + p->interface = PMIC_I2C; + p->hw.i2c.addr = CONFIG_SYS_DIALOG_PMIC_I2C_ADDR; + p->hw.i2c.tx_num = 1; + p->bus = bus; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_fsl.c b/qemu/roms/u-boot/drivers/power/power_fsl.c new file mode 100644 index 000000000..ac0b541d7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_fsl.c @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <spi.h> +#include <power/pmic.h> +#include <fsl_pmic.h> +#include <errno.h> + +#if defined(CONFIG_PMIC_FSL_MC13892) +#define FSL_PMIC_I2C_LENGTH 3 +#elif defined(CONFIG_PMIC_FSL_MC34704) +#define FSL_PMIC_I2C_LENGTH 1 +#endif + +#if defined(CONFIG_POWER_SPI) +static u32 pmic_spi_prepare_tx(u32 reg, u32 *val, u32 write) +{ + return (write << 31) | (reg << 25) | (*val & 0x00FFFFFF); +} +#endif + +int pmic_init(unsigned char bus) +{ + static const char name[] = "FSL_PMIC"; + struct pmic *p = pmic_alloc(); + + if (!p) { + printf("%s: POWER allocation error!\n", __func__); + return -ENOMEM; + } + + p->name = name; + p->number_of_regs = PMIC_NUM_OF_REGS; + p->bus = bus; + +#if defined(CONFIG_POWER_SPI) + p->interface = PMIC_SPI; + p->hw.spi.cs = CONFIG_FSL_PMIC_CS; + p->hw.spi.clk = CONFIG_FSL_PMIC_CLK; + p->hw.spi.mode = CONFIG_FSL_PMIC_MODE; + p->hw.spi.bitlen = CONFIG_FSL_PMIC_BITLEN; + p->hw.spi.flags = SPI_XFER_BEGIN | SPI_XFER_END; + p->hw.spi.prepare_tx = pmic_spi_prepare_tx; +#elif defined(CONFIG_POWER_I2C) + p->interface = PMIC_I2C; + p->hw.i2c.addr = CONFIG_SYS_FSL_PMIC_I2C_ADDR; + p->hw.i2c.tx_num = FSL_PMIC_I2C_LENGTH; +#else +#error "You must select CONFIG_POWER_SPI or CONFIG_PMIC_I2C" +#endif + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_i2c.c b/qemu/roms/u-boot/drivers/power/power_i2c.c new file mode 100644 index 000000000..ac768708e --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_i2c.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/types.h> +#include <power/pmic.h> +#include <i2c.h> +#include <compiler.h> + +int pmic_reg_write(struct pmic *p, u32 reg, u32 val) +{ + unsigned char buf[4] = { 0 }; + + if (check_reg(p, reg)) + return -1; + + switch (pmic_i2c_tx_num) { + case 3: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) { + buf[2] = (cpu_to_le32(val) >> 16) & 0xff; + buf[1] = (cpu_to_le32(val) >> 8) & 0xff; + buf[0] = cpu_to_le32(val) & 0xff; + } else { + buf[0] = (cpu_to_le32(val) >> 16) & 0xff; + buf[1] = (cpu_to_le32(val) >> 8) & 0xff; + buf[2] = cpu_to_le32(val) & 0xff; + } + break; + case 2: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) { + buf[1] = (cpu_to_le32(val) >> 8) & 0xff; + buf[0] = cpu_to_le32(val) & 0xff; + } else { + buf[0] = (cpu_to_le32(val) >> 8) & 0xff; + buf[1] = cpu_to_le32(val) & 0xff; + } + break; + case 1: + buf[0] = cpu_to_le32(val) & 0xff; + break; + default: + printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num); + return -1; + } + + if (i2c_write(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num)) + return -1; + + return 0; +} + +int pmic_reg_read(struct pmic *p, u32 reg, u32 *val) +{ + unsigned char buf[4] = { 0 }; + u32 ret_val = 0; + + if (check_reg(p, reg)) + return -1; + + if (i2c_read(pmic_i2c_addr, reg, 1, buf, pmic_i2c_tx_num)) + return -1; + + switch (pmic_i2c_tx_num) { + case 3: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) + ret_val = le32_to_cpu(buf[2] << 16 + | buf[1] << 8 | buf[0]); + else + ret_val = le32_to_cpu(buf[0] << 16 | + buf[1] << 8 | buf[2]); + break; + case 2: + if (p->sensor_byte_order == PMIC_SENSOR_BYTE_ORDER_BIG) + ret_val = le32_to_cpu(buf[1] << 8 | buf[0]); + else + ret_val = le32_to_cpu(buf[0] << 8 | buf[1]); + break; + case 1: + ret_val = le32_to_cpu(buf[0]); + break; + default: + printf("%s: invalid tx_num: %d", __func__, pmic_i2c_tx_num); + return -1; + } + memcpy(val, &ret_val, sizeof(ret_val)); + + return 0; +} + +int pmic_probe(struct pmic *p) +{ + i2c_set_bus_num(p->bus); + debug("Bus: %d PMIC:%s probed!\n", p->bus, p->name); + if (i2c_probe(pmic_i2c_addr)) { + printf("Can't find PMIC:%s\n", p->name); + return -1; + } + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/power_spi.c b/qemu/roms/u-boot/drivers/power/power_spi.c new file mode 100644 index 000000000..fb455a006 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/power_spi.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * (C) Copyright 2010 + * Stefano Babic, DENX Software Engineering, sbabic@denx.de + * + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <linux/types.h> +#include <power/pmic.h> +#include <spi.h> + +static struct spi_slave *slave; + +void pmic_spi_free(struct spi_slave *slave) +{ + if (slave) + spi_free_slave(slave); +} + +struct spi_slave *pmic_spi_probe(struct pmic *p) +{ + return spi_setup_slave(p->bus, + p->hw.spi.cs, + p->hw.spi.clk, + p->hw.spi.mode); +} + +static u32 pmic_reg(struct pmic *p, u32 reg, u32 *val, u32 write) +{ + u32 pmic_tx, pmic_rx; + u32 tmp; + + if (!slave) { + slave = pmic_spi_probe(p); + + if (!slave) + return -1; + } + + if (check_reg(p, reg)) + return -1; + + if (spi_claim_bus(slave)) + return -1; + + pmic_tx = p->hw.spi.prepare_tx(reg, val, write); + + tmp = cpu_to_be32(pmic_tx); + + if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx, + pmic_spi_flags)) { + spi_release_bus(slave); + return -1; + } + + if (write) { + pmic_tx = p->hw.spi.prepare_tx(reg, val, 0); + tmp = cpu_to_be32(pmic_tx); + if (spi_xfer(slave, pmic_spi_bitlen, &tmp, &pmic_rx, + pmic_spi_flags)) { + spi_release_bus(slave); + return -1; + } + } + + spi_release_bus(slave); + *val = cpu_to_be32(pmic_rx); + + return 0; +} + +int pmic_reg_write(struct pmic *p, u32 reg, u32 val) +{ + if (pmic_reg(p, reg, &val, 1)) + return -1; + + return 0; +} + +int pmic_reg_read(struct pmic *p, u32 reg, u32 *val) +{ + if (pmic_reg(p, reg, val, 0)) + return -1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/tps6586x.c b/qemu/roms/u-boot/drivers/power/tps6586x.c new file mode 100644 index 000000000..d29d96953 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/tps6586x.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2010,2011 NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <tps6586x.h> +#include <asm/io.h> +#include <i2c.h> + +static int bus_num; /* I2C bus we are on */ +#define I2C_ADDRESS 0x34 /* chip requires this address */ +static char inited; /* 1 if we have been inited */ + +enum { + /* Registers that we access */ + SUPPLY_CONTROL1 = 0x20, + SUPPLY_CONTROL2, + SM1_VOLTAGE_V1 = 0x23, + SM1_VOLTAGE_V2, + SM0_VOLTAGE_V1 = 0x26, + SM0_VOLTAGE_V2, + PFM_MODE = 0x47, + + /* Bits in the supply control registers */ + CTRL_SM1_RAMP = 0x01, + CTRL_SM1_SUPPLY2 = 0x02, + CTRL_SM0_RAMP = 0x04, + CTRL_SM0_SUPPLY2 = 0x08, +}; + +#define MAX_I2C_RETRY 3 +static int tps6586x_read(int reg) +{ + int i; + uchar data; + int retval = -1; + int old_bus_num; + + old_bus_num = i2c_get_bus_num(); + i2c_set_bus_num(bus_num); + + for (i = 0; i < MAX_I2C_RETRY; ++i) { + if (!i2c_read(I2C_ADDRESS, reg, 1, &data, 1)) { + retval = (int)data; + goto exit; + } + + /* i2c access failed, retry */ + udelay(100); + } + +exit: + i2c_set_bus_num(old_bus_num); + debug("pmu_read %x=%x\n", reg, retval); + if (retval < 0) + debug("%s: failed to read register %#x: %d\n", __func__, reg, + retval); + return retval; +} + +static int tps6586x_write(int reg, uchar *data, uint len) +{ + int i; + int retval = -1; + int old_bus_num; + + old_bus_num = i2c_get_bus_num(); + i2c_set_bus_num(bus_num); + + for (i = 0; i < MAX_I2C_RETRY; ++i) { + if (!i2c_write(I2C_ADDRESS, reg, 1, data, len)) { + retval = 0; + goto exit; + } + + /* i2c access failed, retry */ + udelay(100); + } + +exit: + i2c_set_bus_num(old_bus_num); + debug("pmu_write %x=%x: ", reg, retval); + for (i = 0; i < len; i++) + debug("%x ", data[i]); + if (retval) + debug("%s: failed to write register %#x\n", __func__, reg); + return retval; +} + +/* + * Get current voltage of SM0 and SM1 + * + * @param sm0 Place to put SM0 voltage + * @param sm1 Place to put SM1 voltage + * @return 0 if ok, -1 on error + */ +static int read_voltages(int *sm0, int *sm1) +{ + int ctrl1, ctrl2; + int is_v2; + + /* + * Each vdd has two supply sources, ie, v1 and v2. + * The supply control reg1 and reg2 determine the current selection. + */ + ctrl1 = tps6586x_read(SUPPLY_CONTROL1); + ctrl2 = tps6586x_read(SUPPLY_CONTROL2); + if (ctrl1 == -1 || ctrl2 == -1) + return -1; + + /* Figure out whether V1 or V2 is selected */ + is_v2 = (ctrl1 | ctrl2) & CTRL_SM0_SUPPLY2; + *sm0 = tps6586x_read(is_v2 ? SM0_VOLTAGE_V2 : SM0_VOLTAGE_V1); + *sm1 = tps6586x_read(is_v2 ? SM1_VOLTAGE_V2 : SM1_VOLTAGE_V1); + if (*sm0 == -1 || *sm1 == -1) + return -1; + + return 0; +} + +static int set_voltage(int reg, int data, int rate) +{ + uchar control_bit; + uchar buff[3]; + + control_bit = (reg == SM0_VOLTAGE_V1 ? CTRL_SM0_RAMP : CTRL_SM1_RAMP); + + /* + * Only one supply is needed in u-boot. set both v1 and v2 to + * same value. + * + * When both v1 and v2 are set to same value, we just need to set + * control1 reg to trigger the supply selection. + */ + buff[0] = buff[1] = (uchar)data; + buff[2] = rate; + + /* write v1, v2 and rate, then trigger */ + if (tps6586x_write(reg, buff, 3) || + tps6586x_write(SUPPLY_CONTROL1, &control_bit, 1)) + return -1; + + return 0; +} + +static int calculate_next_voltage(int voltage, int target, int step) +{ + int diff = voltage < target ? step : -step; + + if (abs(target - voltage) > step) + voltage += diff; + else + voltage = target; + + return voltage; +} + +int tps6586x_set_pwm_mode(int mask) +{ + uchar val; + int ret; + + assert(inited); + ret = tps6586x_read(PFM_MODE); + if (ret != -1) { + val = (uchar)ret; + val |= mask; + + ret = tps6586x_write(PFM_MODE, &val, 1); + } + + if (ret == -1) + debug("%s: Failed to read/write PWM mode reg\n", __func__); + + return ret; +} + +int tps6586x_adjust_sm0_sm1(int sm0_target, int sm1_target, int step, int rate, + int min_sm0_over_sm1) +{ + int sm0, sm1; + int bad; + + assert(inited); + + /* get current voltage settings */ + if (read_voltages(&sm0, &sm1)) { + debug("%s: Cannot read voltage settings\n", __func__); + return -1; + } + + /* + * if vdd_core < vdd_cpu + rel + * skip + * + * This condition may happen when system reboots due to kernel crash. + */ + if (min_sm0_over_sm1 != -1 && sm0 < sm1 + min_sm0_over_sm1) { + debug("%s: SM0 is %d, SM1 is %d, but min_sm0_over_sm1 is %d\n", + __func__, sm0, sm1, min_sm0_over_sm1); + return -1; + } + + /* + * Since vdd_core and vdd_cpu may both stand at either greater or less + * than their nominal voltage, the adjustment may go either directions. + * + * Make sure vdd_core is always higher than vdd_cpu with certain margin. + * So, find out which vdd to adjust first in each step. + * + * case 1: both sm0 and sm1 need to move up + * adjust sm0 before sm1 + * + * case 2: both sm0 and sm1 need to move down + * adjust sm1 before sm0 + * + * case 3: sm0 moves down and sm1 moves up + * adjusting either one first is fine. + * + * Adjust vdd_core and vdd_cpu one step at a time until they reach + * their nominal values. + */ + bad = 0; + while (!bad && (sm0 != sm0_target || sm1 != sm1_target)) { + int adjust_sm0_late = 0; /* flag to adjust vdd_core later */ + + debug("%d-%d %d-%d ", sm0, sm0_target, sm1, sm1_target); + + if (sm0 != sm0_target) { + /* + * if case 1 and case 3, set new sm0 first. + * otherwise, hold down until new sm1 is set. + */ + sm0 = calculate_next_voltage(sm0, sm0_target, step); + if (sm1 < sm1_target) + bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate); + else + adjust_sm0_late = 1; + } + + if (sm1 != sm1_target) { + sm1 = calculate_next_voltage(sm1, sm1_target, step); + bad |= set_voltage(SM1_VOLTAGE_V1, sm1, rate); + } + + if (adjust_sm0_late) + bad |= set_voltage(SM0_VOLTAGE_V1, sm0, rate); + debug("%d\n", adjust_sm0_late); + } + debug("%d-%d %d-%d done\n", sm0, sm0_target, sm1, sm1_target); + + return bad ? -1 : 0; +} + +int tps6586x_init(int bus) +{ + bus_num = bus; + inited = 1; + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/power/twl4030.c b/qemu/roms/u-boot/drivers/power/twl4030.c new file mode 100644 index 000000000..3e5031046 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/twl4030.c @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2009 Wind River Systems, Inc. + * Tom Rix <Tom.Rix at windriver.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * twl4030_power_reset_init is derived from code on omapzoom, + * git://git.omapzoom.com/repo/u-boot.git + * + * Copyright (C) 2007-2009 Texas Instruments, Inc. + * + * twl4030_power_init is from cpu/omap3/common.c, power_init_r + * + * (C) Copyright 2004-2008 + * Texas Instruments, <www.ti.com> + * + * Author : + * Sunil Kumar <sunilsaini05 at gmail.com> + * Shashi Ranjan <shashiranjanmca05 at gmail.com> + * + * Derived from Beagle Board and 3430 SDP code by + * Richard Woodruff <r-woodruff2 at ti.com> + * Syed Mohammed Khasim <khasim at ti.com> + */ + +#include <twl4030.h> + +/* + * Power Reset + */ +void twl4030_power_reset_init(void) +{ + u8 val = 0; + if (twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_P1_SW_EVENTS, &val)) { + printf("Error:TWL4030: failed to read the power register\n"); + printf("Could not initialize hardware reset\n"); + } else { + val |= TWL4030_PM_MASTER_SW_EVENTS_STOPON_PWRON; + if (twl4030_i2c_write_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_P1_SW_EVENTS, val)) { + printf("Error:TWL4030: failed to write the power register\n"); + printf("Could not initialize hardware reset\n"); + } + } +} + +/* + * Set Device Group and Voltage + */ +void twl4030_pmrecv_vsel_cfg(u8 vsel_reg, u8 vsel_val, + u8 dev_grp, u8 dev_grp_sel) +{ + int ret; + + /* Select the Voltage */ + ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, vsel_reg, + vsel_val); + if (ret != 0) { + printf("Could not write vsel to reg %02x (%d)\n", + vsel_reg, ret); + return; + } + + /* Select the Device Group (enable the supply if dev_grp_sel != 0) */ + ret = twl4030_i2c_write_u8(TWL4030_CHIP_PM_RECEIVER, dev_grp, + dev_grp_sel); + if (ret != 0) + printf("Could not write grp_sel to reg %02x (%d)\n", + dev_grp, ret); +} + +void twl4030_power_init(void) +{ + /* set VAUX3 to 2.8V */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VAUX3_DEDICATED, + TWL4030_PM_RECEIVER_VAUX3_VSEL_28, + TWL4030_PM_RECEIVER_VAUX3_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); + + /* set VPLL2 to 1.8V */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VPLL2_DEDICATED, + TWL4030_PM_RECEIVER_VPLL2_VSEL_18, + TWL4030_PM_RECEIVER_VPLL2_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_ALL); + + /* set VDAC to 1.8V */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VDAC_DEDICATED, + TWL4030_PM_RECEIVER_VDAC_VSEL_18, + TWL4030_PM_RECEIVER_VDAC_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); +} + +void twl4030_power_mmc_init(void) +{ + /* Set VMMC1 to 3.15 Volts */ + twl4030_pmrecv_vsel_cfg(TWL4030_PM_RECEIVER_VMMC1_DEDICATED, + TWL4030_PM_RECEIVER_VMMC1_VSEL_32, + TWL4030_PM_RECEIVER_VMMC1_DEV_GRP, + TWL4030_PM_RECEIVER_DEV_GRP_P1); +} diff --git a/qemu/roms/u-boot/drivers/power/twl6030.c b/qemu/roms/u-boot/drivers/power/twl6030.c new file mode 100644 index 000000000..a1c6663a2 --- /dev/null +++ b/qemu/roms/u-boot/drivers/power/twl6030.c @@ -0,0 +1,235 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <config.h> +#ifdef CONFIG_TWL6030_POWER + +#include <twl6030.h> + +static struct twl6030_data *twl; + +static struct twl6030_data twl6030_info = { + .chip_type = chip_TWL6030, + .adc_rbase = GPCH0_LSB, + .adc_ctrl = CTRL_P2, + .adc_enable = CTRL_P2_SP2, + .vbat_mult = TWL6030_VBAT_MULT, + .vbat_shift = TWL6030_VBAT_SHIFT, +}; + +static struct twl6030_data twl6032_info = { + .chip_type = chip_TWL6032, + .adc_rbase = TWL6032_GPCH0_LSB, + .adc_ctrl = TWL6032_CTRL_P1, + .adc_enable = CTRL_P1_SP1, + .vbat_mult = TWL6032_VBAT_MULT, + .vbat_shift = TWL6032_VBAT_SHIFT, +}; + +static int twl6030_gpadc_read_channel(u8 channel_no) +{ + u8 lsb = 0; + u8 msb = 0; + int ret = 0; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, + twl->adc_rbase + channel_no * 2, &lsb); + if (ret) + return ret; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, + twl->adc_rbase + 1 + channel_no * 2, &msb); + if (ret) + return ret; + + return (msb << 8) | lsb; +} + +static int twl6030_gpadc_sw2_trigger(void) +{ + u8 val; + int ret = 0; + + ret = twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + twl->adc_ctrl, twl->adc_enable); + if (ret) + return ret; + + /* Waiting until the SW1 conversion ends*/ + val = CTRL_P2_BUSY; + + while (!((val & CTRL_P2_EOCP2) && (!(val & CTRL_P2_BUSY)))) { + ret = twl6030_i2c_read_u8(TWL6030_CHIP_ADC, + twl->adc_ctrl, &val); + if (ret) + return ret; + udelay(1000); + } + + return 0; +} + +void twl6030_stop_usb_charging(void) +{ + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CONTROLLER_CTRL1, 0); + + return; +} + +void twl6030_start_usb_charging(void) +{ + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_VICHRG, CHARGERUSB_VICHRG_1500); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_CINLIMIT, CHARGERUSB_CIN_LIMIT_NONE); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CONTROLLER_INT_MASK, MBAT_TEMP); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_INT_MASK, MASK_MCHARGERUSB_THMREG); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_VOREG, CHARGERUSB_VOREG_4P0); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CHARGERUSB_CTRL2, CHARGERUSB_CTRL2_VITERM_400); + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, CHARGERUSB_CTRL1, TERM); + /* Enable USB charging */ + twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, + CONTROLLER_CTRL1, CONTROLLER_CTRL1_EN_CHARGER); + return; +} + +int twl6030_get_battery_current(void) +{ + int battery_current = 0; + u8 msb = 0; + u8 lsb = 0; + + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, FG_REG_11, &msb); + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, FG_REG_10, &lsb); + battery_current = ((msb << 8) | lsb); + + /* convert 10 bit signed number to 16 bit signed number */ + if (battery_current >= 0x2000) + battery_current = (battery_current - 0x4000); + + battery_current = battery_current * 3000 / 4096; + printf("Battery Current: %d mA\n", battery_current); + + return battery_current; +} + +int twl6030_get_battery_voltage(void) +{ + int battery_volt = 0; + int ret = 0; + u8 vbatch; + + if (twl->chip_type == chip_TWL6030) { + vbatch = TWL6030_GPADC_VBAT_CHNL; + } else { + ret = twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + TWL6032_GPSELECT_ISB, + TWL6032_GPADC_VBAT_CHNL); + if (ret) + return ret; + vbatch = 0; + } + + /* Start GPADC SW conversion */ + ret = twl6030_gpadc_sw2_trigger(); + if (ret) { + printf("Failed to convert battery voltage\n"); + return ret; + } + + /* measure Vbat voltage */ + battery_volt = twl6030_gpadc_read_channel(vbatch); + if (battery_volt < 0) { + printf("Failed to read battery voltage\n"); + return ret; + } + battery_volt = (battery_volt * twl->vbat_mult) >> twl->vbat_shift; + printf("Battery Voltage: %d mV\n", battery_volt); + + return battery_volt; +} + +void twl6030_init_battery_charging(void) +{ + u8 val = 0; + int battery_volt = 0; + int ret = 0; + + ret = twl6030_i2c_read_u8(TWL6030_CHIP_USB, USB_PRODUCT_ID_LSB, &val); + if (ret) { + puts("twl6030_init_battery_charging(): could not determine chip!\n"); + return; + } + if (val == 0x30) { + twl = &twl6030_info; + } else if (val == 0x32) { + twl = &twl6032_info; + } else { + puts("twl6030_init_battery_charging(): unsupported chip type\n"); + return; + } + + /* Enable VBAT measurement */ + if (twl->chip_type == chip_TWL6030) { + twl6030_i2c_write_u8(TWL6030_CHIP_PM, MISC1, VBAT_MEAS); + twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + TWL6030_GPADC_CTRL, + GPADC_CTRL_SCALER_DIV4); + } else { + twl6030_i2c_write_u8(TWL6030_CHIP_ADC, + TWL6032_GPADC_CTRL2, + GPADC_CTRL2_CH18_SCALER_EN); + } + + /* Enable GPADC module */ + ret = twl6030_i2c_write_u8(TWL6030_CHIP_CHARGER, TOGGLE1, FGS | GPADCS); + if (ret) { + printf("Failed to enable GPADC\n"); + return; + } + + battery_volt = twl6030_get_battery_voltage(); + if (battery_volt < 0) + return; + + if (battery_volt < 3000) + printf("Main battery voltage too low!\n"); + + /* Check for the presence of USB charger */ + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, CONTROLLER_STAT1, &val); + + /* check for battery presence indirectly via Fuel gauge */ + if ((val & VBUS_DET) && (battery_volt < 3300)) + twl6030_start_usb_charging(); + + return; +} + +void twl6030_power_mmc_init() +{ + /* set voltage to 3.0 and turnon for APP */ + twl6030_i2c_write_u8(TWL6030_CHIP_PM, VMMC_CFG_VOLTATE, 0x15); + twl6030_i2c_write_u8(TWL6030_CHIP_PM, VMMC_CFG_STATE, 0x21); +} + +void twl6030_usb_device_settings() +{ + u8 data = 0; + + /* Select APP Group and set state to ON */ + twl6030_i2c_write_u8(TWL6030_CHIP_PM, VUSB_CFG_STATE, 0x21); + + twl6030_i2c_read_u8(TWL6030_CHIP_PM, MISC2, &data); + data |= 0x10; + + /* Select the input supply for VBUS regulator */ + twl6030_i2c_write_u8(TWL6030_CHIP_PM, MISC2, data); +} +#endif |