123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212 |
- /******************************************************************************
- * Copyright (C) 2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2013-2022 NXP
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- ******************************************************************************/
- /*
- * Copyright (C) 2010 Trusted Logic S.A.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <linux/module.h>
- #include <linux/interrupt.h>
- #include <linux/delay.h>
- #include <linux/uaccess.h>
- #include <linux/gpio.h>
- #ifdef CONFIG_COMPAT
- #include <linux/compat.h>
- #endif
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- #include <linux/platform_device.h>
- #endif
- #include "common_ese.h"
- #include "p73.h"
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- #include <linux/regulator/consumer.h>
- #include <linux/pinctrl/consumer.h>
- #include "common.h"
- static int nfc_reboot_cb(struct notifier_block *nb,
- unsigned long action, void *data);
- #endif
- #ifdef FEATURE_CORE_RESET_NTF_CHECK
- struct nfc_core_reset_type {
- char name[16];
- int data_len;
- u8 err_type;
- };
- /* if Power Reset comes after NFC on, it's normal operation */
- struct nfc_core_reset_type core_reset[] = {
- {"Unrecover", 0xA, 0x00},
- {"Power Reset", 0x9, 0x01}, /* at new version */
- {"Power Reset", 0xA, 0x01}, /* at old version */
- {"Assert", 0x6, 0xA0},
- {"Clock lost", 0x6, 0xA4}
- };
- static bool is_core_reset;
- bool nfc_check_core_reset_type(u8 *data, int len)
- {
- int arr_size = ARRAY_SIZE(core_reset);
- int i;
- bool found = false;
- for (i = 0; i < arr_size; i++) {
- if (core_reset[i].data_len == len &&
- core_reset[i].err_type == data[0]) {
- found = true;
- break;
- }
- }
- if (found) {
- char info[64] = {0x0, };
- int j, idx = 0;
- idx = snprintf(info, 64, "6000%02X", len);
- for (j = 0; j < len; j++)
- idx += snprintf(info + idx, 64 - idx, "%02X", data[j]);
- NFC_LOG_INFO("CORE_RESET: %s (%s)\n", core_reset[i].name, info);
- nfc_print_status();
- }
- return found;
- }
- void nfc_check_is_core_reset_ntf(u8 *data, int len)
- {
- if (is_core_reset) {
- /* check payload */
- nfc_check_core_reset_type(data, len);
- is_core_reset = false;
- } else if (len == 3) { /* check header */
- /* check if it's core reset ntf */
- if (data[0] == 0x60 && data[1] == 0x00) {
- switch (data[2]) {
- case 0x06:
- case 0x0A:
- is_core_reset = true;
- break;
- default:
- is_core_reset = false;
- }
- } else {
- is_core_reset = false;
- }
- }
- }
- #endif
- /**
- * i2c_disable_irq()
- *
- * Check if interrupt is disabled or not
- * and disable interrupt
- *
- * Return: int
- */
- int i2c_disable_irq(struct nfc_dev *dev)
- {
- unsigned long flags;
- spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags);
- if (dev->i2c_dev.irq_enabled) {
- disable_irq_nosync(dev->i2c_dev.client->irq);
- dev->i2c_dev.irq_enabled = false;
- }
- spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags);
- return 0;
- }
- /**
- * i2c_enable_irq()
- *
- * Check if interrupt is enabled or not
- * and enable interrupt
- *
- * Return: int
- */
- int i2c_enable_irq(struct nfc_dev *dev)
- {
- unsigned long flags;
- spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags);
- if (!dev->i2c_dev.irq_enabled) {
- dev->i2c_dev.irq_enabled = true;
- enable_irq(dev->i2c_dev.client->irq);
- }
- spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags);
- return 0;
- }
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- void i2c_disable_clk_irq(struct nfc_dev *dev)
- {
- struct platform_configs *nfc_configs = &dev->configs;
- struct platform_gpio *nfc_gpio = &dev->configs.gpio;
- if (nfc_configs->clk_req_wake || nfc_configs->clk_req_all_trigger) {
- if (nfc_gpio->clk_req_irq_enabled) {
- disable_irq_nosync(nfc_gpio->clk_req_irq);
- nfc_gpio->clk_req_irq_enabled = false;
- }
- dev->clk_req_wakelock = false;
- }
- }
- void i2c_enable_clk_irq(struct nfc_dev *dev)
- {
- struct platform_configs *nfc_configs = &dev->configs;
- struct platform_gpio *nfc_gpio = &dev->configs.gpio;
- if (nfc_configs->clk_req_wake || nfc_configs->clk_req_all_trigger) {
- if (!nfc_gpio->clk_req_irq_enabled) {
- enable_irq(nfc_gpio->clk_req_irq);
- nfc_gpio->clk_req_irq_enabled = true;
- }
- }
- }
- #endif
- static irqreturn_t i2c_irq_handler(int irq, void *dev_id)
- {
- struct nfc_dev *nfc_dev = dev_id;
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- wake_lock_timeout(&nfc_dev->nfc_wake_lock, 2*HZ);
- #else
- struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev;
- if (device_may_wakeup(&i2c_dev->client->dev))
- pm_wakeup_event(&i2c_dev->client->dev, WAKEUP_SRC_TIMEOUT);
- #endif
- i2c_disable_irq(nfc_dev);
- wake_up(&nfc_dev->read_wq);
- NFC_LOG_REC("irq\n");
- return IRQ_HANDLED;
- }
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- static irqreturn_t nfc_clk_req_irq_handler(int irq, void *dev_id)
- {
- struct nfc_dev *nfc_dev = dev_id;
- struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
- struct platform_configs *nfc_configs = &nfc_dev->configs;
- static bool nfc_clk_status;
- if (nfc_configs->change_clkreq_for_acpm)
- return IRQ_HANDLED;
- if (nfc_configs->clk_req_all_trigger) {
- if (gpio_get_value(nfc_gpio->clk_req)) {
- NFC_LOG_REC("clk_req 1\n");
- if (!wake_lock_active(&nfc_dev->nfc_clk_wake_lock))
- wake_lock(&nfc_dev->nfc_clk_wake_lock);
- if (nfc_configs->nfc_clock && !nfc_clk_status) {
- if (clk_prepare_enable(nfc_configs->nfc_clock)) {
- NFC_LOG_REC("clock enable failed\n");
- return IRQ_HANDLED;
- }
- nfc_clk_status = true;
- }
- } else {
- NFC_LOG_REC("clk_req 0\n");
- if (wake_lock_active(&nfc_dev->nfc_clk_wake_lock))
- wake_unlock(&nfc_dev->nfc_clk_wake_lock);
- if (nfc_configs->nfc_clock && nfc_clk_status) {
- clk_disable_unprepare(nfc_configs->nfc_clock);
- nfc_clk_status = false;
- }
- }
- } else {
- NFC_LOG_REC("clk_req w:%d\n", nfc_dev->clk_req_wakelock);
- if (nfc_dev->clk_req_wakelock) {
- nfc_dev->clk_req_wakelock = false;
- wake_lock_timeout(&nfc_dev->nfc_clk_wake_lock, 2*HZ);
- }
- }
- return IRQ_HANDLED;
- }
- #define PRINT_NFC_BUF 0
- #if PRINT_NFC_BUF
- static void print_hex_buf(const char *tag, const char *data, int len)
- {
- pr_info("%s len: %d\n", tag, len);
- print_hex_dump(KERN_DEBUG, tag, DUMP_PREFIX_OFFSET, 16, 8,
- data, len, false);
- }
- #else
- static void print_hex_buf(const char *tag, const char *data, int len)
- {
- do {} while (0);
- }
- #endif/*PRINT_NFC_BUF*/
- static void secnfc_check_screen_on_rsp(struct nfc_dev *nfc_dev,
- const char *buf, size_t count)
- {
- if (nfc_dev->screen_on_cmd && nfc_dev->screen_cfg) {
- nfc_dev->screen_on_cmd = false;
- nfc_dev->screen_cfg = false;
- NFC_LOG_INFO("scrn_on\n");
- }
- }
- static void secnfc_check_screen_off_rsp(struct nfc_dev *nfc_dev,
- const char *buf, size_t count)
- {
- if (!nfc_dev->screen_off_cmd || !nfc_dev->screen_cfg) {
- nfc_dev->screen_off_rsp_count = 0;
- return;
- }
- if (!nfc_dev->screen_off_rsp_count && count == 3/*header*/) {
- nfc_dev->screen_off_rsp_count++;
- return;
- }
- if (nfc_dev->screen_off_rsp_count == 1/*payload*/) {
- if (!buf[0]) {//00 or 0000
- if (wake_lock_active(&nfc_dev->nfc_wake_lock))
- wake_unlock(&nfc_dev->nfc_wake_lock);
- NFC_LOG_INFO("scrn_off\n");
- nfc_dev->screen_cfg = false;
- nfc_dev->screen_off_cmd = false;
- }
- nfc_dev->screen_off_rsp_count = 0;
- return;
- }
- nfc_dev->screen_off_rsp_count = 0;
- }
- #endif
- int i2c_read(struct nfc_dev *nfc_dev, char *buf, size_t count, int timeout)
- {
- int ret;
- struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev;
- struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
- NFC_LOG_REC("rd: %zu\n", count);
- if (timeout > NCI_CMD_RSP_TIMEOUT_MS)
- timeout = NCI_CMD_RSP_TIMEOUT_MS;
- if (count > MAX_NCI_BUFFER_SIZE)
- count = MAX_NCI_BUFFER_SIZE;
- if (!gpio_get_value(nfc_gpio->irq)) {
- while (1) {
- ret = 0;
- i2c_enable_irq(nfc_dev);
- if (!gpio_get_value(nfc_gpio->irq)) {
- if (timeout) {
- ret = wait_event_interruptible_timeout(
- nfc_dev->read_wq,
- !i2c_dev->irq_enabled,
- msecs_to_jiffies(timeout));
- if (ret <= 0) {
- NFC_LOG_ERR("%s: timeout error\n",
- __func__);
- goto err;
- }
- } else {
- ret = wait_event_interruptible(
- nfc_dev->read_wq,
- !i2c_dev->irq_enabled);
- if (ret) {
- NFC_LOG_ERR("%s: err wakeup of wq\n",
- __func__);
- goto err;
- }
- }
- }
- i2c_disable_irq(nfc_dev);
- if (gpio_get_value(nfc_gpio->irq))
- break;
- if (!gpio_get_value(nfc_gpio->ven)) {
- NFC_LOG_ERR("%s: releasing read\n", __func__);
- ret = -EIO;
- goto err;
- }
- /*
- * NFC service wanted to close the driver so,
- * release the calling reader thread asap.
- *
- * This can happen in case of nfc node close call from
- * eSE HAL in that case the NFC HAL reader thread
- * will again call read system call
- */
- if (nfc_dev->release_read) {
- NFC_LOG_REC("%s: releasing read\n", __func__);
- return 0;
- }
- NFC_LOG_ERR("%s: spurious interrupt detected\n", __func__);
- }
- }
- memset(buf, 0x00, count);
- /* Read data */
- ret = i2c_master_recv(nfc_dev->i2c_dev.client, buf, count);
- if (ret <= 0) {
- NFC_LOG_ERR("%s: returned %d\n", __func__, ret);
- goto err;
- }
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- print_hex_buf("nfc_rd: ", buf, count);
- secnfc_check_screen_on_rsp(nfc_dev, buf, ret);
- secnfc_check_screen_off_rsp(nfc_dev, buf, ret);
- #ifdef FEATURE_CORE_RESET_NTF_CHECK
- nfc_check_is_core_reset_ntf(buf, ret);
- #endif
- #endif
- /* check if it's response of cold reset command
- * NFC HAL process shouldn't receive this data as
- * command was sent by driver
- */
- if (nfc_dev->cold_reset.rsp_pending) {
- if (IS_PROP_CMD_RSP(buf)) {
- /* Read data */
- ret = i2c_master_recv(nfc_dev->i2c_dev.client,
- &buf[NCI_PAYLOAD_IDX],
- buf[NCI_PAYLOAD_LEN_IDX]);
- if (ret <= 0) {
- NFC_LOG_ERR("%s: error reading cold rst/prot rsp\n",
- __func__);
- goto err;
- }
- wakeup_on_prop_rsp(nfc_dev, buf);
- /*
- * NFC process doesn't know about cold reset command
- * being sent as it was initiated by eSE process
- * we shouldn't return any data to NFC process
- */
- return 0;
- }
- }
- err:
- return ret;
- }
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- #define SCREEN_ONOFF_CMD_SZ 4
- #define SCREEN_SET_CFG_SZ 7
- static bool secnfc_check_screen_on_cmd(struct nfc_dev *nfc_dev,
- const char *buf, size_t count)
- {
- if (count != SCREEN_ONOFF_CMD_SZ)
- return false;
- if (buf[0] == 0x20 && buf[1] == 0x09 && buf[2] == 0x01 && buf[3] == 0x2)
- return true;
- return false;
- }
- static bool secnfc_check_screen_off_cmd(struct nfc_dev *nfc_dev,
- const char *buf, size_t count)
- {
- if (count != SCREEN_ONOFF_CMD_SZ)
- return false;
- if (buf[0] == 0x20 && buf[1] == 0x09 && buf[2] == 0x01 &&
- (buf[3] == 0x1 || buf[3] == 0x3))
- return true;
- return false;
- }
- static bool secnfc_check_screen_cfg(struct nfc_dev *nfc_dev,
- const char *buf, size_t count)
- {
- if (count != SCREEN_SET_CFG_SZ)
- return false;
- if (buf[0] == 0x20 && buf[1] == 0x02 && buf[2] == 0x04 &&
- buf[3] == 0x01 && buf[4] == 0x02 &&
- buf[5] == 0x01 && buf[6] == 0x00)
- return true;
- return false;
- }
- static void secnfc_configure_clk_irq_based_on_screen_onoff(struct nfc_dev *nfc_dev, bool is_screen_on)
- {
- struct platform_configs *nfc_configs = &nfc_dev->configs;
- if (!nfc_configs->disable_clk_irq_during_wakeup)
- return;
- if (is_screen_on)
- i2c_disable_clk_irq(nfc_dev);
- else
- i2c_enable_clk_irq(nfc_dev);
- }
- static void secnfc_check_screen_onoff(struct nfc_dev *nfc_dev,
- const char *buf, size_t count)
- {
- if (secnfc_check_screen_cfg(nfc_dev, buf, count)) {
- nfc_dev->screen_cfg = true;
- } else if (secnfc_check_screen_on_cmd(nfc_dev, buf, count)) {
- nfc_dev->screen_on_cmd = true;
- secnfc_configure_clk_irq_based_on_screen_onoff(nfc_dev, true);
- } else if (secnfc_check_screen_off_cmd(nfc_dev, buf, count)) {
- nfc_dev->screen_off_cmd = true;
- secnfc_configure_clk_irq_based_on_screen_onoff(nfc_dev, false);
- } else {
- nfc_dev->screen_on_cmd = false;
- nfc_dev->screen_off_cmd = false;
- nfc_dev->screen_cfg = false;
- }
- }
- #endif
- int i2c_write(struct nfc_dev *nfc_dev, const char *buf, size_t count,
- int max_retry_cnt)
- {
- int ret = -EINVAL;
- int retry_cnt;
- struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
- if (count > MAX_DL_BUFFER_SIZE)
- count = MAX_DL_BUFFER_SIZE;
- NFC_LOG_REC("wr: %zu\n", count);
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- print_hex_buf("nfc_wr: ", buf, count);
- secnfc_check_screen_onoff(nfc_dev, buf, count);
- #endif
- /*
- * Wait for any pending read for max 15ms before write
- * This is to avoid any packet corruption during read, when
- * the host cmds resets NFCC during any parallel read operation
- */
- for (retry_cnt = 1; retry_cnt <= MAX_WRITE_IRQ_COUNT; retry_cnt++) {
- if (gpio_get_value(nfc_gpio->irq)) {
- NFC_LOG_ERR("%s: irq high during write, wait\n", __func__);
- usleep_range(WRITE_RETRY_WAIT_TIME_US,
- WRITE_RETRY_WAIT_TIME_US + 100);
- } else {
- break;
- }
- if (retry_cnt == MAX_WRITE_IRQ_COUNT &&
- gpio_get_value(nfc_gpio->irq)) {
- NFC_LOG_ERR("%s: allow after maximum wait\n", __func__);
- }
- }
- for (retry_cnt = 1; retry_cnt <= max_retry_cnt; retry_cnt++) {
- ret = i2c_master_send(nfc_dev->i2c_dev.client, buf, count);
- if (ret <= 0) {
- NFC_LOG_ERR("%s: write failed ret(%d), maybe in standby\n",
- __func__, ret);
- usleep_range(WRITE_RETRY_WAIT_TIME_US,
- WRITE_RETRY_WAIT_TIME_US + 100);
- } else if (ret != count) {
- NFC_LOG_ERR("%s: failed to write %d\n", __func__, ret);
- ret = -EIO;
- } else if (ret == count)
- break;
- }
- return ret;
- }
- ssize_t nfc_i2c_dev_read(struct file *filp, char __user *buf, size_t count,
- loff_t *offset)
- {
- int ret;
- struct nfc_dev *nfc_dev = (struct nfc_dev *)filp->private_data;
- if (!nfc_dev) {
- NFC_LOG_ERR("%s: device doesn't exist anymore\n", __func__);
- return -ENODEV;
- }
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (count > MAX_NCI_BUFFER_SIZE) {
- //NFC_LOG_ERR("%s: too big count %u\n", __func__, count);
- return -EINVAL;
- }
- #endif
- mutex_lock(&nfc_dev->read_mutex);
- if (filp->f_flags & O_NONBLOCK) {
- ret = i2c_master_recv(nfc_dev->i2c_dev.client, nfc_dev->read_kbuf, count);
- NFC_LOG_ERR("%s: NONBLOCK read ret = %d\n", __func__, ret);
- } else {
- ret = i2c_read(nfc_dev, nfc_dev->read_kbuf, count, 0);
- }
- if (ret > 0) {
- if (copy_to_user(buf, nfc_dev->read_kbuf, ret)) {
- NFC_LOG_ERR("%s: failed to copy to user space\n", __func__);
- ret = -EFAULT;
- }
- }
- mutex_unlock(&nfc_dev->read_mutex);
- return ret;
- }
- ssize_t nfc_i2c_dev_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *offset)
- {
- int ret;
- struct nfc_dev *nfc_dev = (struct nfc_dev *)filp->private_data;
- if (count > MAX_DL_BUFFER_SIZE)
- count = MAX_DL_BUFFER_SIZE;
- if (!nfc_dev) {
- NFC_LOG_ERR("%s: device doesn't exist anymore\n", __func__);
- return -ENODEV;
- }
- mutex_lock(&nfc_dev->write_mutex);
- if (copy_from_user(nfc_dev->write_kbuf, buf, count)) {
- NFC_LOG_ERR("%s: failed to copy from user space\n", __func__);
- mutex_unlock(&nfc_dev->write_mutex);
- return -EFAULT;
- }
- ret = i2c_write(nfc_dev, nfc_dev->write_kbuf, count, NO_RETRY);
- mutex_unlock(&nfc_dev->write_mutex);
- return ret;
- }
- static const struct file_operations nfc_i2c_dev_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .read = nfc_i2c_dev_read,
- .write = nfc_i2c_dev_write,
- .open = nfc_dev_open,
- .flush = nfc_dev_flush,
- .release = nfc_dev_close,
- .unlocked_ioctl = nfc_dev_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = nfc_dev_compat_ioctl,
- #endif
- };
- #if (KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE)
- int nfc_i2c_dev_probe(struct i2c_client *client)
- #else
- int nfc_i2c_dev_probe(struct i2c_client *client,const struct i2c_device_id *id)
- #endif
- {
- int ret = 0;
- struct nfc_dev *nfc_dev = NULL;
- struct i2c_dev *i2c_dev = NULL;
- struct platform_configs *nfc_configs = NULL;
- struct platform_gpio *nfc_gpio = NULL;
- #ifdef CONFIG_SEC_NFC_LOGGER
- #ifdef CONFIG_SEC_NFC_LOGGER_ADD_ACPM_LOG
- nfc_logger_acpm_log_init(0x0);
- #endif
- nfc_logger_register_nfc_stauts_func(nfc_print_status);
- #endif
- NFC_LOG_INFO("%s: enter\n", __func__);
- nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL);
- if (nfc_dev == NULL) {
- ret = -ENOMEM;
- goto err;
- }
- nfc_configs = &nfc_dev->configs;
- nfc_gpio = &nfc_configs->gpio;
- /* retrieve details of gpios from dt */
- ret = nfc_parse_dt(&client->dev, nfc_configs, PLATFORM_IF_I2C);
- if (ret) {
- NFC_LOG_ERR("%s: failed to parse dt\n", __func__);
- goto err_free_nfc_dev;
- }
- if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- NFC_LOG_ERR("%s: need I2C_FUNC_I2C\n", __func__);
- ret = -ENODEV;
- goto err_free_nfc_dev;
- }
- nfc_dev->read_kbuf = kzalloc(MAX_NCI_BUFFER_SIZE, GFP_DMA | GFP_KERNEL);
- if (!nfc_dev->read_kbuf) {
- ret = -ENOMEM;
- goto err_free_nfc_dev;
- }
- nfc_dev->write_kbuf = kzalloc(MAX_DL_BUFFER_SIZE, GFP_DMA | GFP_KERNEL);
- if (!nfc_dev->write_kbuf) {
- ret = -ENOMEM;
- goto err_free_read_kbuf;
- }
- nfc_dev->interface = PLATFORM_IF_I2C;
- nfc_dev->nfc_state = NFC_STATE_NCI;
- nfc_dev->i2c_dev.client = client;
- i2c_dev = &nfc_dev->i2c_dev;
- nfc_dev->nfc_read = i2c_read;
- nfc_dev->nfc_write = i2c_write;
- nfc_dev->nfc_enable_intr = i2c_enable_irq;
- nfc_dev->nfc_disable_intr = i2c_disable_irq;
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- nfc_dev->nfc_enable_clk_intr = i2c_enable_clk_irq;
- nfc_dev->nfc_disable_clk_intr = i2c_disable_clk_irq;
- /* reboot notifier callback */
- nfc_dev->reboot_nb.notifier_call = nfc_reboot_cb;
- register_reboot_notifier(&nfc_dev->reboot_nb);
- #endif
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- if (!nfc_configs->late_pvdd_en) {
- ret = configure_gpio(nfc_gpio->ven, GPIO_OUTPUT);
- if (ret) {
- NFC_LOG_ERR("%s: unable to request nfc reset gpio [%d]\n", __func__,
- nfc_gpio->ven);
- goto err_free_write_kbuf;
- }
- }
- #else
- ret = configure_gpio(nfc_gpio->ven, GPIO_OUTPUT);
- if (ret) {
- NFC_LOG_ERR("%s: unable to request nfc reset gpio [%d]\n", __func__,
- nfc_gpio->ven);
- goto err_free_write_kbuf;
- }
- #endif
- ret = configure_gpio(nfc_gpio->irq, GPIO_IRQ);
- if (ret <= 0) {
- NFC_LOG_ERR("%s: unable to request nfc irq gpio [%d]\n", __func__,
- nfc_gpio->irq);
- goto err_free_gpio;
- }
- client->irq = ret;
- if (gpio_is_valid(nfc_gpio->dwl_req)) {
- ret = configure_gpio(nfc_gpio->dwl_req, GPIO_OUTPUT);
- if (ret) {
- NFC_LOG_ERR("%s: unable to request nfc firm downl gpio [%d]\n",
- __func__, nfc_gpio->dwl_req);
- }
- }
- /* init mutex and queues */
- init_waitqueue_head(&nfc_dev->read_wq);
- mutex_init(&nfc_dev->read_mutex);
- mutex_init(&nfc_dev->write_mutex);
- mutex_init(&nfc_dev->dev_ref_mutex);
- spin_lock_init(&i2c_dev->irq_enabled_lock);
- common_ese_init(nfc_dev);
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- wake_lock_init(&nfc_dev->nfc_wake_lock, WAKE_LOCK_SUSPEND, "nfc_wake_lock");
- #endif
- #ifndef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- ret = nfc_misc_register(nfc_dev, &nfc_i2c_dev_fops, DEV_COUNT,
- NFC_CHAR_DEV_NAME, CLASS_NAME);
- if (ret) {
- NFC_LOG_ERR("%s: nfc_misc_register failed\n", __func__);
- goto err_mutex_destroy;
- }
- #endif
- /* interrupt initializations */
- NFC_LOG_INFO("%s: requesting IRQ %d\n", __func__, client->irq);
- i2c_dev->irq_enabled = true;
- ret = request_irq(client->irq, i2c_irq_handler, IRQF_TRIGGER_HIGH,
- client->name, nfc_dev);
- if (ret) {
- NFC_LOG_ERR("%s: request_irq failed\n", __func__);
- goto err_nfc_misc_unregister;
- }
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (nfc_configs->clk_req_wake || nfc_configs->clk_req_all_trigger) {
- unsigned long irq_flag = IRQF_TRIGGER_RISING | IRQF_ONESHOT;
- wake_lock_init(&nfc_dev->nfc_clk_wake_lock, WAKE_LOCK_SUSPEND, "nfc_clk_wake_lock");
- if (nfc_configs->clk_req_all_trigger)
- irq_flag |= IRQF_TRIGGER_FALLING;
- ret = configure_gpio(nfc_gpio->clk_req, GPIO_IRQ);
- if (ret <= 0) {
- NFC_LOG_ERR("%s: unable to request nfc clk req irq gpio [%d]\n", __func__, nfc_gpio->irq);
- } else {
- nfc_gpio->clk_req_irq = ret;
- NFC_LOG_INFO("clk_req IRQ num %d\n", nfc_gpio->clk_req_irq);
- ret = request_threaded_irq(nfc_gpio->clk_req_irq, NULL, nfc_clk_req_irq_handler,
- irq_flag, "pn547_clk_req", nfc_dev);
- if (ret)
- NFC_LOG_ERR("clk_req_irq failed\n");
- else {
- nfc_gpio->clk_req_irq_enabled = true;
- i2c_disable_clk_irq(nfc_dev);
- }
- }
- }
- if (!nfc_configs->late_pvdd_en)
- nfc_power_control(nfc_dev);
- #else
- gpio_set_ven(nfc_dev, 1);
- gpio_set_ven(nfc_dev, 0);
- gpio_set_ven(nfc_dev, 1);
- #endif
- i2c_disable_irq(nfc_dev);
- i2c_set_clientdata(client, nfc_dev);
- i2c_dev->irq_wake_up = false;
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- #ifdef CONFIG_CLK_ACPM_INIT
- if (nfc_configs->change_clkreq_for_acpm) {
- NFC_LOG_INFO("acpm_init_nfc_clk_req clk_req : %d\n", nfc_gpio->clk_req);
- acpm_init_eint_nfc_clk_req(nfc_gpio->clk_req);
- }
- #endif
- #if IS_ENABLED(CONFIG_NFC_SN2XX_ESE_SUPPORT)
- store_nfc_i2c_device(&client->dev);
- #endif
- nfc_probe_done(nfc_dev);
- #endif
- NFC_LOG_INFO("%s: probing nfc i2c successfully\n", __func__);
- return 0;
- err_nfc_misc_unregister:
- #ifndef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- nfc_misc_unregister(nfc_dev, DEV_COUNT);
- err_mutex_destroy:
- #endif
- mutex_destroy(&nfc_dev->dev_ref_mutex);
- mutex_destroy(&nfc_dev->read_mutex);
- mutex_destroy(&nfc_dev->write_mutex);
- err_free_gpio:
- gpio_free_all(nfc_dev);
- err_free_write_kbuf:
- kfree(nfc_dev->write_kbuf);
- err_free_read_kbuf:
- kfree(nfc_dev->read_kbuf);
- err_free_nfc_dev:
- kfree(nfc_dev);
- err:
- NFC_LOG_ERR("%s: probing not successful, check hardware\n", __func__);
- return ret;
- }
- #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
- int nfc_i2c_dev_remove(struct i2c_client *client)
- #else
- void nfc_i2c_dev_remove(struct i2c_client *client)
- #endif
- {
- int ret = 0;
- struct nfc_dev *nfc_dev = NULL;
- NFC_LOG_INFO("%s: remove device\n", __func__);
- nfc_dev = i2c_get_clientdata(client);
- if (!nfc_dev) {
- NFC_LOG_ERR("%s: device doesn't exist anymore\n", __func__);
- ret = -ENODEV;
- goto end;
- }
- if (nfc_dev->dev_ref_count > 0) {
- NFC_LOG_ERR("%s: device already in use\n", __func__);
- ret = -EBUSY;
- goto end;
- }
- free_irq(client->irq, nfc_dev);
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- nfc_misc_unregister(NULL, DEV_COUNT);
- #else
- nfc_misc_unregister(nfc_dev, DEV_COUNT);
- #endif
- #ifdef CONFIG_SEC_NFC_LOGGER
- nfc_logger_deinit();
- #endif
- wake_lock_destroy(&nfc_dev->nfc_wake_lock);
- mutex_destroy(&nfc_dev->read_mutex);
- mutex_destroy(&nfc_dev->write_mutex);
- gpio_free_all(nfc_dev);
- kfree(nfc_dev->read_kbuf);
- kfree(nfc_dev->write_kbuf);
- kfree(nfc_dev);
- end:
- NFC_LOG_INFO("%s: ret :%d\n", __func__, ret);
- #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
- return ret;
- #endif
- }
- int nfc_i2c_dev_suspend(struct device *device)
- {
- struct i2c_client *client = to_i2c_client(device);
- struct nfc_dev *nfc_dev = i2c_get_clientdata(client);
- struct i2c_dev *i2c_dev = NULL;
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- struct platform_configs *nfc_configs = &nfc_dev->configs;
- struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
- #endif
- NFC_LOG_INFO_WITH_DATE("suspend\n");
- if (!nfc_dev) {
- NFC_LOG_ERR("%s: device doesn't exist anymore\n", __func__);
- return -ENODEV;
- }
- i2c_dev = &nfc_dev->i2c_dev;
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (i2c_dev->irq_enabled && !i2c_dev->irq_wake_up) {
- if (!enable_irq_wake(client->irq))
- i2c_dev->irq_wake_up = true;
- }
- if (nfc_configs->clk_req_wake || nfc_configs->clk_req_all_trigger) {
- enable_irq_wake(nfc_gpio->clk_req_irq);
- nfc_dev->clk_req_wakelock = true;
- }
- #else
- if (device_may_wakeup(&client->dev) && i2c_dev->irq_enabled) {
- if (!enable_irq_wake(client->irq))
- i2c_dev->irq_wake_up = true;
- }
- #endif
- NFC_LOG_DBG("%s: irq_wake_up = %d\n", __func__, i2c_dev->irq_wake_up);
- return 0;
- }
- int nfc_i2c_dev_resume(struct device *device)
- {
- struct i2c_client *client = to_i2c_client(device);
- struct nfc_dev *nfc_dev = i2c_get_clientdata(client);
- struct i2c_dev *i2c_dev = NULL;
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- struct platform_configs *nfc_configs = &nfc_dev->configs;
- struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
- #endif
- NFC_LOG_INFO_WITH_DATE("resume\n");
- if (!nfc_dev) {
- NFC_LOG_ERR("%s: device doesn't exist anymore\n", __func__);
- return -ENODEV;
- }
- i2c_dev = &nfc_dev->i2c_dev;
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (i2c_dev->irq_wake_up) {
- if (!disable_irq_wake(client->irq))
- i2c_dev->irq_wake_up = false;
- }
- if (nfc_configs->clk_req_wake || nfc_configs->clk_req_all_trigger)
- disable_irq_wake(nfc_gpio->clk_req_irq);
- #else
- if (device_may_wakeup(&client->dev) && i2c_dev->irq_wake_up) {
- if (!disable_irq_wake(client->irq))
- i2c_dev->irq_wake_up = false;
- }
- #endif
- NFC_LOG_DBG("%s: irq_wake_up = %d\n", __func__, i2c_dev->irq_wake_up);
- return 0;
- }
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- #define SN300_PVDD_MIN 1100000
- #define SN300_PVDD_MAX 1300000
- static int nfc_pvdd_off(struct nfc_dev *nfc_dev)
- {
- struct platform_configs *nfc_configs;
- int ret = 0, pvdd_val;
- // NFC_LOG_INFO("nfc_pvdd_off ++\n");
- nfc_configs = &nfc_dev->configs;
- pvdd_val = regulator_get_voltage(nfc_configs->nfc_pvdd);
- NFC_LOG_INFO("pvdd_val %d\n", pvdd_val);
-
- if (SN300_PVDD_MIN < pvdd_val && pvdd_val < SN300_PVDD_MAX) {
- // NFC_LOG_INFO("nfc IO force off\n");
- ret = nfc_regulator_onoff(nfc_dev, 0);
- if (ret < 0)
- NFC_LOG_ERR("%s pn547 regulator_on FAIL %d\n", __func__, ret);
- }
- // NFC_LOG_INFO("nfc_pvdd_off --\n");
- //exit:
- return ret;
- }
- static int nfc_reboot_cb(struct notifier_block *nb,
- unsigned long action, void *data)
- {
- struct nfc_dev *nfc_dev = container_of(nb, struct nfc_dev, reboot_nb);
- struct platform_gpio *nfc_gpio;
- struct platform_configs *nfc_configs;
- int ret;
- if (!nfc_dev) {
- NFC_LOG_INFO("no nfc dev\n");
- goto exit;
- }
- NFC_LOG_INFO("nfc_reboot_cb ++\n");
- nfc_gpio = &nfc_dev->configs.gpio;
- nfc_configs = &nfc_dev->configs;
- if (nfc_configs->ap_vendor == AP_VENDOR_QCT) {
- ret = gpio_direction_output(nfc_gpio->ven, 0);
- if (ret)
- NFC_LOG_ERR("VEN control fail %d\n", ret);
- usleep_range(300, 301);
- nfc_pvdd_off(nfc_dev);
- }
- NFC_LOG_INFO("nfc_reboot_cb --\n");
- exit:
- return NOTIFY_OK;
- }
- static void nfc_shutdown(struct i2c_client *client)
- {
- struct nfc_dev *nfc_dev = i2c_get_clientdata(client);
- int ret;
- NFC_LOG_INFO("nfc_shutdown ++\n");
- if (nfc_dev) {
- struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio;
- struct platform_configs *nfc_configs = &nfc_dev->configs;
- /*
- * In case of S.LSI, NFC card mode doesn't work in LPM.
- * so, it's skipped on S.LSI models using sn110. it need to be checked
- */
- if (nfc_configs->ap_vendor != AP_VENDOR_SLSI) {
- if (nfc_configs->ap_vendor == AP_VENDOR_MTK) {
- /*use internal pull-up case*/
- struct pinctrl *pinctrl = NULL;
- pinctrl = devm_pinctrl_get_select(&client->dev, "i2c_off");
- if (IS_ERR_OR_NULL(pinctrl))
- NFC_LOG_ERR("Failed to pinctrl i2c_off\n");
- else
- devm_pinctrl_put(pinctrl);
- NFC_LOG_ERR("i2c off pinctrl called\n");
- };
- ret = gpio_direction_output(nfc_gpio->ven, 0);
- if (ret)
- NFC_LOG_ERR("VEN control fail %d\n", ret);
- if (nfc_configs->ap_vendor == AP_VENDOR_QCT) {
- usleep_range(300, 301);
- nfc_pvdd_off(nfc_dev);
- }
- }
- }
- NFC_LOG_INFO("nfc_shutdown --\n");
- }
- #endif
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- static int nfc_platform_probe(struct platform_device *pdev)
- {
- int ret = -1;
- nfc_parse_dt_for_platform_device(&pdev->dev);
- ret = nfc_misc_register(NULL, &nfc_i2c_dev_fops, DEV_COUNT,
- NFC_CHAR_DEV_NAME, CLASS_NAME);
- if (ret)
- NFC_LOG_ERR("%s: nfc_misc_register failed\n", __func__);
- NFC_LOG_INFO("%s: finished...\n", __func__);
- return 0;
- }
- static int nfc_platform_remove(struct platform_device *pdev)
- {
- NFC_LOG_INFO("Entry : %s\n", __func__);
- return 0;
- }
- static const struct of_device_id nfc_platform_match_table[] = {
- { .compatible = "nfc_platform",},
- {},
- };
- static struct platform_driver nfc_platform_driver = {
- .driver = {
- .name = "nfc_platform",
- .owner = THIS_MODULE,
- #ifdef CONFIG_OF
- .of_match_table = nfc_platform_match_table,
- #endif
- },
- .probe = nfc_platform_probe,
- .remove = nfc_platform_remove,
- };
- #endif /* CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE */
- static const struct i2c_device_id nfc_i2c_dev_id[] = {{NFC_I2C_DEV_ID, 0},
- {}};
- static const struct of_device_id nfc_i2c_dev_match_table[] = {
- {
- .compatible = NFC_I2C_DRV_STR,
- },
- {}
- };
- static const struct dev_pm_ops nfc_i2c_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(
- nfc_i2c_dev_suspend, nfc_i2c_dev_resume) };
- static struct i2c_driver nfc_i2c_dev_driver = {
- .id_table = nfc_i2c_dev_id,
- .probe = nfc_i2c_dev_probe,
- .remove = nfc_i2c_dev_remove,
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- .shutdown = nfc_shutdown,
- #endif
- .driver = {
- .name = NFC_I2C_DRV_STR,
- .pm = &nfc_i2c_dev_pm_ops,
- .of_match_table = nfc_i2c_dev_match_table,
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- },
- };
- MODULE_DEVICE_TABLE(of, nfc_i2c_dev_match_table);
- #if IS_MODULE(CONFIG_SAMSUNG_NFC)
- extern int p61_dev_init(void);
- extern void p61_dev_exit(void);
- static int __init nfc_i2c_dev_init(void)
- {
- int ret = 0;
- #ifdef CONFIG_SEC_NFC_LOGGER
- nfc_logger_init();
- nfc_logger_set_max_count(-1);
- #endif
- NFC_LOG_INFO("%s: Loading NXP NFC I2C driver\n", __func__);
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (nfc_get_lpcharge() == LPM_TRUE)
- return ret;
- #endif
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- ret = platform_driver_register(&nfc_platform_driver);
- NFC_LOG_INFO("%s: platform_driver_register, ret %d\n", __func__, ret);
- #endif
- ret = i2c_add_driver(&nfc_i2c_dev_driver);
- if (ret != 0)
- NFC_LOG_ERR("%s: NFC I2C add driver error ret %d\n", __func__, ret);
- #if IS_ENABLED(CONFIG_NFC_SN2XX_ESE_SUPPORT)
- ret = p61_dev_init();
- #endif
- return ret;
- }
- module_init(nfc_i2c_dev_init);
- static void __exit nfc_i2c_dev_exit(void)
- {
- NFC_LOG_INFO("%s: Unloading NXP NFC I2C driver\n", __func__);
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (nfc_get_lpcharge() == LPM_TRUE)
- return;
- #endif
- #if IS_ENABLED(CONFIG_NFC_SN2XX_ESE_SUPPORT)
- p61_dev_exit();
- #endif
- i2c_del_driver(&nfc_i2c_dev_driver);
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- platform_driver_unregister(&nfc_platform_driver);
- #endif
- }
- module_exit(nfc_i2c_dev_exit);
- #else /* not IS_MODULE(CONFIG_SAMSUNG_NFC) */
- static int __init nfc_i2c_dev_init(void)
- {
- int ret = 0;
- #ifdef CONFIG_SEC_NFC_LOGGER
- nfc_logger_init();
- nfc_logger_set_max_count(-1);
- #endif
- NFC_LOG_INFO("%s: Loading NXP NFC I2C driver\n", __func__);
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (nfc_get_lpcharge() == LPM_TRUE)
- return ret;
- #endif
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- ret = platform_driver_register(&nfc_platform_driver);
- NFC_LOG_INFO("%s: platform_driver_register, ret %d\n", __func__, ret);
- #endif
- ret = i2c_add_driver(&nfc_i2c_dev_driver);
- if (ret != 0)
- NFC_LOG_ERR("%s: NFC I2C add driver error ret %d\n", __func__, ret);
- return ret;
- }
- module_init(nfc_i2c_dev_init);
- static void __exit nfc_i2c_dev_exit(void)
- {
- NFC_LOG_INFO("%s: Unloading NXP NFC I2C driver\n", __func__);
- #if IS_ENABLED(CONFIG_SAMSUNG_NFC)
- if (nfc_get_lpcharge() == LPM_TRUE)
- return;
- #endif
- i2c_del_driver(&nfc_i2c_dev_driver);
- #ifdef CONFIG_MAKE_NODE_USING_PLATFORM_DEVICE
- platform_driver_unregister(&nfc_platform_driver);
- #endif
- }
- module_exit(nfc_i2c_dev_exit);
- #endif /* IS_MODULE(CONFIG_SAMSUNG_NFC) */
- MODULE_DESCRIPTION("NXP NFC I2C driver");
- MODULE_LICENSE("GPL");
|