diff --git a/drivers/nfc/qti/nfc_common.c b/drivers/nfc/qti/nfc_common.c index c7c883fbaee1..5e1f78e1da47 100644 --- a/drivers/nfc/qti/nfc_common.c +++ b/drivers/nfc/qti/nfc_common.c @@ -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 diff --git a/drivers/nfc/qti/nfc_common.h b/drivers/nfc/qti/nfc_common.h index 000069fb6cd0..f3cc42314e07 100644 --- a/drivers/nfc/qti/nfc_common.h +++ b/drivers/nfc/qti/nfc_common.h @@ -23,6 +23,7 @@ #include #include #include +#include #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_ diff --git a/drivers/nfc/qti/nfc_i2c_drv.c b/drivers/nfc/qti/nfc_i2c_drv.c index 924c8531df5a..69d024534a58 100644 --- a/drivers/nfc/qti/nfc_i2c_drv.c +++ b/drivers/nfc/qti/nfc_i2c_drv.c @@ -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); diff --git a/drivers/nfc/qti/nfc_i3c_drv.c b/drivers/nfc/qti/nfc_i3c_drv.c index b4fb9e9ac1dc..8e7117f2e4c5 100644 --- a/drivers/nfc/qti/nfc_i3c_drv.c +++ b/drivers/nfc/qti/nfc_i3c_drv.c @@ -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;