diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c index 2dd18dd78677..17b6d1aea4c7 100644 --- a/drivers/nfc/nq-nci.c +++ b/drivers/nfc/nq-nci.c @@ -47,6 +47,7 @@ MODULE_DEVICE_TABLE(of, msm_match_table); #define MAX_BUFFER_SIZE (320) #define WAKEUP_SRC_TIMEOUT (2000) +#define MAX_RETRY_COUNT 3 struct nqx_dev { wait_queue_head_t read_wq; @@ -264,6 +265,35 @@ out: return ret; } +/** + * nqx_standby_write() + * @buf: pointer to data buffer + * @len: # of bytes need to transfer + * + * write data buffer over I2C and retry + * if NFCC is in stand by mode + * + * Return: # of bytes written or -ve value in case of error + */ +static int nqx_standby_write(struct nqx_dev *nqx_dev, + const unsigned char *buf, size_t len) +{ + int ret = -EINVAL; + int retry_cnt; + + for (retry_cnt = 1; retry_cnt <= MAX_RETRY_COUNT; retry_cnt++) { + ret = i2c_master_send(nqx_dev->client, buf, len); + if (ret < 0) { + dev_err(&nqx_dev->client->dev, + "%s: write failed, Maybe in Standby Mode - Retry(%d)\n", + __func__, retry_cnt); + usleep_range(1000, 1100); + } else if (ret == len) + break; + } + return ret; +} + /* * Power management of the eSE * NFC & eSE ON : NFC_EN high and eSE_pwr_req high. @@ -273,39 +303,95 @@ out: static int nqx_ese_pwr(struct nqx_dev *nqx_dev, unsigned long int arg) { int r = -1; + const unsigned char svdd_off_cmd_warn[] = {0x2F, 0x31, 0x01, 0x01}; + const unsigned char svdd_off_cmd_done[] = {0x2F, 0x31, 0x01, 0x00}; + + if (!gpio_is_valid(nqx_dev->ese_gpio)) { + dev_err(&nqx_dev->client->dev, + "%s: ese_gpio is not valid\n", __func__); + return -EINVAL; + } - /* Let's store the NFC_EN pin state */ if (arg == 0) { /* * We want to power on the eSE and to do so we need the * eSE_pwr_req pin and the NFC_EN pin to be high */ - nqx_dev->nfc_ven_enabled = gpio_get_value(nqx_dev->en_gpio); - if (!nqx_dev->nfc_ven_enabled) { - gpio_set_value(nqx_dev->en_gpio, 1); - /* hardware dependent delay */ - usleep_range(1000, 1100); - } - if (gpio_is_valid(nqx_dev->ese_gpio)) { + if (gpio_get_value(nqx_dev->ese_gpio)) { + dev_dbg(&nqx_dev->client->dev, "ese_gpio is already high\n"); + r = 0; + } else { + /** + * Let's store the NFC_EN pin state + * only if the eSE is not yet on + */ + nqx_dev->nfc_ven_enabled = + gpio_get_value(nqx_dev->en_gpio); + if (!nqx_dev->nfc_ven_enabled) { + gpio_set_value(nqx_dev->en_gpio, 1); + /* hardware dependent delay */ + usleep_range(1000, 1100); + } + gpio_set_value(nqx_dev->ese_gpio, 1); if (gpio_get_value(nqx_dev->ese_gpio)) { - dev_dbg(&nqx_dev->client->dev, "ese_gpio is already high\n"); + dev_dbg(&nqx_dev->client->dev, "ese_gpio is enabled\n"); r = 0; - } else { - gpio_set_value(nqx_dev->ese_gpio, 1); - if (gpio_get_value(nqx_dev->ese_gpio)) { - dev_dbg(&nqx_dev->client->dev, "ese_gpio is enabled\n"); - r = 0; - } } } } else if (arg == 1) { - if (gpio_is_valid(nqx_dev->ese_gpio)) { - gpio_set_value(nqx_dev->ese_gpio, 0); - if (!gpio_get_value(nqx_dev->ese_gpio)) { - dev_dbg(&nqx_dev->client->dev, "ese_gpio is disabled\n"); - r = 0; + if (nqx_dev->nfc_ven_enabled && + ((nqx_dev->nqx_info.info.chip_type == NFCC_NQ_220) || + (nqx_dev->nqx_info.info.chip_type == NFCC_PN66T))) { + /** + * Let's inform the CLF we're + * powering off the eSE + */ + r = nqx_standby_write(nqx_dev, svdd_off_cmd_warn, + sizeof(svdd_off_cmd_warn)); + if (r < 0) { + dev_err(&nqx_dev->client->dev, + "%s: write failed after max retry\n", + __func__); + return -ENXIO; } + dev_dbg(&nqx_dev->client->dev, + "%s: svdd_off_cmd_warn sent\n", __func__); + + /* let's power down the eSE */ + gpio_set_value(nqx_dev->ese_gpio, 0); + dev_dbg(&nqx_dev->client->dev, + "%s: nqx_dev->ese_gpio set to 0\n", __func__); + + /** + * Time needed for the SVDD capacitor + * to get discharged + */ + usleep_range(8000, 8100); + + /* Let's inform the CLF the eSE is now off */ + r = nqx_standby_write(nqx_dev, svdd_off_cmd_done, + sizeof(svdd_off_cmd_done)); + if (r < 0) { + dev_err(&nqx_dev->client->dev, + "%s: write failed after max retry\n", + __func__); + return -ENXIO; + } + dev_dbg(&nqx_dev->client->dev, + "%s: svdd_off_cmd_done sent\n", __func__); + } else { + /** + * In case the NFC is off, + * there's no need to send the i2c commands + */ + gpio_set_value(nqx_dev->ese_gpio, 0); } + + if (!gpio_get_value(nqx_dev->ese_gpio)) { + dev_dbg(&nqx_dev->client->dev, "ese_gpio is disabled\n"); + r = 0; + } + if (!nqx_dev->nfc_ven_enabled) { /* hardware dependent delay */ usleep_range(1000, 1100); @@ -313,12 +399,7 @@ static int nqx_ese_pwr(struct nqx_dev *nqx_dev, unsigned long int arg) gpio_set_value(nqx_dev->en_gpio, 0); } } else if (arg == 3) { - if (!nqx_dev->nfc_ven_enabled) - r = 0; - else { - if (gpio_is_valid(nqx_dev->ese_gpio)) - r = gpio_get_value(nqx_dev->ese_gpio); - } + r = gpio_get_value(nqx_dev->ese_gpio); } return r; } @@ -624,6 +705,10 @@ static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev) dev_dbg(&client->dev, "%s: ## NFCC == NQ330 ##\n", __func__); break; + case NFCC_PN66T: + dev_dbg(&client->dev, + "%s: ## NFCC == PN66T ##\n", __func__); + break; default: dev_err(&client->dev, "%s: - NFCC HW not Supported\n", __func__); diff --git a/drivers/nfc/nq-nci.h b/drivers/nfc/nq-nci.h index c635e818b1f3..f34c4d987143 100644 --- a/drivers/nfc/nq-nci.h +++ b/drivers/nfc/nq-nci.h @@ -48,7 +48,7 @@ enum nfcc_chip_variant { NFCC_NQ_220 = 0x58, /**< NFCC NQ220 */ NFCC_NQ_310 = 0x40, /**< NFCC NQ310 */ NFCC_NQ_330 = 0x51, /**< NFCC NQ330 */ + NFCC_PN66T = 0x18, /**< NFCC PN66T */ NFCC_NOT_SUPPORTED = 0xFF /**< NFCC is not supported */ }; - #endif