mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-26 22:24:09 +08:00
a0a7014cd4
This encodes the voltage shifts (the number of bits to shift the register to get to the selector) into the regulator info. We have several new variants of the AB8500 so this varies a lot. Signed-off-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
868 lines
21 KiB
C
868 lines
21 KiB
C
/*
|
|
* Copyright (C) ST-Ericsson SA 2010
|
|
*
|
|
* License Terms: GNU General Public License v2
|
|
*
|
|
* Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
|
|
* Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
|
|
*
|
|
* AB8500 peripheral regulators
|
|
*
|
|
* AB8500 supports the following regulators:
|
|
* VAUX1/2/3, VINTCORE, VTVOUT, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA
|
|
*/
|
|
#include <linux/init.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/err.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/mfd/abx500.h>
|
|
#include <linux/mfd/abx500/ab8500.h>
|
|
#include <linux/of.h>
|
|
#include <linux/regulator/of_regulator.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/regulator/ab8500.h>
|
|
#include <linux/slab.h>
|
|
|
|
/**
|
|
* struct ab8500_regulator_info - ab8500 regulator information
|
|
* @dev: device pointer
|
|
* @desc: regulator description
|
|
* @regulator_dev: regulator device
|
|
* @update_bank: bank to control on/off
|
|
* @update_reg: register to control on/off
|
|
* @update_mask: mask to enable/disable regulator
|
|
* @update_val_enable: bits to enable the regulator in normal (high power) mode
|
|
* @voltage_bank: bank to control regulator voltage
|
|
* @voltage_reg: register to control regulator voltage
|
|
* @voltage_mask: mask to control regulator voltage
|
|
* @voltage_shift: shift to control regulator voltage
|
|
* @delay: startup/set voltage delay in us
|
|
*/
|
|
struct ab8500_regulator_info {
|
|
struct device *dev;
|
|
struct regulator_desc desc;
|
|
struct regulator_dev *regulator;
|
|
u8 update_bank;
|
|
u8 update_reg;
|
|
u8 update_mask;
|
|
u8 update_val_enable;
|
|
u8 voltage_bank;
|
|
u8 voltage_reg;
|
|
u8 voltage_mask;
|
|
u8 voltage_shift;
|
|
unsigned int delay;
|
|
};
|
|
|
|
/* voltage tables for the vauxn/vintcore supplies */
|
|
static const unsigned int ldo_vauxn_voltages[] = {
|
|
1100000,
|
|
1200000,
|
|
1300000,
|
|
1400000,
|
|
1500000,
|
|
1800000,
|
|
1850000,
|
|
1900000,
|
|
2500000,
|
|
2650000,
|
|
2700000,
|
|
2750000,
|
|
2800000,
|
|
2900000,
|
|
3000000,
|
|
3300000,
|
|
};
|
|
|
|
static const unsigned int ldo_vaux3_voltages[] = {
|
|
1200000,
|
|
1500000,
|
|
1800000,
|
|
2100000,
|
|
2500000,
|
|
2750000,
|
|
2790000,
|
|
2910000,
|
|
};
|
|
|
|
static const unsigned int ldo_vintcore_voltages[] = {
|
|
1200000,
|
|
1225000,
|
|
1250000,
|
|
1275000,
|
|
1300000,
|
|
1325000,
|
|
1350000,
|
|
};
|
|
|
|
static int ab8500_regulator_enable(struct regulator_dev *rdev)
|
|
{
|
|
int ret;
|
|
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
|
|
|
if (info == NULL) {
|
|
dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = abx500_mask_and_set_register_interruptible(info->dev,
|
|
info->update_bank, info->update_reg,
|
|
info->update_mask, info->update_val_enable);
|
|
if (ret < 0)
|
|
dev_err(rdev_get_dev(rdev),
|
|
"couldn't set enable bits for regulator\n");
|
|
|
|
dev_vdbg(rdev_get_dev(rdev),
|
|
"%s-enable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
|
|
info->desc.name, info->update_bank, info->update_reg,
|
|
info->update_mask, info->update_val_enable);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ab8500_regulator_disable(struct regulator_dev *rdev)
|
|
{
|
|
int ret;
|
|
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
|
|
|
if (info == NULL) {
|
|
dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = abx500_mask_and_set_register_interruptible(info->dev,
|
|
info->update_bank, info->update_reg,
|
|
info->update_mask, 0x0);
|
|
if (ret < 0)
|
|
dev_err(rdev_get_dev(rdev),
|
|
"couldn't set disable bits for regulator\n");
|
|
|
|
dev_vdbg(rdev_get_dev(rdev),
|
|
"%s-disable (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n",
|
|
info->desc.name, info->update_bank, info->update_reg,
|
|
info->update_mask, 0x0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
|
|
{
|
|
int ret;
|
|
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
|
u8 regval;
|
|
|
|
if (info == NULL) {
|
|
dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = abx500_get_register_interruptible(info->dev,
|
|
info->update_bank, info->update_reg, ®val);
|
|
if (ret < 0) {
|
|
dev_err(rdev_get_dev(rdev),
|
|
"couldn't read 0x%x register\n", info->update_reg);
|
|
return ret;
|
|
}
|
|
|
|
dev_vdbg(rdev_get_dev(rdev),
|
|
"%s-is_enabled (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
|
|
" 0x%x\n",
|
|
info->desc.name, info->update_bank, info->update_reg,
|
|
info->update_mask, regval);
|
|
|
|
if (regval & info->update_mask)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev)
|
|
{
|
|
int ret, val;
|
|
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
|
u8 regval;
|
|
|
|
if (info == NULL) {
|
|
dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = abx500_get_register_interruptible(info->dev,
|
|
info->voltage_bank, info->voltage_reg, ®val);
|
|
if (ret < 0) {
|
|
dev_err(rdev_get_dev(rdev),
|
|
"couldn't read voltage reg for regulator\n");
|
|
return ret;
|
|
}
|
|
|
|
dev_vdbg(rdev_get_dev(rdev),
|
|
"%s-get_voltage (bank, reg, mask, shift, value): "
|
|
"0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
|
|
info->desc.name, info->voltage_bank,
|
|
info->voltage_reg, info->voltage_mask,
|
|
info->voltage_shift, regval);
|
|
|
|
val = regval & info->voltage_mask;
|
|
return val >> info->voltage_shift;
|
|
}
|
|
|
|
static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
|
|
unsigned selector)
|
|
{
|
|
int ret;
|
|
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
|
u8 regval;
|
|
|
|
if (info == NULL) {
|
|
dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set the registers for the request */
|
|
regval = (u8)selector << info->voltage_shift;
|
|
ret = abx500_mask_and_set_register_interruptible(info->dev,
|
|
info->voltage_bank, info->voltage_reg,
|
|
info->voltage_mask, regval);
|
|
if (ret < 0)
|
|
dev_err(rdev_get_dev(rdev),
|
|
"couldn't set voltage reg for regulator\n");
|
|
|
|
dev_vdbg(rdev_get_dev(rdev),
|
|
"%s-set_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
|
|
" 0x%x\n",
|
|
info->desc.name, info->voltage_bank, info->voltage_reg,
|
|
info->voltage_mask, regval);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ab8500_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
|
|
unsigned int old_sel,
|
|
unsigned int new_sel)
|
|
{
|
|
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
|
|
|
|
return info->delay;
|
|
}
|
|
|
|
static struct regulator_ops ab8500_regulator_ops = {
|
|
.enable = ab8500_regulator_enable,
|
|
.disable = ab8500_regulator_disable,
|
|
.is_enabled = ab8500_regulator_is_enabled,
|
|
.get_voltage_sel = ab8500_regulator_get_voltage_sel,
|
|
.set_voltage_sel = ab8500_regulator_set_voltage_sel,
|
|
.list_voltage = regulator_list_voltage_table,
|
|
.set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
|
|
};
|
|
|
|
static struct regulator_ops ab8500_regulator_fixed_ops = {
|
|
.enable = ab8500_regulator_enable,
|
|
.disable = ab8500_regulator_disable,
|
|
.is_enabled = ab8500_regulator_is_enabled,
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
};
|
|
|
|
static struct ab8500_regulator_info
|
|
ab8500_regulator_info[AB8500_NUM_REGULATORS] = {
|
|
/*
|
|
* Variable Voltage Regulators
|
|
* name, min mV, max mV,
|
|
* update bank, reg, mask, enable val
|
|
* volt bank, reg, mask
|
|
*/
|
|
[AB8500_LDO_AUX1] = {
|
|
.desc = {
|
|
.name = "LDO-AUX1",
|
|
.ops = &ab8500_regulator_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_AUX1,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
|
|
.volt_table = ldo_vauxn_voltages,
|
|
},
|
|
.update_bank = 0x04,
|
|
.update_reg = 0x09,
|
|
.update_mask = 0x03,
|
|
.update_val_enable = 0x01,
|
|
.voltage_bank = 0x04,
|
|
.voltage_reg = 0x1f,
|
|
.voltage_mask = 0x0f,
|
|
},
|
|
[AB8500_LDO_AUX2] = {
|
|
.desc = {
|
|
.name = "LDO-AUX2",
|
|
.ops = &ab8500_regulator_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_AUX2,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = ARRAY_SIZE(ldo_vauxn_voltages),
|
|
.volt_table = ldo_vauxn_voltages,
|
|
},
|
|
.update_bank = 0x04,
|
|
.update_reg = 0x09,
|
|
.update_mask = 0x0c,
|
|
.update_val_enable = 0x04,
|
|
.voltage_bank = 0x04,
|
|
.voltage_reg = 0x20,
|
|
.voltage_mask = 0x0f,
|
|
},
|
|
[AB8500_LDO_AUX3] = {
|
|
.desc = {
|
|
.name = "LDO-AUX3",
|
|
.ops = &ab8500_regulator_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_AUX3,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = ARRAY_SIZE(ldo_vaux3_voltages),
|
|
.volt_table = ldo_vaux3_voltages,
|
|
},
|
|
.update_bank = 0x04,
|
|
.update_reg = 0x0a,
|
|
.update_mask = 0x03,
|
|
.update_val_enable = 0x01,
|
|
.voltage_bank = 0x04,
|
|
.voltage_reg = 0x21,
|
|
.voltage_mask = 0x07,
|
|
},
|
|
[AB8500_LDO_INTCORE] = {
|
|
.desc = {
|
|
.name = "LDO-INTCORE",
|
|
.ops = &ab8500_regulator_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_INTCORE,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = ARRAY_SIZE(ldo_vintcore_voltages),
|
|
.volt_table = ldo_vintcore_voltages,
|
|
},
|
|
.update_bank = 0x03,
|
|
.update_reg = 0x80,
|
|
.update_mask = 0x44,
|
|
.update_val_enable = 0x04,
|
|
.voltage_bank = 0x03,
|
|
.voltage_reg = 0x80,
|
|
.voltage_mask = 0x38,
|
|
.voltage_shift = 3,
|
|
},
|
|
|
|
/*
|
|
* Fixed Voltage Regulators
|
|
* name, fixed mV,
|
|
* update bank, reg, mask, enable val
|
|
*/
|
|
[AB8500_LDO_TVOUT] = {
|
|
.desc = {
|
|
.name = "LDO-TVOUT",
|
|
.ops = &ab8500_regulator_fixed_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_TVOUT,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = 1,
|
|
.min_uV = 2000000,
|
|
.enable_time = 10000,
|
|
},
|
|
.delay = 10000,
|
|
.update_bank = 0x03,
|
|
.update_reg = 0x80,
|
|
.update_mask = 0x82,
|
|
.update_val_enable = 0x02,
|
|
},
|
|
[AB8500_LDO_USB] = {
|
|
.desc = {
|
|
.name = "LDO-USB",
|
|
.ops = &ab8500_regulator_fixed_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_USB,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = 1,
|
|
.min_uV = 3300000,
|
|
},
|
|
.update_bank = 0x03,
|
|
.update_reg = 0x82,
|
|
.update_mask = 0x03,
|
|
.update_val_enable = 0x01,
|
|
},
|
|
[AB8500_LDO_AUDIO] = {
|
|
.desc = {
|
|
.name = "LDO-AUDIO",
|
|
.ops = &ab8500_regulator_fixed_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_AUDIO,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = 1,
|
|
.min_uV = 2000000,
|
|
},
|
|
.update_bank = 0x03,
|
|
.update_reg = 0x83,
|
|
.update_mask = 0x02,
|
|
.update_val_enable = 0x02,
|
|
},
|
|
[AB8500_LDO_ANAMIC1] = {
|
|
.desc = {
|
|
.name = "LDO-ANAMIC1",
|
|
.ops = &ab8500_regulator_fixed_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_ANAMIC1,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = 1,
|
|
.min_uV = 2050000,
|
|
},
|
|
.update_bank = 0x03,
|
|
.update_reg = 0x83,
|
|
.update_mask = 0x08,
|
|
.update_val_enable = 0x08,
|
|
},
|
|
[AB8500_LDO_ANAMIC2] = {
|
|
.desc = {
|
|
.name = "LDO-ANAMIC2",
|
|
.ops = &ab8500_regulator_fixed_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_ANAMIC2,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = 1,
|
|
.min_uV = 2050000,
|
|
},
|
|
.update_bank = 0x03,
|
|
.update_reg = 0x83,
|
|
.update_mask = 0x10,
|
|
.update_val_enable = 0x10,
|
|
},
|
|
[AB8500_LDO_DMIC] = {
|
|
.desc = {
|
|
.name = "LDO-DMIC",
|
|
.ops = &ab8500_regulator_fixed_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_DMIC,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = 1,
|
|
.min_uV = 1800000,
|
|
},
|
|
.update_bank = 0x03,
|
|
.update_reg = 0x83,
|
|
.update_mask = 0x04,
|
|
.update_val_enable = 0x04,
|
|
},
|
|
[AB8500_LDO_ANA] = {
|
|
.desc = {
|
|
.name = "LDO-ANA",
|
|
.ops = &ab8500_regulator_fixed_ops,
|
|
.type = REGULATOR_VOLTAGE,
|
|
.id = AB8500_LDO_ANA,
|
|
.owner = THIS_MODULE,
|
|
.n_voltages = 1,
|
|
.min_uV = 1200000,
|
|
},
|
|
.update_bank = 0x04,
|
|
.update_reg = 0x06,
|
|
.update_mask = 0x0c,
|
|
.update_val_enable = 0x04,
|
|
},
|
|
|
|
|
|
};
|
|
|
|
struct ab8500_reg_init {
|
|
u8 bank;
|
|
u8 addr;
|
|
u8 mask;
|
|
};
|
|
|
|
#define REG_INIT(_id, _bank, _addr, _mask) \
|
|
[_id] = { \
|
|
.bank = _bank, \
|
|
.addr = _addr, \
|
|
.mask = _mask, \
|
|
}
|
|
|
|
static struct ab8500_reg_init ab8500_reg_init[] = {
|
|
/*
|
|
* 0x30, VanaRequestCtrl
|
|
* 0x0C, VpllRequestCtrl
|
|
* 0xc0, VextSupply1RequestCtrl
|
|
*/
|
|
REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xfc),
|
|
/*
|
|
* 0x03, VextSupply2RequestCtrl
|
|
* 0x0c, VextSupply3RequestCtrl
|
|
* 0x30, Vaux1RequestCtrl
|
|
* 0xc0, Vaux2RequestCtrl
|
|
*/
|
|
REG_INIT(AB8500_REGUREQUESTCTRL3, 0x03, 0x05, 0xff),
|
|
/*
|
|
* 0x03, Vaux3RequestCtrl
|
|
* 0x04, SwHPReq
|
|
*/
|
|
REG_INIT(AB8500_REGUREQUESTCTRL4, 0x03, 0x06, 0x07),
|
|
/*
|
|
* 0x08, VanaSysClkReq1HPValid
|
|
* 0x20, Vaux1SysClkReq1HPValid
|
|
* 0x40, Vaux2SysClkReq1HPValid
|
|
* 0x80, Vaux3SysClkReq1HPValid
|
|
*/
|
|
REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xe8),
|
|
/*
|
|
* 0x10, VextSupply1SysClkReq1HPValid
|
|
* 0x20, VextSupply2SysClkReq1HPValid
|
|
* 0x40, VextSupply3SysClkReq1HPValid
|
|
*/
|
|
REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x70),
|
|
/*
|
|
* 0x08, VanaHwHPReq1Valid
|
|
* 0x20, Vaux1HwHPReq1Valid
|
|
* 0x40, Vaux2HwHPReq1Valid
|
|
* 0x80, Vaux3HwHPReq1Valid
|
|
*/
|
|
REG_INIT(AB8500_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xe8),
|
|
/*
|
|
* 0x01, VextSupply1HwHPReq1Valid
|
|
* 0x02, VextSupply2HwHPReq1Valid
|
|
* 0x04, VextSupply3HwHPReq1Valid
|
|
*/
|
|
REG_INIT(AB8500_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x07),
|
|
/*
|
|
* 0x08, VanaHwHPReq2Valid
|
|
* 0x20, Vaux1HwHPReq2Valid
|
|
* 0x40, Vaux2HwHPReq2Valid
|
|
* 0x80, Vaux3HwHPReq2Valid
|
|
*/
|
|
REG_INIT(AB8500_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xe8),
|
|
/*
|
|
* 0x01, VextSupply1HwHPReq2Valid
|
|
* 0x02, VextSupply2HwHPReq2Valid
|
|
* 0x04, VextSupply3HwHPReq2Valid
|
|
*/
|
|
REG_INIT(AB8500_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x07),
|
|
/*
|
|
* 0x20, VanaSwHPReqValid
|
|
* 0x80, Vaux1SwHPReqValid
|
|
*/
|
|
REG_INIT(AB8500_REGUSWHPREQVALID1, 0x03, 0x0d, 0xa0),
|
|
/*
|
|
* 0x01, Vaux2SwHPReqValid
|
|
* 0x02, Vaux3SwHPReqValid
|
|
* 0x04, VextSupply1SwHPReqValid
|
|
* 0x08, VextSupply2SwHPReqValid
|
|
* 0x10, VextSupply3SwHPReqValid
|
|
*/
|
|
REG_INIT(AB8500_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f),
|
|
/*
|
|
* 0x02, SysClkReq2Valid1
|
|
* ...
|
|
* 0x80, SysClkReq8Valid1
|
|
*/
|
|
REG_INIT(AB8500_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe),
|
|
/*
|
|
* 0x02, SysClkReq2Valid2
|
|
* ...
|
|
* 0x80, SysClkReq8Valid2
|
|
*/
|
|
REG_INIT(AB8500_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe),
|
|
/*
|
|
* 0x02, VTVoutEna
|
|
* 0x04, Vintcore12Ena
|
|
* 0x38, Vintcore12Sel
|
|
* 0x40, Vintcore12LP
|
|
* 0x80, VTVoutLP
|
|
*/
|
|
REG_INIT(AB8500_REGUMISC1, 0x03, 0x80, 0xfe),
|
|
/*
|
|
* 0x02, VaudioEna
|
|
* 0x04, VdmicEna
|
|
* 0x08, Vamic1Ena
|
|
* 0x10, Vamic2Ena
|
|
*/
|
|
REG_INIT(AB8500_VAUDIOSUPPLY, 0x03, 0x83, 0x1e),
|
|
/*
|
|
* 0x01, Vamic1_dzout
|
|
* 0x02, Vamic2_dzout
|
|
*/
|
|
REG_INIT(AB8500_REGUCTRL1VAMIC, 0x03, 0x84, 0x03),
|
|
/*
|
|
* 0x0c, VanaRegu
|
|
* 0x03, VpllRegu
|
|
*/
|
|
REG_INIT(AB8500_VPLLVANAREGU, 0x04, 0x06, 0x0f),
|
|
/*
|
|
* 0x01, VrefDDREna
|
|
* 0x02, VrefDDRSleepMode
|
|
*/
|
|
REG_INIT(AB8500_VREFDDR, 0x04, 0x07, 0x03),
|
|
/*
|
|
* 0x03, VextSupply1Regu
|
|
* 0x0c, VextSupply2Regu
|
|
* 0x30, VextSupply3Regu
|
|
* 0x40, ExtSupply2Bypass
|
|
* 0x80, ExtSupply3Bypass
|
|
*/
|
|
REG_INIT(AB8500_EXTSUPPLYREGU, 0x04, 0x08, 0xff),
|
|
/*
|
|
* 0x03, Vaux1Regu
|
|
* 0x0c, Vaux2Regu
|
|
*/
|
|
REG_INIT(AB8500_VAUX12REGU, 0x04, 0x09, 0x0f),
|
|
/*
|
|
* 0x03, Vaux3Regu
|
|
*/
|
|
REG_INIT(AB8500_VRF1VAUX3REGU, 0x04, 0x0a, 0x03),
|
|
/*
|
|
* 0x3f, Vsmps1Sel1
|
|
*/
|
|
REG_INIT(AB8500_VSMPS1SEL1, 0x04, 0x13, 0x3f),
|
|
/*
|
|
* 0x0f, Vaux1Sel
|
|
*/
|
|
REG_INIT(AB8500_VAUX1SEL, 0x04, 0x1f, 0x0f),
|
|
/*
|
|
* 0x0f, Vaux2Sel
|
|
*/
|
|
REG_INIT(AB8500_VAUX2SEL, 0x04, 0x20, 0x0f),
|
|
/*
|
|
* 0x07, Vaux3Sel
|
|
*/
|
|
REG_INIT(AB8500_VRF1VAUX3SEL, 0x04, 0x21, 0x07),
|
|
/*
|
|
* 0x01, VextSupply12LP
|
|
*/
|
|
REG_INIT(AB8500_REGUCTRL2SPARE, 0x04, 0x22, 0x01),
|
|
/*
|
|
* 0x04, Vaux1Disch
|
|
* 0x08, Vaux2Disch
|
|
* 0x10, Vaux3Disch
|
|
* 0x20, Vintcore12Disch
|
|
* 0x40, VTVoutDisch
|
|
* 0x80, VaudioDisch
|
|
*/
|
|
REG_INIT(AB8500_REGUCTRLDISCH, 0x04, 0x43, 0xfc),
|
|
/*
|
|
* 0x02, VanaDisch
|
|
* 0x04, VdmicPullDownEna
|
|
* 0x10, VdmicDisch
|
|
*/
|
|
REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16),
|
|
};
|
|
|
|
static __devinit int
|
|
ab8500_regulator_init_registers(struct platform_device *pdev, int id, int value)
|
|
{
|
|
int err;
|
|
|
|
if (value & ~ab8500_reg_init[id].mask) {
|
|
dev_err(&pdev->dev,
|
|
"Configuration error: value outside mask.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = abx500_mask_and_set_register_interruptible(
|
|
&pdev->dev,
|
|
ab8500_reg_init[id].bank,
|
|
ab8500_reg_init[id].addr,
|
|
ab8500_reg_init[id].mask,
|
|
value);
|
|
if (err < 0) {
|
|
dev_err(&pdev->dev,
|
|
"Failed to initialize 0x%02x, 0x%02x.\n",
|
|
ab8500_reg_init[id].bank,
|
|
ab8500_reg_init[id].addr);
|
|
return err;
|
|
}
|
|
|
|
dev_vdbg(&pdev->dev,
|
|
"init: 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
|
|
ab8500_reg_init[id].bank,
|
|
ab8500_reg_init[id].addr,
|
|
ab8500_reg_init[id].mask,
|
|
value);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __devinit int ab8500_regulator_register(struct platform_device *pdev,
|
|
struct regulator_init_data *init_data,
|
|
int id,
|
|
struct device_node *np)
|
|
{
|
|
struct ab8500_regulator_info *info = NULL;
|
|
struct regulator_config config = { };
|
|
int err;
|
|
|
|
/* assign per-regulator data */
|
|
info = &ab8500_regulator_info[id];
|
|
info->dev = &pdev->dev;
|
|
|
|
config.dev = &pdev->dev;
|
|
config.init_data = init_data;
|
|
config.driver_data = info;
|
|
config.of_node = np;
|
|
|
|
/* fix for hardware before ab8500v2.0 */
|
|
if (abx500_get_chip_id(info->dev) < 0x20) {
|
|
if (info->desc.id == AB8500_LDO_AUX3) {
|
|
info->desc.n_voltages =
|
|
ARRAY_SIZE(ldo_vauxn_voltages);
|
|
info->desc.volt_table = ldo_vauxn_voltages;
|
|
info->voltage_mask = 0xf;
|
|
}
|
|
}
|
|
|
|
/* register regulator with framework */
|
|
info->regulator = regulator_register(&info->desc, &config);
|
|
if (IS_ERR(info->regulator)) {
|
|
err = PTR_ERR(info->regulator);
|
|
dev_err(&pdev->dev, "failed to register regulator %s\n",
|
|
info->desc.name);
|
|
/* when we fail, un-register all earlier regulators */
|
|
while (--id >= 0) {
|
|
info = &ab8500_regulator_info[id];
|
|
regulator_unregister(info->regulator);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct of_regulator_match ab8500_regulator_matches[] = {
|
|
{ .name = "ab8500_ldo_aux1", .driver_data = (void *) AB8500_LDO_AUX1, },
|
|
{ .name = "ab8500_ldo_aux2", .driver_data = (void *) AB8500_LDO_AUX2, },
|
|
{ .name = "ab8500_ldo_aux3", .driver_data = (void *) AB8500_LDO_AUX3, },
|
|
{ .name = "ab8500_ldo_intcore", .driver_data = (void *) AB8500_LDO_INTCORE, },
|
|
{ .name = "ab8500_ldo_tvout", .driver_data = (void *) AB8500_LDO_TVOUT, },
|
|
{ .name = "ab8500_ldo_usb", .driver_data = (void *) AB8500_LDO_USB, },
|
|
{ .name = "ab8500_ldo_audio", .driver_data = (void *) AB8500_LDO_AUDIO, },
|
|
{ .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB8500_LDO_ANAMIC1, },
|
|
{ .name = "ab8500_ldo_amamic2", .driver_data = (void *) AB8500_LDO_ANAMIC2, },
|
|
{ .name = "ab8500_ldo_dmic", .driver_data = (void *) AB8500_LDO_DMIC, },
|
|
{ .name = "ab8500_ldo_ana", .driver_data = (void *) AB8500_LDO_ANA, },
|
|
};
|
|
|
|
static __devinit int
|
|
ab8500_regulator_of_probe(struct platform_device *pdev, struct device_node *np)
|
|
{
|
|
int err, i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
|
|
err = ab8500_regulator_register(
|
|
pdev, ab8500_regulator_matches[i].init_data,
|
|
i, ab8500_regulator_matches[i].of_node);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
|
|
{
|
|
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
|
|
struct ab8500_platform_data *pdata;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
int i, err;
|
|
|
|
if (np) {
|
|
err = of_regulator_match(&pdev->dev, np,
|
|
ab8500_regulator_matches,
|
|
ARRAY_SIZE(ab8500_regulator_matches));
|
|
if (err < 0) {
|
|
dev_err(&pdev->dev,
|
|
"Error parsing regulator init data: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
err = ab8500_regulator_of_probe(pdev, np);
|
|
return err;
|
|
}
|
|
|
|
if (!ab8500) {
|
|
dev_err(&pdev->dev, "null mfd parent\n");
|
|
return -EINVAL;
|
|
}
|
|
pdata = dev_get_platdata(ab8500->dev);
|
|
if (!pdata) {
|
|
dev_err(&pdev->dev, "null pdata\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* make sure the platform data has the correct size */
|
|
if (pdata->num_regulator != ARRAY_SIZE(ab8500_regulator_info)) {
|
|
dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* initialize registers */
|
|
for (i = 0; i < pdata->num_regulator_reg_init; i++) {
|
|
int id, value;
|
|
|
|
id = pdata->regulator_reg_init[i].id;
|
|
value = pdata->regulator_reg_init[i].value;
|
|
|
|
/* check for configuration errors */
|
|
if (id >= AB8500_NUM_REGULATOR_REGISTERS) {
|
|
dev_err(&pdev->dev,
|
|
"Configuration error: id outside range.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = ab8500_regulator_init_registers(pdev, id, value);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
/* register all regulators */
|
|
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
|
|
err = ab8500_regulator_register(pdev, &pdata->regulator[i], i, NULL);
|
|
if (err < 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __devexit int ab8500_regulator_remove(struct platform_device *pdev)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
|
|
struct ab8500_regulator_info *info = NULL;
|
|
info = &ab8500_regulator_info[i];
|
|
|
|
dev_vdbg(rdev_get_dev(info->regulator),
|
|
"%s-remove\n", info->desc.name);
|
|
|
|
regulator_unregister(info->regulator);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver ab8500_regulator_driver = {
|
|
.probe = ab8500_regulator_probe,
|
|
.remove = __devexit_p(ab8500_regulator_remove),
|
|
.driver = {
|
|
.name = "ab8500-regulator",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
};
|
|
|
|
static int __init ab8500_regulator_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = platform_driver_register(&ab8500_regulator_driver);
|
|
if (ret != 0)
|
|
pr_err("Failed to register ab8500 regulator: %d\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
subsys_initcall(ab8500_regulator_init);
|
|
|
|
static void __exit ab8500_regulator_exit(void)
|
|
{
|
|
platform_driver_unregister(&ab8500_regulator_driver);
|
|
}
|
|
module_exit(ab8500_regulator_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
|
|
MODULE_DESCRIPTION("Regulator Driver for ST-Ericsson AB8500 Mixed-Sig PMIC");
|
|
MODULE_ALIAS("platform:ab8500-regulator");
|