18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * tps62360.c -- TI tps62360
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Driver for processor core supply tps62360, tps62361B, tps62362 and tps62363.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2012, NVIDIA Corporation.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Laxman Dewangan <ldewangan@nvidia.com>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
118c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as
128c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
158c2ecf20Sopenharmony_ci * whether express or implied; without even the implied warranty of
168c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
178c2ecf20Sopenharmony_ci * General Public License for more details.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License
208c2ecf20Sopenharmony_ci * along with this program; if not, write to the Free Software
218c2ecf20Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
228c2ecf20Sopenharmony_ci * 02111-1307, USA
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/kernel.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/init.h>
288c2ecf20Sopenharmony_ci#include <linux/err.h>
298c2ecf20Sopenharmony_ci#include <linux/of.h>
308c2ecf20Sopenharmony_ci#include <linux/of_device.h>
318c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
328c2ecf20Sopenharmony_ci#include <linux/regulator/of_regulator.h>
338c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
348c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h>
358c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h>
368c2ecf20Sopenharmony_ci#include <linux/regulator/tps62360.h>
378c2ecf20Sopenharmony_ci#include <linux/gpio.h>
388c2ecf20Sopenharmony_ci#include <linux/i2c.h>
398c2ecf20Sopenharmony_ci#include <linux/slab.h>
408c2ecf20Sopenharmony_ci#include <linux/regmap.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Register definitions */
438c2ecf20Sopenharmony_ci#define REG_VSET0		0
448c2ecf20Sopenharmony_ci#define REG_VSET1		1
458c2ecf20Sopenharmony_ci#define REG_VSET2		2
468c2ecf20Sopenharmony_ci#define REG_VSET3		3
478c2ecf20Sopenharmony_ci#define REG_CONTROL		4
488c2ecf20Sopenharmony_ci#define REG_TEMP		5
498c2ecf20Sopenharmony_ci#define REG_RAMPCTRL		6
508c2ecf20Sopenharmony_ci#define REG_CHIPID		8
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define FORCE_PWM_ENABLE	BIT(7)
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cienum chips {TPS62360, TPS62361, TPS62362, TPS62363};
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define TPS62360_BASE_VOLTAGE	770000
578c2ecf20Sopenharmony_ci#define TPS62360_N_VOLTAGES	64
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci#define TPS62361_BASE_VOLTAGE	500000
608c2ecf20Sopenharmony_ci#define TPS62361_N_VOLTAGES	128
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* tps 62360 chip information */
638c2ecf20Sopenharmony_cistruct tps62360_chip {
648c2ecf20Sopenharmony_ci	struct device *dev;
658c2ecf20Sopenharmony_ci	struct regulator_desc desc;
668c2ecf20Sopenharmony_ci	struct regulator_dev *rdev;
678c2ecf20Sopenharmony_ci	struct regmap *regmap;
688c2ecf20Sopenharmony_ci	int vsel0_gpio;
698c2ecf20Sopenharmony_ci	int vsel1_gpio;
708c2ecf20Sopenharmony_ci	u8 voltage_reg_mask;
718c2ecf20Sopenharmony_ci	bool en_internal_pulldn;
728c2ecf20Sopenharmony_ci	bool en_discharge;
738c2ecf20Sopenharmony_ci	bool valid_gpios;
748c2ecf20Sopenharmony_ci	int lru_index[4];
758c2ecf20Sopenharmony_ci	int curr_vset_vsel[4];
768c2ecf20Sopenharmony_ci	int curr_vset_id;
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/*
808c2ecf20Sopenharmony_ci * find_voltage_set_register: Find new voltage configuration register
818c2ecf20Sopenharmony_ci * (VSET) id.
828c2ecf20Sopenharmony_ci * The finding of the new VSET register will be based on the LRU mechanism.
838c2ecf20Sopenharmony_ci * Each VSET register will have different voltage configured . This
848c2ecf20Sopenharmony_ci * Function will look if any of the VSET register have requested voltage set
858c2ecf20Sopenharmony_ci * or not.
868c2ecf20Sopenharmony_ci *     - If it is already there then it will make that register as most
878c2ecf20Sopenharmony_ci *       recently used and return as found so that caller need not to set
888c2ecf20Sopenharmony_ci *       the VSET register but need to set the proper gpios to select this
898c2ecf20Sopenharmony_ci *       VSET register.
908c2ecf20Sopenharmony_ci *     - If requested voltage is not found then it will use the least
918c2ecf20Sopenharmony_ci *       recently mechanism to get new VSET register for new configuration
928c2ecf20Sopenharmony_ci *       and will return not_found so that caller need to set new VSET
938c2ecf20Sopenharmony_ci *       register and then gpios (both).
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_cistatic bool find_voltage_set_register(struct tps62360_chip *tps,
968c2ecf20Sopenharmony_ci		int req_vsel, int *vset_reg_id)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	int i;
998c2ecf20Sopenharmony_ci	bool found = false;
1008c2ecf20Sopenharmony_ci	int new_vset_reg = tps->lru_index[3];
1018c2ecf20Sopenharmony_ci	int found_index = 3;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	for (i = 0; i < 4; ++i) {
1048c2ecf20Sopenharmony_ci		if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) {
1058c2ecf20Sopenharmony_ci			new_vset_reg = tps->lru_index[i];
1068c2ecf20Sopenharmony_ci			found_index = i;
1078c2ecf20Sopenharmony_ci			found = true;
1088c2ecf20Sopenharmony_ci			goto update_lru_index;
1098c2ecf20Sopenharmony_ci		}
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ciupdate_lru_index:
1138c2ecf20Sopenharmony_ci	for (i = found_index; i > 0; i--)
1148c2ecf20Sopenharmony_ci		tps->lru_index[i] = tps->lru_index[i - 1];
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	tps->lru_index[0] = new_vset_reg;
1178c2ecf20Sopenharmony_ci	*vset_reg_id = new_vset_reg;
1188c2ecf20Sopenharmony_ci	return found;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int tps62360_dcdc_get_voltage_sel(struct regulator_dev *dev)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct tps62360_chip *tps = rdev_get_drvdata(dev);
1248c2ecf20Sopenharmony_ci	int vsel;
1258c2ecf20Sopenharmony_ci	unsigned int data;
1268c2ecf20Sopenharmony_ci	int ret;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data);
1298c2ecf20Sopenharmony_ci	if (ret < 0) {
1308c2ecf20Sopenharmony_ci		dev_err(tps->dev, "%s(): register %d read failed with err %d\n",
1318c2ecf20Sopenharmony_ci			__func__, REG_VSET0 + tps->curr_vset_id, ret);
1328c2ecf20Sopenharmony_ci		return ret;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	vsel = (int)data & tps->voltage_reg_mask;
1358c2ecf20Sopenharmony_ci	return vsel;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int tps62360_dcdc_set_voltage_sel(struct regulator_dev *dev,
1398c2ecf20Sopenharmony_ci					 unsigned selector)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct tps62360_chip *tps = rdev_get_drvdata(dev);
1428c2ecf20Sopenharmony_ci	int ret;
1438c2ecf20Sopenharmony_ci	bool found = false;
1448c2ecf20Sopenharmony_ci	int new_vset_id = tps->curr_vset_id;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	/*
1478c2ecf20Sopenharmony_ci	 * If gpios are available to select the VSET register then least
1488c2ecf20Sopenharmony_ci	 * recently used register for new configuration.
1498c2ecf20Sopenharmony_ci	 */
1508c2ecf20Sopenharmony_ci	if (tps->valid_gpios)
1518c2ecf20Sopenharmony_ci		found = find_voltage_set_register(tps, selector, &new_vset_id);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (!found) {
1548c2ecf20Sopenharmony_ci		ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id,
1558c2ecf20Sopenharmony_ci				tps->voltage_reg_mask, selector);
1568c2ecf20Sopenharmony_ci		if (ret < 0) {
1578c2ecf20Sopenharmony_ci			dev_err(tps->dev,
1588c2ecf20Sopenharmony_ci				"%s(): register %d update failed with err %d\n",
1598c2ecf20Sopenharmony_ci				 __func__, REG_VSET0 + new_vset_id, ret);
1608c2ecf20Sopenharmony_ci			return ret;
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci		tps->curr_vset_id = new_vset_id;
1638c2ecf20Sopenharmony_ci		tps->curr_vset_vsel[new_vset_id] = selector;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* Select proper VSET register vio gpios */
1678c2ecf20Sopenharmony_ci	if (tps->valid_gpios) {
1688c2ecf20Sopenharmony_ci		gpio_set_value_cansleep(tps->vsel0_gpio, new_vset_id & 0x1);
1698c2ecf20Sopenharmony_ci		gpio_set_value_cansleep(tps->vsel1_gpio,
1708c2ecf20Sopenharmony_ci					(new_vset_id >> 1) & 0x1);
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	return 0;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic int tps62360_set_mode(struct regulator_dev *rdev, unsigned int mode)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct tps62360_chip *tps = rdev_get_drvdata(rdev);
1788c2ecf20Sopenharmony_ci	int i;
1798c2ecf20Sopenharmony_ci	int val;
1808c2ecf20Sopenharmony_ci	int ret;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* Enable force PWM mode in FAST mode only. */
1838c2ecf20Sopenharmony_ci	switch (mode) {
1848c2ecf20Sopenharmony_ci	case REGULATOR_MODE_FAST:
1858c2ecf20Sopenharmony_ci		val = FORCE_PWM_ENABLE;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	case REGULATOR_MODE_NORMAL:
1898c2ecf20Sopenharmony_ci		val = 0;
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	default:
1938c2ecf20Sopenharmony_ci		return -EINVAL;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (!tps->valid_gpios) {
1978c2ecf20Sopenharmony_ci		ret = regmap_update_bits(tps->regmap,
1988c2ecf20Sopenharmony_ci			REG_VSET0 + tps->curr_vset_id, FORCE_PWM_ENABLE, val);
1998c2ecf20Sopenharmony_ci		if (ret < 0)
2008c2ecf20Sopenharmony_ci			dev_err(tps->dev,
2018c2ecf20Sopenharmony_ci				"%s(): register %d update failed with err %d\n",
2028c2ecf20Sopenharmony_ci				__func__, REG_VSET0 + tps->curr_vset_id, ret);
2038c2ecf20Sopenharmony_ci		return ret;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* If gpios are valid then all register set need to be control */
2078c2ecf20Sopenharmony_ci	for (i = 0; i < 4; ++i) {
2088c2ecf20Sopenharmony_ci		ret = regmap_update_bits(tps->regmap,
2098c2ecf20Sopenharmony_ci					REG_VSET0 + i, FORCE_PWM_ENABLE, val);
2108c2ecf20Sopenharmony_ci		if (ret < 0) {
2118c2ecf20Sopenharmony_ci			dev_err(tps->dev,
2128c2ecf20Sopenharmony_ci				"%s(): register %d update failed with err %d\n",
2138c2ecf20Sopenharmony_ci				__func__, REG_VSET0 + i, ret);
2148c2ecf20Sopenharmony_ci			return ret;
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci	return ret;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic unsigned int tps62360_get_mode(struct regulator_dev *rdev)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct tps62360_chip *tps = rdev_get_drvdata(rdev);
2238c2ecf20Sopenharmony_ci	unsigned int data;
2248c2ecf20Sopenharmony_ci	int ret;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data);
2278c2ecf20Sopenharmony_ci	if (ret < 0) {
2288c2ecf20Sopenharmony_ci		dev_err(tps->dev, "%s(): register %d read failed with err %d\n",
2298c2ecf20Sopenharmony_ci			__func__, REG_VSET0 + tps->curr_vset_id, ret);
2308c2ecf20Sopenharmony_ci		return ret;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci	return (data & FORCE_PWM_ENABLE) ?
2338c2ecf20Sopenharmony_ci				REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic const struct regulator_ops tps62360_dcdc_ops = {
2378c2ecf20Sopenharmony_ci	.get_voltage_sel	= tps62360_dcdc_get_voltage_sel,
2388c2ecf20Sopenharmony_ci	.set_voltage_sel	= tps62360_dcdc_set_voltage_sel,
2398c2ecf20Sopenharmony_ci	.list_voltage		= regulator_list_voltage_linear,
2408c2ecf20Sopenharmony_ci	.map_voltage		= regulator_map_voltage_linear,
2418c2ecf20Sopenharmony_ci	.set_voltage_time_sel	= regulator_set_voltage_time_sel,
2428c2ecf20Sopenharmony_ci	.set_mode		= tps62360_set_mode,
2438c2ecf20Sopenharmony_ci	.get_mode		= tps62360_get_mode,
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int tps62360_init_dcdc(struct tps62360_chip *tps,
2478c2ecf20Sopenharmony_ci		struct tps62360_regulator_platform_data *pdata)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	int ret;
2508c2ecf20Sopenharmony_ci	unsigned int ramp_ctrl;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	/* Initialize internal pull up/down control */
2538c2ecf20Sopenharmony_ci	if (tps->en_internal_pulldn)
2548c2ecf20Sopenharmony_ci		ret = regmap_write(tps->regmap, REG_CONTROL, 0xE0);
2558c2ecf20Sopenharmony_ci	else
2568c2ecf20Sopenharmony_ci		ret = regmap_write(tps->regmap, REG_CONTROL, 0x0);
2578c2ecf20Sopenharmony_ci	if (ret < 0) {
2588c2ecf20Sopenharmony_ci		dev_err(tps->dev,
2598c2ecf20Sopenharmony_ci			"%s(): register %d write failed with err %d\n",
2608c2ecf20Sopenharmony_ci			__func__, REG_CONTROL, ret);
2618c2ecf20Sopenharmony_ci		return ret;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	/* Reset output discharge path to reduce power consumption */
2658c2ecf20Sopenharmony_ci	ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), 0);
2668c2ecf20Sopenharmony_ci	if (ret < 0) {
2678c2ecf20Sopenharmony_ci		dev_err(tps->dev,
2688c2ecf20Sopenharmony_ci			"%s(): register %d update failed with err %d\n",
2698c2ecf20Sopenharmony_ci			__func__, REG_RAMPCTRL, ret);
2708c2ecf20Sopenharmony_ci		return ret;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Get ramp value from ramp control register */
2748c2ecf20Sopenharmony_ci	ret = regmap_read(tps->regmap, REG_RAMPCTRL, &ramp_ctrl);
2758c2ecf20Sopenharmony_ci	if (ret < 0) {
2768c2ecf20Sopenharmony_ci		dev_err(tps->dev,
2778c2ecf20Sopenharmony_ci			"%s(): register %d read failed with err %d\n",
2788c2ecf20Sopenharmony_ci			__func__, REG_RAMPCTRL, ret);
2798c2ecf20Sopenharmony_ci		return ret;
2808c2ecf20Sopenharmony_ci	}
2818c2ecf20Sopenharmony_ci	ramp_ctrl = (ramp_ctrl >> 5) & 0x7;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* ramp mV/us = 32/(2^ramp_ctrl) */
2848c2ecf20Sopenharmony_ci	tps->desc.ramp_delay = DIV_ROUND_UP(32000, BIT(ramp_ctrl));
2858c2ecf20Sopenharmony_ci	return ret;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic const struct regmap_config tps62360_regmap_config = {
2898c2ecf20Sopenharmony_ci	.reg_bits		= 8,
2908c2ecf20Sopenharmony_ci	.val_bits		= 8,
2918c2ecf20Sopenharmony_ci	.max_register		= REG_CHIPID,
2928c2ecf20Sopenharmony_ci	.cache_type		= REGCACHE_RBTREE,
2938c2ecf20Sopenharmony_ci};
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic struct tps62360_regulator_platform_data *
2968c2ecf20Sopenharmony_ci	of_get_tps62360_platform_data(struct device *dev,
2978c2ecf20Sopenharmony_ci				      const struct regulator_desc *desc)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct tps62360_regulator_platform_data *pdata;
3008c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
3038c2ecf20Sopenharmony_ci	if (!pdata)
3048c2ecf20Sopenharmony_ci		return NULL;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	pdata->reg_init_data = of_get_regulator_init_data(dev, dev->of_node,
3078c2ecf20Sopenharmony_ci							  desc);
3088c2ecf20Sopenharmony_ci	if (!pdata->reg_init_data) {
3098c2ecf20Sopenharmony_ci		dev_err(dev, "Not able to get OF regulator init data\n");
3108c2ecf20Sopenharmony_ci		return NULL;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	pdata->vsel0_gpio = of_get_named_gpio(np, "vsel0-gpio", 0);
3148c2ecf20Sopenharmony_ci	pdata->vsel1_gpio = of_get_named_gpio(np, "vsel1-gpio", 0);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (of_find_property(np, "ti,vsel0-state-high", NULL))
3178c2ecf20Sopenharmony_ci		pdata->vsel0_def_state = 1;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (of_find_property(np, "ti,vsel1-state-high", NULL))
3208c2ecf20Sopenharmony_ci		pdata->vsel1_def_state = 1;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (of_find_property(np, "ti,enable-pull-down", NULL))
3238c2ecf20Sopenharmony_ci		pdata->en_internal_pulldn = true;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (of_find_property(np, "ti,enable-vout-discharge", NULL))
3268c2ecf20Sopenharmony_ci		pdata->en_discharge = true;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	return pdata;
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci#if defined(CONFIG_OF)
3328c2ecf20Sopenharmony_cistatic const struct of_device_id tps62360_of_match[] = {
3338c2ecf20Sopenharmony_ci	 { .compatible = "ti,tps62360", .data = (void *)TPS62360},
3348c2ecf20Sopenharmony_ci	 { .compatible = "ti,tps62361", .data = (void *)TPS62361},
3358c2ecf20Sopenharmony_ci	 { .compatible = "ti,tps62362", .data = (void *)TPS62362},
3368c2ecf20Sopenharmony_ci	 { .compatible = "ti,tps62363", .data = (void *)TPS62363},
3378c2ecf20Sopenharmony_ci	{},
3388c2ecf20Sopenharmony_ci};
3398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tps62360_of_match);
3408c2ecf20Sopenharmony_ci#endif
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic int tps62360_probe(struct i2c_client *client,
3438c2ecf20Sopenharmony_ci				     const struct i2c_device_id *id)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	struct regulator_config config = { };
3468c2ecf20Sopenharmony_ci	struct tps62360_regulator_platform_data *pdata;
3478c2ecf20Sopenharmony_ci	struct regulator_dev *rdev;
3488c2ecf20Sopenharmony_ci	struct tps62360_chip *tps;
3498c2ecf20Sopenharmony_ci	int ret;
3508c2ecf20Sopenharmony_ci	int i;
3518c2ecf20Sopenharmony_ci	int chip_id;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	pdata = dev_get_platdata(&client->dev);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
3568c2ecf20Sopenharmony_ci	if (!tps)
3578c2ecf20Sopenharmony_ci		return -ENOMEM;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	tps->desc.name = client->name;
3608c2ecf20Sopenharmony_ci	tps->desc.id = 0;
3618c2ecf20Sopenharmony_ci	tps->desc.ops = &tps62360_dcdc_ops;
3628c2ecf20Sopenharmony_ci	tps->desc.type = REGULATOR_VOLTAGE;
3638c2ecf20Sopenharmony_ci	tps->desc.owner = THIS_MODULE;
3648c2ecf20Sopenharmony_ci	tps->desc.uV_step = 10000;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (client->dev.of_node) {
3678c2ecf20Sopenharmony_ci		const struct of_device_id *match;
3688c2ecf20Sopenharmony_ci		match = of_match_device(of_match_ptr(tps62360_of_match),
3698c2ecf20Sopenharmony_ci				&client->dev);
3708c2ecf20Sopenharmony_ci		if (!match) {
3718c2ecf20Sopenharmony_ci			dev_err(&client->dev, "Error: No device match found\n");
3728c2ecf20Sopenharmony_ci			return -ENODEV;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci		chip_id = (int)(long)match->data;
3758c2ecf20Sopenharmony_ci		if (!pdata)
3768c2ecf20Sopenharmony_ci			pdata = of_get_tps62360_platform_data(&client->dev,
3778c2ecf20Sopenharmony_ci							      &tps->desc);
3788c2ecf20Sopenharmony_ci	} else if (id) {
3798c2ecf20Sopenharmony_ci		chip_id = id->driver_data;
3808c2ecf20Sopenharmony_ci	} else {
3818c2ecf20Sopenharmony_ci		dev_err(&client->dev, "No device tree match or id table match found\n");
3828c2ecf20Sopenharmony_ci		return -ENODEV;
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	if (!pdata) {
3868c2ecf20Sopenharmony_ci		dev_err(&client->dev, "%s(): Platform data not found\n",
3878c2ecf20Sopenharmony_ci						__func__);
3888c2ecf20Sopenharmony_ci		return -EIO;
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	tps->en_discharge = pdata->en_discharge;
3928c2ecf20Sopenharmony_ci	tps->en_internal_pulldn = pdata->en_internal_pulldn;
3938c2ecf20Sopenharmony_ci	tps->vsel0_gpio = pdata->vsel0_gpio;
3948c2ecf20Sopenharmony_ci	tps->vsel1_gpio = pdata->vsel1_gpio;
3958c2ecf20Sopenharmony_ci	tps->dev = &client->dev;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	switch (chip_id) {
3988c2ecf20Sopenharmony_ci	case TPS62360:
3998c2ecf20Sopenharmony_ci	case TPS62362:
4008c2ecf20Sopenharmony_ci		tps->desc.min_uV = TPS62360_BASE_VOLTAGE;
4018c2ecf20Sopenharmony_ci		tps->voltage_reg_mask = 0x3F;
4028c2ecf20Sopenharmony_ci		tps->desc.n_voltages = TPS62360_N_VOLTAGES;
4038c2ecf20Sopenharmony_ci		break;
4048c2ecf20Sopenharmony_ci	case TPS62361:
4058c2ecf20Sopenharmony_ci	case TPS62363:
4068c2ecf20Sopenharmony_ci		tps->desc.min_uV = TPS62361_BASE_VOLTAGE;
4078c2ecf20Sopenharmony_ci		tps->voltage_reg_mask = 0x7F;
4088c2ecf20Sopenharmony_ci		tps->desc.n_voltages = TPS62361_N_VOLTAGES;
4098c2ecf20Sopenharmony_ci		break;
4108c2ecf20Sopenharmony_ci	default:
4118c2ecf20Sopenharmony_ci		return -ENODEV;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	tps->regmap = devm_regmap_init_i2c(client, &tps62360_regmap_config);
4158c2ecf20Sopenharmony_ci	if (IS_ERR(tps->regmap)) {
4168c2ecf20Sopenharmony_ci		ret = PTR_ERR(tps->regmap);
4178c2ecf20Sopenharmony_ci		dev_err(&client->dev,
4188c2ecf20Sopenharmony_ci			"%s(): regmap allocation failed with err %d\n",
4198c2ecf20Sopenharmony_ci			__func__, ret);
4208c2ecf20Sopenharmony_ci		return ret;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, tps);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	tps->curr_vset_id = (pdata->vsel1_def_state & 1) * 2 +
4258c2ecf20Sopenharmony_ci				(pdata->vsel0_def_state & 1);
4268c2ecf20Sopenharmony_ci	tps->lru_index[0] = tps->curr_vset_id;
4278c2ecf20Sopenharmony_ci	tps->valid_gpios = false;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (gpio_is_valid(tps->vsel0_gpio) && gpio_is_valid(tps->vsel1_gpio)) {
4308c2ecf20Sopenharmony_ci		int gpio_flags;
4318c2ecf20Sopenharmony_ci		gpio_flags = (pdata->vsel0_def_state) ?
4328c2ecf20Sopenharmony_ci				GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
4338c2ecf20Sopenharmony_ci		ret = devm_gpio_request_one(&client->dev, tps->vsel0_gpio,
4348c2ecf20Sopenharmony_ci				gpio_flags, "tps62360-vsel0");
4358c2ecf20Sopenharmony_ci		if (ret) {
4368c2ecf20Sopenharmony_ci			dev_err(&client->dev,
4378c2ecf20Sopenharmony_ci				"%s(): Could not obtain vsel0 GPIO %d: %d\n",
4388c2ecf20Sopenharmony_ci				__func__, tps->vsel0_gpio, ret);
4398c2ecf20Sopenharmony_ci			return ret;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		gpio_flags = (pdata->vsel1_def_state) ?
4438c2ecf20Sopenharmony_ci				GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
4448c2ecf20Sopenharmony_ci		ret = devm_gpio_request_one(&client->dev, tps->vsel1_gpio,
4458c2ecf20Sopenharmony_ci				gpio_flags, "tps62360-vsel1");
4468c2ecf20Sopenharmony_ci		if (ret) {
4478c2ecf20Sopenharmony_ci			dev_err(&client->dev,
4488c2ecf20Sopenharmony_ci				"%s(): Could not obtain vsel1 GPIO %d: %d\n",
4498c2ecf20Sopenharmony_ci				__func__, tps->vsel1_gpio, ret);
4508c2ecf20Sopenharmony_ci			return ret;
4518c2ecf20Sopenharmony_ci		}
4528c2ecf20Sopenharmony_ci		tps->valid_gpios = true;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		/*
4558c2ecf20Sopenharmony_ci		 * Initialize the lru index with vset_reg id
4568c2ecf20Sopenharmony_ci		 * The index 0 will be most recently used and
4578c2ecf20Sopenharmony_ci		 * set with the tps->curr_vset_id */
4588c2ecf20Sopenharmony_ci		for (i = 0; i < 4; ++i)
4598c2ecf20Sopenharmony_ci			tps->lru_index[i] = i;
4608c2ecf20Sopenharmony_ci		tps->lru_index[0] = tps->curr_vset_id;
4618c2ecf20Sopenharmony_ci		tps->lru_index[tps->curr_vset_id] = 0;
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	ret = tps62360_init_dcdc(tps, pdata);
4658c2ecf20Sopenharmony_ci	if (ret < 0) {
4668c2ecf20Sopenharmony_ci		dev_err(tps->dev, "%s(): Init failed with err = %d\n",
4678c2ecf20Sopenharmony_ci				__func__, ret);
4688c2ecf20Sopenharmony_ci		return ret;
4698c2ecf20Sopenharmony_ci	}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	config.dev = &client->dev;
4728c2ecf20Sopenharmony_ci	config.init_data = pdata->reg_init_data;
4738c2ecf20Sopenharmony_ci	config.driver_data = tps;
4748c2ecf20Sopenharmony_ci	config.of_node = client->dev.of_node;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/* Register the regulators */
4778c2ecf20Sopenharmony_ci	rdev = devm_regulator_register(&client->dev, &tps->desc, &config);
4788c2ecf20Sopenharmony_ci	if (IS_ERR(rdev)) {
4798c2ecf20Sopenharmony_ci		dev_err(tps->dev,
4808c2ecf20Sopenharmony_ci			"%s(): regulator register failed with err %s\n",
4818c2ecf20Sopenharmony_ci			__func__, id->name);
4828c2ecf20Sopenharmony_ci		return PTR_ERR(rdev);
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	tps->rdev = rdev;
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void tps62360_shutdown(struct i2c_client *client)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct tps62360_chip *tps = i2c_get_clientdata(client);
4928c2ecf20Sopenharmony_ci	int st;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	if (!tps->en_discharge)
4958c2ecf20Sopenharmony_ci		return;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/* Configure the output discharge path */
4988c2ecf20Sopenharmony_ci	st = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2));
4998c2ecf20Sopenharmony_ci	if (st < 0)
5008c2ecf20Sopenharmony_ci		dev_err(tps->dev,
5018c2ecf20Sopenharmony_ci			"%s(): register %d update failed with err %d\n",
5028c2ecf20Sopenharmony_ci			__func__, REG_RAMPCTRL, st);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic const struct i2c_device_id tps62360_id[] = {
5068c2ecf20Sopenharmony_ci	{.name = "tps62360", .driver_data = TPS62360},
5078c2ecf20Sopenharmony_ci	{.name = "tps62361", .driver_data = TPS62361},
5088c2ecf20Sopenharmony_ci	{.name = "tps62362", .driver_data = TPS62362},
5098c2ecf20Sopenharmony_ci	{.name = "tps62363", .driver_data = TPS62363},
5108c2ecf20Sopenharmony_ci	{},
5118c2ecf20Sopenharmony_ci};
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tps62360_id);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_cistatic struct i2c_driver tps62360_i2c_driver = {
5168c2ecf20Sopenharmony_ci	.driver = {
5178c2ecf20Sopenharmony_ci		.name = "tps62360",
5188c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(tps62360_of_match),
5198c2ecf20Sopenharmony_ci	},
5208c2ecf20Sopenharmony_ci	.probe = tps62360_probe,
5218c2ecf20Sopenharmony_ci	.shutdown = tps62360_shutdown,
5228c2ecf20Sopenharmony_ci	.id_table = tps62360_id,
5238c2ecf20Sopenharmony_ci};
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistatic int __init tps62360_init(void)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	return i2c_add_driver(&tps62360_i2c_driver);
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_cisubsys_initcall(tps62360_init);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void __exit tps62360_cleanup(void)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	i2c_del_driver(&tps62360_i2c_driver);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_cimodule_exit(tps62360_cleanup);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
5388c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TPS6236x voltage regulator driver");
5398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
540