94f0283bdd
Update drivers/power/supply/act8945a.c Co-authored-by: Petro Karashchenko <petro.karashchenko@gmail.com>
1098 lines
30 KiB
C
1098 lines
30 KiB
C
/****************************************************************************
|
|
* 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
|
|
|
|
/* Ramp times are fixed for this regulator */
|
|
|
|
#define ETIME_0 0
|
|
#define ETIME_400 400
|
|
#define ETIME_800 800
|
|
|
|
#define ACT8945A_REG(_id, _vsel, _etime) \
|
|
[ACT8945A_##_id] = \
|
|
{ \
|
|
.n_voltages = ACT8945A_NUM_VOLTAGES, \
|
|
.vsel_reg = ACT8945A_##_id##_##_vsel, \
|
|
.vsel_mask = ACT8945A_VSET_MASK, \
|
|
.name = CONFIG_ACT8945A_##_id##_NAME, \
|
|
.id = ACT8945A_##_id, \
|
|
.enable_reg = ACT8945A_##_id##_CONTROL, \
|
|
.enable_mask = ACT8945A_EN_MASK, \
|
|
.enable_time = (_etime), \
|
|
.ramp_delay = 400, \
|
|
.uv_step = 0, \
|
|
.min_uv = CONFIG_ACT8945A_##_id##_MIN_UV, \
|
|
.max_uv = CONFIG_ACT8945A_##_id##_MAX_UV, \
|
|
.pulldown = ACT8945A_##_id##_PULLDOWN, \
|
|
.pulldown_reg = ACT8945A_##_id##_CONTROL, \
|
|
.pulldown_mask = ACT8945A_PULLDOWN_MASK, \
|
|
.apply_uv = ACT8945A_##_id##_APPLY_UV, \
|
|
.boot_on = ACT8945A_##_id##_BOOT_ON, \
|
|
}
|
|
/****************************************************************************
|
|
* 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, ETIME_0),
|
|
ACT8945A_REG(DCDC2, VSET0, ETIME_400),
|
|
ACT8945A_REG(DCDC3, VSET0, ETIME_0),
|
|
ACT8945A_REG(LDO1, VSET, ETIME_800),
|
|
ACT8945A_REG(LDO2, VSET, ETIME_0),
|
|
ACT8945A_REG(LDO3, VSET, ETIME_0),
|
|
ACT8945A_REG(LDO4, VSET, ETIME_0),
|
|
};
|
|
|
|
static const struct regulator_desc_s g_alt_act8945a_regulators[] =
|
|
{
|
|
ACT8945A_REG(DCDC1, VSET1, ETIME_0),
|
|
ACT8945A_REG(DCDC2, VSET1, 400000),
|
|
ACT8945A_REG(DCDC3, VSET1, ETIME_0),
|
|
ACT8945A_REG(LDO1, VSET, 800000),
|
|
ACT8945A_REG(LDO2, VSET, ETIME_0),
|
|
ACT8945A_REG(LDO3, VSET, ETIME_0),
|
|
ACT8945A_REG(LDO4, VSET, ETIME_0),
|
|
};
|
|
|
|
/* 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, ®addr, 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, ®val) == 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, ®val) == 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, ®val) == 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, ®val) == 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, ®val) == 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, ®val) == 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, ®val) == 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, ®val) == 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, ®val) == 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, ®val) == 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;
|
|
bool reg_failed = false;
|
|
|
|
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)
|
|
{
|
|
reg_failed = true;
|
|
pwrerr("ERROR: failed to register act8945a regulator %s\n",
|
|
priv->regulators[regnum].name);
|
|
}
|
|
}
|
|
|
|
if (reg_failed)
|
|
{
|
|
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 */
|