#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
#include <linux/mfd/s5m87xx/s5m-core.h>
#include <linux/mfd/s5m87xx/s5m-pmic.h>
+#define S5M8767_OPMODE_MASK 0x3
+#define S5M8767_OPMODE_SHIFT 6
+#define S5M8767_OPMODE_SM (S5M8767_OPMODE_MASK << S5M8767_OPMODE_SHIFT)
+
struct s5m8767_info {
struct device *dev;
struct s5m87xx_dev *iodev;
[S5M8767_BUCK7] = NULL,
[S5M8767_BUCK8] = NULL,
[S5M8767_BUCK9] = &buck_voltage_val3,
+ [S5M8767_EN32KHZ_AP] = NULL,
+ [S5M8767_EN32KHZ_CP] = NULL,
+ [S5M8767_EN32KHZ_BT] = NULL,
};
static int s5m8767_list_voltage(struct regulator_dev *rdev,
return val;
}
-static int s5m8767_get_register(struct regulator_dev *rdev, int *reg)
+static int s5m8767_get_register(struct regulator_dev *rdev,
+ int *reg, int *mask, int *pattern)
{
int reg_id = rdev_get_id(rdev);
+ struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
+ int i;
+ int op_mode = 0x3;
+ for (i = 0; i < s5m8767->iodev->pdata->num_regulators; i++)
+ if (reg_id == s5m8767->iodev->pdata->regulators[i].id)
+ break;
+
+ if (i == s5m8767->iodev->pdata->num_regulators) {
+ dev_err(s5m8767->iodev->dev, "No matching regulators\n");
+ return -ENODEV;
+ }
+#ifdef CONFIG_OF
+ op_mode = s5m8767->iodev->pdata->regulators[i].reg_op_mode;
+#endif
+ *pattern = op_mode << S5M8767_OPMODE_SHIFT;
+ *mask = S5M8767_OPMODE_SM;
switch (reg_id) {
case S5M8767_LDO1 ... S5M8767_LDO2:
case S5M8767_BUCK6 ... S5M8767_BUCK9:
*reg = S5M8767_REG_BUCK6CTRL1 + (reg_id - S5M8767_BUCK6) * 2;
break;
+ case S5M8767_EN32KHZ_AP...S5M8767_EN32KHZ_BT:
+ *reg = S5M8767_REG_CTRL1;
+ *pattern = 1 << (reg_id - S5M8767_EN32KHZ_AP);
+ *mask = 1 << (reg_id - S5M8767_EN32KHZ_AP);
+ break;
default:
return -EINVAL;
}
static int s5m8767_reg_is_enabled(struct regulator_dev *rdev)
{
struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
- int ret, reg;
- int mask = 0xc0, pattern = 0xc0;
- u8 val;
+ int ret, reg, mask, pattern;
+ unsigned int val;
- ret = s5m8767_get_register(rdev, ®);
+ ret = s5m8767_get_register(rdev, ®, &mask, &pattern);
if (ret == -EINVAL)
return 1;
else if (ret)
static int s5m8767_reg_enable(struct regulator_dev *rdev)
{
struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
- int ret, reg;
- int mask = 0xc0, pattern = 0xc0;
+ int ret, reg, mask, pattern;
- ret = s5m8767_get_register(rdev, ®);
+ ret = s5m8767_get_register(rdev, ®, &mask, &pattern);
if (ret)
return ret;
static int s5m8767_reg_disable(struct regulator_dev *rdev)
{
struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
- int ret, reg;
- int mask = 0xc0, pattern = 0xc0;
+ int ret, reg, mask, pattern;
- ret = s5m8767_get_register(rdev, ®);
+ ret = s5m8767_get_register(rdev, ®, &mask, &pattern);
if (ret)
return ret;
- return s5m_reg_update(s5m8767->iodev, reg, ~pattern, mask);
+ return s5m_reg_update(s5m8767->iodev, reg, ~mask, mask);
}
static int s5m8767_get_voltage_register(struct regulator_dev *rdev, int *_reg)
struct s5m8767_info *s5m8767 = rdev_get_drvdata(rdev);
int reg, mask, ret;
int reg_id = rdev_get_id(rdev);
- u8 val;
+ unsigned int val;
ret = s5m8767_get_voltage_register(rdev, ®);
if (ret)
const struct s5m_voltage_desc *desc;
int reg_id = rdev_get_id(rdev);
int sel, reg, mask, ret;
- u8 val;
+ unsigned int val;
switch (reg_id) {
case S5M8767_LDO1 ... S5M8767_LDO28:
.set_voltage_time_sel = s5m8767_set_voltage_time_sel,
};
+static struct regulator_ops s5m8767_32k_ops = {
+ .list_voltage = s5m8767_list_voltage,
+ .is_enabled = s5m8767_reg_is_enabled,
+ .enable = s5m8767_reg_enable,
+ .disable = s5m8767_reg_disable,
+};
+
#define regulator_desc_ldo(num) { \
.name = "LDO"#num, \
.id = S5M8767_LDO##num, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
}
+#define regulator_desc_32khz(x) { \
+ .name = "EN32KHZ_" __stringify(x), \
+ .id = S5M8767_EN32KHZ_##x, \
+ .ops = &s5m8767_32k_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+}
static struct regulator_desc regulators[] = {
regulator_desc_ldo(1),
regulator_desc_buck(7),
regulator_desc_buck(8),
regulator_desc_buck(9),
+ regulator_desc_32khz(AP),
+ regulator_desc_32khz(CP),
+ regulator_desc_32khz(BT),
};
+#ifdef CONFIG_OF
+static int s5m8767_pmic_dt_parse_pdata(struct s5m87xx_dev *iodev,
+ struct s5m_platform_data *pdata)
+{
+ struct device_node *pmic_np, *regulators_np, *reg_np;
+ struct s5m_regulator_data *rdata;
+ unsigned int i;
+
+ pmic_np = iodev->dev->of_node;
+ if (!pmic_np) {
+ dev_err(iodev->dev, "could not find pmic sub-node\n");
+ return -ENODEV;
+ }
+
+ regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators");
+ if (!regulators_np) {
+ dev_err(iodev->dev, "could not find regulators sub-node\n");
+ return -EINVAL;
+ }
+
+ /* count the number of regulators to be supported in pmic */
+ pdata->num_regulators = 0;
+ for_each_child_of_node(regulators_np, reg_np)
+ pdata->num_regulators++;
+
+ rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) *
+ pdata->num_regulators, GFP_KERNEL);
+ if (!rdata) {
+ dev_err(iodev->dev,
+ "could not allocate memory for regulator data\n");
+ return -ENOMEM;
+ }
+
+ pdata->regulators = rdata;
+ for_each_child_of_node(regulators_np, reg_np) {
+ for (i = 0; i < ARRAY_SIZE(regulators); i++)
+ if (!of_node_cmp(reg_np->name, regulators[i].name))
+ break;
+
+ if (i == ARRAY_SIZE(regulators)) {
+ dev_warn(iodev->dev,
+ "No configuration data for regulator %s\n",
+ reg_np->name);
+ continue;
+ }
+
+ rdata->id = i;
+ rdata->initdata = of_get_regulator_init_data(
+ iodev->dev, reg_np);
+ rdata->reg_node = reg_np;
+ if (of_property_read_u32(reg_np, "reg_op_mode",
+ &rdata->reg_op_mode)) {
+ dev_warn(iodev->dev, "no op_mode property property at %s\n",
+ reg_np->full_name);
+ /*
+ * Set operating mode to NORMAL "ON" as default. The
+ * 32KHz clocks are being turned on and kept on by
+ * default, so the below mode setting does not impact
+ * it.
+ */
+ rdata->reg_op_mode = S5M8767_OPMODE_MASK;
+ }
+ rdata++;
+ }
+
+ if (!of_property_read_u32(pmic_np, "s5m8767,buck_ramp_delay", &i))
+ pdata->buck_ramp_delay = i & 0xf;
+
+ if (of_get_property(pmic_np, "s5m8767,buck2_ramp_enable", NULL))
+ pdata->buck2_ramp_enable = 1;
+
+ if (of_get_property(pmic_np, "s5m8767,buck3_ramp_enable", NULL))
+ pdata->buck3_ramp_enable = 1;
+
+ if (of_get_property(pmic_np, "s5m8767,buck4_ramp_enable", NULL))
+ pdata->buck4_ramp_enable = 1;
+
+ return 0;
+}
+#else
+static int s5m8767_pmic_dt_parse_pdata(struct s5m87xx_dev *iodev,
+ struct s5m_platform_data *pdata)
+{
+ return 0;
+}
+#endif /* CONFIG_OF */
+
static __devinit int s5m8767_pmic_probe(struct platform_device *pdev)
{
struct s5m87xx_dev *iodev = dev_get_drvdata(pdev->dev.parent);
- struct s5m_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct s5m_platform_data *pdata = iodev->pdata;
struct regulator_dev **rdev;
struct s5m8767_info *s5m8767;
int i, ret, size;
return -ENODEV;
}
+ if (iodev->dev->of_node) {
+ ret = s5m8767_pmic_dt_parse_pdata(iodev, pdata);
+ if (ret)
+ return ret;
+ }
+
if (pdata->buck2_gpiodvs) {
if (pdata->buck3_gpiodvs || pdata->buck4_gpiodvs) {
dev_err(&pdev->dev, "S5M8767 GPIO DVS NOT VALID\n");
if (!s5m8767)
return -ENOMEM;
- size = sizeof(struct regulator_dev *) * (S5M8767_REG_MAX - 2);
+ if (!pdata->num_regulators)
+ pdata->num_regulators = S5M8767_REG_MAX - 2;
+
+ size = sizeof(struct regulator_dev *) * pdata->num_regulators;
s5m8767->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (!s5m8767->rdev)
return -ENOMEM;
rdev = s5m8767->rdev;
s5m8767->dev = &pdev->dev;
s5m8767->iodev = iodev;
- s5m8767->num_regulators = S5M8767_REG_MAX - 2;
+ s5m8767->num_regulators = pdata->num_regulators;
platform_set_drvdata(pdev, s5m8767);
s5m8767->buck_gpioindex = pdata->buck_default_idx;
(desc->max - desc->min) / desc->step + 1;
rdev[i] = regulator_register(®ulators[id], s5m8767->dev,
- pdata->regulators[i].initdata, s5m8767, NULL);
+ pdata->regulators[i].initdata,
+ s5m8767,
+ pdata->regulators[i].reg_node);
if (IS_ERR(rdev[i])) {
ret = PTR_ERR(rdev[i]);
dev_err(s5m8767->dev, "regulator init failed for %d\n",