Commit eb76d840 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'regulator/topic/max77686',...

Merge remote-tracking branches 'regulator/topic/max77686', 'regulator/topic/max8973', 'regulator/topic/maxim', 'regulator/topic/palmas' and 'regulator/topic/pv88080' into regulator-next
......@@ -32,6 +32,13 @@ Optional properties:
Enhanced transient response (ETR) will affect the configuration of CKADV.
-junction-warn-millicelsius: u32, junction warning temperature threshold
in millicelsius. If die temperature crosses this level then
device generates the warning interrupts.
Please note that thermal functionality is only supported on MAX77621. The
supported threshold warning temperature for MAX77621 are 120 degC and 140 degC.
Example:
max8973@1b {
......
* Powerventure Semiconductor PV88080 Voltage Regulator
Required properties:
- compatible: "pvs,pv88080".
- reg: I2C slave address, usually 0x49.
- interrupts: the interrupt outputs of the controller
- regulators: A node that houses a sub-node for each regulator within the
device. Each sub-node is identified using the node's name, with valid
values listed below. The content of each sub-node is defined by the
standard binding for regulators; see regulator.txt.
BUCK1, BUCK2, and BUCK3.
Optional properties:
- Any optional property defined in regulator.txt
Example
pmic: pv88080@49 {
compatible = "pvs,pv88080";
reg = <0x49>;
interrupt-parent = <&gpio>;
interrupts = <24 24>;
regulators {
BUCK1 {
regulator-name = "buck1";
regulator-min-microvolt = < 600000>;
regulator-max-microvolt = <1393750>;
regulator-min-microamp = < 220000>;
regulator-max-microamp = <7040000>;
};
BUCK2 {
regulator-name = "buck2";
regulator-min-microvolt = < 600000>;
regulator-max-microvolt = <1393750>;
regulator-min-microamp = <1496000>;
regulator-max-microamp = <4189000>;
};
BUCK3 {
regulator-name = "buck3";
regulator-min-microvolt = <1400000>;
regulator-max-microvolt = <2193750>;
regulator-min-microamp = <1496000>;
regulator-max-microamp = <4189000>;
};
};
};
......@@ -7020,9 +7020,9 @@ M: Chanwoo Choi <cw00.choi@samsung.com>
M: Krzysztof Kozlowski <k.kozlowski@samsung.com>
L: linux-kernel@vger.kernel.org
S: Supported
F: drivers/*/max14577.c
F: drivers/*/max14577*.c
F: drivers/*/max77686*.c
F: drivers/*/max77693.c
F: drivers/*/max77693*.c
F: drivers/extcon/extcon-max14577.c
F: drivers/extcon/extcon-max77693.c
F: drivers/rtc/rtc-max77686.c
......
......@@ -418,6 +418,7 @@ config REGULATOR_MAX8952
config REGULATOR_MAX8973
tristate "Maxim MAX8973 voltage regulator "
depends on I2C
depends on THERMAL && THERMAL_OF
select REGMAP_I2C
help
The MAXIM MAX8973 high-efficiency. three phase, DC-DC step-down
......@@ -557,6 +558,13 @@ config REGULATOR_PV88060
Say y here to support the voltage regulators and convertors
PV88060
config REGULATOR_PV88080
tristate "Powerventure Semiconductor PV88080 regulator"
depends on I2C
select REGMAP_I2C
help
Say y here to support the buck convertors on PV88080
config REGULATOR_PV88090
tristate "Powerventure Semiconductor PV88090 regulator"
depends on I2C
......
......@@ -47,7 +47,7 @@ obj-$(CONFIG_REGULATOR_LP8788) += lp8788-buck.o
obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
obj-$(CONFIG_REGULATOR_LP8755) += lp8755.o
obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577.o
obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
......@@ -56,10 +56,10 @@ obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
obj-$(CONFIG_REGULATOR_MAX8973) += max8973-regulator.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997-regulator.o
obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o
obj-$(CONFIG_REGULATOR_MAX77686) += max77686-regulator.o
obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o
obj-$(CONFIG_REGULATOR_MAX77693) += max77693-regulator.o
obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
......@@ -72,6 +72,7 @@ obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PV88060) += pv88060-regulator.o
obj-$(CONFIG_REGULATOR_PV88080) += pv88080-regulator.o
obj-$(CONFIG_REGULATOR_PV88090) += pv88090-regulator.o
obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
......
......@@ -41,6 +41,8 @@
#define MAX77686_LDO_LOW_UVSTEP 25000
#define MAX77686_BUCK_MINUV 750000
#define MAX77686_BUCK_UVSTEP 50000
#define MAX77686_BUCK_ENABLE_TIME 40 /* us */
#define MAX77686_DVS_ENABLE_TIME 22 /* us */
#define MAX77686_RAMP_DELAY 100000 /* uV/us */
#define MAX77686_DVS_RAMP_DELAY 27500 /* uV/us */
#define MAX77686_DVS_MINUV 600000
......@@ -422,6 +424,7 @@ static struct regulator_ops max77686_buck_dvs_ops = {
.min_uV = MAX77686_BUCK_MINUV, \
.uV_step = MAX77686_BUCK_UVSTEP, \
.ramp_delay = MAX77686_RAMP_DELAY, \
.enable_time = MAX77686_BUCK_ENABLE_TIME, \
.n_voltages = MAX77686_VSEL_MASK + 1, \
.vsel_reg = MAX77686_REG_BUCK5OUT + (num - 5) * 2, \
.vsel_mask = MAX77686_VSEL_MASK, \
......@@ -439,6 +442,7 @@ static struct regulator_ops max77686_buck_dvs_ops = {
.min_uV = MAX77686_BUCK_MINUV, \
.uV_step = MAX77686_BUCK_UVSTEP, \
.ramp_delay = MAX77686_RAMP_DELAY, \
.enable_time = MAX77686_BUCK_ENABLE_TIME, \
.n_voltages = MAX77686_VSEL_MASK + 1, \
.vsel_reg = MAX77686_REG_BUCK1OUT, \
.vsel_mask = MAX77686_VSEL_MASK, \
......@@ -456,6 +460,7 @@ static struct regulator_ops max77686_buck_dvs_ops = {
.min_uV = MAX77686_DVS_MINUV, \
.uV_step = MAX77686_DVS_UVSTEP, \
.ramp_delay = MAX77686_DVS_RAMP_DELAY, \
.enable_time = MAX77686_DVS_ENABLE_TIME, \
.n_voltages = MAX77686_DVS_VSEL_MASK + 1, \
.vsel_reg = MAX77686_REG_BUCK2DVS1 + (num - 2) * 10, \
.vsel_mask = MAX77686_DVS_VSEL_MASK, \
......@@ -553,17 +558,7 @@ static struct platform_driver max77686_pmic_driver = {
.id_table = max77686_pmic_id,
};
static int __init max77686_pmic_init(void)
{
return platform_driver_register(&max77686_pmic_driver);
}
subsys_initcall(max77686_pmic_init);
static void __exit max77686_pmic_cleanup(void)
{
platform_driver_unregister(&max77686_pmic_driver);
}
module_exit(max77686_pmic_cleanup);
module_platform_driver(max77686_pmic_driver);
MODULE_DESCRIPTION("MAXIM 77686 Regulator Driver");
MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
......
......@@ -5,7 +5,7 @@
* Simon Glass <sjg@chromium.org>
*
* Copyright (C) 2012 Samsung Electronics
* Chiwoong Byun <woong.byun@smasung.com>
* Chiwoong Byun <woong.byun@samsung.com>
* Jonghwa Lee <jonghwa3.lee@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
......
......@@ -38,6 +38,9 @@
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/thermal.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
/* Register definitions */
#define MAX8973_VOUT 0x0
......@@ -74,6 +77,7 @@
#define MAX8973_WDTMR_ENABLE BIT(6)
#define MAX8973_DISCH_ENBABLE BIT(5)
#define MAX8973_FT_ENABLE BIT(4)
#define MAX77621_T_JUNCTION_120 BIT(7)
#define MAX8973_CKKADV_TRIP_MASK 0xC
#define MAX8973_CKKADV_TRIP_DISABLE 0xC
......@@ -93,6 +97,12 @@
#define MAX8973_VOLATGE_STEP 6250
#define MAX8973_BUCK_N_VOLTAGE 0x80
#define MAX77621_CHIPID_TJINT_S BIT(0)
#define MAX77621_NORMAL_OPERATING_TEMP 100000
#define MAX77621_TJINT_WARNING_TEMP_120 120000
#define MAX77621_TJINT_WARNING_TEMP_140 140000
enum device_id {
MAX8973,
MAX77621
......@@ -112,6 +122,9 @@ struct max8973_chip {
int curr_gpio_val;
struct regulator_ops ops;
enum device_id id;
int junction_temp_warning;
int irq;
struct thermal_zone_device *tz_device;
};
/*
......@@ -391,6 +404,10 @@ static int max8973_init_dcdc(struct max8973_chip *max,
if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE)
control1 |= MAX8973_FREQSHIFT_9PER;
if ((pdata->junction_temp_warning == MAX77621_TJINT_WARNING_TEMP_120) &&
(max->id == MAX77621))
control2 |= MAX77621_T_JUNCTION_120;
if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE))
control2 |= MAX8973_DISCH_ENBABLE;
......@@ -457,6 +474,79 @@ static int max8973_init_dcdc(struct max8973_chip *max,
return ret;
}
static int max8973_thermal_read_temp(void *data, int *temp)
{
struct max8973_chip *mchip = data;
unsigned int val;
int ret;
ret = regmap_read(mchip->regmap, MAX8973_CHIPID1, &val);
if (ret < 0) {
dev_err(mchip->dev, "Failed to read register CHIPID1, %d", ret);
return ret;
}
/* +1 degC to trigger cool devive */
if (val & MAX77621_CHIPID_TJINT_S)
*temp = mchip->junction_temp_warning + 1000;
else
*temp = MAX77621_NORMAL_OPERATING_TEMP;
return 0;
}
static irqreturn_t max8973_thermal_irq(int irq, void *data)
{
struct max8973_chip *mchip = data;
thermal_zone_device_update(mchip->tz_device);
return IRQ_HANDLED;
}
static const struct thermal_zone_of_device_ops max77621_tz_ops = {
.get_temp = max8973_thermal_read_temp,
};
static int max8973_thermal_init(struct max8973_chip *mchip)
{
struct thermal_zone_device *tzd;
struct irq_data *irq_data;
unsigned long irq_flags = 0;
int ret;
if (mchip->id != MAX77621)
return 0;
tzd = devm_thermal_zone_of_sensor_register(mchip->dev, 0, mchip,
&max77621_tz_ops);
if (IS_ERR(tzd)) {
ret = PTR_ERR(tzd);
dev_err(mchip->dev, "Failed to register thermal sensor: %d\n",
ret);
return ret;
}
if (mchip->irq <= 0)
return 0;
irq_data = irq_get_irq_data(mchip->irq);
if (irq_data)
irq_flags = irqd_get_trigger_type(irq_data);
ret = devm_request_threaded_irq(mchip->dev, mchip->irq, NULL,
max8973_thermal_irq,
IRQF_ONESHOT | IRQF_SHARED | irq_flags,
dev_name(mchip->dev), mchip);
if (ret < 0) {
dev_err(mchip->dev, "Failed to request irq %d, %d\n",
mchip->irq, ret);
return ret;
}
return 0;
}
static const struct regmap_config max8973_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
......@@ -521,6 +611,11 @@ static struct max8973_regulator_platform_data *max8973_parse_dt(
pdata->control_flags |= MAX8973_CONTROL_CLKADV_TRIP_DISABLED;
}
pdata->junction_temp_warning = MAX77621_TJINT_WARNING_TEMP_140;
ret = of_property_read_u32(np, "junction-warn-millicelsius", &pval);
if (!ret && (pval <= MAX77621_TJINT_WARNING_TEMP_120))
pdata->junction_temp_warning = MAX77621_TJINT_WARNING_TEMP_120;
return pdata;
}
......@@ -608,6 +703,7 @@ static int max8973_probe(struct i2c_client *client,
max->enable_external_control = pdata->enable_ext_control;
max->curr_gpio_val = pdata->dvs_def_state;
max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
max->junction_temp_warning = pdata->junction_temp_warning;
if (gpio_is_valid(max->enable_gpio))
max->enable_external_control = true;
......@@ -718,6 +814,7 @@ static int max8973_probe(struct i2c_client *client,
return ret;
}
max8973_thermal_init(max);
return 0;
}
......
......@@ -2,7 +2,7 @@
* max8997.c - Regulator driver for the Maxim 8997/8966
*
* Copyright (C) 2011 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@smasung.com>
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* 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
......
......@@ -944,6 +944,8 @@ static int palmas_ldo_registration(struct palmas_pmic *pmic,
if (id == PALMAS_REG_LDO9) {
desc->ops = &palmas_ops_ldo9;
desc->bypass_reg = desc->enable_reg;
desc->bypass_val_on =
PALMAS_LDO9_CTRL_LDO_BYPASS_EN;
desc->bypass_mask =
PALMAS_LDO9_CTRL_LDO_BYPASS_EN;
}
......@@ -1055,6 +1057,8 @@ static int tps65917_ldo_registration(struct palmas_pmic *pmic,
id == TPS65917_REG_LDO2) {
desc->ops = &tps65917_ops_ldo_1_2;
desc->bypass_reg = desc->enable_reg;
desc->bypass_val_on =
TPS65917_LDO1_CTRL_BYPASS_EN;
desc->bypass_mask =
TPS65917_LDO1_CTRL_BYPASS_EN;
}
......@@ -1206,6 +1210,7 @@ static int palmas_smps_registration(struct palmas_pmic *pmic,
desc->enable_mask = SMPS10_BOOST_EN;
desc->bypass_reg = PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
PALMAS_SMPS10_CTRL);
desc->bypass_val_on = SMPS10_BYPASS_EN;
desc->bypass_mask = SMPS10_BYPASS_EN;
desc->min_uV = 3750000;
desc->uV_step = 1250000;
......@@ -1462,7 +1467,7 @@ static struct palmas_pmic_driver_data tps65917_ddata = {
.ldo_register = tps65917_ldo_registration,
};
static void palmas_dt_to_pdata(struct device *dev,
static int palmas_dt_to_pdata(struct device *dev,
struct device_node *node,
struct palmas_pmic_platform_data *pdata,
struct palmas_pmic_driver_data *ddata)
......@@ -1474,7 +1479,7 @@ static void palmas_dt_to_pdata(struct device *dev,
regulators = of_get_child_by_name(node, "regulators");
if (!regulators) {
dev_info(dev, "regulator node not found\n");
return;
return 0;
}
ret = of_regulator_match(dev, regulators, ddata->palmas_matches,
......@@ -1482,25 +1487,29 @@ static void palmas_dt_to_pdata(struct device *dev,
of_node_put(regulators);
if (ret < 0) {
dev_err(dev, "Error parsing regulator init data: %d\n", ret);
return;
return 0;
}
for (idx = 0; idx < ddata->max_reg; idx++) {
if (!ddata->palmas_matches[idx].init_data ||
!ddata->palmas_matches[idx].of_node)
continue;
static struct of_regulator_match *match;
struct palmas_reg_init *rinit;
struct device_node *np;
pdata->reg_data[idx] = ddata->palmas_matches[idx].init_data;
match = &ddata->palmas_matches[idx];
np = match->of_node;
pdata->reg_init[idx] = devm_kzalloc(dev,
sizeof(struct palmas_reg_init), GFP_KERNEL);
if (!match->init_data || !np)
continue;
rinit = devm_kzalloc(dev, sizeof(*rinit), GFP_KERNEL);
if (!rinit)
return -ENOMEM;
pdata->reg_init[idx]->warm_reset =
of_property_read_bool(ddata->palmas_matches[idx].of_node,
"ti,warm-reset");
pdata->reg_data[idx] = match->init_data;
pdata->reg_init[idx] = rinit;
ret = of_property_read_u32(ddata->palmas_matches[idx].of_node,
"ti,roof-floor", &prop);
rinit->warm_reset = of_property_read_bool(np, "ti,warm-reset");
ret = of_property_read_u32(np, "ti,roof-floor", &prop);
/* EINVAL: Property not found */
if (ret != -EINVAL) {
int econtrol;
......@@ -1522,31 +1531,29 @@ static void palmas_dt_to_pdata(struct device *dev,
WARN_ON(1);
dev_warn(dev,
"%s: Invalid roof-floor option: %u\n",
palmas_matches[idx].name, prop);
match->name, prop);
break;
}
}
pdata->reg_init[idx]->roof_floor = econtrol;
rinit->roof_floor = econtrol;
}
ret = of_property_read_u32(ddata->palmas_matches[idx].of_node,
"ti,mode-sleep", &prop);
ret = of_property_read_u32(np, "ti,mode-sleep", &prop);
if (!ret)
pdata->reg_init[idx]->mode_sleep = prop;
rinit->mode_sleep = prop;
ret = of_property_read_bool(ddata->palmas_matches[idx].of_node,
"ti,smps-range");
ret = of_property_read_bool(np, "ti,smps-range");
if (ret)
pdata->reg_init[idx]->vsel =
PALMAS_SMPS12_VOLTAGE_RANGE;
rinit->vsel = PALMAS_SMPS12_VOLTAGE_RANGE;
if (idx == PALMAS_REG_LDO8)
pdata->enable_ldo8_tracking = of_property_read_bool(
ddata->palmas_matches[idx].of_node,
"ti,enable-ldo8-tracking");
np, "ti,enable-ldo8-tracking");
}
pdata->ldo6_vibrator = of_property_read_bool(node, "ti,ldo6-vibrator");
return 0;
}
static const struct of_device_id of_palmas_match_tbl[] = {
......@@ -1628,7 +1635,9 @@ static int palmas_regulators_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, pmic);
pmic->palmas->pmic_ddata = driver_data;
palmas_dt_to_pdata(&pdev->dev, node, pdata, driver_data);
ret = palmas_dt_to_pdata(&pdev->dev, node, pdata, driver_data);
if (ret)
return ret;
ret = palmas_smps_read(palmas, PALMAS_SMPS_CTRL, &reg);
if (ret)
......
/*
* pv88080-regulator.c - Regulator device driver for PV88080
* Copyright (C) 2016 Powerventure Semiconductor Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regmap.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/regulator/of_regulator.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
#include "pv88080-regulator.h"
#define PV88080_MAX_REGULATORS 3
/* PV88080 REGULATOR IDs */
enum {
/* BUCKs */
PV88080_ID_BUCK1,
PV88080_ID_BUCK2,
PV88080_ID_BUCK3,
};
struct pv88080_regulator {
struct regulator_desc desc;
/* Current limiting */
unsigned int n_current_limits;
const int *current_limits;
unsigned int limit_mask;
unsigned int conf;
unsigned int conf2;
unsigned int conf5;
};
struct pv88080 {
struct device *dev;
struct regmap *regmap;
struct regulator_dev *rdev[PV88080_MAX_REGULATORS];
};
struct pv88080_buck_voltage {
int min_uV;
int max_uV;
int uV_step;
};
static const struct regmap_config pv88080_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
/* Current limits array (in uA) for BUCK1, BUCK2, BUCK3.
* Entry indexes corresponds to register values.
*/
static const int pv88080_buck1_limits[] = {
3230000, 5130000, 6960000, 8790000
};
static const int pv88080_buck23_limits[] = {
1496000, 2393000, 3291000, 4189000
};
static const struct pv88080_buck_voltage pv88080_buck_vol[2] = {
{
.min_uV = 600000,
.max_uV = 1393750,
.uV_step = 6250,
},
{
.min_uV = 1400000,
.max_uV = 2193750,
.uV_step = 6250,
},
};
static unsigned int pv88080_buck_get_mode(struct regulator_dev *rdev)
{
struct pv88080_regulator *info = rdev_get_drvdata(rdev);
unsigned int data;
int ret, mode = 0;
ret = regmap_read(rdev->regmap, info->conf, &data);
if (ret < 0)
return ret;
switch (data & PV88080_BUCK1_MODE_MASK) {
case PV88080_BUCK_MODE_SYNC:
mode = REGULATOR_MODE_FAST;
break;
case PV88080_BUCK_MODE_AUTO:
mode = REGULATOR_MODE_NORMAL;
break;
case PV88080_BUCK_MODE_SLEEP:
mode = REGULATOR_MODE_STANDBY;
break;
default:
return -EINVAL;
}
return mode;
}
static int pv88080_buck_set_mode(struct regulator_dev *rdev,
unsigned int mode)
{
struct pv88080_regulator *info = rdev_get_drvdata(rdev);
int val = 0;
switch (mode) {
case REGULATOR_MODE_FAST:
val = PV88080_BUCK_MODE_SYNC;
break;
case REGULATOR_MODE_NORMAL:
val = PV88080_BUCK_MODE_AUTO;
break;
case REGULATOR_MODE_STANDBY:
val = PV88080_BUCK_MODE_SLEEP;
break;
default:
return -EINVAL;
}
return regmap_update_bits(rdev->regmap, info->conf,
PV88080_BUCK1_MODE_MASK, val);
}
static int pv88080_set_current_limit(struct regulator_dev *rdev, int min,
int max)
{
struct pv88080_regulator *info = rdev_get_drvdata(rdev);
int i;
/* search for closest to maximum */
for (i = info->n_current_limits; i >= 0; i--) {
if (min <= info->current_limits[i]
&& max >= info->current_limits[i]) {
return regmap_update_bits(rdev->regmap,
info->conf,
info->limit_mask,
i << PV88080_BUCK1_ILIM_SHIFT);
}
}
return -EINVAL;
}
static int pv88080_get_current_limit(struct regulator_dev *rdev)
{
struct pv88080_regulator *info = rdev_get_drvdata(rdev);
unsigned int data;
int ret;
ret = regmap_read(rdev->regmap, info->conf, &data);
if (ret < 0)
return ret;
data = (data & info->limit_mask) >> PV88080_BUCK1_ILIM_SHIFT;
return info->current_limits[data];
}
static struct regulator_ops pv88080_buck_ops = {
.get_mode = pv88080_buck_get_mode,
.set_mode = pv88080_buck_set_mode,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.is_enabled = regulator_is_enabled_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.list_voltage = regulator_list_voltage_linear,
.set_current_limit = pv88080_set_current_limit,
.get_current_limit = pv88080_get_current_limit,
};
#define PV88080_BUCK(chip, regl_name, min, step, max, limits_array) \
{\
.desc = {\
.id = chip##_ID_##regl_name,\
.name = __stringify(chip##_##regl_name),\
.of_match = of_match_ptr(#regl_name),\
.regulators_node = of_match_ptr("regulators"),\
.type = REGULATOR_VOLTAGE,\
.owner = THIS_MODULE,\
.ops = &pv88080_buck_ops,\
.min_uV = min, \
.uV_step = step, \
.n_voltages = ((max) - (min))/(step) + 1, \
.enable_reg = PV88080_REG_##regl_name##_CONF0, \
.enable_mask = PV88080_##regl_name##_EN, \
.vsel_reg = PV88080_REG_##regl_name##_CONF0, \
.vsel_mask = PV88080_V##regl_name##_MASK, \
},\
.current_limits = limits_array, \
.n_current_limits = ARRAY_SIZE(limits_array), \
.limit_mask = PV88080_##regl_name##_ILIM_MASK, \
.conf = PV88080_REG_##regl_name##_CONF1, \
.conf2 = PV88080_REG_##regl_name##_CONF2, \
.conf5 = PV88080_REG_##regl_name##_CONF5, \
}
static struct pv88080_regulator pv88080_regulator_info[] = {
PV88080_BUCK(PV88080, BUCK1, 600000, 6250, 1393750,
pv88080_buck1_limits),
PV88080_BUCK(PV88080, BUCK2, 600000, 6250, 1393750,
pv88080_buck23_limits),
PV88080_BUCK(PV88080, BUCK3, 600000, 6250, 1393750,
pv88080_buck23_limits),
};
static irqreturn_t pv88080_irq_handler(int irq, void *data)
{
struct pv88080 *chip = data;
int i, reg_val, err, ret = IRQ_NONE;
err = regmap_read(chip->regmap, PV88080_REG_EVENT_A, &reg_val);
if (err < 0)
goto error_i2c;
if (reg_val & PV88080_E_VDD_FLT) {
for (i = 0; i < PV88080_MAX_REGULATORS; i++) {
if (chip->rdev[i] != NULL) {
regulator_notifier_call_chain(chip->rdev[i],
REGULATOR_EVENT_UNDER_VOLTAGE,
NULL);
}
}
err = regmap_write(chip->regmap, PV88080_REG_EVENT_A,
PV88080_E_VDD_FLT);
if (err < 0)
goto error_i2c;
ret = IRQ_HANDLED;
}
if (reg_val & PV88080_E_OVER_TEMP) {
for (i = 0; i < PV88080_MAX_REGULATORS; i++) {
if (chip->rdev[i] != NULL) {
regulator_notifier_call_chain(chip->rdev[i],
REGULATOR_EVENT_OVER_TEMP,
NULL);
}
}
err = regmap_write(chip->regmap, PV88080_REG_EVENT_A,
PV88080_E_OVER_TEMP);
if (err < 0)
goto error_i2c;
ret = IRQ_HANDLED;
}
return ret;
error_i2c:
dev_err(chip->dev, "I2C error : %d\n", err);
return IRQ_NONE;
}
/*
* I2C driver interface functions
*/
static int pv88080_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct regulator_init_data *init_data = dev_get_platdata(&i2c->dev);
struct pv88080 *chip;
struct regulator_config config = { };
int i, error, ret;
unsigned int conf2, conf5;
chip = devm_kzalloc(&i2c->dev, sizeof(struct pv88080), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->dev = &i2c->dev;
chip->regmap = devm_regmap_init_i2c(i2c, &pv88080_regmap_config);
if (IS_ERR(chip->regmap)) {
error = PTR_ERR(chip->regmap);
dev_err(chip->dev, "Failed to allocate register map: %d\n",
error);
return error;
}
i2c_set_clientdata(i2c, chip);
if (i2c->irq != 0) {
ret = regmap_write(chip->regmap, PV88080_REG_MASK_A, 0xFF);
if (ret < 0) {
dev_err(chip->dev,
"Failed to mask A reg: %d\n", ret);
return ret;
}
ret = regmap_write(chip->regmap, PV88080_REG_MASK_B, 0xFF);
if (ret < 0) {
dev_err(chip->dev,
"Failed to mask B reg: %d\n", ret);
return ret;
}
ret = regmap_write(chip->regmap, PV88080_REG_MASK_C, 0xFF);
if (ret < 0) {
dev_err(chip->dev,
"Failed to mask C reg: %d\n", ret);
return ret;
}
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
pv88080_irq_handler,
IRQF_TRIGGER_LOW|IRQF_ONESHOT,
"pv88080", chip);
if (ret != 0) {
dev_err(chip->dev, "Failed to request IRQ: %d\n",
i2c->irq);
return ret;
}
ret = regmap_update_bits(chip->regmap, PV88080_REG_MASK_A,
PV88080_M_VDD_FLT | PV88080_M_OVER_TEMP, 0);
if (ret < 0) {
dev_err(chip->dev,
"Failed to update mask reg: %d\n", ret);
return ret;
}
} else {
dev_warn(chip->dev, "No IRQ configured\n");
}
config.dev = chip->dev;
config.regmap = chip->regmap;
for (i = 0; i < PV88080_MAX_REGULATORS; i++) {
if (init_data)
config.init_data = &init_data[i];
ret = regmap_read(chip->regmap,
pv88080_regulator_info[i].conf2, &conf2);
if (ret < 0)
return ret;
conf2 = ((conf2 >> PV88080_BUCK_VDAC_RANGE_SHIFT) &
PV88080_BUCK_VDAC_RANGE_MASK);
ret = regmap_read(chip->regmap,
pv88080_regulator_info[i].conf5, &conf5);
if (ret < 0)
return ret;
conf5 = ((conf5 >> PV88080_BUCK_VRANGE_GAIN_SHIFT) &
PV88080_BUCK_VRANGE_GAIN_MASK);
pv88080_regulator_info[i].desc.min_uV =
pv88080_buck_vol[conf2].min_uV * (conf5+1);
pv88080_regulator_info[i].desc.uV_step =
pv88080_buck_vol[conf2].uV_step * (conf5+1);
pv88080_regulator_info[i].desc.n_voltages =
((pv88080_buck_vol[conf2].max_uV * (conf5+1))
- (pv88080_regulator_info[i].desc.min_uV))
/(pv88080_regulator_info[i].desc.uV_step) + 1;
config.driver_data = (void *)&pv88080_regulator_info[i];
chip->rdev[i] = devm_regulator_register(chip->dev,
&pv88080_regulator_info[i].desc, &config);
if (IS_ERR(chip->rdev[i])) {
dev_err(chip->dev,
"Failed to register PV88080 regulator\n");
return PTR_ERR(chip->rdev[i]);
}
}
return 0;
}
static const struct i2c_device_id pv88080_i2c_id[] = {
{"pv88080", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, pv88080_i2c_id);
#ifdef CONFIG_OF
static const struct of_device_id pv88080_dt_ids[] = {
{ .compatible = "pvs,pv88080", .data = &pv88080_i2c_id[0] },
{},
};
MODULE_DEVICE_TABLE(of, pv88080_dt_ids);
#endif
static struct i2c_driver pv88080_regulator_driver = {
.driver = {
.name = "pv88080",
.of_match_table = of_match_ptr(pv88080_dt_ids),
},
.probe = pv88080_i2c_probe,
.id_table = pv88080_i2c_id,
};
module_i2c_driver(pv88080_regulator_driver);
MODULE_AUTHOR("James Ban <James.Ban.opensource@diasemi.com>");
MODULE_DESCRIPTION("Regulator device driver for Powerventure PV88080");
MODULE_LICENSE("GPL");
/*
* pv88080-regulator.h - Regulator definitions for PV88080
* Copyright (C) 2016 Powerventure Semiconductor Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __PV88080_REGISTERS_H__
#define __PV88080_REGISTERS_H__
/* System Control and Event Registers */
#define PV88080_REG_EVENT_A 0x04
#define PV88080_REG_MASK_A 0x09
#define PV88080_REG_MASK_B 0x0a
#define PV88080_REG_MASK_C 0x0b
/* Regulator Registers */
#define PV88080_REG_BUCK1_CONF0 0x27
#define PV88080_REG_BUCK1_CONF1 0x28
#define PV88080_REG_BUCK1_CONF2 0x59
#define PV88080_REG_BUCK1_CONF5 0x5c
#define PV88080_REG_BUCK2_CONF0 0x29
#define PV88080_REG_BUCK2_CONF1 0x2a
#define PV88080_REG_BUCK2_CONF2 0x61
#define PV88080_REG_BUCK2_CONF5 0x64
#define PV88080_REG_BUCK3_CONF0 0x2b
#define PV88080_REG_BUCK3_CONF1 0x2c
#define PV88080_REG_BUCK3_CONF2 0x69
#define PV88080_REG_BUCK3_CONF5 0x6c
/* PV88080_REG_EVENT_A (addr=0x04) */
#define PV88080_E_VDD_FLT 0x01
#define PV88080_E_OVER_TEMP 0x02
/* PV88080_REG_MASK_A (addr=0x09) */
#define PV88080_M_VDD_FLT 0x01
#define PV88080_M_OVER_TEMP 0x02
/* PV88080_REG_BUCK1_CONF0 (addr=0x27) */
#define PV88080_BUCK1_EN 0x80
#define PV88080_VBUCK1_MASK 0x7F
/* PV88080_REG_BUCK2_CONF0 (addr=0x29) */
#define PV88080_BUCK2_EN 0x80
#define PV88080_VBUCK2_MASK 0x7F
/* PV88080_REG_BUCK3_CONF0 (addr=0x2b) */
#define PV88080_BUCK3_EN 0x80
#define PV88080_VBUCK3_MASK 0x7F
/* PV88080_REG_BUCK1_CONF1 (addr=0x28) */
#define PV88080_BUCK1_ILIM_SHIFT 2
#define PV88080_BUCK1_ILIM_MASK 0x0C
#define PV88080_BUCK1_MODE_MASK 0x03
/* PV88080_REG_BUCK2_CONF1 (addr=0x2a) */
#define PV88080_BUCK2_ILIM_SHIFT 2
#define PV88080_BUCK2_ILIM_MASK 0x0C
#define PV88080_BUCK2_MODE_MASK 0x03
/* PV88080_REG_BUCK3_CONF1 (addr=0x2c) */
#define PV88080_BUCK3_ILIM_SHIFT 2
#define PV88080_BUCK3_ILIM_MASK 0x0C
#define PV88080_BUCK3_MODE_MASK 0x03
#define PV88080_BUCK_MODE_SLEEP 0x00
#define PV88080_BUCK_MODE_AUTO 0x01
#define PV88080_BUCK_MODE_SYNC 0x02
/* PV88080_REG_BUCK2_CONF2 (addr=0x61) */
/* PV88080_REG_BUCK3_CONF2 (addr=0x69) */
#define PV88080_BUCK_VDAC_RANGE_SHIFT 7
#define PV88080_BUCK_VDAC_RANGE_MASK 0x01
#define PV88080_BUCK_VDAC_RANGE_1 0x00
#define PV88080_BUCK_VDAC_RANGE_2 0x01
/* PV88080_REG_BUCK2_CONF5 (addr=0x64) */
/* PV88080_REG_BUCK3_CONF5 (addr=0x6c) */
#define PV88080_BUCK_VRANGE_GAIN_SHIFT 0
#define PV88080_BUCK_VRANGE_GAIN_MASK 0x01
#define PV88080_BUCK_VRANGE_GAIN_1 0x00
#define PV88080_BUCK_VRANGE_GAIN_2 0x01
#endif /* __PV88080_REGISTERS_H__ */
......@@ -54,6 +54,10 @@
* @reg_init_data: The regulator init data.
* @control_flags: Control flags which are ORed value of above flags to
* configure device.
* @junction_temp_warning: Junction temp in millicelcius on which warning need
* to be set. Thermal functionality is only supported on
* MAX77621. The threshold warning supported by MAX77621
* are 120C and 140C.
* @enable_ext_control: Enable the voltage enable/disable through external
* control signal from EN input pin. If it is false then
* voltage output will be enabled/disabled through EN bit of
......@@ -67,6 +71,7 @@
struct max8973_regulator_platform_data {
struct regulator_init_data *reg_init_data;
unsigned long control_flags;
unsigned long junction_temp_warning;
bool enable_ext_control;
int enable_gpio;
int dvs_gpio;
......
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