Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a8901698 authored by Gaurav Singhal's avatar Gaurav Singhal
Browse files

NFC: Add support for LDO voting



Vote for LDO regulator during probe() if LDO related
properties are present in the device tree otherwise
we will avoid LDO configuration as it's optional.

Configure VEN gpio low and unvote for LDO during driver unloading.

Change-Id: I36ecdc5cef5afb6ddb0ef6ea4db6e745e567ab7b
Signed-off-by: default avatarGaurav Singhal <gsinghal@codeaurora.org>
parent 17439c2c
Loading
Loading
Loading
Loading
+134 −1
Original line number Diff line number Diff line
@@ -10,9 +10,10 @@


int nfc_parse_dt(struct device *dev, struct platform_gpio *nfc_gpio,
		 uint8_t interface)
		 struct platform_ldo *ldo, uint8_t interface)
{
	struct device_node *np = dev->of_node;
	int ret;

	if (!np) {
		pr_err("nfc of_node NULL\n");
@@ -49,9 +50,139 @@ int nfc_parse_dt(struct device *dev, struct platform_gpio *nfc_gpio,
	pr_info("%s: ven %d, dwl req %d, clkreq %d\n", __func__,
		nfc_gpio->ven, nfc_gpio->dwl_req, nfc_gpio->clkreq);

	// optional property
	ret = of_property_read_u32_array(np, NFC_LDO_VOL_DT_NAME,
			(u32 *) ldo->vdd_levels,
			ARRAY_SIZE(ldo->vdd_levels));
	if (ret) {
		dev_err(dev, "error reading NFC VDDIO min and max value\n");
		// set default as per datasheet
		ldo->vdd_levels[0] = NFC_VDDIO_MIN;
		ldo->vdd_levels[1] = NFC_VDDIO_MAX;
	}

	// optional property
	ret = of_property_read_u32(np, NFC_LDO_CUR_DT_NAME, &ldo->max_current);
	if (ret) {
		dev_err(dev, "error reading NFC current value\n");
		// set default as per datasheet
		ldo->max_current = NFC_CURRENT_MAX;
	}

	return 0;
}

/**
 * nfc_ldo_vote()
 * @nfc_dev: NFC device containing regulator handle
 *
 * LDO voting based on voltage and current entries in DT
 *
 * Return: 0 on success and -ve on failure
 */
int nfc_ldo_vote(struct nfc_dev *nfc_dev)
{
	int ret;

	ret =  regulator_set_voltage(nfc_dev->reg,
			nfc_dev->ldo.vdd_levels[0],
			nfc_dev->ldo.vdd_levels[1]);
	if (ret < 0) {
		pr_err("%s: set voltage failed\n", __func__);
		return ret;
	}

	/* pass expected current from NFC in uA */
	ret = regulator_set_load(nfc_dev->reg, nfc_dev->ldo.max_current);
	if (ret < 0) {
		pr_err("%s: set load failed\n", __func__);
		return ret;
	}

	ret = regulator_enable(nfc_dev->reg);
	if (ret < 0)
		pr_err("%s: regulator_enable failed\n", __func__);
	else
		nfc_dev->is_vreg_enabled = true;
	return ret;
}

/**
 * nfc_ldo_config()
 * @dev: device instance to read DT entry
 * @nfc_dev: NFC device containing regulator handle
 *
 * Configure LDO if entry is present in DT file otherwise
 * return with success as it's optional
 *
 * Return: 0 on success and -ve on failure
 */
int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev)
{
	int ret;

	if (of_get_property(dev->of_node, NFC_LDO_SUPPLY_NAME, NULL)) {
		// Get the regulator handle
		nfc_dev->reg = regulator_get(dev, NFC_LDO_SUPPLY_DT_NAME);
		if (IS_ERR(nfc_dev->reg)) {
			ret = PTR_ERR(nfc_dev->reg);
			nfc_dev->reg = NULL;
			pr_err("%s: regulator_get failed, ret = %d\n",
				__func__, ret);
			return ret;
		}
	} else {
		nfc_dev->reg = NULL;
		pr_err("%s: regulator entry not present\n", __func__);
		// return success as it's optional to configure LDO
		return 0;
	}

	// LDO config supported by platform DT
	ret = nfc_ldo_vote(nfc_dev);
	if (ret < 0) {
		pr_err("%s: LDO voting failed, ret = %d\n", __func__, ret);
		regulator_put(nfc_dev->reg);
	}
	return ret;
}

/**
 * nfc_ldo_unvote()
 * @nfc_dev: NFC device containing regulator handle
 *
 * set voltage and load to zero and disable regulator
 *
 * Return: 0 on success and -ve on failure
 */
int nfc_ldo_unvote(struct nfc_dev *nfc_dev)
{
	int ret;

	if (!nfc_dev->is_vreg_enabled) {
		pr_err("%s: regulator already disabled\n", __func__);
		return -EINVAL;
	}

	ret = regulator_disable(nfc_dev->reg);
	if (ret < 0) {
		pr_err("%s: regulator_disable failed\n", __func__);
		return ret;
	}
	nfc_dev->is_vreg_enabled = false;

	ret =  regulator_set_voltage(nfc_dev->reg, 0, NFC_VDDIO_MAX);
	if (ret < 0) {
		pr_err("%s: set voltage failed\n", __func__);
		return ret;
	}

	ret = regulator_set_load(nfc_dev->reg, 0);
	if (ret < 0)
		pr_err("%s: set load failed\n", __func__);
	return ret;
}

void gpio_set_ven(struct nfc_dev *nfc_dev, int value)
{
	if (gpio_get_value(nfc_dev->gpio.ven) != value) {
@@ -295,6 +426,7 @@ int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg)
		} else {
			pr_debug("ven already HIGH\n");
		}
		nfc_dev->is_ese_session_active = true;
	} else if (arg == ESE_POWER_OFF) {
		if (!nfc_dev->nfc_ven_enabled) {
			pr_debug("NFC not enabled, disabling ven\n");
@@ -302,6 +434,7 @@ int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg)
		} else {
			pr_debug("keep ven high as NFC is enabled\n");
		}
		nfc_dev->is_ese_session_active = false;
	} else if (arg == ESE_COLD_RESET) {

		// set default value for status as failure
+24 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/nfcinfo.h>
#include <linux/regulator/consumer.h>

#include "nfc_i2c_drv.h"
#include "nfc_i3c_drv.h"
@@ -86,6 +87,15 @@
#define DTS_FWDN_GPIO_STR	"qcom,sn-firm"
#define DTS_CLKREQ_GPIO_STR	"qcom,sn-clkreq"
#define DTS_CLKSRC_GPIO_STR	"qcom,clk-src"
#define NFC_LDO_SUPPLY_DT_NAME	"qcom,nq-vdd-1p8"
#define NFC_LDO_SUPPLY_NAME	"qcom,nq-vdd-1p8-supply"
#define NFC_LDO_VOL_DT_NAME	"qcom,nq-vdd-1p8-voltage"
#define NFC_LDO_CUR_DT_NAME	"qcom,nq-vdd-1p8-current"

//as per SN1x0 datasheet
#define NFC_VDDIO_MIN		1650000 //in uV
#define NFC_VDDIO_MAX		1950000 //in uV
#define NFC_CURRENT_MAX		157000 //in uA

enum ese_ioctl_request {
	/* eSE POWER ON */
@@ -163,6 +173,12 @@ struct platform_gpio {
	unsigned int dwl_req;
};

// NFC LDO entries from DT
struct platform_ldo {
	int vdd_levels[2];
	int max_current;
};

//Features specific Parameters
struct cold_reset {
	wait_queue_head_t read_wq;
@@ -186,12 +202,16 @@ struct nfc_dev {
	uint8_t interface;
	/* NFC VEN pin state */
	bool nfc_ven_enabled;
	bool is_vreg_enabled;
	bool is_ese_session_active;
	union {
		struct i2c_dev i2c_dev;
		struct i3c_dev i3c_dev;
	};
	struct platform_gpio gpio;
	struct platform_ldo ldo;
	struct cold_reset cold_reset;
	struct regulator *reg;

	/* read buffer*/
	size_t kbuflen;
@@ -211,7 +231,7 @@ int nfc_dev_open(struct inode *inode, struct file *filp);
int nfc_dev_close(struct inode *inode, struct file *filp);
long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg);
int nfc_parse_dt(struct device *dev, struct platform_gpio *nfc_gpio,
		 uint8_t interface);
		 struct platform_ldo *ldo, uint8_t interface);
int nfc_misc_probe(struct nfc_dev *nfc_dev,
		      const struct file_operations *nfc_fops, int count,
		      char *devname, char *classname);
@@ -220,5 +240,8 @@ int configure_gpio(unsigned int gpio, int flag);
void read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header);
void gpio_set_ven(struct nfc_dev *nfc_dev, int value);
int nfcc_hw_check(struct nfc_dev *nfc_dev);
int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev);
int nfc_ldo_vote(struct nfc_dev *nfc_dev);
int nfc_ldo_unvote(struct nfc_dev *nfc_dev);

#endif //_NFC_COMMON_H_
+23 −1
Original line number Diff line number Diff line
@@ -256,12 +256,13 @@ int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id)
	struct nfc_dev *nfc_dev = NULL;
	struct i2c_dev *i2c_dev = NULL;
	struct platform_gpio nfc_gpio;
	struct platform_ldo nfc_ldo;

	pr_debug("%s: enter\n", __func__);

	//retrieve details of gpios from dt

	ret = nfc_parse_dt(&client->dev, &nfc_gpio, PLATFORM_IF_I2C);
	ret = nfc_parse_dt(&client->dev, &nfc_gpio, &nfc_ldo, PLATFORM_IF_I2C);
	if (ret) {
		pr_err("%s : failed to parse dt\n", __func__);
		goto err;
@@ -341,6 +342,12 @@ int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id)
	i2c_disable_irq(nfc_dev);
	i2c_set_clientdata(client, nfc_dev);

	ret = nfc_ldo_config(&client->dev, nfc_dev);
	if (ret) {
		pr_err("LDO config failed\n");
		goto err_ldo_config_failed;
	}

	ret = nfcc_hw_check(nfc_dev);
	if (ret) {
		pr_err("nfc hw check failed ret %d\n", ret);
@@ -349,11 +356,17 @@ int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id)

	device_init_wakeup(&client->dev, true);
	i2c_dev->irq_wake_up = false;
	nfc_dev->is_ese_session_active = false;

	pr_info("%s success\n", __func__);
	return 0;

err_nfcc_hw_check:
	if (nfc_dev->reg) {
		nfc_ldo_unvote(nfc_dev);
		regulator_put(nfc_dev->reg);
	}
err_ldo_config_failed:
	free_irq(client->irq, nfc_dev);
err_nfc_misc_remove:
	nfc_misc_remove(nfc_dev, DEV_COUNT);
@@ -386,6 +399,15 @@ int nfc_i2c_dev_remove(struct i2c_client *client)
		ret = -ENODEV;
		return ret;
	}

	gpio_set_value(nfc_dev->gpio.ven, 0);
	// HW dependent delay before LDO goes into LPM mode
	usleep_range(10000, 10100);
	if (nfc_dev->reg) {
		nfc_ldo_unvote(nfc_dev);
		regulator_put(nfc_dev->reg);
	}

	device_init_wakeup(&client->dev, false);
	free_irq(client->irq, nfc_dev);
	nfc_misc_remove(nfc_dev, DEV_COUNT);
+2 −1
Original line number Diff line number Diff line
@@ -584,12 +584,13 @@ int nfc_i3c_dev_probe(struct i3c_device *device)
	int ret = 0;
	struct nfc_dev *nfc_dev = NULL;
	struct platform_gpio nfc_gpio;
	struct platform_ldo nfc_ldo;

	pr_debug("%s: enter\n", __func__);

	//retrieve gpio details from dt

	ret = nfc_parse_dt(&device->dev, &nfc_gpio, PLATFORM_IF_I3C);
	ret = nfc_parse_dt(&device->dev, &nfc_gpio, &nfc_ldo, PLATFORM_IF_I3C);
	if (ret) {
		pr_err("%s : failed to parse nfc dt node\n", __func__);
		goto err;