blob: bb4dd836a23bc5c30f5bda550abcf476f6fa3fbf [file] [log] [blame]
/*
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
#include <common.h>
#include <i2c.h>
#include <s5m8767.h>
/* Chip register numbers (not exported from this module) */
enum {
REG_BBAT = 0x0e,
/* Bits for BBAT */
BBAT_BBCHOSTEN_MASK = 1 << 0,
BBAT_BBCVS_SHIFT = 3,
BBAT_BBCVS_MASK = 3 << BBAT_BBCVS_SHIFT,
};
struct s5m8767_para s5m8767_param[] = {/*{regnum, vol_addr, vol_bitpos,
vol_bitmask, reg_enaddr, reg_enbitpos, reg_enbitmask, reg_enbiton,
reg_enbitoff, vol_min, vol_div}*/
/* | voltage ----| | enable --------------| voltage */
/*regnum addr bpos mask addr bpos mask on off min div */
{S5M8767_BUCK1, 0x33, 0x0, 0xFF, 0x32, 0x6, 0x3, 0x3, 0x0, 650, 6250},
{S5M8767_BUCK2, 0x35, 0x0, 0xFF, 0x34, 0x6, 0x3, 0x1, 0x0, 600, 6250},
{S5M8767_BUCK3, 0x3E, 0x0, 0xFF, 0x3D, 0x6, 0x3, 0x1, 0x0, 600, 6250},
{S5M8767_BUCK4, 0x47, 0x0, 0xFF, 0x46, 0x6, 0x3, 0x1, 0x0, 600, 6250},
{S5M8767_BUCK5, 0x50, 0x0, 0xFF, 0x4F, 0x6, 0x3, 0x3, 0x0, 650, 6250},
{S5M8767_BUCK6, 0x55, 0x0, 0xFF, 0x54, 0x6, 0x3, 0x3, 0x0, 650, 6250},
{S5M8767_BUCK7, 0x57, 0x0, 0xFF, 0x56, 0x6, 0x3, 0x3, 0x0, 750, 12500},
{S5M8767_BUCK8, 0x59, 0x0, 0xFF, 0x58, 0x6, 0x3, 0x3, 0x0, 750, 12500},
{S5M8767_BUCK9, 0x5B, 0x0, 0xFF, 0x5A, 0x6, 0x3, 0x3, 0x0, 750, 12500},
{S5M8767_LDO1, 0x5C, 0x0, 0x3F, 0x5C, 0x6, 0x3, 0x3, 0x0, 800, 25000},
{S5M8767_LDO2, 0x5D, 0x0, 0x3F, 0x5D, 0x6, 0x3, 0x1, 0x0, 800, 25000},
{S5M8767_LDO3, 0x61, 0x0, 0x3F, 0x61, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO4, 0x62, 0x0, 0x3F, 0x62, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO5, 0x63, 0x0, 0x3F, 0x63, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO6, 0x64, 0x0, 0x3F, 0x64, 0x6, 0x3, 0x1, 0x0, 800, 25000},
{S5M8767_LDO7, 0x65, 0x0, 0x3F, 0x65, 0x6, 0x3, 0x1, 0x0, 800, 25000},
{S5M8767_LDO8, 0x66, 0x0, 0x3F, 0x66, 0x6, 0x3, 0x1, 0x0, 800, 25000},
{S5M8767_LDO9, 0x67, 0x0, 0x3F, 0x67, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO10, 0x68, 0x0, 0x3F, 0x68, 0x6, 0x3, 0x1, 0x0, 800, 50000},
{S5M8767_LDO11, 0x69, 0x0, 0x3F, 0x69, 0x6, 0x3, 0x1, 0x0, 800, 50000},
{S5M8767_LDO12, 0x6A, 0x0, 0x3F, 0x6A, 0x6, 0x3, 0x1, 0x0, 800, 50000},
{S5M8767_LDO13, 0x6B, 0x0, 0x3F, 0x6B, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO14, 0x6C, 0x0, 0x3F, 0x6C, 0x6, 0x3, 0x1, 0x0, 800, 50000},
{S5M8767_LDO15, 0x6D, 0x0, 0x3F, 0x6D, 0x6, 0x3, 0x1, 0x0, 800, 25000},
{S5M8767_LDO16, 0x6E, 0x0, 0x3F, 0x6E, 0x6, 0x3, 0x1, 0x0, 800, 50000},
{S5M8767_LDO17, 0x6F, 0x0, 0x3F, 0x6F, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO18, 0x70, 0x0, 0x3F, 0x70, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO19, 0x71, 0x0, 0x3F, 0x71, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO20, 0x72, 0x0, 0x3F, 0x72, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO21, 0x73, 0x0, 0x3F, 0x73, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO22, 0x74, 0x0, 0x3F, 0x74, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO23, 0x75, 0x0, 0x3F, 0x75, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO24, 0x76, 0x0, 0x3F, 0x76, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO25, 0x77, 0x0, 0x3F, 0x77, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO26, 0x78, 0x0, 0x3F, 0x78, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO27, 0x79, 0x0, 0x3F, 0x79, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_LDO28, 0x7A, 0x0, 0x3F, 0x7A, 0x6, 0x3, 0x3, 0x0, 800, 50000},
{S5M8767_EN32KHZ_CP, 0x0, 0x0, 0x0, 0x0A, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0},
};
/*
* Write a value to a register
*
* @param chip_addr i2c addr for s5m8767
* @param reg reg number to write
* @param val value to be written
*
*/
static inline int s5m8767_i2c_write(unsigned char chip_addr,
unsigned int reg, unsigned char val)
{
return i2c_write(chip_addr, reg, 1, &val, 1);
}
/*
* Read a value from a register
*
* @param chip_addr i2c addr for s5m8767
* @param reg reg number to write
* @param val value to be written
*
*/
static inline int s5m8767_i2c_read(unsigned char chip_addr,
unsigned int reg, unsigned char *val)
{
return i2c_read(chip_addr, reg, 1, val, 1);
}
/*
* Enable the s5m8767 register
*
* @param reg register number of buck/ldo to be enabled
* @param enable enable or disable bit
*
* S5M8767_REG_DISABLE = 0,
needed to set the buck/ldo enable bit OFF
* @return Return 0 if ok, else -1
*/
static int s5m8767_enablereg(enum s5m8767_regnum reg, int enable)
{
struct s5m8767_para *pmic;
unsigned char read_data;
int ret;
pmic = &s5m8767_param[reg];
ret = s5m8767_i2c_read(S5M8767_I2C_ADDR, pmic->reg_enaddr,
&read_data);
if (ret != 0) {
debug("s5m8767 i2c read failed.\n");
return -1;
}
if (enable == S5M8767_REG_DISABLE) {
clrbits_8(&read_data,
pmic->reg_enbitmask << pmic->reg_enbitpos);
} else {
clrsetbits_8(&read_data,
pmic->reg_enbitmask << pmic->reg_enbitpos,
pmic->reg_enbiton << pmic->reg_enbitpos);
}
ret = s5m8767_i2c_write(S5M8767_I2C_ADDR,
pmic->reg_enaddr, read_data);
if (ret != 0) {
debug("s5m8767 i2c write failed.\n");
return -1;
}
return 0;
}
static int s5m8767_do_volsetting(enum s5m8767_regnum reg, unsigned int volt,
int enable, int volt_units)
{
struct s5m8767_para *pmic;
unsigned char read_data;
int vol_level = 0;
int ret;
pmic = &s5m8767_param[reg];
if (pmic->vol_addr == 0) {
debug("not a voltage register.\n");
return -1;
}
ret = s5m8767_i2c_read(S5M8767_I2C_ADDR, pmic->vol_addr, &read_data);
if (ret != 0) {
debug("s5m8767 i2c read failed.\n");
return -1;
}
if (volt_units == S5M8767_UV)
vol_level = volt - pmic->vol_min * 1000;
else
vol_level = (volt - pmic->vol_min) * 1000;
if (vol_level < 0) {
debug("Not a valid voltage level to set\n");
return -1;
}
vol_level /= pmic->vol_div;
clrsetbits_8(&read_data, pmic->vol_bitmask << pmic->vol_bitpos,
vol_level << pmic->vol_bitpos);
ret = s5m8767_i2c_write(S5M8767_I2C_ADDR, pmic->vol_addr, read_data);
if (ret != 0) {
debug("s5m8767 i2c write failed.\n");
return -1;
}
ret = s5m8767_enablereg(reg, enable);
if (ret != 0) {
debug("Failed to enable buck/ldo.\n");
return -1;
}
return 0;
}
int s5m8767_volsetting(enum s5m8767_regnum reg, unsigned int volt,
int enable, int volt_units)
{
int old_bus = i2c_get_bus_num();
int ret;
i2c_set_bus_num(0);
ret = s5m8767_do_volsetting(reg, volt, enable, volt_units);
i2c_set_bus_num(old_bus);
return ret;
}
int s5m8767_enable_32khz_cp(void)
{
i2c_set_bus_num(0);
return s5m8767_enablereg(S5M8767_EN32KHZ_CP, S5M8767_REG_ENABLE);
}
int s5m8767_disable_backup_batt(void)
{
unsigned char val;
int ret;
i2c_set_bus_num(0);
ret = s5m8767_i2c_read(S5M8767_I2C_ADDR, REG_BBAT, &val);
if (ret) {
debug("s5m8767 i2c read failed\n");
return ret;
}
/* If we already have the correct values, exit */
if ((val & (BBAT_BBCVS_MASK | BBAT_BBCHOSTEN_MASK)) ==
BBAT_BBCVS_MASK)
return 0;
/* First disable charging */
val &= ~BBAT_BBCHOSTEN_MASK;
ret = s5m8767_i2c_write(S5M8767_I2C_ADDR, REG_BBAT, val);
if (ret) {
debug("s5m8767 i2c write failed\n");
return -1;
}
/* Finally select 3.5V to minimize power consumption */
val |= BBAT_BBCVS_MASK;
ret = s5m8767_i2c_write(S5M8767_I2C_ADDR, REG_BBAT, val);
if (ret) {
debug("s5m8767 i2c write failed\n");
return -1;
}
return 0;
}