Merge changes from topic "allwinner_pmic" into integration
* changes: allwinner: Convert AXP803 regulator setup code into a driver allwinner: a64: power: Use fdt_for_each_subnode allwinner: a64: power: Remove obsolete register check allwinner: a64: power: Remove duplicate DT check allwinner: Build PMIC bus drivers only in BL31 allwinner: a64: power: Make sunxi_turn_off_soc static allwinner: Merge duplicate code in sunxi_power_down allwinner: Clean up PMIC-related error handling allwinner: Synchronize PMIC enumerations allwinner: Enable clock before resetting I2C/RSB
This commit is contained in:
commit
2f3abc19d3
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drivers/allwinner/axp.h>
|
||||||
|
|
||||||
|
const uint8_t axp_chip_id = AXP803_CHIP_ID;
|
||||||
|
const char *const axp_compatible = "x-powers,axp803";
|
||||||
|
|
||||||
|
const struct axp_regulator axp_regulators[] = {
|
||||||
|
{"dcdc1", 1600, 3400, 100, NA, 0x20, 0x10, 0},
|
||||||
|
{"dcdc5", 800, 1840, 10, 32, 0x24, 0x10, 4},
|
||||||
|
{"dcdc6", 600, 1520, 10, 50, 0x25, 0x10, 5},
|
||||||
|
{"dldo1", 700, 3300, 100, NA, 0x15, 0x12, 3},
|
||||||
|
{"dldo2", 700, 4200, 100, 27, 0x16, 0x12, 4},
|
||||||
|
{"dldo3", 700, 3300, 100, NA, 0x17, 0x12, 5},
|
||||||
|
{"dldo4", 700, 3300, 100, NA, 0x18, 0x12, 6},
|
||||||
|
{"fldo1", 700, 1450, 50, NA, 0x1c, 0x13, 2},
|
||||||
|
{}
|
||||||
|
};
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <libfdt.h>
|
||||||
|
|
||||||
|
#include <common/debug.h>
|
||||||
|
#include <drivers/allwinner/axp.h>
|
||||||
|
|
||||||
|
int axp_check_id(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = axp_read(0x03);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret &= 0xcf;
|
||||||
|
if (ret != axp_chip_id) {
|
||||||
|
ERROR("PMIC: Found unknown PMIC %02x\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask)
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = axp_read(reg);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
val = (ret & ~clr_mask) | set_mask;
|
||||||
|
|
||||||
|
return axp_write(reg, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void axp_power_off(void)
|
||||||
|
{
|
||||||
|
/* Set "power disable control" bit */
|
||||||
|
axp_setbits(0x32, BIT(7));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve the voltage from a given regulator DTB node.
|
||||||
|
* Both the regulator-{min,max}-microvolt properties must be present and
|
||||||
|
* have the same value. Return that value in millivolts.
|
||||||
|
*/
|
||||||
|
static int fdt_get_regulator_millivolt(const void *fdt, int node)
|
||||||
|
{
|
||||||
|
const fdt32_t *prop;
|
||||||
|
uint32_t min_volt;
|
||||||
|
|
||||||
|
prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL);
|
||||||
|
if (prop == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
min_volt = fdt32_to_cpu(*prop);
|
||||||
|
|
||||||
|
prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL);
|
||||||
|
if (prop == NULL)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (fdt32_to_cpu(*prop) != min_volt)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return min_volt / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int setup_regulator(const void *fdt, int node,
|
||||||
|
const struct axp_regulator *reg)
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
int mvolt;
|
||||||
|
|
||||||
|
mvolt = fdt_get_regulator_millivolt(fdt, node);
|
||||||
|
if (mvolt < reg->min_volt || mvolt > reg->max_volt)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
val = (mvolt / reg->step) - (reg->min_volt / reg->step);
|
||||||
|
if (val > reg->split)
|
||||||
|
val = ((val - reg->split) / 2) + reg->split;
|
||||||
|
|
||||||
|
axp_write(reg->volt_reg, val);
|
||||||
|
axp_setbits(reg->switch_reg, BIT(reg->switch_bit));
|
||||||
|
|
||||||
|
INFO("PMIC: %s voltage: %d.%03dV\n", reg->dt_name,
|
||||||
|
mvolt / 1000, mvolt % 1000);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool should_enable_regulator(const void *fdt, int node)
|
||||||
|
{
|
||||||
|
if (fdt_getprop(fdt, node, "phandle", NULL) != NULL)
|
||||||
|
return true;
|
||||||
|
if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void axp_setup_regulators(const void *fdt)
|
||||||
|
{
|
||||||
|
int node;
|
||||||
|
bool dc1sw = false;
|
||||||
|
|
||||||
|
if (fdt == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* locate the PMIC DT node, bail out if not found */
|
||||||
|
node = fdt_node_offset_by_compatible(fdt, -1, axp_compatible);
|
||||||
|
if (node < 0) {
|
||||||
|
WARN("PMIC: No PMIC DT node, skipping setup\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL)) {
|
||||||
|
axp_clrbits(0x8f, BIT(4));
|
||||||
|
axp_setbits(0x30, BIT(2));
|
||||||
|
INFO("PMIC: Enabling DRIVEVBUS\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* descend into the "regulators" subnode */
|
||||||
|
node = fdt_subnode_offset(fdt, node, "regulators");
|
||||||
|
if (node < 0) {
|
||||||
|
WARN("PMIC: No regulators DT node, skipping setup\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterate over all regulators to find used ones */
|
||||||
|
fdt_for_each_subnode(node, fdt, node) {
|
||||||
|
const struct axp_regulator *reg;
|
||||||
|
const char *name;
|
||||||
|
int length;
|
||||||
|
|
||||||
|
/* We only care if it's always on or referenced. */
|
||||||
|
if (!should_enable_regulator(fdt, node))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
name = fdt_get_name(fdt, node, &length);
|
||||||
|
for (reg = axp_regulators; reg->dt_name; reg++) {
|
||||||
|
if (!strncmp(name, reg->dt_name, length)) {
|
||||||
|
setup_regulator(fdt, node, reg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(name, "dc1sw", length)) {
|
||||||
|
/* Delay DC1SW enablement to avoid overheating. */
|
||||||
|
dc1sw = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If DLDO2 is enabled after DC1SW, the PMIC overheats and shuts
|
||||||
|
* down. So always enable DC1SW as the very last regulator.
|
||||||
|
*/
|
||||||
|
if (dc1sw) {
|
||||||
|
INFO("PMIC: Enabling DC1SW\n");
|
||||||
|
axp_setbits(0x12, BIT(7));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AXP_H
|
||||||
|
#define AXP_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define NA 0xff
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AXP803_CHIP_ID = 0x41,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct axp_regulator {
|
||||||
|
const char *dt_name;
|
||||||
|
uint16_t min_volt;
|
||||||
|
uint16_t max_volt;
|
||||||
|
uint16_t step;
|
||||||
|
unsigned char split;
|
||||||
|
unsigned char volt_reg;
|
||||||
|
unsigned char switch_reg;
|
||||||
|
unsigned char switch_bit;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const uint8_t axp_chip_id;
|
||||||
|
extern const char *const axp_compatible;
|
||||||
|
extern const struct axp_regulator axp_regulators[];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since the PMIC can be connected to multiple bus types,
|
||||||
|
* low-level read/write functions must be provided by the platform
|
||||||
|
*/
|
||||||
|
int axp_read(uint8_t reg);
|
||||||
|
int axp_write(uint8_t reg, uint8_t val);
|
||||||
|
int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask);
|
||||||
|
#define axp_clrbits(reg, clr_mask) axp_clrsetbits(reg, clr_mask, 0)
|
||||||
|
#define axp_setbits(reg, set_mask) axp_clrsetbits(reg, 0, set_mask)
|
||||||
|
|
||||||
|
int axp_check_id(void);
|
||||||
|
void axp_power_off(void);
|
||||||
|
void axp_setup_regulators(const void *fdt);
|
||||||
|
|
||||||
|
#endif /* AXP_H */
|
|
@ -12,7 +12,7 @@ void sunxi_configure_mmu_el3(int flags);
|
||||||
void sunxi_cpu_on(u_register_t mpidr);
|
void sunxi_cpu_on(u_register_t mpidr);
|
||||||
void sunxi_cpu_off(u_register_t mpidr);
|
void sunxi_cpu_off(u_register_t mpidr);
|
||||||
void sunxi_disable_secondary_cpus(u_register_t primary_mpidr);
|
void sunxi_disable_secondary_cpus(u_register_t primary_mpidr);
|
||||||
void __dead2 sunxi_power_down(void);
|
void sunxi_power_down(void);
|
||||||
|
|
||||||
int sunxi_pmic_setup(uint16_t socid, const void *fdt);
|
int sunxi_pmic_setup(uint16_t socid, const void *fdt);
|
||||||
void sunxi_security_setup(void);
|
void sunxi_security_setup(void);
|
||||||
|
|
|
@ -150,16 +150,16 @@ int sunxi_init_platform_r_twi(uint16_t socid, bool use_rsb)
|
||||||
/* set both pins to pull-up */
|
/* set both pins to pull-up */
|
||||||
mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x1c, 0x0fU, 0x5U);
|
mmio_clrsetbits_32(SUNXI_R_PIO_BASE + 0x1c, 0x0fU, 0x5U);
|
||||||
|
|
||||||
/* assert, then de-assert reset of I2C/RSB controller */
|
|
||||||
mmio_clrbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit);
|
|
||||||
mmio_setbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit);
|
|
||||||
|
|
||||||
/* un-gate clock */
|
/* un-gate clock */
|
||||||
if (socid != SUNXI_SOC_H6)
|
if (socid != SUNXI_SOC_H6)
|
||||||
mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x28, device_bit);
|
mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x28, device_bit);
|
||||||
else
|
else
|
||||||
mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x19c, device_bit | BIT(0));
|
mmio_setbits_32(SUNXI_R_PRCM_BASE + 0x19c, device_bit | BIT(0));
|
||||||
|
|
||||||
|
/* assert, then de-assert reset of I2C/RSB controller */
|
||||||
|
mmio_clrbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit);
|
||||||
|
mmio_setbits_32(SUNXI_R_PRCM_BASE + reset_offset, device_bit);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
* Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -65,6 +65,11 @@ static void __dead2 sunxi_system_off(void)
|
||||||
sunxi_disable_secondary_cpus(read_mpidr());
|
sunxi_disable_secondary_cpus(read_mpidr());
|
||||||
|
|
||||||
sunxi_power_down();
|
sunxi_power_down();
|
||||||
|
|
||||||
|
udelay(1000);
|
||||||
|
ERROR("PSCI: Cannot turn off system, halting\n");
|
||||||
|
wfi();
|
||||||
|
panic();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __dead2 sunxi_system_reset(void)
|
static void __dead2 sunxi_system_reset(void)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
#
|
#
|
||||||
|
@ -7,4 +7,6 @@
|
||||||
# The differences between the platform are covered by the include files.
|
# The differences between the platform are covered by the include files.
|
||||||
include plat/allwinner/common/allwinner-common.mk
|
include plat/allwinner/common/allwinner-common.mk
|
||||||
|
|
||||||
PLAT_BL_COMMON_SOURCES += drivers/allwinner/sunxi_rsb.c
|
BL31_SOURCES += drivers/allwinner/axp/axp803.c \
|
||||||
|
drivers/allwinner/axp/common.c \
|
||||||
|
drivers/allwinner/sunxi_rsb.c
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
* Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
* Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
|
* Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
@ -7,14 +7,11 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <libfdt.h>
|
|
||||||
|
|
||||||
#include <platform_def.h>
|
#include <platform_def.h>
|
||||||
|
|
||||||
#include <arch_helpers.h>
|
|
||||||
#include <common/debug.h>
|
#include <common/debug.h>
|
||||||
|
#include <drivers/allwinner/axp.h>
|
||||||
#include <drivers/allwinner/sunxi_rsb.h>
|
#include <drivers/allwinner/sunxi_rsb.h>
|
||||||
#include <drivers/delay_timer.h>
|
|
||||||
#include <lib/mmio.h>
|
#include <lib/mmio.h>
|
||||||
|
|
||||||
#include <sunxi_def.h>
|
#include <sunxi_def.h>
|
||||||
|
@ -22,6 +19,7 @@
|
||||||
#include <sunxi_private.h>
|
#include <sunxi_private.h>
|
||||||
|
|
||||||
static enum pmic_type {
|
static enum pmic_type {
|
||||||
|
UNKNOWN,
|
||||||
GENERIC_H5,
|
GENERIC_H5,
|
||||||
GENERIC_A64,
|
GENERIC_A64,
|
||||||
REF_DESIGN_H5, /* regulators controlled by GPIO pins on port L */
|
REF_DESIGN_H5, /* regulators controlled by GPIO pins on port L */
|
||||||
|
@ -38,7 +36,7 @@ static enum pmic_type {
|
||||||
* disabled.
|
* disabled.
|
||||||
* This function only cares about peripherals.
|
* This function only cares about peripherals.
|
||||||
*/
|
*/
|
||||||
void sunxi_turn_off_soc(uint16_t socid)
|
static void sunxi_turn_off_soc(uint16_t socid)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -113,175 +111,22 @@ static int rsb_init(void)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Associate the 8-bit runtime address with the 12-bit bus address. */
|
/* Associate the 8-bit runtime address with the 12-bit bus address. */
|
||||||
return rsb_assign_runtime_address(AXP803_HW_ADDR,
|
ret = rsb_assign_runtime_address(AXP803_HW_ADDR,
|
||||||
AXP803_RT_ADDR);
|
AXP803_RT_ADDR);
|
||||||
}
|
if (ret)
|
||||||
|
|
||||||
static int axp_write(uint8_t reg, uint8_t val)
|
|
||||||
{
|
|
||||||
return rsb_write(AXP803_RT_ADDR, reg, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int axp_clrsetbits(uint8_t reg, uint8_t clr_mask, uint8_t set_mask)
|
|
||||||
{
|
|
||||||
uint8_t regval;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = rsb_read(AXP803_RT_ADDR, reg);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
regval = (ret & ~clr_mask) | set_mask;
|
return axp_check_id();
|
||||||
|
|
||||||
return rsb_write(AXP803_RT_ADDR, reg, regval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define axp_clrbits(reg, clr_mask) axp_clrsetbits(reg, clr_mask, 0)
|
int axp_read(uint8_t reg)
|
||||||
#define axp_setbits(reg, set_mask) axp_clrsetbits(reg, 0, set_mask)
|
|
||||||
|
|
||||||
static bool should_enable_regulator(const void *fdt, int node)
|
|
||||||
{
|
{
|
||||||
if (fdt_getprop(fdt, node, "phandle", NULL) != NULL)
|
return rsb_read(AXP803_RT_ADDR, reg);
|
||||||
return true;
|
|
||||||
if (fdt_getprop(fdt, node, "regulator-always-on", NULL) != NULL)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
int axp_write(uint8_t reg, uint8_t val)
|
||||||
* Retrieve the voltage from a given regulator DTB node.
|
|
||||||
* Both the regulator-{min,max}-microvolt properties must be present and
|
|
||||||
* have the same value. Return that value in millivolts.
|
|
||||||
*/
|
|
||||||
static int fdt_get_regulator_millivolt(const void *fdt, int node)
|
|
||||||
{
|
{
|
||||||
const fdt32_t *prop;
|
return rsb_write(AXP803_RT_ADDR, reg, val);
|
||||||
uint32_t min_volt;
|
|
||||||
|
|
||||||
prop = fdt_getprop(fdt, node, "regulator-min-microvolt", NULL);
|
|
||||||
if (prop == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
min_volt = fdt32_to_cpu(*prop);
|
|
||||||
|
|
||||||
prop = fdt_getprop(fdt, node, "regulator-max-microvolt", NULL);
|
|
||||||
if (prop == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (fdt32_to_cpu(*prop) != min_volt)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return min_volt / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NO_SPLIT 0xff
|
|
||||||
|
|
||||||
static const struct axp_regulator {
|
|
||||||
char *dt_name;
|
|
||||||
uint16_t min_volt;
|
|
||||||
uint16_t max_volt;
|
|
||||||
uint16_t step;
|
|
||||||
unsigned char split;
|
|
||||||
unsigned char volt_reg;
|
|
||||||
unsigned char switch_reg;
|
|
||||||
unsigned char switch_bit;
|
|
||||||
} regulators[] = {
|
|
||||||
{"dcdc1", 1600, 3400, 100, NO_SPLIT, 0x20, 0x10, 0},
|
|
||||||
{"dcdc5", 800, 1840, 10, 32, 0x24, 0x10, 4},
|
|
||||||
{"dcdc6", 600, 1520, 10, 50, 0x25, 0x10, 5},
|
|
||||||
{"dldo1", 700, 3300, 100, NO_SPLIT, 0x15, 0x12, 3},
|
|
||||||
{"dldo2", 700, 4200, 100, 27, 0x16, 0x12, 4},
|
|
||||||
{"dldo3", 700, 3300, 100, NO_SPLIT, 0x17, 0x12, 5},
|
|
||||||
{"dldo4", 700, 3300, 100, NO_SPLIT, 0x18, 0x12, 6},
|
|
||||||
{"fldo1", 700, 1450, 50, NO_SPLIT, 0x1c, 0x13, 2},
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
static int setup_regulator(const void *fdt, int node,
|
|
||||||
const struct axp_regulator *reg)
|
|
||||||
{
|
|
||||||
int mvolt;
|
|
||||||
uint8_t regval;
|
|
||||||
|
|
||||||
if (!should_enable_regulator(fdt, node))
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
mvolt = fdt_get_regulator_millivolt(fdt, node);
|
|
||||||
if (mvolt < reg->min_volt || mvolt > reg->max_volt)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
regval = (mvolt / reg->step) - (reg->min_volt / reg->step);
|
|
||||||
if (regval > reg->split)
|
|
||||||
regval = ((regval - reg->split) / 2) + reg->split;
|
|
||||||
|
|
||||||
axp_write(reg->volt_reg, regval);
|
|
||||||
if (reg->switch_reg < 0xff)
|
|
||||||
axp_setbits(reg->switch_reg, BIT(reg->switch_bit));
|
|
||||||
|
|
||||||
INFO("PMIC: AXP803: %s voltage: %d.%03dV\n", reg->dt_name,
|
|
||||||
mvolt / 1000, mvolt % 1000);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setup_axp803_rails(const void *fdt)
|
|
||||||
{
|
|
||||||
int node;
|
|
||||||
bool dc1sw = false;
|
|
||||||
|
|
||||||
/* locate the PMIC DT node, bail out if not found */
|
|
||||||
node = fdt_node_offset_by_compatible(fdt, -1, "x-powers,axp803");
|
|
||||||
if (node < 0) {
|
|
||||||
WARN("BL31: PMIC: Cannot find AXP803 DT node, skipping initial setup.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fdt_getprop(fdt, node, "x-powers,drive-vbus-en", NULL)) {
|
|
||||||
axp_clrbits(0x8f, BIT(4));
|
|
||||||
axp_setbits(0x30, BIT(2));
|
|
||||||
INFO("PMIC: AXP803: Enabling DRIVEVBUS\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* descend into the "regulators" subnode */
|
|
||||||
node = fdt_subnode_offset(fdt, node, "regulators");
|
|
||||||
if (node < 0) {
|
|
||||||
WARN("BL31: PMIC: Cannot find regulators subnode, skipping initial setup.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* iterate over all regulators to find used ones */
|
|
||||||
for (node = fdt_first_subnode(fdt, node);
|
|
||||||
node >= 0;
|
|
||||||
node = fdt_next_subnode(fdt, node)) {
|
|
||||||
const struct axp_regulator *reg;
|
|
||||||
const char *name;
|
|
||||||
int length;
|
|
||||||
|
|
||||||
/* We only care if it's always on or referenced. */
|
|
||||||
if (!should_enable_regulator(fdt, node))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
name = fdt_get_name(fdt, node, &length);
|
|
||||||
for (reg = regulators; reg->dt_name; reg++) {
|
|
||||||
if (!strncmp(name, reg->dt_name, length)) {
|
|
||||||
setup_regulator(fdt, node, reg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strncmp(name, "dc1sw", length)) {
|
|
||||||
/* Delay DC1SW enablement to avoid overheating. */
|
|
||||||
dc1sw = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* If DLDO2 is enabled after DC1SW, the PMIC overheats and shuts
|
|
||||||
* down. So always enable DC1SW as the very last regulator.
|
|
||||||
*/
|
|
||||||
if (dc1sw) {
|
|
||||||
INFO("PMIC: AXP803: Enabling DC1SW\n");
|
|
||||||
axp_setbits(0x12, BIT(7));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sunxi_pmic_setup(uint16_t socid, const void *fdt)
|
int sunxi_pmic_setup(uint16_t socid, const void *fdt)
|
||||||
|
@ -290,11 +135,16 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt)
|
||||||
|
|
||||||
switch (socid) {
|
switch (socid) {
|
||||||
case SUNXI_SOC_H5:
|
case SUNXI_SOC_H5:
|
||||||
|
NOTICE("PMIC: Assuming H5 reference regulator design\n");
|
||||||
|
|
||||||
pmic = REF_DESIGN_H5;
|
pmic = REF_DESIGN_H5;
|
||||||
NOTICE("BL31: PMIC: Defaulting to PortL GPIO according to H5 reference design.\n");
|
|
||||||
break;
|
break;
|
||||||
case SUNXI_SOC_A64:
|
case SUNXI_SOC_A64:
|
||||||
pmic = GENERIC_A64;
|
pmic = GENERIC_A64;
|
||||||
|
|
||||||
|
INFO("PMIC: Probing AXP803 on RSB\n");
|
||||||
|
|
||||||
ret = sunxi_init_platform_r_twi(socid, true);
|
ret = sunxi_init_platform_r_twi(socid, true);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -304,20 +154,16 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
pmic = AXP803_RSB;
|
pmic = AXP803_RSB;
|
||||||
NOTICE("BL31: PMIC: Detected AXP803 on RSB.\n");
|
axp_setup_regulators(fdt);
|
||||||
|
|
||||||
if (fdt)
|
|
||||||
setup_axp803_rails(fdt);
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTICE("BL31: PMIC: No support for Allwinner %x SoC.\n", socid);
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __dead2 sunxi_power_down(void)
|
void sunxi_power_down(void)
|
||||||
{
|
{
|
||||||
switch (pmic) {
|
switch (pmic) {
|
||||||
case GENERIC_H5:
|
case GENERIC_H5:
|
||||||
|
@ -355,16 +201,10 @@ void __dead2 sunxi_power_down(void)
|
||||||
/* (Re-)init RSB in case the rich OS has disabled it. */
|
/* (Re-)init RSB in case the rich OS has disabled it. */
|
||||||
sunxi_init_platform_r_twi(SUNXI_SOC_A64, true);
|
sunxi_init_platform_r_twi(SUNXI_SOC_A64, true);
|
||||||
rsb_init();
|
rsb_init();
|
||||||
|
axp_power_off();
|
||||||
/* Set "power disable control" bit */
|
|
||||||
axp_setbits(0x32, BIT(7));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
udelay(1000);
|
|
||||||
ERROR("PSCI: Cannot turn off system, halting.\n");
|
|
||||||
wfi();
|
|
||||||
panic();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#
|
#
|
||||||
# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: BSD-3-Clause
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
#
|
#
|
||||||
|
@ -7,4 +7,4 @@
|
||||||
# The differences between the platform are covered by the include files.
|
# The differences between the platform are covered by the include files.
|
||||||
include plat/allwinner/common/allwinner-common.mk
|
include plat/allwinner/common/allwinner-common.mk
|
||||||
|
|
||||||
PLAT_BL_COMMON_SOURCES += drivers/mentor/i2c/mi2cv.c
|
BL31_SOURCES += drivers/mentor/i2c/mi2cv.c
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
|
* Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
|
||||||
* Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
|
* Copyright (c) 2018, Icenowy Zheng <icenowy@aosc.io>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-3-Clause
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
@ -21,27 +21,33 @@
|
||||||
#define AXP805_ADDR 0x36
|
#define AXP805_ADDR 0x36
|
||||||
#define AXP805_ID 0x03
|
#define AXP805_ID 0x03
|
||||||
|
|
||||||
enum pmic_type {
|
static enum pmic_type {
|
||||||
NO_PMIC,
|
UNKNOWN,
|
||||||
AXP805,
|
AXP805,
|
||||||
};
|
} pmic;
|
||||||
|
|
||||||
enum pmic_type pmic;
|
|
||||||
|
|
||||||
int axp_i2c_read(uint8_t chip, uint8_t reg, uint8_t *val)
|
int axp_i2c_read(uint8_t chip, uint8_t reg, uint8_t *val)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = i2c_write(chip, 0, 0, ®, 1);
|
ret = i2c_write(chip, 0, 0, ®, 1);
|
||||||
|
if (ret == 0)
|
||||||
|
ret = i2c_read(chip, 0, 0, val, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
ERROR("PMIC: Cannot read AXP805 register %02x\n", reg);
|
||||||
|
|
||||||
return i2c_read(chip, 0, 0, val, 1);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int axp_i2c_write(uint8_t chip, uint8_t reg, uint8_t val)
|
int axp_i2c_write(uint8_t chip, uint8_t reg, uint8_t val)
|
||||||
{
|
{
|
||||||
return i2c_write(chip, reg, 1, &val, 1);
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_write(chip, reg, 1, &val, 1);
|
||||||
|
if (ret)
|
||||||
|
ERROR("PMIC: Cannot write AXP805 register %02x\n", reg);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int axp805_probe(void)
|
static int axp805_probe(void)
|
||||||
|
@ -49,21 +55,18 @@ static int axp805_probe(void)
|
||||||
int ret;
|
int ret;
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
|
|
||||||
|
/* Switch the AXP805 to master/single-PMIC mode. */
|
||||||
ret = axp_i2c_write(AXP805_ADDR, 0xff, 0x0);
|
ret = axp_i2c_write(AXP805_ADDR, 0xff, 0x0);
|
||||||
if (ret) {
|
if (ret)
|
||||||
ERROR("PMIC: Cannot put AXP805 to master mode.\n");
|
return ret;
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = axp_i2c_read(AXP805_ADDR, AXP805_ID, &val);
|
ret = axp_i2c_read(AXP805_ADDR, AXP805_ID, &val);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
if (!ret && ((val & 0xcf) == 0x40))
|
val &= 0xcf;
|
||||||
NOTICE("PMIC: AXP805 detected\n");
|
if (val != 0x40) {
|
||||||
else if (ret) {
|
ERROR("PMIC: Found unknown PMIC %02x\n", val);
|
||||||
ERROR("PMIC: Cannot communicate with AXP805.\n");
|
|
||||||
return -EPERM;
|
|
||||||
} else {
|
|
||||||
ERROR("PMIC: Non-AXP805 chip attached at AXP805's address.\n");
|
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,23 +77,25 @@ int sunxi_pmic_setup(uint16_t socid, const void *fdt)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sunxi_init_platform_r_twi(SUNXI_SOC_H6, false);
|
INFO("PMIC: Probing AXP805 on I2C\n");
|
||||||
|
|
||||||
|
ret = sunxi_init_platform_r_twi(SUNXI_SOC_H6, false);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* initialise mi2cv driver */
|
/* initialise mi2cv driver */
|
||||||
i2c_init((void *)SUNXI_R_I2C_BASE);
|
i2c_init((void *)SUNXI_R_I2C_BASE);
|
||||||
|
|
||||||
NOTICE("PMIC: Probing AXP805\n");
|
|
||||||
pmic = AXP805;
|
|
||||||
|
|
||||||
ret = axp805_probe();
|
ret = axp805_probe();
|
||||||
if (ret)
|
if (ret)
|
||||||
pmic = NO_PMIC;
|
return ret;
|
||||||
else
|
|
||||||
pmic = AXP805;
|
pmic = AXP805;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __dead2 sunxi_power_down(void)
|
void sunxi_power_down(void)
|
||||||
{
|
{
|
||||||
uint8_t val;
|
uint8_t val;
|
||||||
|
|
||||||
|
@ -106,9 +111,4 @@ void __dead2 sunxi_power_down(void)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
udelay(1000);
|
|
||||||
ERROR("PSCI: Cannot communicate with PMIC, halting\n");
|
|
||||||
wfi();
|
|
||||||
panic();
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue