diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /roms/u-boot/drivers/power | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'roms/u-boot/drivers/power')
29 files changed, 3213 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/power/Makefile b/roms/u-boot/drivers/power/Makefile new file mode 100644 index 00000000..53ff97d7 --- /dev/null +++ b/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/roms/u-boot/drivers/power/battery/Makefile b/roms/u-boot/drivers/power/battery/Makefile new file mode 100644 index 00000000..f864f043 --- /dev/null +++ b/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/roms/u-boot/drivers/power/battery/bat_trats.c b/roms/u-boot/drivers/power/battery/bat_trats.c new file mode 100644 index 00000000..41b179fc --- /dev/null +++ b/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/roms/u-boot/drivers/power/battery/bat_trats2.c b/roms/u-boot/drivers/power/battery/bat_trats2.c new file mode 100644 index 00000000..94015aa4 --- /dev/null +++ b/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/roms/u-boot/drivers/power/exynos-tmu.c b/roms/u-boot/drivers/power/exynos-tmu.c new file mode 100644 index 00000000..9a093a5b --- /dev/null +++ b/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/roms/u-boot/drivers/power/ftpmu010.c b/roms/u-boot/drivers/power/ftpmu010.c new file mode 100644 index 00000000..78128c64 --- /dev/null +++ b/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/roms/u-boot/drivers/power/fuel_gauge/Makefile b/roms/u-boot/drivers/power/fuel_gauge/Makefile new file mode 100644 index 00000000..3b349f93 --- /dev/null +++ b/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/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c b/roms/u-boot/drivers/power/fuel_gauge/fg_max17042.c new file mode 100644 index 00000000..154ca6a6 --- /dev/null +++ b/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/roms/u-boot/drivers/power/mfd/Makefile b/roms/u-boot/drivers/power/mfd/Makefile new file mode 100644 index 00000000..43afe842 --- /dev/null +++ b/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/roms/u-boot/drivers/power/mfd/fg_max77693.c b/roms/u-boot/drivers/power/mfd/fg_max77693.c new file mode 100644 index 00000000..4519fed4 --- /dev/null +++ b/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/roms/u-boot/drivers/power/mfd/muic_max77693.c b/roms/u-boot/drivers/power/mfd/muic_max77693.c new file mode 100644 index 00000000..e71012de --- /dev/null +++ b/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/roms/u-boot/drivers/power/mfd/pmic_max77693.c b/roms/u-boot/drivers/power/mfd/pmic_max77693.c new file mode 100644 index 00000000..1a4416b5 --- /dev/null +++ b/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/roms/u-boot/drivers/power/palmas.c b/roms/u-boot/drivers/power/palmas.c new file mode 100644 index 00000000..cfbc9dc5 --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/Makefile b/roms/u-boot/drivers/power/pmic/Makefile new file mode 100644 index 00000000..4129bdab --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/muic_max8997.c b/roms/u-boot/drivers/power/pmic/muic_max8997.c new file mode 100644 index 00000000..720342e5 --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/pmic_max77686.c b/roms/u-boot/drivers/power/pmic/pmic_max77686.c new file mode 100644 index 00000000..d4c430e2 --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/pmic_max8997.c b/roms/u-boot/drivers/power/pmic/pmic_max8997.c new file mode 100644 index 00000000..ba016923 --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/pmic_max8998.c b/roms/u-boot/drivers/power/pmic/pmic_max8998.c new file mode 100644 index 00000000..ca2f504c --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c b/roms/u-boot/drivers/power/pmic/pmic_pfuze100.c new file mode 100644 index 00000000..22c1f15e --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/pmic_tps65217.c b/roms/u-boot/drivers/power/pmic/pmic_tps65217.c new file mode 100644 index 00000000..36e9024b --- /dev/null +++ b/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/roms/u-boot/drivers/power/pmic/pmic_tps65910.c b/roms/u-boot/drivers/power/pmic/pmic_tps65910.c new file mode 100644 index 00000000..7ee1160e --- /dev/null +++ b/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/roms/u-boot/drivers/power/power_core.c b/roms/u-boot/drivers/power/power_core.c new file mode 100644 index 00000000..fe1f3160 --- /dev/null +++ b/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/roms/u-boot/drivers/power/power_dialog.c b/roms/u-boot/drivers/power/power_dialog.c new file mode 100644 index 00000000..fc95d5a1 --- /dev/null +++ b/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/roms/u-boot/drivers/power/power_fsl.c b/roms/u-boot/drivers/power/power_fsl.c new file mode 100644 index 00000000..ac0b541d --- /dev/null +++ b/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/roms/u-boot/drivers/power/power_i2c.c b/roms/u-boot/drivers/power/power_i2c.c new file mode 100644 index 00000000..ac768708 --- /dev/null +++ b/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/roms/u-boot/drivers/power/power_spi.c b/roms/u-boot/drivers/power/power_spi.c new file mode 100644 index 00000000..fb455a00 --- /dev/null +++ b/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/roms/u-boot/drivers/power/tps6586x.c b/roms/u-boot/drivers/power/tps6586x.c new file mode 100644 index 00000000..d29d9695 --- /dev/null +++ b/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/roms/u-boot/drivers/power/twl4030.c b/roms/u-boot/drivers/power/twl4030.c new file mode 100644 index 00000000..3e503104 --- /dev/null +++ b/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/roms/u-boot/drivers/power/twl6030.c b/roms/u-boot/drivers/power/twl6030.c new file mode 100644 index 00000000..a1c6663a --- /dev/null +++ b/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  | 
