Merge "NFC: Add support for LDO voting"

This commit is contained in:
qctecmdr 2020-06-03 12:43:50 -07:00 • committed by Gerrit - the friendly Code Review server
commit 043aeefccb
4 changed files with 183 additions and 4 deletions

View file

@ -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

View file

@ -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_

View file

@ -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);

View file

@ -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;