Commit 18bcd0c8 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6:
  regulator: Add MODULE_DEVICE_TABLE to max8997 and max8998
  regulator: fix tps6524x section mismatch
  regulator: Remove more wm831x-specific IRQ operations
  regulator: add ab8500 enable and raise time delays
  regulator: provide consumer interface for fall/rise time
  regulator: add set_voltage_time_sel infrastructure
  regulator: initialization for ab8500 regulators
  regulator: add support for USB voltage regulator
  regulator: switch the ab3100 to use enable_time()
  Regulator: add suspend-finish API for regulator core.
  regulator: fix typo in Kconfig
  regulator: Convert WM831x regulators to genirq
  regulator: If we fail when setting up a supply say which supply
parents 7b724a22 a51b907b
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
#include <linux/regulator/ab8500.h> #include <linux/regulator/ab8500.h>
extern struct ab8500_regulator_reg_init
ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS];
extern struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS]; extern struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS];
#endif #endif
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/amba/serial.h> #include <linux/amba/serial.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/mfd/ab8500.h> #include <linux/mfd/ab8500.h>
#include <linux/regulator/ab8500.h>
#include <linux/mfd/tc3589x.h> #include <linux/mfd/tc3589x.h>
#include <linux/leds-lp5521.h> #include <linux/leds-lp5521.h>
#include <linux/input.h> #include <linux/input.h>
......
...@@ -126,7 +126,7 @@ config REGULATOR_MAX8998 ...@@ -126,7 +126,7 @@ config REGULATOR_MAX8998
and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages. and S5PC1XX chips to control VCC_CORE and VCC_USIM voltages.
config REGULATOR_TWL4030 config REGULATOR_TWL4030
bool "TI TWL4030/TWL5030/TWL6030/TPS695x0 PMIC" bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC"
depends on TWL4030_CORE depends on TWL4030_CORE
help help
This driver supports the voltage regulators provided by This driver supports the voltage regulators provided by
......
...@@ -206,29 +206,6 @@ static int ab3100_enable_regulator(struct regulator_dev *reg) ...@@ -206,29 +206,6 @@ static int ab3100_enable_regulator(struct regulator_dev *reg)
return err; return err;
} }
/* Per-regulator power on delay from spec */
switch (abreg->regreg) {
case AB3100_LDO_A: /* Fallthrough */
case AB3100_LDO_C: /* Fallthrough */
case AB3100_LDO_D: /* Fallthrough */
case AB3100_LDO_E: /* Fallthrough */
case AB3100_LDO_H: /* Fallthrough */
case AB3100_LDO_K:
udelay(200);
break;
case AB3100_LDO_F:
udelay(600);
break;
case AB3100_LDO_G:
udelay(400);
break;
case AB3100_BUCK:
mdelay(1);
break;
default:
break;
}
return 0; return 0;
} }
...@@ -450,11 +427,37 @@ static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg) ...@@ -450,11 +427,37 @@ static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg)
return abreg->plfdata->external_voltage; return abreg->plfdata->external_voltage;
} }
static int ab3100_enable_time_regulator(struct regulator_dev *reg)
{
struct ab3100_regulator *abreg = reg->reg_data;
/* Per-regulator power on delay from spec */
switch (abreg->regreg) {
case AB3100_LDO_A: /* Fallthrough */
case AB3100_LDO_C: /* Fallthrough */
case AB3100_LDO_D: /* Fallthrough */
case AB3100_LDO_E: /* Fallthrough */
case AB3100_LDO_H: /* Fallthrough */
case AB3100_LDO_K:
return 200;
case AB3100_LDO_F:
return 600;
case AB3100_LDO_G:
return 400;
case AB3100_BUCK:
return 1000;
default:
break;
}
return 0;
}
static struct regulator_ops regulator_ops_fixed = { static struct regulator_ops regulator_ops_fixed = {
.enable = ab3100_enable_regulator, .enable = ab3100_enable_regulator,
.disable = ab3100_disable_regulator, .disable = ab3100_disable_regulator,
.is_enabled = ab3100_is_enabled_regulator, .is_enabled = ab3100_is_enabled_regulator,
.get_voltage = ab3100_get_voltage_regulator, .get_voltage = ab3100_get_voltage_regulator,
.enable_time = ab3100_enable_time_regulator,
}; };
static struct regulator_ops regulator_ops_variable = { static struct regulator_ops regulator_ops_variable = {
...@@ -464,6 +467,7 @@ static struct regulator_ops regulator_ops_variable = { ...@@ -464,6 +467,7 @@ static struct regulator_ops regulator_ops_variable = {
.get_voltage = ab3100_get_voltage_regulator, .get_voltage = ab3100_get_voltage_regulator,
.set_voltage = ab3100_set_voltage_regulator, .set_voltage = ab3100_set_voltage_regulator,
.list_voltage = ab3100_list_voltage_regulator, .list_voltage = ab3100_list_voltage_regulator,
.enable_time = ab3100_enable_time_regulator,
}; };
static struct regulator_ops regulator_ops_variable_sleepable = { static struct regulator_ops regulator_ops_variable_sleepable = {
...@@ -474,6 +478,7 @@ static struct regulator_ops regulator_ops_variable_sleepable = { ...@@ -474,6 +478,7 @@ static struct regulator_ops regulator_ops_variable_sleepable = {
.set_voltage = ab3100_set_voltage_regulator, .set_voltage = ab3100_set_voltage_regulator,
.set_suspend_voltage = ab3100_set_suspend_voltage_regulator, .set_suspend_voltage = ab3100_set_suspend_voltage_regulator,
.list_voltage = ab3100_list_voltage_regulator, .list_voltage = ab3100_list_voltage_regulator,
.enable_time = ab3100_enable_time_regulator,
}; };
/* /*
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* AB8500 peripheral regulators * AB8500 peripheral regulators
* *
* AB8500 supports the following regulators: * AB8500 supports the following regulators:
* VAUX1/2/3, VINTCORE, VTVOUT, VAUDIO, VAMIC1/2, VDMIC, VANA * VAUX1/2/3, VINTCORE, VTVOUT, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
* @voltage_mask: mask to control regulator voltage * @voltage_mask: mask to control regulator voltage
* @voltages: supported voltage table * @voltages: supported voltage table
* @voltages_len: number of supported voltages for the regulator * @voltages_len: number of supported voltages for the regulator
* @delay: startup/set voltage delay in us
*/ */
struct ab8500_regulator_info { struct ab8500_regulator_info {
struct device *dev; struct device *dev;
...@@ -55,6 +56,7 @@ struct ab8500_regulator_info { ...@@ -55,6 +56,7 @@ struct ab8500_regulator_info {
u8 voltage_mask; u8 voltage_mask;
int const *voltages; int const *voltages;
int voltages_len; int voltages_len;
unsigned int delay;
}; };
/* voltage tables for the vauxn/vintcore supplies */ /* voltage tables for the vauxn/vintcore supplies */
...@@ -290,6 +292,29 @@ static int ab8500_regulator_set_voltage(struct regulator_dev *rdev, ...@@ -290,6 +292,29 @@ static int ab8500_regulator_set_voltage(struct regulator_dev *rdev,
return ret; return ret;
} }
static int ab8500_regulator_enable_time(struct regulator_dev *rdev)
{
struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
return info->delay;
}
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);
int ret;
/* If the regulator isn't on, it won't take time here */
ret = ab8500_regulator_is_enabled(rdev);
if (ret < 0)
return ret;
if (!ret)
return 0;
return info->delay;
}
static struct regulator_ops ab8500_regulator_ops = { static struct regulator_ops ab8500_regulator_ops = {
.enable = ab8500_regulator_enable, .enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable, .disable = ab8500_regulator_disable,
...@@ -297,6 +322,8 @@ static struct regulator_ops ab8500_regulator_ops = { ...@@ -297,6 +322,8 @@ static struct regulator_ops ab8500_regulator_ops = {
.get_voltage = ab8500_regulator_get_voltage, .get_voltage = ab8500_regulator_get_voltage,
.set_voltage = ab8500_regulator_set_voltage, .set_voltage = ab8500_regulator_set_voltage,
.list_voltage = ab8500_list_voltage, .list_voltage = ab8500_list_voltage,
.enable_time = ab8500_regulator_enable_time,
.set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
}; };
static int ab8500_fixed_get_voltage(struct regulator_dev *rdev) static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
...@@ -317,6 +344,8 @@ static struct regulator_ops ab8500_regulator_fixed_ops = { ...@@ -317,6 +344,8 @@ static struct regulator_ops ab8500_regulator_fixed_ops = {
.is_enabled = ab8500_regulator_is_enabled, .is_enabled = ab8500_regulator_is_enabled,
.get_voltage = ab8500_fixed_get_voltage, .get_voltage = ab8500_fixed_get_voltage,
.list_voltage = ab8500_list_voltage, .list_voltage = ab8500_list_voltage,
.enable_time = ab8500_regulator_enable_time,
.set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
}; };
static struct ab8500_regulator_info static struct ab8500_regulator_info
...@@ -426,12 +455,28 @@ static struct ab8500_regulator_info ...@@ -426,12 +455,28 @@ static struct ab8500_regulator_info
.owner = THIS_MODULE, .owner = THIS_MODULE,
.n_voltages = 1, .n_voltages = 1,
}, },
.delay = 10000,
.fixed_uV = 2000000, .fixed_uV = 2000000,
.update_bank = 0x03, .update_bank = 0x03,
.update_reg = 0x80, .update_reg = 0x80,
.update_mask = 0x82, .update_mask = 0x82,
.update_val_enable = 0x02, .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,
},
.fixed_uV = 3300000,
.update_bank = 0x03,
.update_reg = 0x82,
.update_mask = 0x03,
.update_val_enable = 0x01,
},
[AB8500_LDO_AUDIO] = { [AB8500_LDO_AUDIO] = {
.desc = { .desc = {
.name = "LDO-AUDIO", .name = "LDO-AUDIO",
...@@ -511,6 +556,186 @@ static struct ab8500_regulator_info ...@@ -511,6 +556,186 @@ static struct ab8500_regulator_info
}; };
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_probe(struct platform_device *pdev) static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
{ {
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
...@@ -529,10 +754,51 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev) ...@@ -529,10 +754,51 @@ static __devinit int ab8500_regulator_probe(struct platform_device *pdev)
/* make sure the platform data has the correct size */ /* make sure the platform data has the correct size */
if (pdata->num_regulator != ARRAY_SIZE(ab8500_regulator_info)) { if (pdata->num_regulator != ARRAY_SIZE(ab8500_regulator_info)) {
dev_err(&pdev->dev, "platform configuration error\n"); dev_err(&pdev->dev, "Configuration error: size mismatch.\n");
return -EINVAL; return -EINVAL;
} }
/* initialize registers */
for (i = 0; i < pdata->num_regulator_reg_init; i++) {
int id;
u8 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;
}
if (value & ~ab8500_reg_init[id].mask) {
dev_err(&pdev->dev,
"Configuration error: value outside mask.\n");
return -EINVAL;
}
/* initialize register */
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);
}
/* register all regulators */ /* register all regulators */
for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) { for (i = 0; i < ARRAY_SIZE(ab8500_regulator_info); i++) {
struct ab8500_regulator_info *info = NULL; struct ab8500_regulator_info *info = NULL;
......
...@@ -1629,6 +1629,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, ...@@ -1629,6 +1629,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV) int min_uV, int max_uV)
{ {
int ret; int ret;
int delay = 0;
unsigned int selector; unsigned int selector;
trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV); trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV);
...@@ -1662,6 +1663,22 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, ...@@ -1662,6 +1663,22 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
} }
} }
/*
* If we can't obtain the old selector there is not enough
* info to call set_voltage_time_sel().
*/
if (rdev->desc->ops->set_voltage_time_sel &&
rdev->desc->ops->get_voltage_sel) {
unsigned int old_selector = 0;
ret = rdev->desc->ops->get_voltage_sel(rdev);
if (ret < 0)
return ret;
old_selector = ret;
delay = rdev->desc->ops->set_voltage_time_sel(rdev,
old_selector, selector);
}
if (best_val != INT_MAX) { if (best_val != INT_MAX) {
ret = rdev->desc->ops->set_voltage_sel(rdev, selector); ret = rdev->desc->ops->set_voltage_sel(rdev, selector);
selector = best_val; selector = best_val;
...@@ -1672,6 +1689,14 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, ...@@ -1672,6 +1689,14 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
ret = -EINVAL; ret = -EINVAL;
} }
/* Insert any necessary delays */
if (delay >= 1000) {
mdelay(delay / 1000);
udelay(delay % 1000);
} else if (delay) {
udelay(delay);
}
if (ret == 0) if (ret == 0)
_notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
NULL); NULL);
...@@ -1739,6 +1764,51 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) ...@@ -1739,6 +1764,51 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
} }
EXPORT_SYMBOL_GPL(regulator_set_voltage); EXPORT_SYMBOL_GPL(regulator_set_voltage);
/**
* regulator_set_voltage_time - get raise/fall time
* @regulator: regulator source
* @old_uV: starting voltage in microvolts
* @new_uV: target voltage in microvolts
*
* Provided with the starting and ending voltage, this function attempts to
* calculate the time in microseconds required to rise or fall to this new
* voltage.
*/
int regulator_set_voltage_time(struct regulator *regulator,
int old_uV, int new_uV)
{
struct regulator_dev *rdev = regulator->rdev;
struct regulator_ops *ops = rdev->desc->ops;
int old_sel = -1;
int new_sel = -1;
int voltage;
int i;
/* Currently requires operations to do this */
if (!ops->list_voltage || !ops->set_voltage_time_sel
|| !rdev->desc->n_voltages)
return -EINVAL;
for (i = 0; i < rdev->desc->n_voltages; i++) {
/* We only look for exact voltage matches here */
voltage = regulator_list_voltage(regulator, i);
if (voltage < 0)
return -EINVAL;
if (voltage == 0)
continue;
if (voltage == old_uV)
old_sel = i;
if (voltage == new_uV)
new_sel = i;
}
if (old_sel < 0 || new_sel < 0)
return -EINVAL;
return ops->set_voltage_time_sel(rdev, old_sel, new_sel);
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_time);
/** /**
* regulator_sync_voltage - re-apply last regulator output voltage * regulator_sync_voltage - re-apply last regulator output voltage
* @regulator: regulator source * @regulator: regulator source
...@@ -2565,8 +2635,11 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, ...@@ -2565,8 +2635,11 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
init_data->consumer_supplies[i].dev, init_data->consumer_supplies[i].dev,
init_data->consumer_supplies[i].dev_name, init_data->consumer_supplies[i].dev_name,
init_data->consumer_supplies[i].supply); init_data->consumer_supplies[i].supply);
if (ret < 0) if (ret < 0) {
dev_err(dev, "Failed to set supply %s\n",
init_data->consumer_supplies[i].supply);
goto unset_supplies; goto unset_supplies;
}
} }
list_add(&rdev->list, &regulator_list); list_add(&rdev->list, &regulator_list);
...@@ -2652,6 +2725,47 @@ int regulator_suspend_prepare(suspend_state_t state) ...@@ -2652,6 +2725,47 @@ int regulator_suspend_prepare(suspend_state_t state)
} }
EXPORT_SYMBOL_GPL(regulator_suspend_prepare); EXPORT_SYMBOL_GPL(regulator_suspend_prepare);
/**
* regulator_suspend_finish - resume regulators from system wide suspend
*
* Turn on regulators that might be turned off by regulator_suspend_prepare
* and that should be turned on according to the regulators properties.
*/
int regulator_suspend_finish(void)
{
struct regulator_dev *rdev;
int ret = 0, error;
mutex_lock(&regulator_list_mutex);
list_for_each_entry(rdev, &regulator_list, list) {
struct regulator_ops *ops = rdev->desc->ops;
mutex_lock(&rdev->mutex);
if ((rdev->use_count > 0 || rdev->constraints->always_on) &&
ops->enable) {
error = ops->enable(rdev);
if (error)
ret = error;
} else {
if (!has_full_constraints)
goto unlock;
if (!ops->disable)
goto unlock;
if (ops->is_enabled && !ops->is_enabled(rdev))
goto unlock;
error = ops->disable(rdev);
if (error)
ret = error;
}
unlock:
mutex_unlock(&rdev->mutex);
}
mutex_unlock(&regulator_list_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_suspend_finish);
/** /**
* regulator_has_full_constraints - the system has fully specified constraints * regulator_has_full_constraints - the system has fully specified constraints
* *
......
...@@ -1185,6 +1185,7 @@ static const struct platform_device_id max8997_pmic_id[] = { ...@@ -1185,6 +1185,7 @@ static const struct platform_device_id max8997_pmic_id[] = {
{ "max8997-pmic", 0}, { "max8997-pmic", 0},
{ }, { },
}; };
MODULE_DEVICE_TABLE(platform, max8997_pmic_id);
static struct platform_driver max8997_pmic_driver = { static struct platform_driver max8997_pmic_driver = {
.driver = { .driver = {
......
...@@ -887,6 +887,7 @@ static const struct platform_device_id max8998_pmic_id[] = { ...@@ -887,6 +887,7 @@ static const struct platform_device_id max8998_pmic_id[] = {
{ "lp3974-pmic", TYPE_LP3974 }, { "lp3974-pmic", TYPE_LP3974 },
{ } { }
}; };
MODULE_DEVICE_TABLE(platform, max8998_pmic_id);
static struct platform_driver max8998_pmic_driver = { static struct platform_driver max8998_pmic_driver = {
.driver = { .driver = {
......
...@@ -596,7 +596,7 @@ static struct regulator_ops regulator_ops = { ...@@ -596,7 +596,7 @@ static struct regulator_ops regulator_ops = {
.get_current_limit = get_current_limit, .get_current_limit = get_current_limit,
}; };
static int __devexit pmic_remove(struct spi_device *spi) static int pmic_remove(struct spi_device *spi)
{ {
struct tps6524x *hw = spi_get_drvdata(spi); struct tps6524x *hw = spi_get_drvdata(spi);
int i; int i;
......
...@@ -565,9 +565,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) ...@@ -565,9 +565,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
} }
irq = platform_get_irq_byname(pdev, "UV"); irq = platform_get_irq_byname(pdev, "UV");
ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq, ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
IRQF_TRIGGER_RISING, dcdc->name, IRQF_TRIGGER_RISING, dcdc->name, dcdc);
dcdc);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret); irq, ret);
...@@ -575,9 +574,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) ...@@ -575,9 +574,8 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
} }
irq = platform_get_irq_byname(pdev, "HC"); irq = platform_get_irq_byname(pdev, "HC");
ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_oc_irq, ret = request_threaded_irq(irq, NULL, wm831x_dcdc_oc_irq,
IRQF_TRIGGER_RISING, dcdc->name, IRQF_TRIGGER_RISING, dcdc->name, dcdc);
dcdc);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
irq, ret); irq, ret);
...@@ -589,7 +587,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) ...@@ -589,7 +587,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev)
return 0; return 0;
err_uv: err_uv:
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
err_regulator: err_regulator:
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
err: err:
...@@ -606,8 +604,8 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev) ...@@ -606,8 +604,8 @@ static __devexit int wm831x_buckv_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "HC"), dcdc); free_irq(platform_get_irq_byname(pdev, "HC"), dcdc);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
if (dcdc->dvs_gpio) if (dcdc->dvs_gpio)
gpio_free(dcdc->dvs_gpio); gpio_free(dcdc->dvs_gpio);
...@@ -756,9 +754,8 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev) ...@@ -756,9 +754,8 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev)
} }
irq = platform_get_irq_byname(pdev, "UV"); irq = platform_get_irq_byname(pdev, "UV");
ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq, ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
IRQF_TRIGGER_RISING, dcdc->name, IRQF_TRIGGER_RISING, dcdc->name, dcdc);
dcdc);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret); irq, ret);
...@@ -783,7 +780,7 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev) ...@@ -783,7 +780,7 @@ static __devexit int wm831x_buckp_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
kfree(dcdc); kfree(dcdc);
...@@ -885,9 +882,9 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev) ...@@ -885,9 +882,9 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev)
} }
irq = platform_get_irq_byname(pdev, "UV"); irq = platform_get_irq_byname(pdev, "UV");
ret = wm831x_request_irq(wm831x, irq, wm831x_dcdc_uv_irq, ret = request_threaded_irq(irq, NULL, wm831x_dcdc_uv_irq,
IRQF_TRIGGER_RISING, dcdc->name, IRQF_TRIGGER_RISING, dcdc->name,
dcdc); dcdc);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret); irq, ret);
...@@ -908,11 +905,10 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev) ...@@ -908,11 +905,10 @@ static __devinit int wm831x_boostp_probe(struct platform_device *pdev)
static __devexit int wm831x_boostp_remove(struct platform_device *pdev) static __devexit int wm831x_boostp_remove(struct platform_device *pdev)
{ {
struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev); struct wm831x_dcdc *dcdc = platform_get_drvdata(pdev);
struct wm831x *wm831x = dcdc->wm831x;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), dcdc); free_irq(platform_get_irq_byname(pdev, "UV"), dcdc);
regulator_unregister(dcdc->regulator); regulator_unregister(dcdc->regulator);
kfree(dcdc); kfree(dcdc);
......
...@@ -198,9 +198,8 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev) ...@@ -198,9 +198,8 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev)
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
ret = wm831x_request_irq(wm831x, irq, wm831x_isink_irq, ret = request_threaded_irq(irq, NULL, wm831x_isink_irq,
IRQF_TRIGGER_RISING, isink->name, IRQF_TRIGGER_RISING, isink->name, isink);
isink);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
irq, ret); irq, ret);
...@@ -221,11 +220,10 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev) ...@@ -221,11 +220,10 @@ static __devinit int wm831x_isink_probe(struct platform_device *pdev)
static __devexit int wm831x_isink_remove(struct platform_device *pdev) static __devexit int wm831x_isink_remove(struct platform_device *pdev)
{ {
struct wm831x_isink *isink = platform_get_drvdata(pdev); struct wm831x_isink *isink = platform_get_drvdata(pdev);
struct wm831x *wm831x = isink->wm831x;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq(pdev, 0), isink); free_irq(platform_get_irq(pdev, 0), isink);
regulator_unregister(isink->regulator); regulator_unregister(isink->regulator);
kfree(isink); kfree(isink);
......
...@@ -354,9 +354,9 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) ...@@ -354,9 +354,9 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
} }
irq = platform_get_irq_byname(pdev, "UV"); irq = platform_get_irq_byname(pdev, "UV");
ret = wm831x_request_irq(wm831x, irq, wm831x_ldo_uv_irq, ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq,
IRQF_TRIGGER_RISING, ldo->name, IRQF_TRIGGER_RISING, ldo->name,
ldo); ldo);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret); irq, ret);
...@@ -377,11 +377,10 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) ...@@ -377,11 +377,10 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev) static __devexit int wm831x_gp_ldo_remove(struct platform_device *pdev)
{ {
struct wm831x_ldo *ldo = platform_get_drvdata(pdev); struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
struct wm831x *wm831x = ldo->wm831x;
platform_set_drvdata(pdev, NULL); platform_set_drvdata(pdev, NULL);
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo); free_irq(platform_get_irq_byname(pdev, "UV"), ldo);
regulator_unregister(ldo->regulator); regulator_unregister(ldo->regulator);
kfree(ldo); kfree(ldo);
...@@ -619,9 +618,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev) ...@@ -619,9 +618,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
} }
irq = platform_get_irq_byname(pdev, "UV"); irq = platform_get_irq_byname(pdev, "UV");
ret = wm831x_request_irq(wm831x, irq, wm831x_ldo_uv_irq, ret = request_threaded_irq(irq, NULL, wm831x_ldo_uv_irq,
IRQF_TRIGGER_RISING, ldo->name, IRQF_TRIGGER_RISING, ldo->name, ldo);
ldo);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n", dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret); irq, ret);
...@@ -642,9 +640,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev) ...@@ -642,9 +640,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
static __devexit int wm831x_aldo_remove(struct platform_device *pdev) static __devexit int wm831x_aldo_remove(struct platform_device *pdev)
{ {
struct wm831x_ldo *ldo = platform_get_drvdata(pdev); struct wm831x_ldo *ldo = platform_get_drvdata(pdev);
struct wm831x *wm831x = ldo->wm831x;
wm831x_free_irq(wm831x, platform_get_irq_byname(pdev, "UV"), ldo); free_irq(platform_get_irq_byname(pdev, "UV"), ldo);
regulator_unregister(ldo->regulator); regulator_unregister(ldo->regulator);
kfree(ldo); kfree(ldo);
......
...@@ -139,17 +139,23 @@ struct ab8500 { ...@@ -139,17 +139,23 @@ struct ab8500 {
u8 oldmask[AB8500_NUM_IRQ_REGS]; u8 oldmask[AB8500_NUM_IRQ_REGS];
}; };
struct regulator_reg_init;
struct regulator_init_data; struct regulator_init_data;
/** /**
* struct ab8500_platform_data - AB8500 platform data * struct ab8500_platform_data - AB8500 platform data
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used * @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
* @init: board-specific initialization after detection of ab8500 * @init: board-specific initialization after detection of ab8500
* @num_regulator_reg_init: number of regulator init registers
* @regulator_reg_init: regulator init registers
* @num_regulator: number of regulators
* @regulator: machine-specific constraints for regulators * @regulator: machine-specific constraints for regulators
*/ */
struct ab8500_platform_data { struct ab8500_platform_data {
int irq_base; int irq_base;
void (*init) (struct ab8500 *); void (*init) (struct ab8500 *);
int num_regulator_reg_init;
struct ab8500_regulator_reg_init *regulator_reg_init;
int num_regulator; int num_regulator;
struct regulator_init_data *regulator; struct regulator_init_data *regulator;
}; };
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
* *
* License Terms: GNU General Public License v2 * License Terms: GNU General Public License v2
* *
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson * Authors: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
* * Bengt Jonsson <bengt.g.jonsson@stericsson.com> for ST-Ericsson
*/ */
#ifndef __LINUX_MFD_AB8500_REGULATOR_H #ifndef __LINUX_MFD_AB8500_REGULATOR_H
...@@ -17,6 +17,7 @@ enum ab8500_regulator_id { ...@@ -17,6 +17,7 @@ enum ab8500_regulator_id {
AB8500_LDO_AUX3, AB8500_LDO_AUX3,
AB8500_LDO_INTCORE, AB8500_LDO_INTCORE,
AB8500_LDO_TVOUT, AB8500_LDO_TVOUT,
AB8500_LDO_USB,
AB8500_LDO_AUDIO, AB8500_LDO_AUDIO,
AB8500_LDO_ANAMIC1, AB8500_LDO_ANAMIC1,
AB8500_LDO_ANAMIC2, AB8500_LDO_ANAMIC2,
...@@ -24,4 +25,50 @@ enum ab8500_regulator_id { ...@@ -24,4 +25,50 @@ enum ab8500_regulator_id {
AB8500_LDO_ANA, AB8500_LDO_ANA,
AB8500_NUM_REGULATORS, AB8500_NUM_REGULATORS,
}; };
/* AB8500 register initialization */
struct ab8500_regulator_reg_init {
int id;
u8 value;
};
#define INIT_REGULATOR_REGISTER(_id, _value) \
{ \
.id = _id, \
.value = _value, \
}
/* AB8500 registers */
enum ab8500_regulator_reg {
AB8500_REGUREQUESTCTRL2,
AB8500_REGUREQUESTCTRL3,
AB8500_REGUREQUESTCTRL4,
AB8500_REGUSYSCLKREQ1HPVALID1,
AB8500_REGUSYSCLKREQ1HPVALID2,
AB8500_REGUHWHPREQ1VALID1,
AB8500_REGUHWHPREQ1VALID2,
AB8500_REGUHWHPREQ2VALID1,
AB8500_REGUHWHPREQ2VALID2,
AB8500_REGUSWHPREQVALID1,
AB8500_REGUSWHPREQVALID2,
AB8500_REGUSYSCLKREQVALID1,
AB8500_REGUSYSCLKREQVALID2,
AB8500_REGUMISC1,
AB8500_VAUDIOSUPPLY,
AB8500_REGUCTRL1VAMIC,
AB8500_VPLLVANAREGU,
AB8500_VREFDDR,
AB8500_EXTSUPPLYREGU,
AB8500_VAUX12REGU,
AB8500_VRF1VAUX3REGU,
AB8500_VAUX1SEL,
AB8500_VAUX2SEL,
AB8500_VRF1VAUX3SEL,
AB8500_REGUCTRL2SPARE,
AB8500_REGUCTRLDISCH,
AB8500_REGUCTRLDISCH2,
AB8500_VSMPS1SEL1,
AB8500_NUM_REGULATOR_REGISTERS,
};
#endif #endif
...@@ -153,6 +153,8 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector); ...@@ -153,6 +153,8 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector);
int regulator_is_supported_voltage(struct regulator *regulator, int regulator_is_supported_voltage(struct regulator *regulator,
int min_uV, int max_uV); int min_uV, int max_uV);
int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV); int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);
int regulator_set_voltage_time(struct regulator *regulator,
int old_uV, int new_uV);
int regulator_get_voltage(struct regulator *regulator); int regulator_get_voltage(struct regulator *regulator);
int regulator_sync_voltage(struct regulator *regulator); int regulator_sync_voltage(struct regulator *regulator);
int regulator_set_current_limit(struct regulator *regulator, int regulator_set_current_limit(struct regulator *regulator,
......
...@@ -63,7 +63,11 @@ enum regulator_status { ...@@ -63,7 +63,11 @@ enum regulator_status {
* when running with the specified parameters. * when running with the specified parameters.
* *
* @enable_time: Time taken for the regulator voltage output voltage to * @enable_time: Time taken for the regulator voltage output voltage to
* stabalise after being enabled, in microseconds. * stabilise after being enabled, in microseconds.
* @set_voltage_time_sel: Time taken for the regulator voltage output voltage
* to stabilise after being set to a new value, in microseconds.
* The function provides the from and to voltage selector, the
* function should return the worst case.
* *
* @set_suspend_voltage: Set the voltage for the regulator when the system * @set_suspend_voltage: Set the voltage for the regulator when the system
* is suspended. * is suspended.
...@@ -103,8 +107,11 @@ struct regulator_ops { ...@@ -103,8 +107,11 @@ struct regulator_ops {
int (*set_mode) (struct regulator_dev *, unsigned int mode); int (*set_mode) (struct regulator_dev *, unsigned int mode);
unsigned int (*get_mode) (struct regulator_dev *); unsigned int (*get_mode) (struct regulator_dev *);
/* Time taken to enable the regulator */ /* Time taken to enable or set voltage on the regulator */
int (*enable_time) (struct regulator_dev *); int (*enable_time) (struct regulator_dev *);
int (*set_voltage_time_sel) (struct regulator_dev *,
unsigned int old_selector,
unsigned int new_selector);
/* report regulator status ... most other accessors report /* report regulator status ... most other accessors report
* control inputs, this reports results of combining inputs * control inputs, this reports results of combining inputs
......
...@@ -186,6 +186,7 @@ struct regulator_init_data { ...@@ -186,6 +186,7 @@ struct regulator_init_data {
}; };
int regulator_suspend_prepare(suspend_state_t state); int regulator_suspend_prepare(suspend_state_t state);
int regulator_suspend_finish(void);
#ifdef CONFIG_REGULATOR #ifdef CONFIG_REGULATOR
void regulator_has_full_constraints(void); void regulator_has_full_constraints(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment