diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c index 19829a9a8623..5f8cc5f061aa 100644 --- a/drivers/nfc/nq-nci.c +++ b/drivers/nfc/nq-nci.c @@ -28,6 +28,7 @@ #ifdef CONFIG_COMPAT #include #endif +#include struct nqx_platform_data { unsigned int irq_gpio; @@ -47,18 +48,6 @@ static const struct of_device_id msm_match_table[] = { MODULE_DEVICE_TABLE(of, msm_match_table); -#define DEV_COUNT 1 -#define DEVICE_NAME "nq-nci" -#define CLASS_NAME "nqx" -#define MAX_BUFFER_SIZE (320) -#define WAKEUP_SRC_TIMEOUT (2000) -#define MAX_RETRY_COUNT 3 -#define NCI_RESET_CMD_LEN 4 -#define NCI_RESET_RSP_LEN 4 -#define NCI_RESET_NTF_LEN 13 -#define NCI_GET_VERSION_CMD_LEN 8 -#define NCI_GET_VERSION_RSP_LEN 12 - struct nqx_dev { wait_queue_head_t read_wq; struct mutex read_mutex; @@ -164,6 +153,16 @@ static irqreturn_t nqx_dev_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int is_data_available_for_read(struct nqx_dev *nqx_dev) +{ + int ret; + + nqx_enable_irq(nqx_dev); + ret = wait_event_interruptible_timeout(nqx_dev->read_wq, + !nqx_dev->irq_enabled, msecs_to_jiffies(MAX_IRQ_WAIT_TIME)); + return ret; +} + static ssize_t nfc_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) { @@ -793,17 +792,143 @@ static const struct file_operations nfc_dev_fops = { #endif }; +/* + * function: get_nfcc_hw_info() + * + * @client: pointer to i2c_client + * @nqx_dev: pointer to nqx_dev structure + * @nci_reset_rsp_payload_len: payload length of NCI reset cmd + * + * Retrieves NFCC HW information based on the type of NFC chip + * used on the device. Depending on the nci_reset_rsp_payload_len + * value, core INIT command will be sent. + * + * NFC HW NCI version Send Core INIT cmd + * NQ3xx or old 1.0 Yes + * NQ4xx 2.0 No + * Sn1x0x 2.0 No + * + * Return: error codes in case of any failure, + * number of bytes read otherwise + */ +static int get_nfcc_hw_info(struct i2c_client *client, + struct nqx_dev *nqx_dev, char nci_reset_rsp_payload_len) +{ + int ret = 0; + + char *nci_init_cmd = NULL; + char *nci_init_rsp = NULL; + char *nci_reset_ntf = NULL; + char *nfcc_hw_info = NULL; + unsigned char nfcc_hw_info_len = 0; + + nci_init_cmd = kzalloc(NCI_INIT_CMD_LEN + 1, GFP_DMA | GFP_KERNEL); + if (!nci_init_cmd) { + ret = -ENOMEM; + goto err_nfcc_hw_info; + } + + nci_init_rsp = kzalloc(NCI_INIT_RSP_LEN + 1, GFP_DMA | GFP_KERNEL); + if (!nci_init_rsp) { + ret = -ENOMEM; + goto err_nfcc_hw_info; + } + + nci_reset_ntf = kzalloc(NCI_RESET_NTF_LEN + 1, GFP_DMA | GFP_KERNEL); + if (!nci_reset_ntf) { + ret = -ENOMEM; + goto err_nfcc_hw_info; + } + + if (nci_reset_rsp_payload_len == NCI_1_0_RESET_RSP_PAYLOAD_LEN) { + /* + * Chipset is NQ330 or older. + * Send core INIT command to get HW info. + */ + nci_init_cmd[0] = 0x20; + nci_init_cmd[1] = 0x01; + nci_init_cmd[2] = 0x00; + ret = nqx_standby_write(nqx_dev, nci_init_cmd, + NCI_INIT_CMD_LEN); + if (ret < 0) { + dev_err(&client->dev, + "%s: - i2c_master_send failed for Core INIT\n", + __func__); + goto err_nfcc_hw_info; + } + + ret = is_data_available_for_read(nqx_dev); + if (ret <= 0) { + nqx_disable_irq(nqx_dev); + goto err_nfcc_hw_info; + } + + /* Read Response of INIT command */ + ret = i2c_master_recv(client, nci_init_rsp, NCI_INIT_RSP_LEN); + if (ret < 0) { + dev_err(&client->dev, + "%s: - i2c_master_recv get INIT rsp Error\n", + __func__); + goto err_nfcc_hw_info; + } + nfcc_hw_info = nci_init_rsp; + } else { + /* + * Chipset is NQ4xx or later. + * Retrieve NTF data from wait queue. + */ + ret = is_data_available_for_read(nqx_dev); + if (ret <= 0) { + nqx_disable_irq(nqx_dev); + goto err_nfcc_hw_info; + } + + /* Read Notification of RESET command */ + ret = i2c_master_recv(client, nci_reset_ntf, NCI_RESET_NTF_LEN); + if (ret < 0) { + dev_err(&client->dev, + "%s: - i2c_master_recv get RESET ntf Error\n", + __func__); + goto err_nfcc_hw_info; + } + nfcc_hw_info = nci_reset_ntf; + } + + /* Save NFCC HW info */ + nfcc_hw_info_len = + NCI_HEADER_LEN + nfcc_hw_info[NCI_PAYLOAD_LENGTH_INDEX]; + if (nfcc_hw_info_len > PAYLOAD_HEADER_LENGTH) { + nqx_dev->nqx_info.info.chip_type = + nfcc_hw_info[nfcc_hw_info_len - + NFCC_HW_CHIP_ID_OFFSET]; + nqx_dev->nqx_info.info.rom_version = + nfcc_hw_info[nfcc_hw_info_len - + NFCC_HW_ROM_VER_OFFSET]; + nqx_dev->nqx_info.info.fw_major = + nfcc_hw_info[nfcc_hw_info_len - + NFCC_HW_MAJOR_NO_OFFSET]; + nqx_dev->nqx_info.info.fw_minor = + nfcc_hw_info[nfcc_hw_info_len - + NFCC_HW_MINOR_NO_OFFSET]; + } + +err_nfcc_hw_info: + + kfree(nci_reset_ntf); + kfree(nci_init_rsp); + kfree(nci_init_cmd); + + return ret; +} + /* Check for availability of NQ_ NFC controller hardware */ static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev) { int ret = 0; - int gpio_retry_count = 0; - unsigned char reset_ntf_len = 0; unsigned int enable_gpio = nqx_dev->en_gpio; char *nci_reset_cmd = NULL; char *nci_reset_rsp = NULL; - char *nci_reset_ntf = NULL; char *nci_get_version_cmd = NULL; char *nci_get_version_rsp = NULL; @@ -819,12 +944,6 @@ static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev) goto done; } - nci_reset_ntf = kzalloc(NCI_RESET_NTF_LEN + 1, GFP_DMA | GFP_KERNEL); - if (!nci_reset_ntf) { - ret = -ENOMEM; - goto done; - } - nci_get_version_cmd = kzalloc(NCI_GET_VERSION_CMD_LEN + 1, GFP_DMA | GFP_KERNEL); if (!nci_get_version_cmd) { @@ -839,7 +958,6 @@ static int nfcc_hw_check(struct i2c_client *client, struct nqx_dev *nqx_dev) goto done; } -reset_enable_gpio: /* making sure that the NFCC starts in a clean state. */ gpio_set_value(enable_gpio, 1);/* HPD : Enable*/ /* hardware dependent delay */ @@ -909,49 +1027,37 @@ reset_enable_gpio: } goto err_nfcc_reset_failed; } - nqx_enable_irq(nqx_dev); - ret = wait_event_interruptible(nqx_dev->read_wq, !nqx_dev->irq_enabled); - if (ret < 0) { + ret = is_data_available_for_read(nqx_dev); + if (ret <= 0) { nqx_disable_irq(nqx_dev); goto err_nfcc_hw_check; } - /* Read Response of RESET command */ - ret = i2c_master_recv(client, nci_reset_rsp, NCI_RESET_RSP_LEN); - if (ret < 0) { + /* Read Header of RESET command */ + ret = i2c_master_recv(client, nci_reset_rsp, NCI_HEADER_LEN); + if (ret != NCI_HEADER_LEN) { dev_err(&client->dev, - "%s: - i2c_master_recv Error\n", __func__); - gpio_retry_count = gpio_retry_count + 1; - if (gpio_retry_count < MAX_RETRY_COUNT) - goto reset_enable_gpio; - goto err_nfcc_hw_check; - } - nqx_enable_irq(nqx_dev); - ret = wait_event_interruptible(nqx_dev->read_wq, !nqx_dev->irq_enabled); - if (ret < 0) { - nqx_disable_irq(nqx_dev); + "%s: - i2c_master_recv get RESET rsp header Error\n", __func__); goto err_nfcc_hw_check; } - /* Read Notification of RESET command */ - ret = i2c_master_recv(client, nci_reset_ntf, NCI_RESET_NTF_LEN); - if (ret < 0) { + ret = i2c_master_recv(client, &nci_reset_rsp[NCI_PAYLOAD_START_INDEX], + nci_reset_rsp[NCI_PAYLOAD_LENGTH_INDEX]); + if (ret != nci_reset_rsp[NCI_PAYLOAD_LENGTH_INDEX]) { dev_err(&client->dev, - "%s: - i2c_master_recv Error\n", __func__); + "%s: - i2c_master_recv get RESET rsp data Error\n", __func__); goto err_nfcc_hw_check; } - reset_ntf_len = 2 + nci_reset_ntf[2]; /*payload + len*/ - if (reset_ntf_len > PAYLOAD_HEADER_LENGTH) { - nqx_dev->nqx_info.info.chip_type = - nci_reset_ntf[reset_ntf_len - 3]; - nqx_dev->nqx_info.info.rom_version = - nci_reset_ntf[reset_ntf_len - 2]; - nqx_dev->nqx_info.info.fw_major = - nci_reset_ntf[reset_ntf_len - 1]; - nqx_dev->nqx_info.info.fw_minor = - nci_reset_ntf[reset_ntf_len]; + /* Retrieve NFCC HW info */ + ret = get_nfcc_hw_info(client, nqx_dev, + nci_reset_rsp[NCI_PAYLOAD_LENGTH_INDEX]); + if (ret < 0) { + dev_err(&client->dev, + "%s: - Error in getting NFCC HW info\n", __func__); + goto err_nfcc_hw_check; } + dev_dbg(&client->dev, "%s: - nq - reset cmd answer : NfcNciRx %x %x %x\n", __func__, nci_reset_rsp[0], @@ -1000,7 +1106,6 @@ err_nfcc_hw_check: done: kfree(nci_reset_rsp); - kfree(nci_reset_ntf); kfree(nci_reset_cmd); kfree(nci_get_version_cmd); kfree(nci_get_version_rsp); diff --git a/drivers/nfc/nq-nci.h b/drivers/nfc/nq-nci.h index ba8a8eba1ade..1b9ee57433e7 100644 --- a/drivers/nfc/nq-nci.h +++ b/drivers/nfc/nq-nci.h @@ -37,6 +37,29 @@ #define BYTE (0x8) #define NCI_IDENTIFIER (0x10) +#define DEV_COUNT 1 +#define DEVICE_NAME "nq-nci" +#define CLASS_NAME "nqx" +#define MAX_BUFFER_SIZE (320) +#define WAKEUP_SRC_TIMEOUT (2000) +#define MAX_RETRY_COUNT 3 +#define NCI_RESET_CMD_LEN 4 +#define NCI_RESET_RSP_LEN 6 +#define NCI_RESET_NTF_LEN 13 +#define NCI_INIT_CMD_LEN 3 +#define NCI_INIT_RSP_LEN 28 +#define NCI_GET_VERSION_CMD_LEN 8 +#define NCI_GET_VERSION_RSP_LEN 12 +#define NCI_HEADER_LEN 3 +#define NCI_1_0_RESET_RSP_PAYLOAD_LEN 3 +#define NCI_PAYLOAD_START_INDEX 3 +#define NCI_PAYLOAD_LENGTH_INDEX (NCI_PAYLOAD_START_INDEX - 1) +#define MAX_IRQ_WAIT_TIME (90) /* in ms */ +#define NFCC_HW_CHIP_ID_OFFSET 4 +#define NFCC_HW_ROM_VER_OFFSET 3 +#define NFCC_HW_MAJOR_NO_OFFSET 2 +#define NFCC_HW_MINOR_NO_OFFSET 1 + enum nfcc_initial_core_reset_ntf { TIMEDOUT_INITIAL_CORE_RESET_NTF = 0, /* 0*/ ARRIVED_INITIAL_CORE_RESET_NTF, /* 1 */