You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2023/01/14 10:49:31 UTC

[nuttx] branch master updated: Add ACT8945A power driver

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git


The following commit(s) were added to refs/heads/master by this push:
     new f5e8c30808 Add ACT8945A power driver
f5e8c30808 is described below

commit f5e8c3080882af15b73ab17ba2041e72cff94d0f
Author: TimJTi <56...@users.noreply.github.com>
AuthorDate: Wed Jan 11 16:57:16 2023 +0000

    Add ACT8945A power driver
    
    Update act8945a.c
    
    Update Kconfig
    
    Update act8945a.c
    
    Corrections (xiaoxiang781216)
---
 drivers/power/supply/Kconfig     |  487 ++++++++++++++++-
 drivers/power/supply/Make.defs   |    4 +
 drivers/power/supply/act8945a.c  | 1081 ++++++++++++++++++++++++++++++++++++++
 drivers/power/supply/regulator.c |   51 +-
 include/nuttx/power/act8945a.h   |   61 +++
 include/nuttx/power/regulator.h  |   46 +-
 6 files changed, 1707 insertions(+), 23 deletions(-)

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 7c453b2575..54d385e93f 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -53,7 +53,7 @@ config REGULATOR
 	bool "Regulator core driver support"
 	default n
 	---help---
-		The regulator core driver implements the uper layer framework that the lower
+		The regulator core driver implements the upper layer framework that the lower
 		layer driver can register with, and the common regulator APIs that are easy
 		for other drivers to call for the control of their power supply.
 
@@ -76,6 +76,489 @@ config REGULATOR_RPMSG
 		the rpmsg channel. The remote device(namely server) is responsible for
 		the parse and the completion.
 
-endif
+menuconfig REGULATOR_ACT8945A
+	bool "Qorvo ACT8945A regulator support - MAKE CHANGES WITH CARE"
+	default n
+	---help---
+		-----------------------------------------------------------------------
+		TAKE GREAT CARE - BOARD DAMAGE MAY RESULT FROM INNAPPROPIATE CHANGES
+		-----------------------------------------------------------------------
+
+		The ACT8945A regulator driver implements the lower regulator ops thats
+		use I2C to control the regulator functions.
+
+if REGULATOR_ACT8945A
+
+menu "ACT8945A Regulator System Functions"
+
+choice
+	prompt "ACT8945A SYSLEV threshold"
+	default ACT8945A_SYSLEV_3000
+		---help---
+		The SYSLEV voltage below which the configured SYSLEV mode will be triggered.
+
+config ACT8945A_SYSLEV_2300
+	bool "2.3V"
+
+config ACT8945A_SYSLEV_2400
+	bool "2.4V"
+
+config ACT8945A_SYSLEV_2500
+	bool "2.5"
+
+config ACT8945A_SYSLEV_2600
+	bool "2.6V"
+
+config ACT8945A_SYSLEV_2700
+	bool "2.7V"
+
+config ACT8945A_SYSLEV_2800
+	bool "2.8V"
+
+config ACT8945A_SYSLEV_2900
+	bool "2.9V"
+
+config ACT8945A_SYSLEV_3000
+	bool "3.0V"
+
+config ACT8945A_SYSLEV_3100
+	bool "3.1V"
+
+config ACT8945A_SYSLEV_3200
+	bool "3.2V"
+
+config ACT8945A_SYSLEV_3300
+	bool "3.3V"
+
+config ACT8945A_SYSLEV_3400
+	bool "3.4V"
+
+config ACT8945A_SYSLEV_3500
+	bool "3.5V"
+
+config ACT8945A_SYSLEV_3600
+	bool "3.6V"
+
+config ACT8945A_SYSLEV_3700
+	bool "3.7V"
+
+config ACT8945A_SYSLEV_3800
+	bool "3.8V"
+
+endchoice # ACT8945A SYSLEV threshold
+
+choice 
+	prompt "ACT8945A SYSLEV mode"
+	default ACT8945A_SYSLEV_MODE_INTERRUPT
+	---help---
+		Determines the response to the SYSLEV voltage detector
+		- Generate an interrupt when Vsys < SYSLEV threshold, or
+		- Automatic shutdown    when Vsys < SYSLEV threshold
+
+config ACT8945A_SYSLEV_MODE_INTERRUPT
+	bool "Interrupt"
+	---help---
+		Generates an interrupt when Vsys < SYSLEV threshold.
+		Selecting this will unmask the SYSLEV interrupt as well.
+
+config ACT8945A_SYSLEV_MODE_SHUTDOWN
+	bool "Shutdown"
+	---help---
+		Automatic shutdown when Vsys < SYSLEV threshold
+
+endchoice # ACT8945A_SYSLEV_MODE
+
+choice
+	prompt "ACT8945A Reset Timer Setting"
+	default ACT8945A_TRST_64
+	---help---
+		Defines the reset timeout threshold.
+
+config ACT8945A_TRST_64
+	bool "64ms"
+
+config ACT8945A_TRST_260
+	bool "260ms"
+
+endchoice # ACT8945A Reset Timer Setting
+
+endmenu # ACT8945A System Functions
+
+menu "ACT8945A DCDC1 Configuration"
+
+config ACT8945A_DCDC1_NAME
+	string "DCDC1 name"
+	default "ACT8945A_DCDC1"
+	---help---
+		This is the name used for the ACT8945A DCDC converter output 1.
+		It is used as the consumer name when you get or put a regulator.
+
+config ACT8945A_DCDC1_BOOT_ON
+	bool "Enable DCDC1"
+	default y
+
+config ACT8945A_DCDC1_APPLY_UV
+	bool "Apply DCDC1 new voltage at initialisation"
+	default n
+	---help---
+		If set, the driver will attempt to set the voltage nearest to MIN_UV,
+		that is also lower than MAX_UV, during initialisation.
+		
+		If not set, MIN_UV and MAX_UV values will be ignored
+		during initialisation.
+
+		If the regulator is enabled here, and APPLY_UV=y, the board
+		voltage will actually change during initialisation.
+
+		If the regulator is not enabled here, and APPLY_UV=y, the
+		regulator voltage will be applied to the device in readiness for
+		the regulator being enabled later.
+
+		DO NOT CHANGE THIS UNLESS YOU KNOW WHAT YOU ARE DOING! It is usually
+		a critical processor voltage rail.
+
+config ACT8945A_DCDC1_MIN_UV
+	int "The minimum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, the voltage nearest to this, and less than
+		MAX_UV will be applied during initialisation.
+
+config ACT8945A_DCDC1_MAX_UV
+	int "The maximum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, this is the maximum voltage that will be
+		applied during initialisation.
+
+endmenu # ACT8945A DCDC1 Configuration
+
+menu "ACT8945A DCDC2 Configuration"
+
+config ACT8945A_DCDC2_NAME
+	string "DCDC2 name"
+	default "ACT8945A_DCDC2"
+	---help---
+		This is the name used for the ACT8945A DCDC converter output 2.
+		It is used as the consumer name when you get or put a regulator.
+	
+config ACT8945A_DCDC2_BOOT_ON
+	bool "Enable DCDC2"
+	default y
+
+config ACT8945A_DCDC2_APPLY_UV
+	bool "Apply DCDC2 new voltage at initialisation"
+	default n
+	---help---
+		If set, the driver will attempt to set the voltage nearest to MIN_UV,
+		that is also lower than MAX_UV, during initialisation.
+		
+		If not set, MIN_UV and MAX_UV values will be ignored
+		during initialisation.
+
+		If the regulator is enabled here, and APPLY_UV=y, the board
+		voltage will actually change during initialisation.
+
+		If the regulator is not enabled here, and APPLY_UV=Y, the
+		regulator voltage will be applied to the device in readiness for
+		the regulator being enabled later.
+
+		DO NOT CHANGE THIS UNLESS YOU KNOW WHAT YOU ARE DOING! It is usually
+		a critical processor voltage rail.
+
+config ACT8945A_DCDC2_MIN_UV
+	int "The minimum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, the voltage nearest to this, and less than
+		MAX_UV will be applied during initialisation.
+
+config ACT8945A_DCDC2_MAX_UV
+	int "The maximum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, this is the maximum voltage that will be
+		applied during initialisation.
+
+endmenu # ACT8945A DCDC2 Configuration
+
+menu "ACT8945A DCDC3 Configuration"
+
+config ACT8945A_DCDC3_NAME
+	string "DCDC3 name"
+	default "ACT8945A_DCDC3"
+	---help---
+		This is the name used for the ACT8945A DCDC converter output 3.
+		It is used as the consumer name when you get or put a regulator.
+
+config ACT8945A_DCDC3_BOOT_ON
+	bool "Enable DCDC3"
+	default y
+
+config ACT8945A_DCDC3_APPLY_UV
+	bool "Apply DCDC3 new voltage at initialisation"
+	default n
+	---help---
+		If set to 1, the driver will attempt to set the voltage nearest to MIN_UV,
+		that is also lower than MAX_UV, during initialisation.
+		
+		If set to 0, MIN_UV and MAX_UV values will be ignored
+		during initialisation.
+
+		If the regulator is enabled here, and APPLY_UV=y, the board
+		voltage will actually change during initialisation.
+
+		If the regulator is not enabled here, and APPLY_UV=y, the
+		regulator voltage will be applied to the device in readiness for
+		the regulator being enabled later.
+
+		DO NOT CHANGE THIS UNLESS YOU KNOW WHAT YOU ARE DOING! It is usually
+		a critical processor voltage rail.
+
+config ACT8945A_DCDC3_MIN_UV
+	int "The minimum acceptable output voltage (600-3900mV)"
+	default 3300
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, the voltage nearest to this, and less than
+		MAX_UV will be applied during initialisation.
+
+config ACT8945A_DCDC3_MAX_UV
+	int "The maximum acceptable output voltage (600-3900mV)"
+	default 3300
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, this is the maximum voltage that will be
+		applied during initialisation.
+
+endmenu # ACT8945A DCDC 3 Configuration
+
+menu "ACT8945A LDO Output 1 Configuration"
+
+config ACT8945A_LDO1_NAME
+	string "LDO1 name"
+	default "ACT8945A_LDO1"
+	---help---
+		This is the name used for the ACT8945A LDO converter output 1.
+		It is used as the consumer name when you get or put a regulator.
+
+config ACT8945A_LDO1_BOOT_ON
+	bool "Enable LDO1"
+	default y
+
+config ACT8945A_LDO1_APPLY_UV
+	bool "Apply LDO1 new voltage at initialisation"
+	default n
+	---help---
+		If set to 1, the driver will attempt to set the voltage nearest to MIN_UV,
+		that is also lower than MAX_UV, during initialisation.
+		
+		If set to 0, MIN_UV and MAX_UV values will be ignored
+		during initialisation.
+
+		If the regulator is enabled here, and APPLY_UV=y, the board
+		voltage will actually change during initialisation.
+
+		If the regulator is not enabled here, and APPLY_UV=y, the
+		regulator voltage will be applied to the device in readiness for
+		the regulator being enabled later.
+
+config ACT8945A_LDO1_MIN_UV
+	int "The minimum acceptable output voltage (600-3900mV)"
+	default 2500
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, the voltage nearest to this, and less than
+		MAX_UV will be applied during initialisation.
+
+config ACT8945A_LDO1_MAX_UV
+	int "The maximum acceptable output voltage (600-3900mV)"
+	default 2500
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, this is the maximum voltage that will be
+		applied during initialisation.
+
+config ACT8945A_LDO1_PULLDOWN
+	bool "Enable LDO1 pulldown when disabled"
+	default y
+	---help---
+		If enabled, the LDO output is discharged through a 1k5 resistor
+		when the LDO is in shutdown
+
+endmenu # ACT8945A LDO Output 1 Configuration
+
+menu "ACT8945A LDO Output 2 Configuration"
+
+config ACT8945A_LDO2_NAME
+	string "LDO2 name"
+	default "ACT8945A_LDO2"
+	---help---
+		This is the name used for the ACT8945A LDO converter output 2.
+		It is used as the consumer name when you get or put a regulator.
+
+config ACT8945A_LDO2_BOOT_ON
+	bool "Enable LDO2"
+	default y
+
+config ACT8945A_LDO2_APPLY_UV
+	bool "Apply LDO2 new voltage at initialisation"
+	default n
+	---help---
+		If set to 1, the driver will attempt to set the voltage nearest to MIN_UV,
+		that is also lower than MAX_UV, during initialisation.
+		
+		If set to 0, MIN_UV and MAX_UV values will be ignored
+		during initialisation.
+
+		If the regulator is enabled here, and APPLY_UV=y, the board
+		voltage will actually change during initialisation.
+
+		If the regulator is not enabled here, and APPLY_UV=y, the
+		regulator voltage will be applied to the device in readiness for
+		the regulator being enabled later.
+
+config ACT8945A_LDO2_MIN_UV
+	int "The minimum acceptable output voltage (600-3900mV)"
+	default 3300
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, the voltage nearest to this, and less than
+		MAX_UV will be applied during initialisation.
+
+config ACT8945A_LDO2_MAX_UV
+	int "The maximum acceptable output voltage (600-3900mV)"
+	default 3300
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, this is the maximum voltage that will be
+		applied during initialisation.
+
+config ACT8945A_LDO2_PULLDOWN
+	bool "Enable LDO2 pulldown when disabled"
+	default y
+	---help---
+		If enabled, the LDO output is discharged through a 1k5 resistor
+		when the LDO is in shutdown
+
+endmenu # ACT8945A LDO Output 2 Configuration
+
+menu "ACT8945A LDO Output 3 Configuration"
+
+config ACT8945A_LDO3_NAME
+	string "LDO3 name"
+	default "ACT8945A_LDO3"
+	---help---
+		This is the name used for the ACT8945A LDO converter output 2.
+		It is used as the consumer name when you get or put a regulator.
+
+config ACT8945A_LDO3_BOOT_ON
+	bool "Enable LDO3"
+	default n
+
+config ACT8945A_LDO3_APPLY_UV
+	bool "Apply LDO3 new voltage at initialisation"
+	default n
+	---help---
+		If set to 1, the driver will attempt to set the voltage nearest to MIN_UV,
+		that is also lower than MAX_UV, during initialisation.
+		
+		If set to 0, MIN_UV and MAX_UV values will be ignored
+		during initialisation.
+
+		If the regulator is enabled here, and APPLY_UV=y, the board
+		voltage will actually change during initialisation.
+
+		If the regulator is not enabled here, and APPLY_UV=y, the
+		regulator voltage will be applied to the device in readiness for
+		the regulator being enabled later.
+
+config ACT8945A_LDO3_MIN_UV
+	int "The minimum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, the voltage nearest to this, and less than
+		MAX_UV will be applied during initialisation.
+
+config ACT8945A_LDO3_MAX_UV
+	int "The maximum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, this is the maximum voltage that will be
+		applied during initialisation.
+
+config ACT8945A_LDO3_PULLDOWN
+	bool "Enable LDO3 pulldown when disabled"
+	default y
+	---help---
+		If enabled, the LDO output is discharged through a 1k5 resistor
+		when the LDO is in shutdown
+
+endmenu # ACT8945A LDO Output 3 Configuration
+
+menu "ACT8945A LDO Output 4 Configuration"
+
+config ACT8945A_LDO4_NAME
+	string "LDO4 name"
+	default "ACT8945A_LDO4"
+	---help---
+		This is the name used for the ACT8945A LDO converter output 2.
+		It is used as the consumer name when you get or put a regulator.
+
+config ACT8945A_LDO4_BOOT_ON
+	bool "Enable LDO4"
+	default n
+
+config ACT8945A_LDO4_APPLY_UV
+	bool "Apply LDO4 new voltage at initialisation"
+	default n
+	---help---
+		If set, the driver will attempt to set the voltage nearest to MIN_UV,
+		that is also lower than MAX_UV, during initialisation.
+		
+		If not, MIN_UV and MAX_UV values will be ignored
+		during initialisation.
+
+		If the regulator is enabled here, and APPLY_UV=y, the board
+		voltage will actually change during initialisation.
+
+		If the regulator is not enabled here, and APPLY_UV=y, the
+		regulator voltage will be applied to the device in readiness for
+		the regulator being enabled later.
+
+config ACT8945A_LDO4_MIN_UV
+	int "The minimum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If =1, the voltage nearest to this, and less than
+		MAX_UV will be applied during initialisation.
+
+config ACT8945A_LDO4_MAX_UV
+	int "The maximum acceptable output voltage (600-3900mV)"
+	default 600
+	range 600 3900
+	---help---
+		If APPLY_UV=Y, this is the maximum voltage that will be
+		applied during initialisation.
+
+config ACT8945A_LDO4_PULLDOWN
+	bool "Enable LDO4 pulldown when disabled"
+	default y
+	---help---
+		If enabled, the LDO output is discharged through a 1k5 resistor
+		when the LDO is in shutdown
+
+endmenu # ACT8945A LDO4 Configuration
+
+endif # REGULATOR_ACT8945A
+
+endif # REGULATOR
 
 endmenu
diff --git a/drivers/power/supply/Make.defs b/drivers/power/supply/Make.defs
index d3178aa117..e4d42b3460 100644
--- a/drivers/power/supply/Make.defs
+++ b/drivers/power/supply/Make.defs
@@ -37,6 +37,10 @@ ifeq ($(CONFIG_REGULATOR_RPMSG), y)
 CSRCS += regulator_rpmsg.c
 endif
 
+ifeq ($(CONFIG_REGULATOR_ACT8945A), y)
+CSRCS += act8945a.c
+endif
+
 endif
 
 DEPPATH += --dep-path power/supply
diff --git a/drivers/power/supply/act8945a.c b/drivers/power/supply/act8945a.c
new file mode 100644
index 0000000000..baa49045f0
--- /dev/null
+++ b/drivers/power/supply/act8945a.c
@@ -0,0 +1,1081 @@
+/****************************************************************************
+ * drivers/power/supply/act8945a.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <debug.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/i2c/i2c_master.h>
+#include <nuttx/power/regulator.h>
+#include <nuttx/power/act8945a.h>
+
+#if defined(CONFIG_I2C) && defined(CONFIG_REGULATOR_ACT8945A)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+#define ACT8945A_BUS_SPEED                        300000
+#define ACT8945A_SLAVE_ADDRESS                    0x5b
+
+#ifdef CONFIG_DEBUG_POWER_ERROR
+#  define act8945a_err(x, ...)        _err(x, ##__VA_ARGS__)
+#else
+#  define act8945a_err(x, ...)        uerr(x, ##__VA_ARGS__)
+#endif
+#ifdef CONFIG_DEBUG_POWER_WARN
+#  define act8945a_warn(x, ...)       _warn(x, ##__VA_ARGS__)
+#else
+#  define act8945a_warn(x, ...)       uwarn(x, ##__VA_ARGS__)
+#endif
+#ifdef CONFIG_DEBUG_POWER_INFO
+#  define act8945a_info(x, ...)       _info(x, ##__VA_ARGS__)
+#else
+#  define act8945a_info(x, ...)       uinfo(x, ##__VA_ARGS__)
+#endif
+
+#define ACT8945A_NUM_VOLTAGES                     64
+
+#define ACT8945A_SYS0                             (0x00)
+#define ACT8945A_SYS1                             (0x01)
+
+#define ACT8945A_DCDC1_VSET0                      (0x20)
+#define ACT8945A_DCDC1_VSET1                      (0x21)
+#define ACT8945A_DCDC1_CONTROL                    (0x22)
+#define ACT8945A_DCDC2_VSET0                      (0x30)
+#define ACT8945A_DCDC2_VSET1                      (0x31)
+#define ACT8945A_DCDC2_CONTROL                    (0x32)
+#define ACT8945A_DCDC3_VSET0                      (0x40)
+#define ACT8945A_DCDC3_VSET1                      (0x41)
+#define ACT8945A_DCDC3_CONTROL                    (0x42)
+#define ACT8945A_LDO1_VSET                        (0x50)
+#define ACT8945A_LDO1_CONTROL                     (0x51)
+#define ACT8945A_LDO2_VSET                        (0x54)
+#define ACT8945A_LDO2_CONTROL                     (0x55)
+#define ACT8945A_LDO3_VSET                        (0x60)
+#define ACT8945A_LDO3_CONTROL                     (0x61)
+#define ACT8945A_LDO4_VSET                        (0x64)
+#define ACT8945A_LDO4_CONTROL                     (0x65)
+
+#define ACT8945A_SYSTRST_MASK                     (0x80)
+#define ACT8945A_SYSMODE_MASK                     (0x40)
+#define ACT8945A_SYSLEV_MASK                      (0x0f)
+
+#define ACT8945A_VSET_MASK                        (0x3f)
+#define ACT8945A_EN_MASK                          (0x80)
+
+#define ACT8945A_SCRATCH_MASK                     (0x0f)
+#define SCRATCH_TEST_VAL                          (0x05)
+
+#define ACT8945A_PULLDOWN_MASK                    (0x40)
+
+#ifdef CONFIG_ACT8945A_TRST_64
+#  define ACT8945A_TRST                           (0x80) /* 64ms*/
+#else
+#  define ACT8945A_TRST                           (0)    /* 260ms */
+#endif
+
+#ifdef CONFIG_ACT8945A_SYSLEV_MODE_INTERRUPT
+#  define ACT8945A_SYSLEV_MODE                    (0x40) /* Interrupt */
+#else
+#  define ACT8945A_SYSLEV_MODE                    (0)    /* Shutdown */
+#endif
+
+#if defined(CONFIG_ACT8945A_SYSLEV_2300)
+#  define ACT8945A_SYSLEV 0
+#elif defined(CONFIG_ACT8945A_SYSLEV_2400)
+#  define ACT8945A_SYSLEV 1
+#elif defined(CONFIG_ACT8945A_SYSLEV_2500)
+#  define ACT8945A_SYSLEV 2
+#elif defined(CONFIG_ACT8945A_SYSLEV_2600)
+#  define ACT8945A_SYSLEV 3
+#elif defined(CONFIG_ACT8945A_SYSLEV_2700)
+#  define ACT8945A_SYSLEV 4
+#elif defined(CONFIG_ACT8945A_SYSLEV_2800)
+#  define ACT8945A_SYSLEV 5
+#elif defined(CONFIG_ACT8945A_SYSLEV_2900)
+#  define ACT8945A_SYSLEV 6
+#elif defined(CONFIG_ACT8945A_SYSLEV_3000)
+#  define ACT8945A_SYSLEV 7
+#elif defined(CONFIG_ACT8945A_SYSLEV_3100)
+#  define ACT8945A_SYSLEV 8
+#elif defined(CONFIG_ACT8945A_SYSLEV_3200)
+#  define ACT8945A_SYSLEV 9
+#elif defined(CONFIG_ACT8945A_SYSLEV_3300)
+#  define ACT8945A_SYSLEV 10
+#elif defined(CONFIG_ACT8945A_SYSLEV_3400)
+#  define ACT8945A_SYSLEV 11
+#elif defined(CONFIG_ACT8945A_SYSLEV_3500)
+#  define ACT8945A_SYSLEV 12
+#elif defined(CONFIG_ACT8945A_SYSLEV_3600)
+#  define ACT8945A_SYSLEV 13
+#elif defined(CONFIG_ACT8945A_SYSLEV_3700)
+#  define ACT8945A_SYSLEV 14
+#elif defined(CONFIG_ACT8945A_SYSLEV_3800)
+#  define ACT8945A_SYSLEV 15
+#else
+#  error No act8945a sys level detect threshold defined
+#endif
+
+#ifdef CONFIG_ACT8945A_DCDC1_BOOT_ON
+#  define ACT8945A_DCDC1_BOOT_ON 1
+#else
+#  define ACT8945A_DCDC1_BOOT_ON 0
+#endif
+
+#ifdef CONFIG_ACT8945A_DCDC2_BOOT_ON
+#  define ACT8945A_DCDC2_BOOT_ON 1
+#else
+#  define ACT8945A_DCDC2_BOOT_ON 0
+#endif
+
+#ifdef CONFIG_ACT8945A_DCDC3_BOOT_ON
+#  define ACT8945A_DCDC3_BOOT_ON 1
+#else
+#  define ACT8945A_DCDC3_BOOT_ON 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO1_BOOT_ON
+#  define ACT8945A_LDO1_BOOT_ON 1
+#else
+#  define ACT8945A_LDO1_BOOT_ON 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO2_BOOT_ON
+#  define ACT8945A_LDO2_BOOT_ON 1
+#else
+#  define ACT8945A_LDO2_BOOT_ON 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO3_BOOT_ON
+#  define ACT8945A_LDO3_BOOT_ON 1
+#else
+#  define ACT8945A_LDO3_BOOT_ON 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO4_BOOT_ON
+#  define ACT8945A_LDO4_BOOT_ON 1
+#else
+#  define ACT8945A_LDO4_BOOT_ON 0
+#endif
+
+#ifdef CONFIG_ACT8945A_DCDC1_APPLY_UV
+#  define ACT8945A_DCDC1_APPLY_UV 1
+#else
+#  define ACT8945A_DCDC1_APPLY_UV 0
+#endif
+
+#ifdef CONFIG_ACT8945A_DCDC2_APPLY_UV
+#  define ACT8945A_DCDC2_APPLY_UV 1
+#else
+#  define ACT8945A_DCDC2_APPLY_UV 0
+#endif
+
+#ifdef CONFIG_ACT8945A_DCDC3_APPLY_UV
+#  define ACT8945A_DCDC3_APPLY_UV 1
+#else
+#  define ACT8945A_DCDC3_APPLY_UV 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO1_APPLY_UV
+#  define ACT8945A_LDO1_APPLY_UV 1
+#else
+#  define ACT8945A_LDO1_APPLY_UV 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO2_APPLY_UV
+#  define ACT8945A_LDO2_APPLY_UV 1
+#else
+#  define ACT8945A_LDO2_APPLY_UV 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO3_APPLY_UV
+#  define ACT8945A_LDO3_APPLY_UV 1
+#else
+#  define ACT8945A_LDO3_APPLY_UV 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO4_APPLY_UV
+#  define ACT8945A_LDO4_APPLY_UV 1
+#else
+#  define ACT8945A_LDO4_APPLY_UV 0
+#endif
+
+#define ACT8945A_DCDC1_PULLDOWN 0
+#define ACT8945A_DCDC2_PULLDOWN 0
+#define ACT8945A_DCDC3_PULLDOWN 0
+
+#ifdef CONFIG_ACT8945A_LDO1_PULLDOWN
+#  define ACT8945A_LDO1_PULLDOWN 1
+#else
+#  define ACT8945A_LDO1_PULLDOWN 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO2_PULLDOWN
+#  define ACT8945A_LDO2_PULLDOWN 1
+#else
+#  define ACT8945A_LDO2_PULLDOWN 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO3_PULLDOWN
+#  define ACT8945A_LDO3_PULLDOWN 1
+#else
+#  define ACT8945A_LDO3_PULLDOWN 0
+#endif
+
+#ifdef CONFIG_ACT8945A_LDO4_PULLDOWN
+#  define ACT8945A_LDO4_PULLDOWN 1
+#else
+#  define ACT8945A_LDO4_PULLDOWN 0
+#endif
+
+#define ACT8945A_REG(_id, _vsel)                      \
+  [ACT8945A_##_id] =                                  \
+  {                                                   \
+    .name          = CONFIG_ACT8945A_##_id##_NAME,    \
+    .id            = ACT8945A_##_id,                  \
+    .n_voltages    = ACT8945A_NUM_VOLTAGES,           \
+    .vsel_reg      = ACT8945A_##_id##_##_vsel,        \
+    .vsel_mask     = ACT8945A_VSET_MASK,              \
+    .enable_reg    = ACT8945A_##_id##_CONTROL,        \
+    .enable_mask   = ACT8945A_EN_MASK,                \
+    .boot_on       = ACT8945A_##_id##_BOOT_ON,        \
+    .min_uv        = CONFIG_ACT8945A_##_id##_MIN_UV,  \
+    .max_uv        = CONFIG_ACT8945A_##_id##_MAX_UV,  \
+    .apply_uv      = ACT8945A_##_id##_APPLY_UV,       \
+    .pulldown      = ACT8945A_##_id##_PULLDOWN,       \
+    .pulldown_reg  = ACT8945A_##_id##_CONTROL,        \
+    .pulldown_mask = ACT8945A_PULLDOWN_MASK,          \
+  }
+/****************************************************************************
+ * Private type
+ ****************************************************************************/
+
+struct regulator_act8945a_priv
+{
+  FAR struct       i2c_master_s     *i2c;
+  uint8_t                           i2c_addr;
+  int                               i2c_freq;
+  FAR const struct regulator_desc_s *regulators;
+  FAR struct       regulator_dev_s  *rdev;
+};
+
+struct act8945a_dev_s
+{
+  /* Common part of the regulator driver visible to the upper-half driver */
+
+  FAR struct regulator_desc_s  *regulator_desc_s;
+
+  /* Data fields specific to the lower half act8945a driver follow */
+
+  FAR struct i2c_master_s      *i2c;      /* I2C interface */
+  uint8_t                      addr;      /* I2C address */
+  uint32_t                     frequency; /* I2C frequency */
+};
+
+enum
+{
+  ACT8945A_DCDC1 = 0,
+  ACT8945A_DCDC2,
+  ACT8945A_DCDC3,
+  ACT8945A_LDO1,
+  ACT8945A_LDO2,
+  ACT8945A_LDO3,
+  ACT8945A_LDO4,
+  ACT8945A_MAX,
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Character driver methods */
+
+/* regulator lower half methods */
+
+static int act8945a_list_voltage(struct regulator_dev_s *rdev,
+                                 unsigned int selector);
+static int act8945a_set_voltage_sel(FAR struct regulator_dev_s *rdev,
+                                    unsigned int selector);
+static int act8945a_get_voltage_sel(FAR struct regulator_dev_s *rdev);
+static int act8945a_enable(struct regulator_dev_s *rdev);
+static int act8945a_is_enabled(struct regulator_dev_s *rdev);
+static int act8945a_disable(struct regulator_dev_s *rdev);
+static int act8945a_write_scratch(struct regulator_act8945a_priv *priv,
+                                  uint8_t value);
+static int act8945a_read_scratch(struct regulator_act8945a_priv *priv,
+                                 uint8_t * scratch);
+static int act8945a_enable_pulldown(struct regulator_dev_s *rdev);
+static int act8945a_disable_pulldown(struct regulator_dev_s *rdev);
+static int act89845a_set_sysmode(struct regulator_act8945a_priv *priv,
+                                 uint8_t mode);
+static int act89845a_set_trstmode(struct regulator_act8945a_priv *priv,
+                                  uint8_t timer);
+static int act89845a_set_syslev(struct regulator_act8945a_priv *priv,
+                                  uint8_t level);
+
+/* I2C support */
+
+static int act8945a_getreg(FAR struct regulator_act8945a_priv *priv,
+                           uint8_t regaddr, uint8_t *regval);
+static int act8945a_putreg(FAR struct regulator_act8945a_priv *priv,
+                           uint8_t regaddr, uint8_t regval);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const int mv_list[] =
+{
+  600,   625,  650,  675,  700,  725,  750,  775,
+  800,   825,  850,  875,  900,  925,  950,  975,
+  1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175,
+  1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550,
+  1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950,
+  2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350,
+  2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100,
+  3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900,
+};
+
+static const struct regulator_desc_s g_act8945a_regulators[] =
+{
+  ACT8945A_REG(DCDC1, VSET0),
+  ACT8945A_REG(DCDC2, VSET0),
+  ACT8945A_REG(DCDC3, VSET0),
+  ACT8945A_REG(LDO1, VSET),
+  ACT8945A_REG(LDO2, VSET),
+  ACT8945A_REG(LDO3, VSET),
+  ACT8945A_REG(LDO4, VSET),
+};
+
+static const struct regulator_desc_s g_alt_act8945a_regulators[] =
+{
+  ACT8945A_REG(DCDC1, VSET1),
+  ACT8945A_REG(DCDC2, VSET1),
+  ACT8945A_REG(DCDC3, VSET1),
+  ACT8945A_REG(LDO1, VSET),
+  ACT8945A_REG(LDO2, VSET),
+  ACT8945A_REG(LDO3, VSET),
+  ACT8945A_REG(LDO4, VSET),
+};
+
+/* Regulator operations */
+
+static const struct regulator_ops_s g_act8945a_ops =
+{
+  act8945a_list_voltage,     /* list_voltage     */
+  NULL,                      /* set_voltage      */
+  act8945a_set_voltage_sel,  /* set_voltage_sel  */
+  NULL,                      /* get_voltage      */
+  act8945a_get_voltage_sel,  /* get_voltage_sel  */
+  act8945a_enable,           /* enable           */
+  act8945a_is_enabled,       /* is_enabled       */
+  act8945a_disable,          /* disable          */
+  act8945a_enable_pulldown,  /* enable pulldown if reg. disabled */
+  act8945a_disable_pulldown, /* disable pulldown if reg. disabled */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: act8945a_getreg
+ *
+ * Description:
+ *   Reads a single register from the ACT8945A regulator
+ *
+ * Input Parameters:
+ *
+ *   priv    - private act8945a device structure
+ *   regaddr - the act8945a register address
+ *   regval  - location to return the act8945a register value
+ *
+ * Returned value:
+ *
+ *   Success, or fail reason
+ *
+ ****************************************************************************/
+
+static int act8945a_getreg(FAR struct regulator_act8945a_priv *priv,
+                           uint8_t regaddr, uint8_t *regval)
+{
+  struct i2c_config_s config;
+  int                 ret;
+  irqstate_t          flags;
+
+  /* Sanity check */
+
+  DEBUGASSERT(priv   != NULL);
+  DEBUGASSERT(regval != NULL);
+
+  /* Set up the I2C configuration */
+
+  config.frequency = priv->i2c_freq;
+  config.address   = priv->i2c_addr;
+  config.addrlen   = 7;
+
+  /* Write the register address */
+
+  flags = spin_lock_irqsave(NULL);
+  ret = i2c_write(priv->i2c, &config, &regaddr, sizeof(regaddr));
+  spin_unlock_irqrestore(NULL, flags);
+  if (ret < 0)
+    {
+      act8945a_err("ERROR: i2c_write failed: %d\n", ret);
+      return ret;
+    }
+
+  /* Restart and read 8 bits from the register */
+
+  flags = spin_lock_irqsave(NULL);
+  ret = i2c_read(priv->i2c, &config, regval, sizeof(*regval));
+  spin_unlock_irqrestore(NULL, flags);
+  if (ret < 0)
+    {
+      act8945a_err("ERROR: i2c_read failed: %d\n", ret);
+      return ret;
+    }
+
+  act8945a_info("INFO: addr: %02x value: %02x\n", regaddr, *regval);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: act8945a_putreg
+ *
+ * Description:
+ *   Writes a single byte to one of the ACT8945A registers.
+ *
+ * Input Parameters:
+ *
+ *   priv    - private act8945a device structure
+ *   regaddr - the act8945a register address
+ *   regval  - value to write to the act8945a register
+ *
+ * Returned value:
+ *
+ *   Success, or fail reason
+ *
+ ****************************************************************************/
+
+static int act8945a_putreg(FAR struct regulator_act8945a_priv *priv,
+                           uint8_t regaddr, uint8_t regval)
+{
+  struct i2c_config_s config;
+  uint8_t             buffer[2];
+  int                 ret;
+  irqstate_t          flags;
+
+  /* Sanity check */
+
+  DEBUGASSERT(priv != NULL);
+
+  /* Set up a 2-byte message to send */
+
+  buffer[0] = regaddr;
+  buffer[1] = regval;
+
+  /* Set up the I2C configuration */
+
+  config.frequency = priv->i2c_freq;
+  config.address   = priv->i2c_addr;
+  config.addrlen   = 7;
+
+  /* Write the register address followed by the data (no RESTART) */
+
+  flags = spin_lock_irqsave(NULL);
+  ret = i2c_write(priv->i2c, &config, buffer, sizeof(buffer));
+  spin_unlock_irqrestore(NULL, flags);
+
+  if (ret < 0)
+    {
+      act8945a_err("ERROR: i2c_write failed: %d\n", ret);
+      return ret;
+    }
+
+  act8945a_info("INFO:addr: %02x value: %02x\n", regaddr, regval);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: act8945a_list_voltage
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev     - The regulator device pointer.
+ *   selector - The selector for the voltage look-up
+ *
+ * Returned Value:
+ *
+ *   The voltage for the selector, or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_list_voltage(FAR struct regulator_dev_s *rdev,
+                                 unsigned int selector)
+{
+  if (selector < ACT8945A_NUM_VOLTAGES)
+    {
+      return (mv_list[selector]);
+    }
+  else
+    {
+      return -EINVAL;
+    }
+}
+
+/****************************************************************************
+ * Name: act8945a_enable_pulldown
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev     - The regulator device pointer.
+ *
+ * Returned Value
+ *
+ *   Success or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_enable_pulldown(struct regulator_dev_s *rdev)
+{
+  FAR struct regulator_act8945a_priv *priv = rdev->priv;
+  FAR const struct regulator_desc_s *regulator = rdev->desc;
+
+  uint8_t regval;
+
+  /* for ease of code commonality we skip any calls for DCDC1-3 */
+
+  if (regulator->id < ACT8945A_LDO1)
+    {
+      return OK;
+    }
+
+  if (act8945a_getreg(priv, regulator->enable_reg, &regval) == 0)
+    {
+      regval |= regulator->pulldown_mask;
+      if (act8945a_putreg(priv, regulator->pulldown_reg, regval) == 0)
+        {
+          pwrinfo("INFO: Output discharge control enabled %s\n",
+                   regulator->name);
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Name: act8945a_disable_pulldown
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev     - The regulator device pointer.
+ *
+ * Returned Value:
+ *
+ *   Success or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_disable_pulldown(struct regulator_dev_s *rdev)
+{
+  FAR struct regulator_act8945a_priv *priv = rdev->priv;
+  FAR const struct regulator_desc_s *regulator = rdev->desc;
+
+  uint8_t regval;
+
+  /* for ease of code commonality we skip any calls for DCDC1-3 */
+
+  if (regulator->id < ACT8945A_LDO1)
+    {
+      return OK;
+    }
+
+  if (act8945a_getreg(priv, regulator->pulldown_reg, &regval) == 0)
+    {
+      regval &= ~regulator->pulldown_mask;
+      if (act8945a_putreg(priv, regulator->enable_reg, regval) == 0)
+        {
+          pwrinfo("INFO: Output discharge control disabled %s\n",
+                   regulator->name);
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Name: act8945a_write_scratch
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   priv     - private act8945a device structure
+ *   value    - The value to write to the 4 scratch bits
+ *
+ * Returned Value:
+ *
+ *   Success or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_write_scratch(struct regulator_act8945a_priv *priv,
+                                  uint8_t value)
+{
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, ACT8945A_SYS1, &regval) == 0)
+    {
+      regval &= ~ACT8945A_SCRATCH_MASK;
+      regval |= value & ACT8945A_SCRATCH_MASK;
+      if (act8945a_putreg(priv, ACT8945A_SYS1, regval) == 0)
+        {
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Name: act8945a_read_scratch
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   priv    - private act8945a device structure
+ *   scratch - pointer to the scratch value read
+ *
+ * Returned Value:
+ *
+ *   The scratch value read, and success or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_read_scratch(struct regulator_act8945a_priv *priv,
+                                 uint8_t *scratch)
+{
+  if (act8945a_getreg(priv, ACT8945A_SYS1, scratch) == 0)
+    {
+      return OK;
+    }
+  else
+    {
+      return -EIO;
+    }
+}
+
+/****************************************************************************
+ * Name: act89845a_set_sysmode
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   priv     - private act8945a device structure
+ *   mode     - The system mode to set
+ *
+ * Returned Value:
+ *
+ *   Success or error code.
+ *
+ ****************************************************************************/
+
+static int act89845a_set_sysmode(struct regulator_act8945a_priv *priv,
+                                 uint8_t mode)
+{
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, ACT8945A_SYS0, &regval) == 0)
+    {
+      regval &= ~ACT8945A_SYSMODE_MASK;
+      regval |= mode & ACT8945A_SYSMODE_MASK;
+      if (act8945a_putreg(priv, ACT8945A_SYS0, regval) == 0)
+        {
+          pwrinfo ("INFO: sysmode set to %d\n", mode >> 6);
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Name: act89845a_set_trstmode
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   priv     - private act8945a device structure
+ *   mode     - The system reset timer setting to set
+ *
+ * Returned Value:
+ *
+ *   Success or error code.
+ *
+ ****************************************************************************/
+
+static int act89845a_set_trstmode(struct regulator_act8945a_priv *priv,
+                                  uint8_t timer)
+{
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, ACT8945A_SYS0, &regval) == 0)
+    {
+      regval &= ~ACT8945A_SYSTRST_MASK;
+      regval |= timer & ACT8945A_SYSTRST_MASK;
+      if (act8945a_putreg(priv, ACT8945A_SYS0, regval) == 0)
+        {
+          pwrinfo ("INFO: sysmode set to %d\n", timer >> 7);
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Name: act89845a_set_syslev
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   priv     - private act8945a device structure
+ *   level    - The system voltage level detect threshold to set
+ *
+ * Returned Value:
+ *
+ *   Success or error code.
+ *
+ ****************************************************************************/
+
+static int act89845a_set_syslev(struct regulator_act8945a_priv *priv,
+                                uint8_t level)
+{
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, ACT8945A_SYS0, &regval) == 0)
+    {
+      regval &= ~ACT8945A_SYSLEV_MASK;
+      regval |= level & ACT8945A_SYSLEV_MASK;
+      if (act8945a_putreg(priv, ACT8945A_SYS0, regval) == 0)
+        {
+          pwrinfo ("INFO: syslevel set to %d\n", level);
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Name: act8945a_set_voltage_sel
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev     - The regulator device pointer.
+ *   selector - The selector to use for the regulator
+ *
+ * Returned Value:
+ *
+ *   Success or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_set_voltage_sel(struct regulator_dev_s *rdev,
+                                    unsigned selector)
+{
+  FAR struct regulator_act8945a_priv *priv = rdev->priv;
+  FAR const struct regulator_desc_s *regulator = rdev->desc;
+
+  if (selector >= ACT8945A_NUM_VOLTAGES)
+    {
+      return -EINVAL;
+    }
+  else
+    {
+      if (act8945a_putreg(priv, regulator->vsel_reg,
+                           selector & regulator->vsel_mask) == 0)
+        {
+          pwrinfo("INFO: reg %s set to %d mV using selector %d \n",
+                   regulator->name, mv_list[selector], selector);
+          return OK;
+        }
+      else
+        {
+          return -EIO;
+        }
+    }
+}
+
+/****************************************************************************
+ * Name: act8945a_get_voltage_sel
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev     - The regulator device pointer.
+ *
+ * Returned Value:
+ *
+ *   The current selector from the device, or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_get_voltage_sel(struct regulator_dev_s *rdev)
+{
+  FAR struct regulator_act8945a_priv *priv = rdev->priv;
+  FAR const struct regulator_desc_s *regulator = rdev->desc;
+
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, regulator->vsel_reg, &regval) == 0)
+    {
+      return regval;
+    }
+  else
+    {
+      return -EIO;
+    }
+}
+
+/****************************************************************************
+ * Name: act8945a_enable
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev     - The regulator device pointer.
+ *
+ * Returned Value:
+ *
+ *   Success, or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_enable(struct regulator_dev_s *rdev)
+{
+  FAR struct regulator_act8945a_priv *priv = rdev->priv;
+  FAR const struct regulator_desc_s *regulator = rdev->desc;
+
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, regulator->vsel_reg, &regval) == 0)
+    {
+      regval |= regulator->enable_mask;
+      if (act8945a_putreg(priv, regulator->enable_reg, regval) == 0)
+        {
+          pwrinfo("INFO: reg %s enabled\n", regulator->name);
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Name: act8945a_is_enabled
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev    - The regulator device pointer.
+ *
+ * Returned Value:
+ *
+ *   0      - disabled
+ *   1      - enabled or disabled
+ *   -value - error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_is_enabled(struct regulator_dev_s *rdev)
+{
+  FAR struct regulator_act8945a_priv *priv = rdev->priv;
+  FAR const struct regulator_desc_s *regulator = rdev->desc;
+
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, regulator->enable_reg, &regval) == 0)
+    {
+      return (regval & regulator->enable_mask);
+    }
+  else
+    {
+      return -ENODEV;
+    }
+}
+
+/****************************************************************************
+ * Name: act8945a_disable
+ *
+ * Description:
+ *
+ * Input Parameters:
+ *
+ *   rdev     - The regulator device pointer.
+ *
+ * Returned Value:
+ *
+ *   Success, or error code.
+ *
+ ****************************************************************************/
+
+static int act8945a_disable(struct regulator_dev_s *rdev)
+{
+  FAR struct regulator_act8945a_priv *priv = rdev->priv;
+  FAR const struct regulator_desc_s *regulator = rdev->desc;
+
+  uint8_t regval;
+
+  if (act8945a_getreg(priv, regulator->vsel_reg, &regval) == 0)
+    {
+      regval &= ~regulator->enable_mask;
+      if (act8945a_putreg(priv, regulator->enable_reg, regval) == 0)
+        {
+          pwrinfo("INFO: reg %s disabled\n", regulator->name);
+          return OK;
+        }
+    }
+
+  return -EIO;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+int act8945a_initialize(FAR struct i2c_master_s *i2c, unsigned int vsel)
+{
+  FAR struct regulator_act8945a_priv *priv;
+
+  uint8_t scratch;
+  int regnum;
+
+  DEBUGASSERT(vsel != 0 && vsel != 1);
+
+  if (!i2c)
+    {
+      return -ENODEV;
+    }
+
+  priv = kmm_zalloc(sizeof(struct regulator_act8945a_priv));
+
+  if (!priv)
+    {
+      return -ENOMEM;
+    }
+
+  priv->i2c      = i2c;
+  priv->i2c_addr = ACT8945A_SLAVE_ADDRESS;
+  priv->i2c_freq = ACT8945A_BUS_SPEED;
+
+  /* Early test to see if we can read the ACT8945A.
+   *
+   * We do this by writing a data pattern into the 4 scratch bits of SYS1
+   * register and making sure we can read it back OK.
+   */
+
+  if (!act8945a_write_scratch(priv, SCRATCH_TEST_VAL))
+    {
+      if (!act8945a_read_scratch(priv, &scratch))
+        {
+          if (scratch != SCRATCH_TEST_VAL)
+            {
+              goto error;
+            }
+        }
+    }
+  else
+    {
+      goto error;
+    }
+
+  /* Initialise with Kconfig values, using correct struct depending on VSEL.
+   * - Some hardware may set VSEL via software.
+   * - Some hardware may have it hard coded and may or may not
+   * - allow the pin to be read back.
+   * - The value must be determined by board specific logic.
+   */
+
+  if (vsel == 0)
+    {
+      priv->regulators = g_act8945a_regulators;
+    }
+  else
+    {
+      priv->regulators = g_alt_act8945a_regulators;
+    }
+
+  for (regnum = ACT8945A_DCDC1; regnum < ACT8945A_NUM_REGS; regnum++)
+    {
+      priv->rdev = regulator_register(&priv->regulators[regnum],
+                                      &g_act8945a_ops, priv);
+      if (priv->rdev == NULL)
+        {
+          pwrerr("ERROR: failed to register act8945a device %d\n");
+          goto error;
+        }
+    }
+
+  act89845a_set_sysmode(priv, ACT8945A_SYSLEV_MODE);
+  act89845a_set_trstmode(priv, ACT8945A_TRST);
+  act89845a_set_syslev(priv, ACT8945A_SYSLEV);
+
+  return OK;
+
+error:
+  kmm_free(priv);
+  return -ENODEV;
+}
+
+#endif /* CONFIG_I2C && CONFIG_REGULATOR_ACT8945A */
diff --git a/drivers/power/supply/regulator.c b/drivers/power/supply/regulator.c
index 4fbef8a974..04c2ad3589 100644
--- a/drivers/power/supply/regulator.c
+++ b/drivers/power/supply/regulator.c
@@ -52,6 +52,8 @@ static int _regulator_do_set_voltage(FAR struct regulator_dev_s *rdev,
                                      int min_uv, int max_uv);
 static int _regulator_set_voltage_unlocked(FAR struct regulator_s *regulator,
                                            int min_uv, int max_uv);
+static int _regulator_do_enable_pulldown(FAR struct regulator_dev_s *rdev);
+static int _regulator_do_disable_pulldown(FAR struct regulator_dev_s *rdev);
 
 /****************************************************************************
  * Private Data
@@ -236,6 +238,38 @@ static int _regulator_get_voltage(FAR struct regulator_dev_s *rdev)
   return ret;
 }
 
+static int _regulator_do_enable_pulldown(FAR struct regulator_dev_s *rdev)
+{
+  int ret = 0;
+
+  if (rdev->ops->enable_pulldown)
+    {
+      ret = rdev->ops->enable_pulldown(rdev);
+      if (ret < 0)
+        {
+          pwrerr("failed to get enable pulldown\n");
+        }
+    }
+
+  return ret;
+}
+
+static int _regulator_do_disable_pulldown(FAR struct regulator_dev_s *rdev)
+{
+  int ret = 0;
+
+  if (rdev->ops->disable_pulldown)
+    {
+      ret = rdev->ops->disable_pulldown(rdev);
+      if (ret < 0)
+        {
+          pwrerr("failed to get disable pulldown\n");
+        }
+    }
+
+  return ret;
+}
+
 static int _regulator_do_set_voltage(FAR struct regulator_dev_s *rdev,
                                      int min_uv, int max_uv)
 {
@@ -743,13 +777,13 @@ regulator_register(FAR const struct regulator_desc_s *regulator_desc,
 
   if (regulator_ops->get_voltage && regulator_ops->get_voltage_sel)
     {
-      pwrerr("get_voltage and get_voltage_sel are assigned\n");
+      pwrerr("get_voltage and get_voltage_sel are both assigned\n");
       return NULL;
     }
 
   if (regulator_ops->set_voltage && regulator_ops->set_voltage_sel)
     {
-      pwrerr("set_voltage and set_voltage_sel are assigned\n");
+      pwrerr("set_voltage and set_voltage_sel are both assigned\n");
       return NULL;
     }
 
@@ -790,8 +824,17 @@ regulator_register(FAR const struct regulator_desc_s *regulator_desc,
 
   if (rdev->desc->apply_uv)
     {
-      _regulator_do_set_voltage(rdev, rdev->desc->apply_uv,
-                                rdev->desc->apply_uv);
+      _regulator_do_set_voltage(rdev, rdev->desc->min_uv,
+                                rdev->desc->max_uv);
+    }
+
+  if (rdev->desc->pulldown)
+    {
+      _regulator_do_enable_pulldown(rdev);
+    }
+  else
+    {
+      _regulator_do_disable_pulldown(rdev);
     }
 
   nxmutex_lock(&g_reg_lock);
diff --git a/include/nuttx/power/act8945a.h b/include/nuttx/power/act8945a.h
new file mode 100644
index 0000000000..80f8b96e8d
--- /dev/null
+++ b/include/nuttx/power/act8945a.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+ * include/nuttx/power/act8945a.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_POWER_BATTERY_ACT8945A_H
+#define __INCLUDE_NUTTX_POWER_BATTERY_ACT8945A_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/fs/ioctl.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#define ACT8945A_NUM_REGS                         7
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#if defined(CONFIG_I2C) && defined(CONFIG_REGULATOR_ACT8945A)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct i2c_master_s;
+int act8945a_initialize(FAR struct i2c_master_s *i2c, unsigned int vsel);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CONFIG_I2C && CONFIG_I2C_ACT8945A */
+#endif
+
diff --git a/include/nuttx/power/regulator.h b/include/nuttx/power/regulator.h
index b9234d90f5..7a63583859 100644
--- a/include/nuttx/power/regulator.h
+++ b/include/nuttx/power/regulator.h
@@ -56,35 +56,47 @@ struct regulator_s
 struct regulator_ops_s
 {
   CODE int (*list_voltage)(FAR struct regulator_dev_s *rdev,
-                           unsigned selector);
+                           unsigned int selector);
   CODE int (*set_voltage)(FAR struct regulator_dev_s *rdev, int min_uv,
-                          int max_uv, FAR unsigned *selector);
+                          int max_uv, FAR unsigned int *selector);
   CODE int (*set_voltage_sel)(FAR struct regulator_dev_s *rdev,
-                              unsigned selector);
+                              unsigned int selector);
   CODE int (*get_voltage)(FAR struct regulator_dev_s *rdev);
   CODE int (*get_voltage_sel)(FAR struct regulator_dev_s *rdev);
   CODE int (*enable)(FAR struct regulator_dev_s *rdev);
   CODE int (*is_enabled)(FAR struct regulator_dev_s *rdev);
   CODE int (*disable)(FAR struct regulator_dev_s *rdev);
+  CODE int (*enable_pulldown)(FAR struct regulator_dev_s *rdev);
+  CODE int (*disable_pulldown)(FAR struct regulator_dev_s *rdev);
 };
 
-/* This structure defines the regulator state structure */
+/* This structure describes the regulators capabilities */
 
 struct regulator_desc_s
 {
-  const char *name;
-  unsigned int n_voltages;
-  unsigned int vsel_reg;
-  unsigned int vsel_mask;
-  unsigned int enable_reg;
-  unsigned int enable_mask;
-  unsigned int enable_time;
-  unsigned int ramp_delay;
-  unsigned int uv_step;
-  unsigned int min_uv;
-  unsigned int max_uv;
-  unsigned int apply_uv;
-  bool boot_on;
+  const char   *name;             /* Regulator output name */
+  unsigned int id;                /* Numerical id for a given regulator of
+                                   * a device
+                                   */
+  unsigned int n_voltages;        /* Number of discrete voltages */
+  unsigned int vsel_reg;          /* Device register for voltage selection */
+  unsigned int vsel_mask;         /* Register mask, for voltage selection */
+  unsigned int enable_reg;        /* Device register for enable/disable */
+  unsigned int enable_mask;       /* Register mask for enable/disable */
+  unsigned int enable_time;       /* Time for initial enable of regulator */
+  unsigned int ramp_delay;        /* Rate of change for setting new voltage */
+  unsigned int uv_step;           /* Voltage per step if linear mapping_uv */
+  unsigned int min_uv;            /* Minimum acceptable voltage */
+  unsigned int max_uv;            /* Maximum acceptable voltage */
+  unsigned int pulldown;          /* Enable pulldown when disabled */
+  unsigned int pulldown_reg;      /* Device register, for pulldown enable */
+  unsigned int pulldown_mask;     /* Register mask, for pulldown enable */
+  unsigned int apply_uv;          /* If true, the voltage specifed (between)                                  * min_uv and max_uv will be applied during
+                                   * initialisation.
+                                   */
+  unsigned int boot_on;           /* true if this regulator is to be enabled
+                                   * at power up/reset
+                                   */
 };
 
 struct regulator_dev_s