mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-13 00:29:50 +00:00
318dbb02b5
We use regmap regulator ops to enable/disable and check if regulator is enabled for various SMPS. However, these depend on valid enable_reg, enable_mask and enable_value in regulator descriptor. Currently we do not populate these for SMPS other than SMPS10, this results in spurious results as regmap assumes that the values are valid and ends up reading register 0x0 RTC:SECONDS_REG on Palmas variants that do have RTC! To fix this, we update proper parameters for the descriptor fields. Further, we want to ensure the behavior consistent with logic prior to commit dbabd624d4eec50b6, where, once you do a set_mode, enable/disable ensure the logic remains consistent and configures Palmas to the configuration that we set with set_mode (since the configuration register is common). To do this, we can rely on the regulator core's regulator_register behavior where the regulator descriptor pointer provided by the regulator driver is stored. (no reallocation and copy is done). This lets us update the enable_value post registration, to remain consistent with the mode we configure as part of set_mode. Fixes: dbabd624d4eec50b6 ("regulator: palmas: Reemove open coded functions with helper functions") Reported-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Nishanth Menon <nm@ti.com> Tested-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Mark Brown <broonie@linaro.org>
1146 lines
29 KiB
C
1146 lines
29 KiB
C
/*
|
|
* Driver for Regulator part of Palmas PMIC Chips
|
|
*
|
|
* Copyright 2011-2013 Texas Instruments Inc.
|
|
*
|
|
* Author: Graeme Gregory <gg@slimlogic.co.uk>
|
|
* Author: Ian Lartey <ian@slimlogic.co.uk>
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/err.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/regulator/driver.h>
|
|
#include <linux/regulator/machine.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/mfd/palmas.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_platform.h>
|
|
#include <linux/regulator/of_regulator.h>
|
|
|
|
struct regs_info {
|
|
char *name;
|
|
char *sname;
|
|
u8 vsel_addr;
|
|
u8 ctrl_addr;
|
|
u8 tstep_addr;
|
|
int sleep_id;
|
|
};
|
|
|
|
static const struct regulator_linear_range smps_low_ranges[] = {
|
|
REGULATOR_LINEAR_RANGE(500000, 0x1, 0x6, 0),
|
|
REGULATOR_LINEAR_RANGE(510000, 0x7, 0x79, 10000),
|
|
REGULATOR_LINEAR_RANGE(1650000, 0x7A, 0x7f, 0),
|
|
};
|
|
|
|
static const struct regulator_linear_range smps_high_ranges[] = {
|
|
REGULATOR_LINEAR_RANGE(1000000, 0x1, 0x6, 0),
|
|
REGULATOR_LINEAR_RANGE(1020000, 0x7, 0x79, 20000),
|
|
REGULATOR_LINEAR_RANGE(3300000, 0x7A, 0x7f, 0),
|
|
};
|
|
|
|
static const struct regs_info palmas_regs_info[] = {
|
|
{
|
|
.name = "SMPS12",
|
|
.sname = "smps1-in",
|
|
.vsel_addr = PALMAS_SMPS12_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS12_CTRL,
|
|
.tstep_addr = PALMAS_SMPS12_TSTEP,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS12,
|
|
},
|
|
{
|
|
.name = "SMPS123",
|
|
.sname = "smps1-in",
|
|
.vsel_addr = PALMAS_SMPS12_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS12_CTRL,
|
|
.tstep_addr = PALMAS_SMPS12_TSTEP,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS12,
|
|
},
|
|
{
|
|
.name = "SMPS3",
|
|
.sname = "smps3-in",
|
|
.vsel_addr = PALMAS_SMPS3_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS3_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS3,
|
|
},
|
|
{
|
|
.name = "SMPS45",
|
|
.sname = "smps4-in",
|
|
.vsel_addr = PALMAS_SMPS45_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS45_CTRL,
|
|
.tstep_addr = PALMAS_SMPS45_TSTEP,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS45,
|
|
},
|
|
{
|
|
.name = "SMPS457",
|
|
.sname = "smps4-in",
|
|
.vsel_addr = PALMAS_SMPS45_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS45_CTRL,
|
|
.tstep_addr = PALMAS_SMPS45_TSTEP,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS45,
|
|
},
|
|
{
|
|
.name = "SMPS6",
|
|
.sname = "smps6-in",
|
|
.vsel_addr = PALMAS_SMPS6_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS6_CTRL,
|
|
.tstep_addr = PALMAS_SMPS6_TSTEP,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS6,
|
|
},
|
|
{
|
|
.name = "SMPS7",
|
|
.sname = "smps7-in",
|
|
.vsel_addr = PALMAS_SMPS7_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS7_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS7,
|
|
},
|
|
{
|
|
.name = "SMPS8",
|
|
.sname = "smps8-in",
|
|
.vsel_addr = PALMAS_SMPS8_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS8_CTRL,
|
|
.tstep_addr = PALMAS_SMPS8_TSTEP,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS8,
|
|
},
|
|
{
|
|
.name = "SMPS9",
|
|
.sname = "smps9-in",
|
|
.vsel_addr = PALMAS_SMPS9_VOLTAGE,
|
|
.ctrl_addr = PALMAS_SMPS9_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS9,
|
|
},
|
|
{
|
|
.name = "SMPS10_OUT2",
|
|
.sname = "smps10-in",
|
|
.ctrl_addr = PALMAS_SMPS10_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS10,
|
|
},
|
|
{
|
|
.name = "SMPS10_OUT1",
|
|
.sname = "smps10-out2",
|
|
.ctrl_addr = PALMAS_SMPS10_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SMPS10,
|
|
},
|
|
{
|
|
.name = "LDO1",
|
|
.sname = "ldo1-in",
|
|
.vsel_addr = PALMAS_LDO1_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO1_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO1,
|
|
},
|
|
{
|
|
.name = "LDO2",
|
|
.sname = "ldo2-in",
|
|
.vsel_addr = PALMAS_LDO2_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO2_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO2,
|
|
},
|
|
{
|
|
.name = "LDO3",
|
|
.sname = "ldo3-in",
|
|
.vsel_addr = PALMAS_LDO3_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO3_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO3,
|
|
},
|
|
{
|
|
.name = "LDO4",
|
|
.sname = "ldo4-in",
|
|
.vsel_addr = PALMAS_LDO4_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO4_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO4,
|
|
},
|
|
{
|
|
.name = "LDO5",
|
|
.sname = "ldo5-in",
|
|
.vsel_addr = PALMAS_LDO5_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO5_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO5,
|
|
},
|
|
{
|
|
.name = "LDO6",
|
|
.sname = "ldo6-in",
|
|
.vsel_addr = PALMAS_LDO6_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO6_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO6,
|
|
},
|
|
{
|
|
.name = "LDO7",
|
|
.sname = "ldo7-in",
|
|
.vsel_addr = PALMAS_LDO7_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO7_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO7,
|
|
},
|
|
{
|
|
.name = "LDO8",
|
|
.sname = "ldo8-in",
|
|
.vsel_addr = PALMAS_LDO8_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO8_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO8,
|
|
},
|
|
{
|
|
.name = "LDO9",
|
|
.sname = "ldo9-in",
|
|
.vsel_addr = PALMAS_LDO9_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDO9_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDO9,
|
|
},
|
|
{
|
|
.name = "LDOLN",
|
|
.sname = "ldoln-in",
|
|
.vsel_addr = PALMAS_LDOLN_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDOLN_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDOLN,
|
|
},
|
|
{
|
|
.name = "LDOUSB",
|
|
.sname = "ldousb-in",
|
|
.vsel_addr = PALMAS_LDOUSB_VOLTAGE,
|
|
.ctrl_addr = PALMAS_LDOUSB_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_LDOUSB,
|
|
},
|
|
{
|
|
.name = "REGEN1",
|
|
.ctrl_addr = PALMAS_REGEN1_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN1,
|
|
},
|
|
{
|
|
.name = "REGEN2",
|
|
.ctrl_addr = PALMAS_REGEN2_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN2,
|
|
},
|
|
{
|
|
.name = "REGEN3",
|
|
.ctrl_addr = PALMAS_REGEN3_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_REGEN3,
|
|
},
|
|
{
|
|
.name = "SYSEN1",
|
|
.ctrl_addr = PALMAS_SYSEN1_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SYSEN1,
|
|
},
|
|
{
|
|
.name = "SYSEN2",
|
|
.ctrl_addr = PALMAS_SYSEN2_CTRL,
|
|
.sleep_id = PALMAS_EXTERNAL_REQSTR_ID_SYSEN2,
|
|
},
|
|
};
|
|
|
|
static unsigned int palmas_smps_ramp_delay[4] = {0, 10000, 5000, 2500};
|
|
|
|
#define SMPS_CTRL_MODE_OFF 0x00
|
|
#define SMPS_CTRL_MODE_ON 0x01
|
|
#define SMPS_CTRL_MODE_ECO 0x02
|
|
#define SMPS_CTRL_MODE_PWM 0x03
|
|
|
|
#define PALMAS_SMPS_NUM_VOLTAGES 122
|
|
#define PALMAS_SMPS10_NUM_VOLTAGES 2
|
|
#define PALMAS_LDO_NUM_VOLTAGES 50
|
|
|
|
#define SMPS10_VSEL (1<<3)
|
|
#define SMPS10_BOOST_EN (1<<2)
|
|
#define SMPS10_BYPASS_EN (1<<1)
|
|
#define SMPS10_SWITCH_EN (1<<0)
|
|
|
|
#define REGULATOR_SLAVE 0
|
|
|
|
static int palmas_smps_read(struct palmas *palmas, unsigned int reg,
|
|
unsigned int *dest)
|
|
{
|
|
unsigned int addr;
|
|
|
|
addr = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, reg);
|
|
|
|
return regmap_read(palmas->regmap[REGULATOR_SLAVE], addr, dest);
|
|
}
|
|
|
|
static int palmas_smps_write(struct palmas *palmas, unsigned int reg,
|
|
unsigned int value)
|
|
{
|
|
unsigned int addr;
|
|
|
|
addr = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE, reg);
|
|
|
|
return regmap_write(palmas->regmap[REGULATOR_SLAVE], addr, value);
|
|
}
|
|
|
|
static int palmas_ldo_read(struct palmas *palmas, unsigned int reg,
|
|
unsigned int *dest)
|
|
{
|
|
unsigned int addr;
|
|
|
|
addr = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE, reg);
|
|
|
|
return regmap_read(palmas->regmap[REGULATOR_SLAVE], addr, dest);
|
|
}
|
|
|
|
static int palmas_ldo_write(struct palmas *palmas, unsigned int reg,
|
|
unsigned int value)
|
|
{
|
|
unsigned int addr;
|
|
|
|
addr = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE, reg);
|
|
|
|
return regmap_write(palmas->regmap[REGULATOR_SLAVE], addr, value);
|
|
}
|
|
|
|
static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode)
|
|
{
|
|
struct palmas_pmic *pmic = rdev_get_drvdata(dev);
|
|
int id = rdev_get_id(dev);
|
|
unsigned int reg;
|
|
bool rail_enable = true;
|
|
|
|
palmas_smps_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®);
|
|
reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
|
|
|
|
if (reg == SMPS_CTRL_MODE_OFF)
|
|
rail_enable = false;
|
|
|
|
switch (mode) {
|
|
case REGULATOR_MODE_NORMAL:
|
|
reg |= SMPS_CTRL_MODE_ON;
|
|
break;
|
|
case REGULATOR_MODE_IDLE:
|
|
reg |= SMPS_CTRL_MODE_ECO;
|
|
break;
|
|
case REGULATOR_MODE_FAST:
|
|
reg |= SMPS_CTRL_MODE_PWM;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
pmic->current_reg_mode[id] = reg & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
|
|
if (rail_enable)
|
|
palmas_smps_write(pmic->palmas,
|
|
palmas_regs_info[id].ctrl_addr, reg);
|
|
|
|
/* Switch the enable value to ensure this is used for enable */
|
|
pmic->desc[id].enable_val = pmic->current_reg_mode[id];
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int palmas_get_mode_smps(struct regulator_dev *dev)
|
|
{
|
|
struct palmas_pmic *pmic = rdev_get_drvdata(dev);
|
|
int id = rdev_get_id(dev);
|
|
unsigned int reg;
|
|
|
|
reg = pmic->current_reg_mode[id] & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
|
|
|
|
switch (reg) {
|
|
case SMPS_CTRL_MODE_ON:
|
|
return REGULATOR_MODE_NORMAL;
|
|
case SMPS_CTRL_MODE_ECO:
|
|
return REGULATOR_MODE_IDLE;
|
|
case SMPS_CTRL_MODE_PWM:
|
|
return REGULATOR_MODE_FAST;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int palmas_smps_set_ramp_delay(struct regulator_dev *rdev,
|
|
int ramp_delay)
|
|
{
|
|
struct palmas_pmic *pmic = rdev_get_drvdata(rdev);
|
|
int id = rdev_get_id(rdev);
|
|
unsigned int reg = 0;
|
|
unsigned int addr = palmas_regs_info[id].tstep_addr;
|
|
int ret;
|
|
|
|
/* SMPS3 and SMPS7 do not have tstep_addr setting */
|
|
switch (id) {
|
|
case PALMAS_REG_SMPS3:
|
|
case PALMAS_REG_SMPS7:
|
|
return 0;
|
|
}
|
|
|
|
if (ramp_delay <= 0)
|
|
reg = 0;
|
|
else if (ramp_delay <= 2500)
|
|
reg = 3;
|
|
else if (ramp_delay <= 5000)
|
|
reg = 2;
|
|
else
|
|
reg = 1;
|
|
|
|
ret = palmas_smps_write(pmic->palmas, addr, reg);
|
|
if (ret < 0) {
|
|
dev_err(pmic->palmas->dev, "TSTEP write failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
pmic->ramp_delay[id] = palmas_smps_ramp_delay[reg];
|
|
return ret;
|
|
}
|
|
|
|
static struct regulator_ops palmas_ops_smps = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.set_mode = palmas_set_mode_smps,
|
|
.get_mode = palmas_get_mode_smps,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.list_voltage = regulator_list_voltage_linear_range,
|
|
.map_voltage = regulator_map_voltage_linear_range,
|
|
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
|
.set_ramp_delay = palmas_smps_set_ramp_delay,
|
|
};
|
|
|
|
static struct regulator_ops palmas_ops_ext_control_smps = {
|
|
.set_mode = palmas_set_mode_smps,
|
|
.get_mode = palmas_get_mode_smps,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.list_voltage = regulator_list_voltage_linear_range,
|
|
.map_voltage = regulator_map_voltage_linear_range,
|
|
.set_voltage_time_sel = regulator_set_voltage_time_sel,
|
|
.set_ramp_delay = palmas_smps_set_ramp_delay,
|
|
};
|
|
|
|
static struct regulator_ops palmas_ops_smps10 = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
.set_bypass = regulator_set_bypass_regmap,
|
|
.get_bypass = regulator_get_bypass_regmap,
|
|
};
|
|
|
|
static int palmas_is_enabled_ldo(struct regulator_dev *dev)
|
|
{
|
|
struct palmas_pmic *pmic = rdev_get_drvdata(dev);
|
|
int id = rdev_get_id(dev);
|
|
unsigned int reg;
|
|
|
|
palmas_ldo_read(pmic->palmas, palmas_regs_info[id].ctrl_addr, ®);
|
|
|
|
reg &= PALMAS_LDO1_CTRL_STATUS;
|
|
|
|
return !!(reg);
|
|
}
|
|
|
|
static struct regulator_ops palmas_ops_ldo = {
|
|
.is_enabled = palmas_is_enabled_ldo,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
};
|
|
|
|
static struct regulator_ops palmas_ops_ext_control_ldo = {
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
.map_voltage = regulator_map_voltage_linear,
|
|
};
|
|
|
|
static struct regulator_ops palmas_ops_extreg = {
|
|
.is_enabled = regulator_is_enabled_regmap,
|
|
.enable = regulator_enable_regmap,
|
|
.disable = regulator_disable_regmap,
|
|
};
|
|
|
|
static struct regulator_ops palmas_ops_ext_control_extreg = {
|
|
};
|
|
|
|
static int palmas_regulator_config_external(struct palmas *palmas, int id,
|
|
struct palmas_reg_init *reg_init)
|
|
{
|
|
int sleep_id = palmas_regs_info[id].sleep_id;
|
|
int ret;
|
|
|
|
ret = palmas_ext_control_req_config(palmas, sleep_id,
|
|
reg_init->roof_floor, true);
|
|
if (ret < 0)
|
|
dev_err(palmas->dev,
|
|
"Ext control config for regulator %d failed %d\n",
|
|
id, ret);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* setup the hardware based sleep configuration of the SMPS/LDO regulators
|
|
* from the platform data. This is different to the software based control
|
|
* supported by the regulator framework as it is controlled by toggling
|
|
* pins on the PMIC such as PREQ, SYSEN, ...
|
|
*/
|
|
static int palmas_smps_init(struct palmas *palmas, int id,
|
|
struct palmas_reg_init *reg_init)
|
|
{
|
|
unsigned int reg;
|
|
unsigned int addr;
|
|
int ret;
|
|
|
|
addr = palmas_regs_info[id].ctrl_addr;
|
|
|
|
ret = palmas_smps_read(palmas, addr, ®);
|
|
if (ret)
|
|
return ret;
|
|
|
|
switch (id) {
|
|
case PALMAS_REG_SMPS10_OUT1:
|
|
case PALMAS_REG_SMPS10_OUT2:
|
|
reg &= ~PALMAS_SMPS10_CTRL_MODE_SLEEP_MASK;
|
|
if (reg_init->mode_sleep)
|
|
reg |= reg_init->mode_sleep <<
|
|
PALMAS_SMPS10_CTRL_MODE_SLEEP_SHIFT;
|
|
break;
|
|
default:
|
|
if (reg_init->warm_reset)
|
|
reg |= PALMAS_SMPS12_CTRL_WR_S;
|
|
else
|
|
reg &= ~PALMAS_SMPS12_CTRL_WR_S;
|
|
|
|
if (reg_init->roof_floor)
|
|
reg |= PALMAS_SMPS12_CTRL_ROOF_FLOOR_EN;
|
|
else
|
|
reg &= ~PALMAS_SMPS12_CTRL_ROOF_FLOOR_EN;
|
|
|
|
reg &= ~PALMAS_SMPS12_CTRL_MODE_SLEEP_MASK;
|
|
if (reg_init->mode_sleep)
|
|
reg |= reg_init->mode_sleep <<
|
|
PALMAS_SMPS12_CTRL_MODE_SLEEP_SHIFT;
|
|
}
|
|
|
|
ret = palmas_smps_write(palmas, addr, reg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (palmas_regs_info[id].vsel_addr && reg_init->vsel) {
|
|
addr = palmas_regs_info[id].vsel_addr;
|
|
|
|
reg = reg_init->vsel;
|
|
|
|
ret = palmas_smps_write(palmas, addr, reg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (reg_init->roof_floor && (id != PALMAS_REG_SMPS10_OUT1) &&
|
|
(id != PALMAS_REG_SMPS10_OUT2)) {
|
|
/* Enable externally controlled regulator */
|
|
addr = palmas_regs_info[id].ctrl_addr;
|
|
ret = palmas_smps_read(palmas, addr, ®);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (!(reg & PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK)) {
|
|
reg |= SMPS_CTRL_MODE_ON;
|
|
ret = palmas_smps_write(palmas, addr, reg);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
return palmas_regulator_config_external(palmas, id, reg_init);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int palmas_ldo_init(struct palmas *palmas, int id,
|
|
struct palmas_reg_init *reg_init)
|
|
{
|
|
unsigned int reg;
|
|
unsigned int addr;
|
|
int ret;
|
|
|
|
addr = palmas_regs_info[id].ctrl_addr;
|
|
|
|
ret = palmas_ldo_read(palmas, addr, ®);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (reg_init->warm_reset)
|
|
reg |= PALMAS_LDO1_CTRL_WR_S;
|
|
else
|
|
reg &= ~PALMAS_LDO1_CTRL_WR_S;
|
|
|
|
if (reg_init->mode_sleep)
|
|
reg |= PALMAS_LDO1_CTRL_MODE_SLEEP;
|
|
else
|
|
reg &= ~PALMAS_LDO1_CTRL_MODE_SLEEP;
|
|
|
|
ret = palmas_ldo_write(palmas, addr, reg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (reg_init->roof_floor) {
|
|
/* Enable externally controlled regulator */
|
|
addr = palmas_regs_info[id].ctrl_addr;
|
|
ret = palmas_update_bits(palmas, PALMAS_LDO_BASE,
|
|
addr, PALMAS_LDO1_CTRL_MODE_ACTIVE,
|
|
PALMAS_LDO1_CTRL_MODE_ACTIVE);
|
|
if (ret < 0) {
|
|
dev_err(palmas->dev,
|
|
"LDO Register 0x%02x update failed %d\n",
|
|
addr, ret);
|
|
return ret;
|
|
}
|
|
return palmas_regulator_config_external(palmas, id, reg_init);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int palmas_extreg_init(struct palmas *palmas, int id,
|
|
struct palmas_reg_init *reg_init)
|
|
{
|
|
unsigned int addr;
|
|
int ret;
|
|
unsigned int val = 0;
|
|
|
|
addr = palmas_regs_info[id].ctrl_addr;
|
|
|
|
if (reg_init->mode_sleep)
|
|
val = PALMAS_REGEN1_CTRL_MODE_SLEEP;
|
|
|
|
ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE,
|
|
addr, PALMAS_REGEN1_CTRL_MODE_SLEEP, val);
|
|
if (ret < 0) {
|
|
dev_err(palmas->dev, "Resource reg 0x%02x update failed %d\n",
|
|
addr, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (reg_init->roof_floor) {
|
|
/* Enable externally controlled regulator */
|
|
addr = palmas_regs_info[id].ctrl_addr;
|
|
ret = palmas_update_bits(palmas, PALMAS_RESOURCE_BASE,
|
|
addr, PALMAS_REGEN1_CTRL_MODE_ACTIVE,
|
|
PALMAS_REGEN1_CTRL_MODE_ACTIVE);
|
|
if (ret < 0) {
|
|
dev_err(palmas->dev,
|
|
"Resource Register 0x%02x update failed %d\n",
|
|
addr, ret);
|
|
return ret;
|
|
}
|
|
return palmas_regulator_config_external(palmas, id, reg_init);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void palmas_enable_ldo8_track(struct palmas *palmas)
|
|
{
|
|
unsigned int reg;
|
|
unsigned int addr;
|
|
int ret;
|
|
|
|
addr = palmas_regs_info[PALMAS_REG_LDO8].ctrl_addr;
|
|
|
|
ret = palmas_ldo_read(palmas, addr, ®);
|
|
if (ret) {
|
|
dev_err(palmas->dev, "Error in reading ldo8 control reg\n");
|
|
return;
|
|
}
|
|
|
|
reg |= PALMAS_LDO8_CTRL_LDO_TRACKING_EN;
|
|
ret = palmas_ldo_write(palmas, addr, reg);
|
|
if (ret < 0) {
|
|
dev_err(palmas->dev, "Error in enabling tracking mode\n");
|
|
return;
|
|
}
|
|
/*
|
|
* When SMPS45 is set to off and LDO8 tracking is enabled, the LDO8
|
|
* output is defined by the LDO8_VOLTAGE.VSEL register divided by two,
|
|
* and can be set from 0.45 to 1.65 V.
|
|
*/
|
|
addr = palmas_regs_info[PALMAS_REG_LDO8].vsel_addr;
|
|
ret = palmas_ldo_read(palmas, addr, ®);
|
|
if (ret) {
|
|
dev_err(palmas->dev, "Error in reading ldo8 voltage reg\n");
|
|
return;
|
|
}
|
|
|
|
reg = (reg << 1) & PALMAS_LDO8_VOLTAGE_VSEL_MASK;
|
|
ret = palmas_ldo_write(palmas, addr, reg);
|
|
if (ret < 0)
|
|
dev_err(palmas->dev, "Error in setting ldo8 voltage reg\n");
|
|
|
|
return;
|
|
}
|
|
|
|
static struct of_regulator_match palmas_matches[] = {
|
|
{ .name = "smps12", },
|
|
{ .name = "smps123", },
|
|
{ .name = "smps3", },
|
|
{ .name = "smps45", },
|
|
{ .name = "smps457", },
|
|
{ .name = "smps6", },
|
|
{ .name = "smps7", },
|
|
{ .name = "smps8", },
|
|
{ .name = "smps9", },
|
|
{ .name = "smps10_out2", },
|
|
{ .name = "smps10_out1", },
|
|
{ .name = "ldo1", },
|
|
{ .name = "ldo2", },
|
|
{ .name = "ldo3", },
|
|
{ .name = "ldo4", },
|
|
{ .name = "ldo5", },
|
|
{ .name = "ldo6", },
|
|
{ .name = "ldo7", },
|
|
{ .name = "ldo8", },
|
|
{ .name = "ldo9", },
|
|
{ .name = "ldoln", },
|
|
{ .name = "ldousb", },
|
|
{ .name = "regen1", },
|
|
{ .name = "regen2", },
|
|
{ .name = "regen3", },
|
|
{ .name = "sysen1", },
|
|
{ .name = "sysen2", },
|
|
};
|
|
|
|
static void palmas_dt_to_pdata(struct device *dev,
|
|
struct device_node *node,
|
|
struct palmas_pmic_platform_data *pdata)
|
|
{
|
|
struct device_node *regulators;
|
|
u32 prop;
|
|
int idx, ret;
|
|
|
|
node = of_node_get(node);
|
|
regulators = of_get_child_by_name(node, "regulators");
|
|
if (!regulators) {
|
|
dev_info(dev, "regulator node not found\n");
|
|
return;
|
|
}
|
|
|
|
ret = of_regulator_match(dev, regulators, palmas_matches,
|
|
PALMAS_NUM_REGS);
|
|
of_node_put(regulators);
|
|
if (ret < 0) {
|
|
dev_err(dev, "Error parsing regulator init data: %d\n", ret);
|
|
return;
|
|
}
|
|
|
|
for (idx = 0; idx < PALMAS_NUM_REGS; idx++) {
|
|
if (!palmas_matches[idx].init_data ||
|
|
!palmas_matches[idx].of_node)
|
|
continue;
|
|
|
|
pdata->reg_data[idx] = palmas_matches[idx].init_data;
|
|
|
|
pdata->reg_init[idx] = devm_kzalloc(dev,
|
|
sizeof(struct palmas_reg_init), GFP_KERNEL);
|
|
|
|
pdata->reg_init[idx]->warm_reset =
|
|
of_property_read_bool(palmas_matches[idx].of_node,
|
|
"ti,warm-reset");
|
|
|
|
ret = of_property_read_u32(palmas_matches[idx].of_node,
|
|
"ti,roof-floor", &prop);
|
|
/* EINVAL: Property not found */
|
|
if (ret != -EINVAL) {
|
|
int econtrol;
|
|
|
|
/* use default value, when no value is specified */
|
|
econtrol = PALMAS_EXT_CONTROL_NSLEEP;
|
|
if (!ret) {
|
|
switch (prop) {
|
|
case 1:
|
|
econtrol = PALMAS_EXT_CONTROL_ENABLE1;
|
|
break;
|
|
case 2:
|
|
econtrol = PALMAS_EXT_CONTROL_ENABLE2;
|
|
break;
|
|
case 3:
|
|
econtrol = PALMAS_EXT_CONTROL_NSLEEP;
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
dev_warn(dev,
|
|
"%s: Invalid roof-floor option: %u\n",
|
|
palmas_matches[idx].name, prop);
|
|
break;
|
|
}
|
|
}
|
|
pdata->reg_init[idx]->roof_floor = econtrol;
|
|
}
|
|
|
|
ret = of_property_read_u32(palmas_matches[idx].of_node,
|
|
"ti,mode-sleep", &prop);
|
|
if (!ret)
|
|
pdata->reg_init[idx]->mode_sleep = prop;
|
|
|
|
ret = of_property_read_bool(palmas_matches[idx].of_node,
|
|
"ti,smps-range");
|
|
if (ret)
|
|
pdata->reg_init[idx]->vsel =
|
|
PALMAS_SMPS12_VOLTAGE_RANGE;
|
|
|
|
if (idx == PALMAS_REG_LDO8)
|
|
pdata->enable_ldo8_tracking = of_property_read_bool(
|
|
palmas_matches[idx].of_node,
|
|
"ti,enable-ldo8-tracking");
|
|
}
|
|
|
|
pdata->ldo6_vibrator = of_property_read_bool(node, "ti,ldo6-vibrator");
|
|
}
|
|
|
|
|
|
static int palmas_regulators_probe(struct platform_device *pdev)
|
|
{
|
|
struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
|
|
struct palmas_pmic_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
struct device_node *node = pdev->dev.of_node;
|
|
struct regulator_dev *rdev;
|
|
struct regulator_config config = { };
|
|
struct palmas_pmic *pmic;
|
|
struct palmas_reg_init *reg_init;
|
|
int id = 0, ret;
|
|
unsigned int addr, reg;
|
|
|
|
if (node && !pdata) {
|
|
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
|
|
|
|
if (!pdata)
|
|
return -ENOMEM;
|
|
|
|
palmas_dt_to_pdata(&pdev->dev, node, pdata);
|
|
}
|
|
|
|
pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
|
|
if (!pmic)
|
|
return -ENOMEM;
|
|
|
|
pmic->dev = &pdev->dev;
|
|
pmic->palmas = palmas;
|
|
palmas->pmic = pmic;
|
|
platform_set_drvdata(pdev, pmic);
|
|
|
|
ret = palmas_smps_read(palmas, PALMAS_SMPS_CTRL, ®);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (reg & PALMAS_SMPS_CTRL_SMPS12_SMPS123_EN)
|
|
pmic->smps123 = 1;
|
|
|
|
if (reg & PALMAS_SMPS_CTRL_SMPS45_SMPS457_EN)
|
|
pmic->smps457 = 1;
|
|
|
|
config.regmap = palmas->regmap[REGULATOR_SLAVE];
|
|
config.dev = &pdev->dev;
|
|
config.driver_data = pmic;
|
|
|
|
for (id = 0; id < PALMAS_REG_LDO1; id++) {
|
|
bool ramp_delay_support = false;
|
|
|
|
/*
|
|
* Miss out regulators which are not available due
|
|
* to slaving configurations.
|
|
*/
|
|
switch (id) {
|
|
case PALMAS_REG_SMPS12:
|
|
case PALMAS_REG_SMPS3:
|
|
if (pmic->smps123)
|
|
continue;
|
|
if (id == PALMAS_REG_SMPS12)
|
|
ramp_delay_support = true;
|
|
break;
|
|
case PALMAS_REG_SMPS123:
|
|
if (!pmic->smps123)
|
|
continue;
|
|
ramp_delay_support = true;
|
|
break;
|
|
case PALMAS_REG_SMPS45:
|
|
case PALMAS_REG_SMPS7:
|
|
if (pmic->smps457)
|
|
continue;
|
|
if (id == PALMAS_REG_SMPS45)
|
|
ramp_delay_support = true;
|
|
break;
|
|
case PALMAS_REG_SMPS457:
|
|
if (!pmic->smps457)
|
|
continue;
|
|
ramp_delay_support = true;
|
|
break;
|
|
case PALMAS_REG_SMPS10_OUT1:
|
|
case PALMAS_REG_SMPS10_OUT2:
|
|
if (!PALMAS_PMIC_HAS(palmas, SMPS10_BOOST))
|
|
continue;
|
|
}
|
|
|
|
if ((id == PALMAS_REG_SMPS6) || (id == PALMAS_REG_SMPS8))
|
|
ramp_delay_support = true;
|
|
|
|
if (ramp_delay_support) {
|
|
addr = palmas_regs_info[id].tstep_addr;
|
|
ret = palmas_smps_read(pmic->palmas, addr, ®);
|
|
if (ret < 0) {
|
|
dev_err(&pdev->dev,
|
|
"reading TSTEP reg failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
pmic->desc[id].ramp_delay =
|
|
palmas_smps_ramp_delay[reg & 0x3];
|
|
pmic->ramp_delay[id] = pmic->desc[id].ramp_delay;
|
|
}
|
|
|
|
/* Initialise sleep/init values from platform data */
|
|
if (pdata && pdata->reg_init[id]) {
|
|
reg_init = pdata->reg_init[id];
|
|
ret = palmas_smps_init(palmas, id, reg_init);
|
|
if (ret)
|
|
return ret;
|
|
} else {
|
|
reg_init = NULL;
|
|
}
|
|
|
|
/* Register the regulators */
|
|
pmic->desc[id].name = palmas_regs_info[id].name;
|
|
pmic->desc[id].id = id;
|
|
|
|
switch (id) {
|
|
case PALMAS_REG_SMPS10_OUT1:
|
|
case PALMAS_REG_SMPS10_OUT2:
|
|
pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES;
|
|
pmic->desc[id].ops = &palmas_ops_smps10;
|
|
pmic->desc[id].vsel_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
|
|
PALMAS_SMPS10_CTRL);
|
|
pmic->desc[id].vsel_mask = SMPS10_VSEL;
|
|
pmic->desc[id].enable_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
|
|
PALMAS_SMPS10_CTRL);
|
|
if (id == PALMAS_REG_SMPS10_OUT1)
|
|
pmic->desc[id].enable_mask = SMPS10_SWITCH_EN;
|
|
else
|
|
pmic->desc[id].enable_mask = SMPS10_BOOST_EN;
|
|
pmic->desc[id].bypass_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
|
|
PALMAS_SMPS10_CTRL);
|
|
pmic->desc[id].bypass_mask = SMPS10_BYPASS_EN;
|
|
pmic->desc[id].min_uV = 3750000;
|
|
pmic->desc[id].uV_step = 1250000;
|
|
break;
|
|
default:
|
|
/*
|
|
* Read and store the RANGE bit for later use
|
|
* This must be done before regulator is probed,
|
|
* otherwise we error in probe with unsupportable
|
|
* ranges. Read the current smps mode for later use.
|
|
*/
|
|
addr = palmas_regs_info[id].vsel_addr;
|
|
pmic->desc[id].n_linear_ranges = 3;
|
|
|
|
ret = palmas_smps_read(pmic->palmas, addr, ®);
|
|
if (ret)
|
|
return ret;
|
|
if (reg & PALMAS_SMPS12_VOLTAGE_RANGE)
|
|
pmic->range[id] = 1;
|
|
if (pmic->range[id])
|
|
pmic->desc[id].linear_ranges = smps_high_ranges;
|
|
else
|
|
pmic->desc[id].linear_ranges = smps_low_ranges;
|
|
|
|
if (reg_init && reg_init->roof_floor)
|
|
pmic->desc[id].ops =
|
|
&palmas_ops_ext_control_smps;
|
|
else
|
|
pmic->desc[id].ops = &palmas_ops_smps;
|
|
pmic->desc[id].n_voltages = PALMAS_SMPS_NUM_VOLTAGES;
|
|
pmic->desc[id].vsel_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
|
|
palmas_regs_info[id].vsel_addr);
|
|
pmic->desc[id].vsel_mask =
|
|
PALMAS_SMPS12_VOLTAGE_VSEL_MASK;
|
|
|
|
/* Read the smps mode for later use. */
|
|
addr = palmas_regs_info[id].ctrl_addr;
|
|
ret = palmas_smps_read(pmic->palmas, addr, ®);
|
|
if (ret)
|
|
return ret;
|
|
pmic->current_reg_mode[id] = reg &
|
|
PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
|
|
|
|
pmic->desc[id].enable_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
|
|
palmas_regs_info[id].ctrl_addr);
|
|
pmic->desc[id].enable_mask =
|
|
PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
|
|
/* set_mode overrides this value */
|
|
pmic->desc[id].enable_val = SMPS_CTRL_MODE_ON;
|
|
}
|
|
|
|
pmic->desc[id].type = REGULATOR_VOLTAGE;
|
|
pmic->desc[id].owner = THIS_MODULE;
|
|
|
|
if (pdata)
|
|
config.init_data = pdata->reg_data[id];
|
|
else
|
|
config.init_data = NULL;
|
|
|
|
pmic->desc[id].supply_name = palmas_regs_info[id].sname;
|
|
config.of_node = palmas_matches[id].of_node;
|
|
|
|
rdev = devm_regulator_register(&pdev->dev, &pmic->desc[id],
|
|
&config);
|
|
if (IS_ERR(rdev)) {
|
|
dev_err(&pdev->dev,
|
|
"failed to register %s regulator\n",
|
|
pdev->name);
|
|
return PTR_ERR(rdev);
|
|
}
|
|
|
|
/* Save regulator for cleanup */
|
|
pmic->rdev[id] = rdev;
|
|
}
|
|
|
|
/* Start this loop from the id left from previous loop */
|
|
for (; id < PALMAS_NUM_REGS; id++) {
|
|
if (pdata && pdata->reg_init[id])
|
|
reg_init = pdata->reg_init[id];
|
|
else
|
|
reg_init = NULL;
|
|
|
|
/* Miss out regulators which are not available due
|
|
* to alternate functions.
|
|
*/
|
|
|
|
/* Register the regulators */
|
|
pmic->desc[id].name = palmas_regs_info[id].name;
|
|
pmic->desc[id].id = id;
|
|
pmic->desc[id].type = REGULATOR_VOLTAGE;
|
|
pmic->desc[id].owner = THIS_MODULE;
|
|
|
|
if (id < PALMAS_REG_REGEN1) {
|
|
pmic->desc[id].n_voltages = PALMAS_LDO_NUM_VOLTAGES;
|
|
if (reg_init && reg_init->roof_floor)
|
|
pmic->desc[id].ops =
|
|
&palmas_ops_ext_control_ldo;
|
|
else
|
|
pmic->desc[id].ops = &palmas_ops_ldo;
|
|
pmic->desc[id].min_uV = 900000;
|
|
pmic->desc[id].uV_step = 50000;
|
|
pmic->desc[id].linear_min_sel = 1;
|
|
pmic->desc[id].enable_time = 500;
|
|
pmic->desc[id].vsel_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
|
|
palmas_regs_info[id].vsel_addr);
|
|
pmic->desc[id].vsel_mask =
|
|
PALMAS_LDO1_VOLTAGE_VSEL_MASK;
|
|
pmic->desc[id].enable_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
|
|
palmas_regs_info[id].ctrl_addr);
|
|
pmic->desc[id].enable_mask =
|
|
PALMAS_LDO1_CTRL_MODE_ACTIVE;
|
|
|
|
/* Check if LDO8 is in tracking mode or not */
|
|
if (pdata && (id == PALMAS_REG_LDO8) &&
|
|
pdata->enable_ldo8_tracking) {
|
|
palmas_enable_ldo8_track(palmas);
|
|
pmic->desc[id].min_uV = 450000;
|
|
pmic->desc[id].uV_step = 25000;
|
|
}
|
|
|
|
/* LOD6 in vibrator mode will have enable time 2000us */
|
|
if (pdata && pdata->ldo6_vibrator &&
|
|
(id == PALMAS_REG_LDO6))
|
|
pmic->desc[id].enable_time = 2000;
|
|
} else {
|
|
pmic->desc[id].n_voltages = 1;
|
|
if (reg_init && reg_init->roof_floor)
|
|
pmic->desc[id].ops =
|
|
&palmas_ops_ext_control_extreg;
|
|
else
|
|
pmic->desc[id].ops = &palmas_ops_extreg;
|
|
pmic->desc[id].enable_reg =
|
|
PALMAS_BASE_TO_REG(PALMAS_RESOURCE_BASE,
|
|
palmas_regs_info[id].ctrl_addr);
|
|
pmic->desc[id].enable_mask =
|
|
PALMAS_REGEN1_CTRL_MODE_ACTIVE;
|
|
}
|
|
|
|
if (pdata)
|
|
config.init_data = pdata->reg_data[id];
|
|
else
|
|
config.init_data = NULL;
|
|
|
|
pmic->desc[id].supply_name = palmas_regs_info[id].sname;
|
|
config.of_node = palmas_matches[id].of_node;
|
|
|
|
rdev = devm_regulator_register(&pdev->dev, &pmic->desc[id],
|
|
&config);
|
|
if (IS_ERR(rdev)) {
|
|
dev_err(&pdev->dev,
|
|
"failed to register %s regulator\n",
|
|
pdev->name);
|
|
return PTR_ERR(rdev);
|
|
}
|
|
|
|
/* Save regulator for cleanup */
|
|
pmic->rdev[id] = rdev;
|
|
|
|
/* Initialise sleep/init values from platform data */
|
|
if (pdata) {
|
|
reg_init = pdata->reg_init[id];
|
|
if (reg_init) {
|
|
if (id < PALMAS_REG_REGEN1)
|
|
ret = palmas_ldo_init(palmas,
|
|
id, reg_init);
|
|
else
|
|
ret = palmas_extreg_init(palmas,
|
|
id, reg_init);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id of_palmas_match_tbl[] = {
|
|
{ .compatible = "ti,palmas-pmic", },
|
|
{ .compatible = "ti,twl6035-pmic", },
|
|
{ .compatible = "ti,twl6036-pmic", },
|
|
{ .compatible = "ti,twl6037-pmic", },
|
|
{ .compatible = "ti,tps65913-pmic", },
|
|
{ .compatible = "ti,tps65914-pmic", },
|
|
{ .compatible = "ti,tps80036-pmic", },
|
|
{ .compatible = "ti,tps659038-pmic", },
|
|
{ /* end */ }
|
|
};
|
|
|
|
static struct platform_driver palmas_driver = {
|
|
.driver = {
|
|
.name = "palmas-pmic",
|
|
.of_match_table = of_palmas_match_tbl,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = palmas_regulators_probe,
|
|
};
|
|
|
|
static int __init palmas_init(void)
|
|
{
|
|
return platform_driver_register(&palmas_driver);
|
|
}
|
|
subsys_initcall(palmas_init);
|
|
|
|
static void __exit palmas_exit(void)
|
|
{
|
|
platform_driver_unregister(&palmas_driver);
|
|
}
|
|
module_exit(palmas_exit);
|
|
|
|
MODULE_AUTHOR("Graeme Gregory <gg@slimlogic.co.uk>");
|
|
MODULE_DESCRIPTION("Palmas voltage regulator driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:palmas-pmic");
|
|
MODULE_DEVICE_TABLE(of, of_palmas_match_tbl);
|