blob: 76aa94547fcd95ac8d731ea87c7b7f50aa25c3fa [file] [log] [blame]
/*
* Copyright (C) 2014 Marvell International Ltd.
* Hongyan Song <hysong@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <i2c.h>
#include <power/pmic.h>
#include <power/marvell88pm_pmic.h>
#include <errno.h>
#define MARVELL_PMIC_BASE "88pm860_base"
#define MARVELL_PMIC_POWER "88pm860_power"
#define MARVELL_PMIC_GPADC "88pm860_gpadc"
#define DVC_CONTROL_REG 0x4F
static struct pmic_chip_desc *g_chip;
enum {
PM860_ID_LDO1 = 0x08,
PM860_ID_LDO2 = 0x0b,
PM860_ID_LDO3,
PM860_ID_LDO4,
PM860_ID_LDO5,
PM860_ID_LDO6,
PM860_ID_LDO7,
PM860_ID_LDO8,
PM860_ID_LDO9,
PM860_ID_LDO10,
PM860_ID_LDO11,
PM860_ID_LDO12,
PM860_ID_LDO13,
PM860_ID_LDO14,
PM860_ID_LDO15,
PM860_ID_LDO16,
PM860_ID_LDO17,
PM860_ID_LDO18,
PM860_ID_LDO19,
PM860_ID_LDO20 = 0x1d,
PM860_ID_BUCK1 = 0x3c,
PM860_ID_BUCK2 = 0x40,
PM860_ID_BUCK3 = 0x41,
PM860_ID_BUCK4 = 0x45,
PM860_ID_BUCK5 = 0x46,
PM860_ID_BUCK6 = 0x4a,
PM860_BUCK_EN1 = 0x50,
PM860_LDO1_8_EN1 = 0x51,
PM860_LDO9_16_EN1 = 0x52,
PM860_LDO17_20_EN1 = 0x53,
};
static unsigned int ldo1_to_3_voltage_table[] = {
1700000, 1800000, 1900000, 2500000, 2800000, 2900000, 3100000, 3300000,
};
static unsigned int ldo3_to_18_voltage_table[] = {
1200000, 1250000, 1700000, 1800000, 1850000, 1900000, 2500000, 2600000,
2700000, 2750000, 2800000, 2850000, 2900000, 3000000, 3100000, 3300000,
};
static unsigned int ldo19_voltage_table[] = {
600000, 650000, 700000, 750000, 800000, 850000, 900000, 950000,
1000000, 1050000, 1100000, 1150000, 1200000, 1300000, 1400000, 1500000,
};
static unsigned int ldo20_voltage_table[] = {
1700000, 1800000, 1900000, 2000000, 2100000, 2500000, 2700000, 2800000,
};
enum {
PM860_VERSION_Z3 = 0,
PM860_VERSION_A0 = 1,
};
static int get_pm860_version(struct pmic *p_base)
{
u32 val;
int ret = 0;
ret = pmic_reg_read(p_base, 0x0, &val);
if (ret) {
printf("%s: read base page 0x0 register fails.\n", __func__);
return PM860_VERSION_A0;
}
if (val == 0x91)
return PM860_VERSION_A0;
else
return PM860_VERSION_Z3;
}
int marvell88pm860_reg_update(struct pmic *p, int reg, unsigned int regval)
{
u32 val;
int ret = 0;
ret = pmic_reg_read(p, reg, &val);
if (ret) {
debug("%s: PMIC %d register read failed\n", __func__, reg);
return -1;
}
val |= regval;
ret = pmic_reg_write(p, reg, val);
if (ret) {
debug("%s: PMIC %d register write failed\n", __func__, reg);
return -1;
}
return 0;
}
static int pm860_buck_volt2hex(unsigned int buck, unsigned int uV)
{
unsigned int hex = 0;
switch (buck) {
case 1:
if (uV > 1800000) {
debug("%d is wrong voltage for BUCK%d\n", uV, buck);
return 0;
}
break;
case 2:
case 3:
case 4:
case 5:
case 6:
if (uV > 3300000) {
debug("%d is wrong voltage for BUCK%d\n", uV, buck);
return 0;
}
break;
default:
debug("%d is wrong voltage for BUCK%d\n", uV, buck);
return -EINVAL;
}
if (uV <= 1600000)
hex = (uV - 600000) / 12500;
else
hex = 0x50 + (uV - 1600000) / 50000;
debug("%s: buck=%d, uV=%d, hex= %d\n", __func__, buck, uV, hex);
return hex;
}
int marvell88pm860_set_buck_vol(struct pmic *p, unsigned int buck, unsigned int uV)
{
unsigned int val, ret, adr = 0;
int hex = 0;
if (buck < 1 || buck > 6) {
printf("%s: %d is wrong buck number\n", __func__, buck);
return -1;
}
switch (buck) {
case 1:
adr = PM860_ID_BUCK1;
break;
case 2:
adr = PM860_ID_BUCK2;
break;
case 3:
adr = PM860_ID_BUCK3;
break;
case 4:
adr = PM860_ID_BUCK4;
break;
case 5:
adr = PM860_ID_BUCK5;
break;
case 6:
adr = PM860_ID_BUCK6;
break;
default:
printf("%s: %d is wrong buck number\n", __func__, buck);
return -EINVAL;
}
hex = pm860_buck_volt2hex(buck, uV);
if (hex < 0)
return -1;
ret = pmic_reg_read(p, adr, &val);
if (ret)
return ret;
val &= ~MARVELL88PM_BUCK_VOLT_MASK;
val |= hex;
ret = pmic_reg_write(p, adr, val);
return ret;
}
static int pm860_buck_hex2volt(unsigned int buck, unsigned int hex)
{
unsigned int uV = 0;
if (hex <= 0x50)
uV = hex * 12500 + 600000;
else
uV = 1600000 + (hex - 0x50) * 50000;
debug("%s: buck=%d, uV=%d, hex= %d\n", __func__, buck, uV, hex);
return uV;
}
int marvell88pm860_get_buck_vol(struct pmic *p, unsigned int buck)
{
unsigned int val, ret, adr = 0;
int uV;
if (buck < 1 || buck > 6) {
printf("%s: %d is wrong buck number\n", __func__, buck);
return -1;
}
switch (buck) {
case 1:
adr = PM860_ID_BUCK1;
break;
case 2:
adr = PM860_ID_BUCK2;
break;
case 3:
adr = PM860_ID_BUCK3;
break;
case 4:
adr = PM860_ID_BUCK4;
break;
case 5:
adr = PM860_ID_BUCK5;
break;
case 6:
adr = PM860_ID_BUCK6;
break;
default:
printf("%s: %d is wrong buck number\n", __func__, buck);
return -EINVAL;
}
ret = pmic_reg_read(p, adr, &val);
if (ret)
return ret;
uV = pm860_buck_hex2volt(buck, val);
if (uV < 0)
return -1;
return uV;
}
static int pm860_ldo_volt2hex(unsigned int ldo, unsigned int uV)
{
unsigned int *voltage_table = NULL, table_size = 0, hex = 0;
/*choose ldo voltage table*/
switch (ldo) {
case 1:
case 2:
case 3:
voltage_table = ldo1_to_3_voltage_table;
table_size = ARRAY_SIZE(ldo1_to_3_voltage_table);
break;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
voltage_table = ldo3_to_18_voltage_table;
table_size = ARRAY_SIZE(ldo3_to_18_voltage_table);
break;
case 19:
voltage_table = ldo19_voltage_table;
table_size = ARRAY_SIZE(ldo19_voltage_table);
break;
case 20:
voltage_table = ldo20_voltage_table;
table_size = ARRAY_SIZE(ldo20_voltage_table);
break;
default:
debug("%s: %d is wrong LDO number\n", __func__, ldo);
return -EINVAL;
}
for (hex = 0; hex < table_size; hex++) {
if (uV <= voltage_table[hex]) {
debug("ldo %d, voltage %d, reg value 0x%x\n", ldo, uV, hex);
return hex;
}
}
return table_size - 1;
}
int marvell88pm860_set_ldo_vol(struct pmic *p, unsigned int ldo, unsigned int uV)
{
unsigned int val, ret, adr, ldo_en_mask;
int hex = 0;
if (ldo < 1 || ldo > 20) {
printf("%s: %d is wrong ldo number\n", __func__, ldo);
return -1;
}
if (ldo == 1)
adr = PM860_ID_LDO1;
else if (ldo == 2)
adr = PM860_ID_LDO2;
else
adr = PM860_ID_LDO2 + ldo - 2;
hex = pm860_ldo_volt2hex(ldo, uV);
if (hex < 0)
return -1;
ret = pmic_reg_read(p, adr, &val);
if (ret)
return ret;
val &= ~MARVELL88PM_LDO_VOLT_MASK;
val |= hex;
ret = pmic_reg_write(p, adr, val);
/* check whether the LDO is enabled, if not enable it.*/
adr = PM860_LDO1_8_EN1 + (ldo - 1)/8;
ldo_en_mask = (ldo - 1)%8;
ret = pmic_reg_read(p, adr, &val);
if (ret)
return ret;
val |= (1 << ldo_en_mask);
ret = pmic_reg_write(p, adr, val);
return ret;
}
int pmic_88pm860_alloc(unsigned char bus, struct pmic_chip_desc *chip)
{
struct pmic *p_base = pmic_alloc();
struct pmic *p_power = pmic_alloc();
struct pmic *p_gpadc = pmic_alloc();
if (!p_base || !p_power || !p_gpadc) {
printf("%s: pmic allocation error!\n", __func__);
return -ENOMEM;
}
if (!chip) {
printf("%s: chip description is empty!\n", __func__);
return -EINVAL;
}
chip->base_name = MARVELL_PMIC_BASE;
chip->power_name = MARVELL_PMIC_POWER;
chip->gpadc_name = MARVELL_PMIC_GPADC;
chip->charger_name = NULL;
chip->fuelgauge_name = NULL;
chip->battery_name = NULL;
p_base->bus = bus;
p_base->hw.i2c.addr = MARVELL88PM_I2C_ADDR;
p_base->name = MARVELL_PMIC_BASE;
p_base->interface = PMIC_I2C;
p_base->number_of_regs = PMIC_NUM_OF_REGS;
p_base->hw.i2c.tx_num = 1;
p_power->bus = bus;
p_power->hw.i2c.addr = MARVELL88PM_I2C_ADDR + 1;
p_power->name = MARVELL_PMIC_POWER;
p_power->interface = PMIC_I2C;
p_power->number_of_regs = PMIC_NUM_OF_REGS;
p_power->hw.i2c.tx_num = 1;
p_gpadc->bus = bus;
p_gpadc->hw.i2c.addr = MARVELL88PM_I2C_ADDR + 2;
p_gpadc->name = MARVELL_PMIC_GPADC;
p_gpadc->interface = PMIC_I2C;
p_gpadc->number_of_regs = PMIC_NUM_OF_REGS;
p_gpadc->hw.i2c.tx_num = 1;
/* get functions */
chip->set_buck_vol = marvell88pm860_set_buck_vol;
chip->set_ldo_vol = marvell88pm860_set_ldo_vol;
chip->get_buck_vol = marvell88pm860_get_buck_vol;
chip->reg_update = marvell88pm860_reg_update;
puts("Board PMIC init\n");
g_chip = chip;
return 0;
}
void pmic_88pm860_base_init(struct pmic *p_base)
{
u32 val;
/* Enable low jitter XO_LJ = 1 */
val = 0x20;
pmic_reg_write(p_base, 0x21, val);
/*disable watchdog timer*/
val = 0x01;
pmic_reg_write(p_base, 0x1d, val);
/*xo_cap sel = 100 0xe8*/
pmic_reg_read(p_base, 0xe8, &val);
val |= (0x4 << 4);
pmic_reg_write(p_base, 0xe8, val);
/* use_xo = ON 0xd0 */
pmic_reg_read(p_base, 0xd0, &val);
val |= (1 << 7);
pmic_reg_write(p_base, 0xd0, val);
/* clk32k_out2_sel = 01 (bit2,3)*/
pmic_reg_read(p_base, 0xe2, &val);
val &= ~(0x3 << 2);
val |= (1 << 2);
pmic_reg_write(p_base, 0xe2, val);
/* set oscillator running locked to 32kHZ*/
pmic_reg_read(p_base, 0x50, &val);
val &= ~(1 << 1);
pmic_reg_write(p_base, 0x50, val);
pmic_reg_read(p_base, 0x55, &val);
val &= ~(1 << 1);
pmic_reg_write(p_base, 0x55, val);
/* enable pwr_hold */
pmic_reg_read(p_base, 0x0d, &val);
val |= (1 << 7);
pmic_reg_write(p_base, 0x0d, val);
/*
* set gpio3 and gpio4 to be DVC mode:
* a puzzle:
* for z3 version:
* pin J4 -> gpio3, can be configured as dvc3
* pin L2 -> gpio4, can be configured as dvc4
* pin D6 -> gpio5, named as gpio3 when J4/L2 are configured as dvc
* for a0 version:
* pin J4 -> gpio4, can be configured as dvc3
* pin L2 -> gpio5, can be configured as dvc4
* pin D6 -> gpio3
*/
if (get_pm860_version(p_base) == PM860_VERSION_Z3) {
pmic_update_bits(p_base, 0x31, 0x7 << 5, 0x7 << 5);
pmic_update_bits(p_base, 0x32, 0x7 << 5, 0x7 << 5);
} else {
pmic_reg_write(p_base, 0x32, 0xee);
}
/*
* print out power up/down log
* save it in EMMD
*/
get_powerup_down_log(g_chip);
}
void pmic_88pm860_gpadc_init(struct pmic *p_gpadc)
{}
void pmic_88pm860_power_init(struct pmic *p_power)
{
u32 val;
/* change drive selection from half to full */
val = 0xff;
pmic_reg_write(p_power, 0x3a, val);
/* use 12.5mV/us for DVC ramp up */
pmic_reg_read(p_power, 0x78, &val);
val &= ~(0x7 << 3);
val |= (0x6 << 3);
pmic_reg_write(p_power, 0x78, val);
/* enable buck1 sleep mode and set sleep voltage to 0.8v */
pmic_update_bits(p_power, 0x30, 0x7f, 0x10);
pmic_update_bits(p_power, 0x5a, 0x3, 0x2);
}
int pmic_88pm860_init(struct pmic_chip_desc *chip)
{
struct pmic *p_base, *p_power, *p_gpadc;
if (!chip) {
printf("---%s: chip is empty.\n", __func__);
return -EINVAL;
}
/*------------base page setting-----------*/
p_base = pmic_get(chip->base_name);
if (!p_base)
return -1;
if (pmic_probe(p_base))
return -1;
pmic_88pm860_base_init(p_base);
board_pmic_base_fixup(p_base);
/*------------gpadc page setting -----------*/
p_gpadc = pmic_get(chip->gpadc_name);
if (!p_gpadc)
return -1;
if (pmic_probe(p_gpadc))
return -1;
pmic_88pm860_gpadc_init(p_gpadc);
board_pmic_gpadc_fixup(p_gpadc);
/*------------power page setting -----------*/
p_power = pmic_get(chip->power_name);
if (!p_power)
return -1;
if (pmic_probe(p_power))
return -1;
pmic_88pm860_power_init(p_power);
board_pmic_power_fixup(p_power);
return 0;
}