blob: 3b240647986f243dfa8115b8a96852af0f1a792c [file]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <utils/util.h>
#include <platsupport/mach/pmic.h>
#include <string.h>
#define mV
#define REG_CHIPID 0x00
#define REG_RESET_DELAY 0x0A
#define MAX77686_CHIPID 0x02
#define MAX77802_CHIPID 0x06
#define MAXXXXXX_CHIPID 0x91
#define NLDO (26 + 1 /* + reserved LDO0 */)
#define LDO_VMIN 800 mV
#define LDO_VSTEP 50 mV
#define LDO_VMASK 0x3F
#define LDO_MV(mv) (( ((mv) + LDO_VSTEP / 2) - LDO_VMIN) / LDO_VSTEP)
#define LDO_GET_MV(reg) (((reg) & LDO_VMASK) * LDO_VSTEP + LDO_VMIN)
#define LDOMODE_OFF (0x0 << 6)
#define LDOMODE_STANDBY (BIT(6))
#define LDOMODE_LOWPWR (0x2 << 6)
#define LDOMODE_ON (0x3 << 6)
#define LDOMODE_MASK (0x3 << 6)
struct max77_config {
int ctrl1_start;
int ctrl2_start;
int nldo;
};
static const struct max77_config max77802_cfg = {
.ctrl1_start = 0x60,
.ctrl2_start = 0x90,
.nldo = 35
};
static const struct max77_config max77686_cfg = {
.ctrl1_start = 0x40,
.ctrl2_start = 0x60,
.nldo = 26
};
static inline const struct max77_config*
pmic_get_priv(pmic_t* pmic) {
return (const struct max77_config*)pmic->priv;
}
static int
pmic_reg_read(pmic_t* pmic, uint8_t reg, void* data, int count)
{
return !(i2c_kvslave_read(&pmic->kvslave, reg, data, count) == count);
}
static int
pmic_reg_write(pmic_t* pmic, uint8_t reg, const void* data, int count)
{
return !(i2c_kvslave_write(&pmic->kvslave, reg, data, count) == count);
}
static int
ldo_valid(pmic_t* pmic, int ldo)
{
int nldo = pmic_nldo(pmic);
return !(nldo < 0 || ldo <= 0 || ldo > nldo);
}
int
pmic_init(i2c_bus_t* i2c, int addr, pmic_t* pmic)
{
uint16_t chip_id;
int ret;
ret = i2c_slave_init(i2c, addr,
I2C_SLAVE_ADDR_7BIT, I2C_SLAVE_SPEED_FAST,
0, &pmic->i2c_slave);
if (ret) {
ZF_LOGE("Failed to register I2C slave");
return -1;
}
ret = i2c_kvslave_init(&pmic->i2c_slave, LITTLE8, LITTLE8, &pmic->kvslave);
if (ret) {
ZF_LOGE("Failed to initialize I2C KV-slave lib instance.");
return -1;
}
/* Read the chip ID */
if (pmic_reg_read(pmic, REG_CHIPID, &chip_id, 2)) {
ZF_LOGE("Bus error");
return -1;
}
/* Check the chip ID */
switch (chip_id) {
case MAX77686_CHIPID:
pmic->priv = (void*)&max77686_cfg;
break;
case MAXXXXXX_CHIPID:
case MAX77802_CHIPID:
pmic->priv = (void*)&max77802_cfg;
break;
default:
ZF_LOGE("Unidentified chip 0x%02x", chip_id);
return -1;
}
ZF_LOGE("found chip ID 0x%x", chip_id);
return 0;
}
int
pmic_nldo(pmic_t* pmic)
{
const struct max77_config* cfg = pmic_get_priv(pmic);
assert(cfg);
return cfg->nldo;
}
int
pmic_ldo_cfg(pmic_t* pmic, int ldo, enum ldo_mode ldo_mode, int milli_volt)
{
if (!ldo_valid(pmic, ldo)) {
return -1;
} else {
const struct max77_config* cfg;
uint8_t v;
cfg = pmic_get_priv(pmic);
assert(cfg);
/* Generate the register data */
v = LDO_MV(milli_volt);
switch (ldo_mode) {
case LDO_OFF:
v |= LDOMODE_OFF;
break;
case LDO_STANDBY:
v |= LDOMODE_STANDBY;
break;
case LDO_LOWPWR:
v |= LDOMODE_LOWPWR;
break;
case LDO_ON:
v |= LDOMODE_ON;
break;
default:
/* Invalid mode */
return -1;
}
/* Write the register */
if (pmic_reg_write(pmic, cfg->ctrl1_start + ldo - 1, &v, 1)) {
return -1;
} else {
return LDO_GET_MV(v);
}
}
}
int
pmic_ldo_get_cfg(pmic_t* pmic, int ldo, enum ldo_mode* ldo_mode)
{
if (!ldo_valid(pmic, ldo)) {
return -1;
} else {
const struct max77_config* cfg;
uint8_t v;
cfg = pmic_get_priv(pmic);
assert(cfg);
/* Read in the register */
if (pmic_reg_read(pmic, cfg->ctrl1_start + ldo - 1, &v, 1)) {
return -1;
}
/* Decode the mode */
if (ldo_mode != NULL) {
switch (v & LDOMODE_MASK) {
case LDOMODE_OFF:
*ldo_mode = LDO_OFF;
break;
case LDOMODE_STANDBY:
*ldo_mode = LDO_STANDBY;
break;
case LDOMODE_LOWPWR:
*ldo_mode = LDO_LOWPWR;
break;
case LDOMODE_ON:
*ldo_mode = LDO_ON;
break;
default:
/* Should never get here */
return -1;
}
}
return LDO_GET_MV(v);
}
}
int
pmic_get_reset_delay(pmic_t* pmic)
{
uint8_t data;
if (pmic_reg_read(pmic, REG_RESET_DELAY, &data, 1)) {
return -1;
} else {
return 1000 * (data >> 1);
}
}
int
pmic_set_reset_delay(pmic_t* pmic, int ms)
{
uint8_t data;
/* Clip ms value */
if (ms < 0) {
ms = 0;
} else if (ms > 10000) {
ms = 10000;
}
/* Write the data */
data = (ms / 1000) << 1;
if (pmic_reg_write(pmic, REG_RESET_DELAY, &data, 1)) {
return -1;
} else {
return data;
}
}
void
pmic_print_status(pmic_t* pmic)
{
uint8_t data;
int nldo;
int err;
int i;
printf("### PMIC ###\n");
err = pmic_reg_read(pmic, REG_CHIPID, &data, 1);
assert(!err);
if (err) {
return;
}
switch (data) {
case MAX77686_CHIPID:
printf("MAX77686");
break;
case MAX77802_CHIPID:
printf("MAX77802");
break;
case MAXXXXXX_CHIPID:
printf("MAXXXXXx");
break;
default:
printf("Unknown CHIP");
}
nldo = pmic_nldo(pmic);
printf(": %d LDOs\n", nldo);
for (i = 1; i < nldo; i++) {
int mv, v;
enum ldo_mode mode;
mv = pmic_ldo_get_cfg(pmic, i, &mode);
v = mv / 1000;
mv -= v * 1000;
printf("LDO%02d: %d.%03dV (", i, v, mv);
switch (mode) {
case LDO_OFF:
printf("Off");
break;
case LDO_STANDBY:
printf("Standby");
break;
case LDO_LOWPWR:
printf("Low power");
break;
case LDO_ON:
printf("On");
break;
default:
printf("Unknown state");
}
printf(")\n");
}
printf("Reset delay: %d ms\n", pmic_get_reset_delay(pmic));
}