/*
* drivers/regulator/ricoh619-regulator.c
*
* Regulator driver for RICOH R5T619 power management chip.
*
* Copyright (C) 2012-2014 RICOH COMPANY,LTD
*
* Based on code
* Copyright (C) 2011 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
/*#define DEBUG 1*/
/*#define VERBOSE_DEBUG 1*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../../arch/arm/mach-mx6/ntx_hwconfig.h"
struct ricoh61x_regulator {
int id;
int sleep_id;
/* Regulator register address.*/
u8 reg_en_reg;
u8 en_bit;
u8 reg_disc_reg;
u8 disc_bit;
u8 vout_reg;
u8 vout_mask;
u8 vout_reg_cache;
u8 sleep_reg;
u8 eco_reg;
u8 eco_bit;
u8 eco_slp_reg;
u8 eco_slp_bit;
/* chip constraints on regulator behavior */
int min_uV;
int max_uV;
int step_uV;
int nsteps;
/* regulator specific turn-on delay */
u16 delay;
/* used by regulator core */
struct regulator_desc desc;
/* Device */
struct device *dev;
};
extern volatile NTX_HWCONFIG *gptHWCFG;
static unsigned int ricoh61x_suspend_status;
static inline struct device *to_ricoh61x_dev(struct regulator_dev *rdev)
{
return rdev_get_dev(rdev)->parent->parent;
}
static int ricoh61x_regulator_enable_time(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
return ri->delay;
}
static int ricoh61x_reg_is_enabled(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
uint8_t control;
int ret;
ret = ricoh61x_read(parent, ri->reg_en_reg, &control);
if (ret < 0) {
dev_err(&rdev->dev, "Error in reading the control register\n");
return ret;
}
return (((control >> ri->en_bit) & 1) == 1);
}
static int ricoh61x_reg_enable(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
int ret;
ret = ricoh61x_set_bits(parent, ri->reg_en_reg, (1 << ri->en_bit));
if (ret < 0) {
dev_err(&rdev->dev, "Error in updating the STATE register\n");
return ret;
}
udelay(ri->delay);
return ret;
}
static int ricoh61x_reg_disable(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
int ret;
ret = ricoh61x_clr_bits(parent, ri->reg_en_reg, (1 << ri->en_bit));
if (ret < 0)
dev_err(&rdev->dev, "Error in updating the STATE register\n");
return ret;
}
static int ricoh61x_list_voltage(struct regulator_dev *rdev, unsigned index)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
return ri->min_uV + (ri->step_uV * index);
}
static int __ricoh61x_set_s_voltage(struct device *parent,
struct ricoh61x_regulator *ri, int min_uV, int max_uV)
{
int vsel;
int ret;
if ((min_uV < ri->min_uV) || (max_uV > ri->max_uV))
return -EDOM;
vsel = (min_uV - ri->min_uV + ri->step_uV - 1)/ri->step_uV;
if (vsel > ri->nsteps)
return -EDOM;
printk ("[%s-%d] at %duv suspend\n",__func__,__LINE__,min_uV);
ret = ricoh61x_update(parent, ri->sleep_reg, vsel, ri->vout_mask);
if (ret < 0)
dev_err(ri->dev, "Error in writing the sleep register\n");
return ret;
}
static int __ricoh61x_set_voltage(struct device *parent,
struct ricoh61x_regulator *ri, int min_uV, int max_uV,
unsigned *selector)
{
int vsel;
int ret;
uint8_t vout_val;
if ((min_uV < ri->min_uV) || (max_uV > ri->max_uV))
return -EDOM;
vsel = (min_uV - ri->min_uV + ri->step_uV - 1)/ri->step_uV;
if (vsel > ri->nsteps)
return -EDOM;
if (selector)
*selector = vsel;
vout_val = (ri->vout_reg_cache & ~ri->vout_mask) |
(vsel & ri->vout_mask);
ret = ricoh61x_write(parent, ri->vout_reg, vout_val);
if (ret < 0)
dev_err(ri->dev, "Error in writing the Voltage register\n");
else
ri->vout_reg_cache = vout_val;
return ret;
}
static int ricoh61x_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
if (ricoh61x_suspend_status)
return -EBUSY;
return __ricoh61x_set_voltage(parent, ri, min_uV, max_uV, selector);
}
static int ricoh61x_get_voltage(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
uint8_t vsel;
vsel = ri->vout_reg_cache & ri->vout_mask;
return ri->min_uV + vsel * ri->step_uV;
}
int ricoh61x_regulator_enable_eco_mode(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
int ret;
ret = ricoh61x_set_bits(parent, ri->eco_reg, (1 << ri->eco_bit));
if (ret < 0)
dev_err(&rdev->dev, "Error Enable LDO eco mode\n");
return ret;
}
EXPORT_SYMBOL_GPL(ricoh61x_regulator_enable_eco_mode);
int ricoh61x_regulator_disable_eco_mode(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
int ret;
ret = ricoh61x_clr_bits(parent, ri->eco_reg, (1 << ri->eco_bit));
if (ret < 0)
dev_err(&rdev->dev, "Error Disable LDO eco mode\n");
return ret;
}
EXPORT_SYMBOL_GPL(ricoh61x_regulator_disable_eco_mode);
int ricoh61x_regulator_enable_eco_slp_mode(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
int ret;
ret = ricoh61x_set_bits(parent, ri->eco_slp_reg,
(1 << ri->eco_slp_bit));
if (ret < 0)
dev_err(&rdev->dev, "Error Enable LDO eco mode in d during sleep\n");
return ret;
}
EXPORT_SYMBOL_GPL(ricoh61x_regulator_enable_eco_slp_mode);
int ricoh61x_regulator_disable_eco_slp_mode(struct regulator_dev *rdev)
{
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
struct device *parent = to_ricoh61x_dev(rdev);
int ret;
ret = ricoh61x_clr_bits(parent, ri->eco_slp_reg,
(1 << ri->eco_slp_bit));
if (ret < 0)
dev_err(&rdev->dev, "Error Enable LDO eco mode in d during sleep\n");
return ret;
}
EXPORT_SYMBOL_GPL(ricoh61x_regulator_disable_eco_slp_mode);
static struct regulator_ops ricoh61x_ops = {
.list_voltage = ricoh61x_list_voltage,
.set_voltage = ricoh61x_set_voltage,
.get_voltage = ricoh61x_get_voltage,
.enable = ricoh61x_reg_enable,
.disable = ricoh61x_reg_disable,
.is_enabled = ricoh61x_reg_is_enabled,
.enable_time = ricoh61x_regulator_enable_time,
};
#define RICOH61x_REG(_id, _en_reg, _en_bit, _disc_reg, _disc_bit, _vout_reg, \
_vout_mask, _ds_reg, _min_mv, _max_mv, _step_uV, _nsteps, \
_ops, _delay, _eco_reg, _eco_bit, _eco_slp_reg, _eco_slp_bit) \
{ \
.reg_en_reg = _en_reg, \
.en_bit = _en_bit, \
.reg_disc_reg = _disc_reg, \
.disc_bit = _disc_bit, \
.vout_reg = _vout_reg, \
.vout_mask = _vout_mask, \
.sleep_reg = _ds_reg, \
.min_uV = _min_mv * 1000, \
.max_uV = _max_mv * 1000, \
.step_uV = _step_uV, \
.nsteps = _nsteps, \
.delay = _delay, \
.id = RICOH619_ID_##_id, \
.sleep_id = RICOH619_DS_##_id, \
.eco_reg = _eco_reg, \
.eco_bit = _eco_bit, \
.eco_slp_reg = _eco_slp_reg, \
.eco_slp_bit = _eco_slp_bit, \
.desc = { \
.name = ricoh619_rails(_id), \
.id = RICOH619_ID_##_id, \
.n_voltages = _nsteps, \
.ops = &_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}, \
}
static struct ricoh61x_regulator ricoh61x_regulator[] = {
RICOH61x_REG(DC1, 0x2C, 0, 0x2C, 1, 0x36, 0xFF, 0x3B,
600, 3500, 12500, 0xE8, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(DC2, 0x2E, 0, 0x2E, 1, 0x37, 0xFF, 0x3C,
600, 3500, 12500, 0xE8, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(DC3, 0x30, 0, 0x30, 1, 0x38, 0xFF, 0x3D,
600, 3500, 12500, 0xE8, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(DC4, 0x32, 0, 0x32, 1, 0x39, 0xFF, 0x3E,
600, 3500, 12500, 0xE8, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(DC5, 0x34, 0, 0x34, 1, 0x3A, 0xFF, 0x3F,
600, 3500, 12500, 0xE8, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(LDO1, 0x44, 0, 0x46, 0, 0x4C, 0x7F, 0x58,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x48, 0, 0x4A, 0),
RICOH61x_REG(LDO2, 0x44, 1, 0x46, 1, 0x4D, 0x7F, 0x59,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x48, 1, 0x4A, 1),
RICOH61x_REG(LDO3, 0x44, 2, 0x46, 2, 0x4E, 0x7F, 0x5A,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x48, 2, 0x4A, 2),
RICOH61x_REG(LDO4, 0x44, 3, 0x46, 3, 0x4F, 0x7F, 0x5B,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x48, 3, 0x4A, 3),
RICOH61x_REG(LDO5, 0x44, 4, 0x46, 4, 0x50, 0x7F, 0x5C,
600, 3500, 25000, 0x74, ricoh61x_ops, 500,
0x48, 4, 0x4A, 4),
RICOH61x_REG(LDO6, 0x44, 5, 0x46, 5, 0x51, 0x7F, 0x5D,
600, 3500, 25000, 0x74, ricoh61x_ops, 500,
0x48, 5, 0x4A, 5),
RICOH61x_REG(LDO7, 0x44, 6, 0x46, 6, 0x52, 0x7F, 0x5E,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(LDO8, 0x44, 7, 0x46, 7, 0x53, 0x7F, 0x5F,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(LDO9, 0x45, 0, 0x47, 0, 0x54, 0x7F, 0x60,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(LDO10, 0x45, 1, 0x47, 1, 0x55, 0x7F, 0x61,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(LDORTC1, 0x45, 4, 0x00, 0, 0x56, 0x7F, 0x00,
1700, 3500, 25000, 0x48, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
RICOH61x_REG(LDORTC2, 0x45, 5, 0x00, 0, 0x57, 0x7F, 0x00,
900, 3500, 25000, 0x68, ricoh61x_ops, 500,
0x00, 0, 0x00, 0),
};
static inline struct ricoh61x_regulator *find_regulator_info(int id)
{
struct ricoh61x_regulator *ri;
int i;
for (i = 0; i < ARRAY_SIZE(ricoh61x_regulator); i++) {
ri = &ricoh61x_regulator[i];
if (ri->desc.id == id)
return ri;
}
return NULL;
}
static int ricoh61x_regulator_preinit(struct device *parent,
struct ricoh61x_regulator *ri,
struct ricoh619_regulator_platform_data *ricoh61x_pdata)
{
int ret = 0;
if (!ricoh61x_pdata->init_apply)
return 0;
if (ricoh61x_pdata->init_uV >= 0) {
ret = __ricoh61x_set_voltage(parent, ri,
ricoh61x_pdata->init_uV,
ricoh61x_pdata->init_uV, 0);
if (ret < 0) {
dev_err(ri->dev, "Not able to initialize voltage %d "
"for rail %d err %d\n", ricoh61x_pdata->init_uV,
ri->desc.id, ret);
return ret;
}
}
if (ricoh61x_pdata->sleep_uV > 0) {
ret = __ricoh61x_set_s_voltage(parent, ri,
ricoh61x_pdata->sleep_uV,
ricoh61x_pdata->sleep_uV);
if (ret < 0) {
dev_err(ri->dev, "Not able to initialize sleep voltage %d "
"for rail %d err %d\n", ricoh61x_pdata->sleep_uV,
ri->desc.id, ret);
return ret;
}
}
if (ricoh61x_pdata->init_enable) {
if (RICOH619_ID_DC1 <= ri->id && RICOH619_ID_DC5 >= ri->id) {
ret = ricoh61x_set_bits(parent, ri->reg_en_reg,
((1 << ri->en_bit)|0x80));
}
else
ret = ricoh61x_set_bits(parent, ri->reg_en_reg,
(1 << ri->en_bit));
}
else
ret = ricoh61x_clr_bits(parent, ri->reg_en_reg,
(1 << ri->en_bit));
if (ret < 0)
dev_err(ri->dev, "Not able to %s rail %d err %d\n",
(ricoh61x_pdata->init_enable) ? "enable" : "disable",
ri->desc.id, ret);
return ret;
}
static inline int ricoh61x_cache_regulator_register(struct device *parent,
struct ricoh61x_regulator *ri)
{
ri->vout_reg_cache = 0;
return ricoh61x_read(parent, ri->vout_reg, &ri->vout_reg_cache);
}
static int __devinit ricoh61x_regulator_probe(struct platform_device *pdev)
{
struct ricoh61x_regulator *ri = NULL;
struct regulator_dev *rdev;
struct ricoh619_regulator_platform_data *tps_pdata;
int id = pdev->id;
int err;
// printk(KERN_INFO "PMU: %s\n", __func__);
ri = find_regulator_info(id);
if (ri == NULL) {
dev_err(&pdev->dev, "invalid regulator ID specified\n");
return -EINVAL;
}
tps_pdata = pdev->dev.platform_data;
ri->dev = &pdev->dev;
ricoh61x_suspend_status = 0;
err = ricoh61x_cache_regulator_register(pdev->dev.parent, ri);
if (err) {
dev_err(&pdev->dev, "Fail in caching register\n");
return err;
}
err = ricoh61x_regulator_preinit(pdev->dev.parent, ri, tps_pdata);
if (err) {
dev_err(&pdev->dev, "Fail in pre-initialisation\n");
return err;
}
rdev = regulator_register(&ri->desc, &pdev->dev,
&tps_pdata->regulator, ri);
if (IS_ERR_OR_NULL(rdev)) {
dev_err(&pdev->dev, "failed to register regulator %s\n",
ri->desc.name);
return PTR_ERR(rdev);
}
platform_set_drvdata(pdev, rdev);
if (ri->eco_slp_reg)
ricoh61x_regulator_enable_eco_slp_mode (rdev);
return 0;
}
static int __devexit ricoh61x_regulator_remove(struct platform_device *pdev)
{
struct regulator_dev *rdev = platform_get_drvdata(pdev);
regulator_unregister(rdev);
return 0;
}
#ifdef CONFIG_PM
extern int gSleep_Mode_Suspend;
static uint8_t regulator_slot[RICOH619_ID_LDORTC2+1];
static int ricoh61x_regulator_suspend(struct device *dev)
{
struct regulator_dev *rdev = platform_get_drvdata(to_platform_device(dev));
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
uint8_t temp;
int offset = 0x16 + (ri->id - RICOH619_ID_DC1);
ricoh61x_read(to_ricoh61x_dev(rdev), offset, ®ulator_slot[ri->id]);
if (gSleep_Mode_Suspend ) {
temp = (regulator_slot[ri->id] & 0xF0) | 0x0E;
switch (ri->id) {
case RICOH619_ID_LDO8: // VDD_EP_1V8
case RICOH619_ID_DC3: // Core2_1V3_ARM
case RICOH619_ID_LDO3: // Core5_1V2
case RICOH619_ID_LDO5: // SPD_3V3
case RICOH619_ID_LDO7: // VDD_PWM
ricoh61x_write(to_ricoh61x_dev(rdev), offset, temp);
break;
case RICOH619_ID_LDO1: // IR_3V3
if(3==gptHWCFG->m_val.bTouchType || 4==gptHWCFG->m_val.bTouchType) {
if(0x03!=gptHWCFG->m_val.bUIConfig) {
ricoh61x_write(to_ricoh61x_dev(rdev), offset, temp);
}
}
break;
}
}
else {
switch (ri->id) {
case RICOH619_ID_LDO8: // VDD_EP_1V8
//ricoh61x_write(to_ricoh61x_dev(rdev), offset, temp);
break;
default:
if (0x0F != (regulator_slot[ri->id] & 0x0F)) {
temp = regulator_slot[ri->id] | 0x0F;
ricoh61x_write(to_ricoh61x_dev(rdev), offset, temp);
}
break;
}
}
ricoh61x_suspend_status = 1;
return 0;
}
static int ricoh61x_regulator_resume(struct device *dev)
{
// printk(KERN_INFO "PMU: %s %s\n", __func__, ri->desc.name);
struct regulator_dev *rdev = platform_get_drvdata(to_platform_device(dev));
struct ricoh61x_regulator *ri = rdev_get_drvdata(rdev);
int offset = 0x16 + (ri->id - RICOH619_ID_DC1);
if (gSleep_Mode_Suspend) {
switch (ri->id) {
case RICOH619_ID_DC3: // Core2_1V3_ARM
case RICOH619_ID_LDO3: // Core5_1V2
case RICOH619_ID_LDO5: // SPD_3V3
case RICOH619_ID_LDO7: // VDD_PWM
case RICOH619_ID_LDO8: // VDD_EP_1V8
ricoh61x_write(to_ricoh61x_dev(rdev), offset, regulator_slot[ri->id]);
break;
case RICOH619_ID_LDO1: // IR_3V3
if(3==gptHWCFG->m_val.bTouchType || 4==gptHWCFG->m_val.bTouchType) {
if(0x03!=gptHWCFG->m_val.bUIConfig) {
ricoh61x_write(to_ricoh61x_dev(rdev), offset, regulator_slot[ri->id]);
}
}
break;
}
}
else {
switch (ri->id) {
case RICOH619_ID_LDO8: // VDD_EP_1V8
//ricoh61x_write(to_ricoh61x_dev(rdev), offset, regulator_slot[ri->id]);
break;
}
}
ricoh61x_suspend_status = 0;
return 0;
}
static const struct dev_pm_ops ricoh61x_regulator_pm_ops = {
.suspend = ricoh61x_regulator_suspend,
.resume = ricoh61x_regulator_resume,
};
#endif
static struct platform_driver ricoh61x_regulator_driver = {
.driver = {
.name = "ricoh61x-regulator",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &ricoh61x_regulator_pm_ops,
#endif
},
.probe = ricoh61x_regulator_probe,
.remove = __devexit_p(ricoh61x_regulator_remove),
};
static int __init ricoh61x_regulator_init(void)
{
printk(KERN_INFO "PMU: %s\n", __func__);
return platform_driver_register(&ricoh61x_regulator_driver);
}
subsys_initcall(ricoh61x_regulator_init);
static void __exit ricoh61x_regulator_exit(void)
{
platform_driver_unregister(&ricoh61x_regulator_driver);
}
module_exit(ricoh61x_regulator_exit);
MODULE_DESCRIPTION("RICOH R5T619 regulator driver");
MODULE_ALIAS("platform:ricoh619-regulator");
MODULE_LICENSE("GPL");