summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/Kconfig54
-rw-r--r--drivers/power/axp209.c72
-rw-r--r--drivers/power/axp221.c245
3 files changed, 333 insertions, 38 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1ec7c0e..e68e16b 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,47 +1,65 @@
config AXP221_POWER
- boolean "axp221 pmic support"
- depends on MACH_SUN6I
+ boolean "axp221 / axp223 pmic support"
+ depends on MACH_SUN6I || MACH_SUN8I
default y
---help---
- Say y here to enable support for the axp221 pmic found on most sun6i
- (A31) boards.
+ Say y here to enable support for the axp221 / axp223 pmic found on most
+ sun6i (A31) / sun8i (A23) boards.
+
+config AXP221_DCDC1_VOLT
+ int "axp221 dcdc1 voltage"
+ depends on AXP221_POWER
+ default 3000
+ ---help---
+ Set the voltage (mV) to program the axp221 dcdc1 at, set to 0 to
+ disable dcdc1. This is typically used as generic 3.3V IO voltage for
+ things like GPIO-s, sdcard interfaces, etc. On most boards this is
+ undervolted to 3.0V to safe battery.
config AXP221_DLDO1_VOLT
int "axp221 dldo1 voltage"
depends on AXP221_POWER
- default -1
+ default 0
---help---
- Set the voltage (mV) to program the axp221 dldo1 at, set to -1 to
- disable dldo1.
+ Set the voltage (mV) to program the axp221 dldo1 at, set to 0 to
+ disable dldo1. On sun6i (A31) boards with ethernet this is often used
+ to power the ethernet phy. On sun8i (A23) boards this is often used to
+ power the wifi.
config AXP221_DLDO4_VOLT
int "axp221 dldo4 voltage"
depends on AXP221_POWER
- default -1
+ default 0
---help---
- Set the voltage (mV) to program the axp221 dldo4 at, set to -1 to
+ Set the voltage (mV) to program the axp221 dldo4 at, set to 0 to
disable dldo4.
config AXP221_ALDO1_VOLT
int "axp221 aldo1 voltage"
depends on AXP221_POWER
- default -1
+ default 0
---help---
- Set the voltage (mV) to program the axp221 aldo1 at, set to -1 to
- disable aldo1.
+ Set the voltage (mV) to program the axp221 aldo1 at, set to 0 to
+ disable aldo1. On sun6i (A31) boards which have a wifi module this is
+ often used to power the wifi module.
config AXP221_ALDO2_VOLT
int "axp221 aldo2 voltage"
depends on AXP221_POWER
- default -1
+ default 0 if MACH_SUN6I
+ default 2500 if MACH_SUN8I
---help---
- Set the voltage (mV) to program the axp221 aldo2 at, set to -1 to
- disable aldo2.
+ Set the voltage (mV) to program the axp221 aldo2 at, set to 0 to
+ disable aldo2. On sun6i (A31) boards this is typically unused and
+ should be disabled, if it is used for LPDDR2 it should be set to 1.8V.
+ On sun8i (A23) this is typically connected to VDD-DLL and must be set
+ to 2.5V.
config AXP221_ALDO3_VOLT
int "axp221 aldo3 voltage"
depends on AXP221_POWER
- default -1
+ default 3000
---help---
- Set the voltage (mV) to program the axp221 aldo3 at, set to -1 to
- disable aldo3.
+ Set the voltage (mV) to program the axp221 aldo3 at, set to 0 to
+ disable aldo3. This is typically connected to VCC-PLL and AVCC and
+ must be set to 3V.
diff --git a/drivers/power/axp209.c b/drivers/power/axp209.c
index 9798e5b..3b1a6a7 100644
--- a/drivers/power/axp209.c
+++ b/drivers/power/axp209.c
@@ -18,6 +18,11 @@ enum axp209_reg {
AXP209_LDO3_VOLTAGE = 0x29,
AXP209_IRQ_STATUS5 = 0x4c,
AXP209_SHUTDOWN = 0x32,
+ AXP209_GPIO0_CTRL = 0x90,
+ AXP209_GPIO1_CTRL = 0x92,
+ AXP209_GPIO2_CTRL = 0x93,
+ AXP209_GPIO_STATE = 0x94,
+ AXP209_GPIO3_CTRL = 0x95,
};
#define AXP209_POWER_STATUS_ON_BY_DC (1 << 0)
@@ -27,6 +32,15 @@ enum axp209_reg {
#define AXP209_POWEROFF (1 << 7)
+#define AXP209_GPIO_OUTPUT_LOW 0x00 /* Drive pin low */
+#define AXP209_GPIO_OUTPUT_HIGH 0x01 /* Drive pin high */
+#define AXP209_GPIO_INPUT 0x02 /* Float pin */
+
+/* GPIO3 is different from the others */
+#define AXP209_GPIO3_OUTPUT_LOW 0x00 /* Drive pin low, Output mode */
+#define AXP209_GPIO3_OUTPUT_HIGH 0x02 /* Float pin, Output mode */
+#define AXP209_GPIO3_INPUT 0x06 /* Float pin, Input mode */
+
static int axp209_write(enum axp209_reg reg, u8 val)
{
return i2c_write(0x34, reg, 1, &val, 1);
@@ -165,3 +179,61 @@ int axp209_power_button(void)
return v & AXP209_IRQ5_PEK_DOWN;
}
+
+static u8 axp209_get_gpio_ctrl_reg(unsigned int pin)
+{
+ switch (pin) {
+ case 0: return AXP209_GPIO0_CTRL;
+ case 1: return AXP209_GPIO1_CTRL;
+ case 2: return AXP209_GPIO2_CTRL;
+ case 3: return AXP209_GPIO3_CTRL;
+ }
+ return 0;
+}
+
+int axp_gpio_direction_input(unsigned int pin)
+{
+ u8 reg = axp209_get_gpio_ctrl_reg(pin);
+ /* GPIO3 is "special" */
+ u8 val = (pin == 3) ? AXP209_GPIO3_INPUT : AXP209_GPIO_INPUT;
+
+ return axp209_write(reg, val);
+}
+
+int axp_gpio_direction_output(unsigned int pin, unsigned int val)
+{
+ u8 reg = axp209_get_gpio_ctrl_reg(pin);
+
+ if (val) {
+ val = (pin == 3) ? AXP209_GPIO3_OUTPUT_HIGH :
+ AXP209_GPIO_OUTPUT_HIGH;
+ } else {
+ val = (pin == 3) ? AXP209_GPIO3_OUTPUT_LOW :
+ AXP209_GPIO_OUTPUT_LOW;
+ }
+
+ return axp209_write(reg, val);
+}
+
+int axp_gpio_get_value(unsigned int pin)
+{
+ u8 val, mask;
+ int rc;
+
+ if (pin == 3) {
+ rc = axp209_read(AXP209_GPIO3_CTRL, &val);
+ mask = 1;
+ } else {
+ rc = axp209_read(AXP209_GPIO_STATE, &val);
+ mask = 1 << (pin + 4);
+ }
+ if (rc)
+ return rc;
+
+ return (val & mask) ? 1 : 0;
+}
+
+int axp_gpio_set_value(unsigned int pin, unsigned int val)
+{
+ return axp_gpio_direction_output(pin, val);
+}
diff --git a/drivers/power/axp221.c b/drivers/power/axp221.c
index 941193a..4c86f09 100644
--- a/drivers/power/axp221.c
+++ b/drivers/power/axp221.c
@@ -1,4 +1,10 @@
/*
+ * AXP221 and AXP223 driver
+ *
+ * IMPORTANT when making changes to this file check that the registers
+ * used are the same for the axp221 and axp223.
+ *
+ * (C) Copyright 2014 Hans de Goede <hdegoede@redhat.com>
* (C) Copyright 2013 Oliver Schinagl <oliver@schinagl.nl>
*
* SPDX-License-Identifier: GPL-2.0+
@@ -7,8 +13,50 @@
#include <common.h>
#include <errno.h>
#include <asm/arch/p2wi.h>
+#include <asm/arch/rsb.h>
#include <axp221.h>
+/*
+ * The axp221 uses the p2wi bus, the axp223 is identical (for all registers
+ * used sofar) but uses the rsb bus. These functions abstract this.
+ */
+static int pmic_bus_init(void)
+{
+#ifdef CONFIG_MACH_SUN6I
+ p2wi_init();
+ return p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR,
+ AXP221_INIT_DATA);
+#else
+ int ret;
+
+ rsb_init();
+
+ ret = rsb_set_device_mode(AXP223_DEVICE_MODE_DATA);
+ if (ret)
+ return ret;
+
+ return rsb_set_device_address(AXP223_DEVICE_ADDR, AXP223_RUNTIME_ADDR);
+#endif
+}
+
+static int pmic_bus_read(const u8 addr, u8 *data)
+{
+#ifdef CONFIG_MACH_SUN6I
+ return p2wi_read(addr, data);
+#else
+ return rsb_read(AXP223_RUNTIME_ADDR, addr, data);
+#endif
+}
+
+static int pmic_bus_write(const u8 addr, u8 data)
+{
+#ifdef CONFIG_MACH_SUN6I
+ return p2wi_write(addr, data);
+#else
+ return rsb_write(AXP223_RUNTIME_ADDR, addr, data);
+#endif
+}
+
static u8 axp221_mvolt_to_cfg(int mvolt, int min, int max, int div)
{
if (mvolt < min)
@@ -24,12 +72,25 @@ static int axp221_setbits(u8 reg, u8 bits)
int ret;
u8 val;
- ret = p2wi_read(reg, &val);
+ ret = pmic_bus_read(reg, &val);
if (ret)
return ret;
val |= bits;
- return p2wi_write(reg, val);
+ return pmic_bus_write(reg, val);
+}
+
+static int axp221_clrbits(u8 reg, u8 bits)
+{
+ int ret;
+ u8 val;
+
+ ret = pmic_bus_read(reg, &val);
+ if (ret)
+ return ret;
+
+ val &= ~bits;
+ return pmic_bus_write(reg, val);
}
int axp221_set_dcdc1(unsigned int mvolt)
@@ -37,40 +98,89 @@ int axp221_set_dcdc1(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 1600, 3400, 100);
- ret = p2wi_write(AXP221_DCDC1_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC1_EN);
+
+ ret = pmic_bus_write(AXP221_DCDC1_CTRL, cfg);
if (ret)
return ret;
- return axp221_setbits(AXP221_OUTPUT_CTRL2,
- AXP221_OUTPUT_CTRL2_DCDC1_EN);
+ ret = axp221_setbits(AXP221_OUTPUT_CTRL2,
+ AXP221_OUTPUT_CTRL2_DCDC1SW_EN);
+ if (ret)
+ return ret;
+
+ return axp221_setbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC1_EN);
}
int axp221_set_dcdc2(unsigned int mvolt)
{
+ int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20);
- return p2wi_write(AXP221_DCDC2_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC2_EN);
+
+ ret = pmic_bus_write(AXP221_DCDC2_CTRL, cfg);
+ if (ret)
+ return ret;
+
+ return axp221_setbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC2_EN);
}
int axp221_set_dcdc3(unsigned int mvolt)
{
+ int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1860, 20);
- return p2wi_write(AXP221_DCDC3_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC3_EN);
+
+ ret = pmic_bus_write(AXP221_DCDC3_CTRL, cfg);
+ if (ret)
+ return ret;
+
+ return axp221_setbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC3_EN);
}
int axp221_set_dcdc4(unsigned int mvolt)
{
+ int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 600, 1540, 20);
- return p2wi_write(AXP221_DCDC4_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC4_EN);
+
+ ret = pmic_bus_write(AXP221_DCDC4_CTRL, cfg);
+ if (ret)
+ return ret;
+
+ return axp221_setbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC4_EN);
}
int axp221_set_dcdc5(unsigned int mvolt)
{
+ int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 1000, 2550, 50);
- return p2wi_write(AXP221_DCDC5_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC5_EN);
+
+ ret = pmic_bus_write(AXP221_DCDC5_CTRL, cfg);
+ if (ret)
+ return ret;
+
+ return axp221_setbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_DCDC5_EN);
}
int axp221_set_dldo1(unsigned int mvolt)
@@ -78,7 +188,11 @@ int axp221_set_dldo1(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
- ret = p2wi_write(AXP221_DLDO1_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL2,
+ AXP221_OUTPUT_CTRL2_DLDO1_EN);
+
+ ret = pmic_bus_write(AXP221_DLDO1_CTRL, cfg);
if (ret)
return ret;
@@ -91,7 +205,11 @@ int axp221_set_dldo2(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
- ret = p2wi_write(AXP221_DLDO2_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL2,
+ AXP221_OUTPUT_CTRL2_DLDO2_EN);
+
+ ret = pmic_bus_write(AXP221_DLDO2_CTRL, cfg);
if (ret)
return ret;
@@ -104,7 +222,11 @@ int axp221_set_dldo3(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
- ret = p2wi_write(AXP221_DLDO3_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL2,
+ AXP221_OUTPUT_CTRL2_DLDO3_EN);
+
+ ret = pmic_bus_write(AXP221_DLDO3_CTRL, cfg);
if (ret)
return ret;
@@ -117,7 +239,11 @@ int axp221_set_dldo4(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
- ret = p2wi_write(AXP221_DLDO4_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL2,
+ AXP221_OUTPUT_CTRL2_DLDO4_EN);
+
+ ret = pmic_bus_write(AXP221_DLDO4_CTRL, cfg);
if (ret)
return ret;
@@ -130,7 +256,11 @@ int axp221_set_aldo1(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
- ret = p2wi_write(AXP221_ALDO1_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_ALDO1_EN);
+
+ ret = pmic_bus_write(AXP221_ALDO1_CTRL, cfg);
if (ret)
return ret;
@@ -143,7 +273,11 @@ int axp221_set_aldo2(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
- ret = p2wi_write(AXP221_ALDO2_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL1,
+ AXP221_OUTPUT_CTRL1_ALDO2_EN);
+
+ ret = pmic_bus_write(AXP221_ALDO2_CTRL, cfg);
if (ret)
return ret;
@@ -156,7 +290,11 @@ int axp221_set_aldo3(unsigned int mvolt)
int ret;
u8 cfg = axp221_mvolt_to_cfg(mvolt, 700, 3300, 100);
- ret = p2wi_write(AXP221_ALDO3_CTRL, cfg);
+ if (mvolt == 0)
+ return axp221_clrbits(AXP221_OUTPUT_CTRL3,
+ AXP221_OUTPUT_CTRL3_ALDO3_EN);
+
+ ret = pmic_bus_write(AXP221_ALDO3_CTRL, cfg);
if (ret)
return ret;
@@ -166,21 +304,88 @@ int axp221_set_aldo3(unsigned int mvolt)
int axp221_init(void)
{
+ /* This cannot be 0 because it is used in SPL before BSS is ready */
+ static int needs_init = 1;
u8 axp_chip_id;
int ret;
- p2wi_init();
- ret = p2wi_change_to_p2wi_mode(AXP221_CHIP_ADDR, AXP221_CTRL_ADDR,
- AXP221_INIT_DATA);
+ if (!needs_init)
+ return 0;
+
+ ret = pmic_bus_init();
if (ret)
return ret;
- ret = p2wi_read(AXP221_CHIP_ID, &axp_chip_id);
+ ret = pmic_bus_read(AXP221_CHIP_ID, &axp_chip_id);
if (ret)
return ret;
if (!(axp_chip_id == 0x6 || axp_chip_id == 0x7 || axp_chip_id == 0x17))
return -ENODEV;
+ needs_init = 0;
return 0;
}
+
+int axp221_get_sid(unsigned int *sid)
+{
+ u8 *dest = (u8 *)sid;
+ int i, ret;
+
+ ret = axp221_init();
+ if (ret)
+ return ret;
+
+ ret = pmic_bus_write(AXP221_PAGE, 1);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < 16; i++) {
+ ret = pmic_bus_read(AXP221_SID + i, &dest[i]);
+ if (ret)
+ return ret;
+ }
+
+ pmic_bus_write(AXP221_PAGE, 0);
+
+ for (i = 0; i < 4; i++)
+ sid[i] = be32_to_cpu(sid[i]);
+
+ return 0;
+}
+
+static int axp_drivebus_setup(void)
+{
+ int ret;
+
+ ret = axp221_init();
+ if (ret)
+ return ret;
+
+ /* Set N_VBUSEN pin to output / DRIVEBUS function */
+ return axp221_clrbits(AXP221_MISC_CTRL, AXP221_MISC_CTRL_N_VBUSEN_FUNC);
+}
+
+int axp_drivebus_enable(void)
+{
+ int ret;
+
+ ret = axp_drivebus_setup();
+ if (ret)
+ return ret;
+
+ /* Set DRIVEBUS high */
+ return axp221_setbits(AXP221_VBUS_IPSOUT, AXP221_VBUS_IPSOUT_DRIVEBUS);
+}
+
+int axp_drivebus_disable(void)
+{
+ int ret;
+
+ ret = axp_drivebus_setup();
+ if (ret)
+ return ret;
+
+ /* Set DRIVEBUS low */
+ return axp221_clrbits(AXP221_VBUS_IPSOUT, AXP221_VBUS_IPSOUT_DRIVEBUS);
+}