/* * SAMSUNG NFC Controller * * Copyright (C) 2013 Samsung Electronics Co.Ltd * Author: Woonki Lee * Heejae Kim * * 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; * */ /* duplicated #define pr_fmt(fmt) "[sec_nfc] %s: " fmt, __func__*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_ESE_SECURE) && defined(CONFIG_ESE_USE_TZ_API) extern int tz_tee_ese_secure_check(void); enum secure_state { NOT_CHECKED, ESE_SECURED, ESE_NOT_SECURED, }; int nfc_ese_secured; #endif #include "nfc_wakelock.h" #include "sec_nfc.h" #ifdef CONFIG_SEC_NFC_LOGGER #include "./nfc_logger/nfc_logger.h" #endif #define SEC_NFC_GET_INFO(dev) i2c_get_clientdata(to_i2c_client(dev)) static int nfc_param_lpcharge = LPM_NO_SUPPORT; module_param(nfc_param_lpcharge, int, 0440); enum sec_nfc_irq { SEC_NFC_SKIP = -1, SEC_NFC_NONE, SEC_NFC_INT, SEC_NFC_READ_TIMES, }; struct sec_nfc_i2c_info { struct i2c_client *i2c_dev; struct mutex read_mutex; enum sec_nfc_irq read_irq; wait_queue_head_t read_wait; size_t buflen; u8 *buf; }; struct sec_nfc_info { struct miscdevice miscdev; struct mutex mutex; enum sec_nfc_mode mode; struct device *dev; struct sec_nfc_platform_data *pdata; struct sec_nfc_i2c_info i2c_info; struct nfc_wake_lock nfc_wake_lock; struct nfc_wake_lock nfc_clk_wake_lock; bool clk_ctl; bool clk_state; struct platform_device *pdev; }; #ifdef CONFIG_ESE_COLDRESET struct mutex coldreset_mutex; struct mutex sleep_wake_mutex; bool sleep_wakeup_state[2]; u8 disable_combo_reset_cmd[4] = { 0x2F, 0x30, 0x01, 0x00}; enum sec_nfc_mode cur_mode; #endif static struct sec_nfc_info *g_nfc_info; #define FEATURE_SEC_NFC_TEST #ifdef FEATURE_SEC_NFC_TEST static bool on_nfc_test; static bool nfc_int_wait; #endif static irqreturn_t sec_nfc_irq_thread_fn(int irq, void *dev_id) { struct sec_nfc_info *info = dev_id; struct sec_nfc_platform_data *pdata = info->pdata; NFC_LOG_REC("irq\n"); #ifdef FEATURE_SEC_NFC_TEST if (on_nfc_test) { nfc_int_wait = true; NFC_LOG_INFO("NFC_TEST: interrupt is raised\n"); wake_up_interruptible(&info->i2c_info.read_wait); return IRQ_HANDLED; } #endif if (gpio_get_value(pdata->irq) == 0) { NFC_LOG_REC("irq-gpio state is low!\n"); return IRQ_HANDLED; } mutex_lock(&info->i2c_info.read_mutex); /* Skip interrupt during power switching * It is released after first write */ if (info->i2c_info.read_irq == SEC_NFC_SKIP) { NFC_LOG_REC("Now power swiching. Skip this IRQ\n"); mutex_unlock(&info->i2c_info.read_mutex); return IRQ_HANDLED; } info->i2c_info.read_irq += SEC_NFC_READ_TIMES; #ifdef CONFIG_SEC_NFC_DUPLICATED_IRQ_WQ_LSI if (info->i2c_info.read_irq >= SEC_NFC_READ_TIMES * 2) { NFC_LOG_ERR("AP called duplicated IRQ handler\n"); info->i2c_info.read_irq -= SEC_NFC_READ_TIMES; mutex_unlock(&info->i2c_info.read_mutex); return IRQ_HANDLED; } #endif mutex_unlock(&info->i2c_info.read_mutex); wake_up_interruptible(&info->i2c_info.read_wait); wake_lock_timeout(&info->nfc_wake_lock, 2*HZ); return IRQ_HANDLED; } static int nfc_state_print(struct sec_nfc_info *info) { struct sec_nfc_platform_data *pdata = info->pdata; struct regulator *regulator_nfc_pvdd; int en = gpio_get_value(info->pdata->ven); int firm = gpio_get_value_cansleep(info->pdata->firm); int irq = gpio_get_value(info->pdata->irq); int pvdd = 0; if (pdata->nfc_pvdd != NULL) { regulator_nfc_pvdd = pdata->nfc_pvdd; pvdd = regulator_is_enabled(regulator_nfc_pvdd); } else { pvdd = gpio_get_value(info->pdata->pvdd); } NFC_LOG_INFO("en(%d) firm(%d) pvdd(%d) irq(%d) mode(%d) clk_state(%d)\n", en, firm, pvdd, irq, info->mode, info->clk_state); return 0; } void sec_nfc_print_status(void) { if (g_nfc_info) nfc_state_print(g_nfc_info); } static ssize_t sec_nfc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct sec_nfc_info *info = container_of(file->private_data, struct sec_nfc_info, miscdev); enum sec_nfc_irq irq; int ret = 0; #ifdef FEATURE_SEC_NFC_TEST if (on_nfc_test) return 0; #endif mutex_lock(&info->mutex); if (info->mode == SEC_NFC_MODE_OFF) { NFC_LOG_ERR("read() nfc is not enabled\n"); ret = -ENODEV; goto out; } mutex_lock(&info->i2c_info.read_mutex); if (count == 0) { if (info->i2c_info.read_irq >= SEC_NFC_INT) info->i2c_info.read_irq--; mutex_unlock(&info->i2c_info.read_mutex); goto out; } irq = info->i2c_info.read_irq; mutex_unlock(&info->i2c_info.read_mutex); if (irq == SEC_NFC_NONE) { if (file->f_flags & O_NONBLOCK) { NFC_LOG_ERR("read() it is nonblock\n"); ret = -EAGAIN; goto out; } } /* i2c recv */ if (count > info->i2c_info.buflen) count = info->i2c_info.buflen; if (count > SEC_NFC_MSG_MAX_SIZE) { NFC_LOG_ERR("read() user required wrong size :%d\n", (u32)count); ret = -EINVAL; goto out; } NFC_LOG_REC("read(%zu)\n", count); mutex_lock(&info->i2c_info.read_mutex); memset(info->i2c_info.buf, 0, count); ret = i2c_master_recv(info->i2c_info.i2c_dev, info->i2c_info.buf, (u32)count); if (ret == -EREMOTEIO) { ret = -ERESTART; goto read_error; } else if (ret != count) { NFC_LOG_ERR("read failed: return: %d count: %d\n", ret, (u32)count); /*ret = -EREMOTEIO;*/ goto read_error; } if (info->i2c_info.read_irq >= SEC_NFC_INT) info->i2c_info.read_irq--; if (info->i2c_info.read_irq == SEC_NFC_READ_TIMES) wake_up_interruptible(&info->i2c_info.read_wait); mutex_unlock(&info->i2c_info.read_mutex); if (copy_to_user(buf, info->i2c_info.buf, ret)) { NFC_LOG_ERR("read() copy failed to user\n"); ret = -EFAULT; } goto out; read_error: NFC_LOG_ERR("read error %d\n", ret); nfc_state_print(info); info->i2c_info.read_irq = SEC_NFC_NONE; mutex_unlock(&info->i2c_info.read_mutex); out: mutex_unlock(&info->mutex); return ret; } static ssize_t sec_nfc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct sec_nfc_info *info = container_of(file->private_data, struct sec_nfc_info, miscdev); int ret = 0; NFC_LOG_DBG("write() count %d\n", (u32)count); #ifdef FEATURE_SEC_NFC_TEST if (on_nfc_test) return 0; #endif mutex_lock(&info->mutex); if (info->mode == SEC_NFC_MODE_OFF) { NFC_LOG_ERR("write() nfc is not enabled\n"); ret = -ENODEV; goto out; } if (count > info->i2c_info.buflen) count = info->i2c_info.buflen; if (count > SEC_NFC_MSG_MAX_SIZE) { NFC_LOG_ERR("write() user required wrong size :%d\n", (u32)count); ret = -EINVAL; goto out; } if (copy_from_user(info->i2c_info.buf, buf, count)) { NFC_LOG_ERR("write() copy failed from user\n"); ret = -EFAULT; goto out; } /* Skip interrupt during power switching * It is released after first write */ NFC_LOG_REC("write(%d)\n", count); mutex_lock(&info->i2c_info.read_mutex); ret = i2c_master_send(info->i2c_info.i2c_dev, info->i2c_info.buf, count); if (info->i2c_info.read_irq == SEC_NFC_SKIP) info->i2c_info.read_irq = SEC_NFC_NONE; mutex_unlock(&info->i2c_info.read_mutex); if (ret == -EREMOTEIO) { NFC_LOG_ERR("write failed: return: %d count: %d\n", ret, (u32)count); ret = -ERESTART; goto write_error; } if (ret != count) { NFC_LOG_ERR("write failed: return: %d count: %d\n", ret, (u32)count); ret = -EREMOTEIO; goto write_error; } goto out; write_error: nfc_state_print(info); out: mutex_unlock(&info->mutex); return ret; } static unsigned int sec_nfc_poll(struct file *file, poll_table *wait) { struct sec_nfc_info *info = container_of(file->private_data, struct sec_nfc_info, miscdev); enum sec_nfc_irq irq; int ret = 0; mutex_lock(&info->mutex); if (info->mode == SEC_NFC_MODE_OFF) { NFC_LOG_ERR("poll() nfc is not enabled\n"); ret = -ENODEV; goto out; } poll_wait(file, &info->i2c_info.read_wait, wait); mutex_lock(&info->i2c_info.read_mutex); irq = info->i2c_info.read_irq; if (irq == SEC_NFC_READ_TIMES) ret = (POLLIN | POLLRDNORM); mutex_unlock(&info->i2c_info.read_mutex); out: mutex_unlock(&info->mutex); return ret; } static int sec_nfc_regulator_onoff(struct sec_nfc_platform_data *data, int onoff) { int rc = 0; struct regulator *regulator_nfc_pvdd = data->nfc_pvdd; if (!regulator_nfc_pvdd) { NFC_LOG_ERR("error: null regulator!\n"); rc = -ENODEV; goto done; } NFC_LOG_INFO("regulator onoff = %d\n", onoff); if (onoff == NFC_I2C_LDO_ON) { rc = regulator_enable(regulator_nfc_pvdd); if (rc) { NFC_LOG_ERR("regulator enable nfc_pvdd failed, rc=%d\n", rc); goto done; } } else { rc = regulator_disable(regulator_nfc_pvdd); if (rc) { NFC_LOG_ERR("regulator disable nfc_pvdd failed, rc=%d\n", rc); goto done; } } NFC_LOG_INFO("success\n"); done: return rc; } void sec_nfc_i2c_irq_clear(struct sec_nfc_info *info) { /* clear interrupt. Interrupt will be occurred at power off */ mutex_lock(&info->i2c_info.read_mutex); info->i2c_info.read_irq = SEC_NFC_NONE; mutex_unlock(&info->i2c_info.read_mutex); } int sec_nfc_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct sec_nfc_info *info = dev_get_drvdata(dev); struct sec_nfc_platform_data *pdata = info->pdata; int ret; NFC_LOG_INFO("probe() start\n"); info->i2c_info.buflen = SEC_NFC_MAX_BUFFER_SIZE; info->i2c_info.buf = kzalloc(SEC_NFC_MAX_BUFFER_SIZE, GFP_KERNEL); if (!info->i2c_info.buf) { NFC_LOG_ERR("probe() failed to allocate memory\n"); return -ENOMEM; } info->i2c_info.i2c_dev = client; info->i2c_info.read_irq = SEC_NFC_NONE; mutex_init(&info->i2c_info.read_mutex); init_waitqueue_head(&info->i2c_info.read_wait); i2c_set_clientdata(client, info); ret = gpio_request(pdata->irq, "nfc_int"); if (ret) { NFC_LOG_ERR("probe() GPIO request is failed to register IRQ\n"); goto err_irq_req; } gpio_direction_input(pdata->irq); ret = request_threaded_irq(client->irq, NULL, sec_nfc_irq_thread_fn, #ifdef CONFIG_SEC_NFC_DUPLICATED_IRQ_WQ_QC IRQF_TRIGGER_RISING | IRQF_ONESHOT | IRQF_NO_SUSPEND, SEC_NFC_DRIVER_NAME, #else IRQF_TRIGGER_RISING | IRQF_ONESHOT, SEC_NFC_DRIVER_NAME, #endif info); if (ret < 0) { NFC_LOG_ERR("probe() failed to register IRQ handler\n"); kfree(info->i2c_info.buf); return ret; } if (of_get_property(dev->of_node, "sec-nfc,ldo_control", NULL)) { if (pdata->nfc_pvdd != NULL) { #if defined(CONFIG_NFC_PVDD_LATE_ENABLE) if (nfc_param_lpcharge == LPM_FALSE) { /*pvdd enable in late init*/ #elif IS_ENABLED(CONFIG_BATTERY_SAMSUNG) if (!lpcharge) { #else if (1/*!lpcharge*/) { #endif ret = sec_nfc_regulator_onoff(pdata, NFC_I2C_LDO_ON); if (ret < 0) NFC_LOG_ERR("regulator on failed: %d\n", ret); if (of_find_property(dev->of_node, "sec-nfc,sw-gpio", NULL)) { if (gpio_is_valid(pdata->i2c_switch)) { ret = gpio_request(pdata->i2c_switch, "nfc_i2c_sw"); if (ret) NFC_LOG_ERR("probe() i2c_swich gpio request failed\n"); gpio_direction_output(pdata->i2c_switch, 1); } } #ifdef CONFIG_ESE_COLDRESET msleep(25); gpio_set_value(pdata->ven, SEC_NFC_PW_ON); #else usleep_range(1000, 1100); #endif } else { #if defined(CONFIG_NFC_PVDD_LATE_ENABLE) NFC_LOG_ERR("regulator off (late enable)\n"); #elif IS_ENABLED(CONFIG_BATTERY_SAMSUNG) NFC_LOG_ERR("regulator off at LPM: %d\n", lpcharge); #else NFC_LOG_ERR("regulator off at LPM\n"); #endif } } } else { ret = gpio_request(pdata->pvdd, "nfc_pvdd"); if (ret) NFC_LOG_ERR("probe() GPIO request is failed to register pvdd gpio\n"); gpio_direction_output(pdata->pvdd, 1); } NFC_LOG_INFO("probe() success\n"); return 0; err_irq_req: return ret; } static irqreturn_t sec_nfc_clk_irq_thread(int irq, void *dev_id) { struct sec_nfc_info *info = dev_id; struct sec_nfc_platform_data *pdata = info->pdata; bool value; if (pdata->irq_all_trigger) { value = gpio_get_value(pdata->clk_req) > 0 ? true : false; NFC_LOG_REC("clk_req: %d < %d\n", value, info->clk_state); if (value == info->clk_state) return IRQ_HANDLED; if (value) { if (!wake_lock_active(&info->nfc_clk_wake_lock)) wake_lock(&info->nfc_clk_wake_lock); if (pdata->clk && clk_prepare_enable(pdata->clk)) { NFC_LOG_ERR("clock enable failed\n"); return IRQ_HANDLED; } } else { if (pdata->clk) clk_disable_unprepare(pdata->clk); if (wake_lock_active(&info->nfc_clk_wake_lock)) wake_unlock(&info->nfc_clk_wake_lock); } info->clk_state = value; } else { wake_lock_timeout(&info->nfc_wake_lock, 2*HZ); NFC_LOG_REC("clk_req irq\n"); } return IRQ_HANDLED; } void sec_nfc_clk_ctl_enable(struct sec_nfc_info *info) { struct sec_nfc_platform_data *pdata = info->pdata; if (info->clk_ctl) return; if (!pdata->clk) return; info->clk_state = false; info->clk_ctl = true; } void sec_nfc_clk_ctl_disable(struct sec_nfc_info *info) { struct sec_nfc_platform_data *pdata = info->pdata; if (wake_lock_active(&info->nfc_clk_wake_lock)) wake_unlock(&info->nfc_clk_wake_lock); if (!info->clk_ctl) return; if (!pdata->clk) return; if (info->clk_state) clk_disable_unprepare(pdata->clk); info->clk_state = false; info->clk_ctl = false; } static bool sec_nfc_check_pin_status(struct sec_nfc_platform_data *pdata, enum sec_nfc_mode mode) { if (mode != SEC_NFC_MODE_OFF) { if (pdata->ven) { if (gpio_get_value(pdata->ven) != SEC_NFC_PW_ON) return false; } } if (mode == SEC_NFC_MODE_BOOTLOADER) { if (pdata->firm) { if (gpio_get_value_cansleep(pdata->firm) != SEC_NFC_FW_ON) return false; } } else { if (pdata->firm) { if (gpio_get_value_cansleep(pdata->firm) != SEC_NFC_FW_OFF) return false; } } return true; } static void sec_nfc_set_mode(struct sec_nfc_info *info, enum sec_nfc_mode mode) { struct sec_nfc_platform_data *pdata = info->pdata; int retry_count = 3; #ifdef CONFIG_ESE_COLDRESET int alreadFirmHigh = 0; int ret; enum sec_nfc_mode oldmode = info->mode; #endif /* intfo lock is aleady gotten before calling this function */ if (info->mode == mode) { NFC_LOG_DBG("power mode is already %d\n", mode); return; } info->mode = mode; /* Skip interrupt during power switching * It is released after first write */ mutex_lock(&info->i2c_info.read_mutex); info->i2c_info.read_irq = SEC_NFC_SKIP; mutex_unlock(&info->i2c_info.read_mutex); #ifdef CONFIG_ESE_COLDRESET mutex_lock(&sleep_wake_mutex); cur_mode = SEC_NFC_MODE_TURNING_ON_OFF; mutex_unlock(&sleep_wake_mutex); memset(sleep_wakeup_state, false, sizeof(sleep_wakeup_state)); if (oldmode == SEC_NFC_MODE_OFF) { if (gpio_get_value_cansleep(pdata->firm) == 1) { alreadFirmHigh = 1; NFC_LOG_INFO("Firm is already high\n"); } else {/*Firm pin is low*/ gpio_set_value_cansleep(pdata->firm, SEC_NFC_FW_ON); msleep(SEC_NFC_VEN_WAIT_TIME); } if (gpio_get_value(pdata->ven) == SEC_NFC_PW_ON) { ret = i2c_master_send(info->i2c_info.i2c_dev, disable_combo_reset_cmd, sizeof(disable_combo_reset_cmd)/sizeof(u8)); NFC_LOG_INFO("disable combo_reset_command ret: %d\n", ret); } else NFC_LOG_INFO("skip disable combo_reset_command\n"); if (alreadFirmHigh == 1) { NFC_LOG_INFO("Firm is already HIGH\n"); } else {/*Firm pin is low*/ usleep_range(3000, 3100); gpio_set_value_cansleep(pdata->firm, SEC_NFC_FW_OFF); } } #endif #ifdef CONFIG_ESE_COLDRESET usleep_range(1000, 1100); NFC_LOG_INFO("FIRMWARE_GUARD_TIME(+1ms) in PW_OFF(total:4ms)\n"); #endif pin_setting_retry: gpio_direction_output(pdata->ven, SEC_NFC_PW_OFF); if (pdata->firm) gpio_direction_output(pdata->firm, SEC_NFC_FW_OFF); if (mode == SEC_NFC_MODE_BOOTLOADER) if (pdata->firm) gpio_direction_output(pdata->firm, SEC_NFC_FW_ON); if (mode != SEC_NFC_MODE_OFF) { msleep(SEC_NFC_VEN_WAIT_TIME); gpio_direction_output(pdata->ven, SEC_NFC_PW_ON); sec_nfc_clk_ctl_enable(info); nfc_state_print(info); enable_irq_wake(info->i2c_info.i2c_dev->irq); msleep(SEC_NFC_VEN_WAIT_TIME/2); /* Workaround: FIRM or VEN is not set sometimes */ if (retry_count-- > 0 && !sec_nfc_check_pin_status(pdata, mode)) { NFC_LOG_INFO("Pin setting retry\n"); sec_nfc_clk_ctl_disable(info); disable_irq_wake(info->i2c_info.i2c_dev->irq); goto pin_setting_retry; } #ifdef CONFIG_SEC_ESE_COLDRESET mutex_lock(&sleep_wake_mutex); cur_mode = mode; mutex_unlock(&sleep_wake_mutex); #endif } else { #ifdef CONFIG_ESE_COLDRESET int PW_OFF_DURATION = 20; ktime_t t0, t1; t0 = ktime_get(); msleep(PW_OFF_DURATION); gpio_set_value(pdata->ven, SEC_NFC_PW_ON); t1 = ktime_get(); NFC_LOG_INFO("DeepStby: PW_OFF duration (%d)ms, real PW_OFF duration is (%ld-%ld)ms\n", PW_OFF_DURATION, t0, t1); NFC_LOG_INFO("DeepStby: enter DeepStby(PW_ON)\n"); mutex_lock(&sleep_wake_mutex); cur_mode = mode; mutex_unlock(&sleep_wake_mutex); #endif sec_nfc_clk_ctl_disable(info); nfc_state_print(info); disable_irq_wake(info->i2c_info.i2c_dev->irq); } if (wake_lock_active(&info->nfc_wake_lock)) wake_unlock(&info->nfc_wake_lock); NFC_LOG_INFO("NFC mode is : %d\n", mode); } #ifdef CONFIG_ESE_COLDRESET struct cold_reset_gpio { int firm_gpio; int coldreset_gpio; }; struct cold_reset_gpio cold_reset_gpio_data; void init_coldreset_mutex(void) { mutex_init(&coldreset_mutex); } void init_sleep_wake_mutex(void) { mutex_init(&sleep_wake_mutex); } void check_and_sleep_nfc(unsigned int gpio, int value) { if (sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] == true || sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] == true) { NFC_LOG_INFO("%s keep wake up state\n", __func__); return; } gpio_set_value_cansleep(gpio, value); } int trig_cold_reset_id(int id) { int wakeup_delay = 20; int duration = 18; ktime_t t0, t1, t2; int isFirmHigh = 0; NFC_LOG_INFO("COLDRESET: enter\n"); if (id == ESE_ID) mutex_lock(&coldreset_mutex); NFC_LOG_INFO("caller id:(%d) coldreset triggered. [wakeup_delay(%d), duration(%d))]\n", id, wakeup_delay, duration); t0 = ktime_get(); if (gpio_get_value_cansleep(cold_reset_gpio_data.firm_gpio) == 1) { isFirmHigh = 1; } else { gpio_set_value_cansleep(cold_reset_gpio_data.firm_gpio, SEC_NFC_FW_ON); msleep(wakeup_delay); } t1 = ktime_get(); gpio_set_value(cold_reset_gpio_data.coldreset_gpio, SEC_NFC_COLDRESET_ON); usleep_range(duration * 1000, duration * 1000 + 10); gpio_set_value(cold_reset_gpio_data.coldreset_gpio, SEC_NFC_COLDRESET_OFF); t2 = ktime_get(); if (isFirmHigh == 1) NFC_LOG_INFO("COLDRESET: FW_PIN already high, do not FW_OFF\n"); else gpio_set_value_cansleep(cold_reset_gpio_data.firm_gpio, SEC_NFC_FW_OFF); NFC_LOG_INFO("COLDRESET: FW_ON time (%ld-%ld)\n", t0, t1); NFC_LOG_INFO("COLDRESET: GPIO3 ON time (%ld-%ld)\n", t1, t2); if (id == ESE_ID) mutex_unlock(&coldreset_mutex); NFC_LOG_INFO("COLDRESET: exit\n"); return 0; } extern int trig_cold_reset(void) { /*only called GTO*/ return trig_cold_reset_id(ESE_ID); } extern int trig_nfc_wakeup(void) { NFC_LOG_INFO("%s\n", __func__); mutex_lock(&sleep_wake_mutex); if (cur_mode != SEC_NFC_MODE_FIRMWARE) { NFC_LOG_ERR("nfc mode not support to wake up\n"); mutex_unlock(&sleep_wake_mutex); return -EPERM; } gpio_set_value_cansleep(cold_reset_gpio_data.firm_gpio, SEC_NFC_WAKE_UP); sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] = true; mutex_unlock(&sleep_wake_mutex); return 0; } extern int trig_nfc_sleep(void) { NFC_LOG_INFO("%s\n", __func__); mutex_lock(&sleep_wake_mutex); if (cur_mode != SEC_NFC_MODE_FIRMWARE) { NFC_LOG_ERR("nfc mode not support to sleep\n"); mutex_unlock(&sleep_wake_mutex); return -EPERM; } sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] = false; check_and_sleep_nfc(cold_reset_gpio_data.firm_gpio, SEC_NFC_WAKE_SLEEP); mutex_unlock(&sleep_wake_mutex); return 0; } #endif static long sec_nfc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct sec_nfc_info *info = container_of(file->private_data, struct sec_nfc_info, miscdev); struct sec_nfc_platform_data *pdata = info->pdata; unsigned int new = (unsigned int)arg; int ret = 0; NFC_LOG_DBG("cmd: 0x%x\n", cmd); mutex_lock(&info->mutex); #ifdef CONFIG_ESE_COLDRESET mutex_lock(&coldreset_mutex); #endif switch (cmd) { case SEC_NFC_DEBUG: NFC_LOG_ERR("SEC_NFC_DEBUG\n"); nfc_state_print(info); break; case SEC_NFC_SET_MODE: if (info->mode == new) break; if (new >= SEC_NFC_MODE_COUNT) { NFC_LOG_ERR("wrong mode (%d)\n", new); ret = -EFAULT; break; } sec_nfc_set_mode(info, new); break; case SEC_NFC_SLEEP: if (info->mode != SEC_NFC_MODE_BOOTLOADER) { if (wake_lock_active(&info->nfc_wake_lock)) wake_unlock(&info->nfc_wake_lock); #ifdef CONFIG_SEC_ESE_COLDRESET mutex_lock(&sleep_wake_mutex); sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] = false; check_and_sleep_nfc(pdata->wake, SEC_NFC_WAKE_SLEEP); mutex_unlock(&sleep_wake_mutex); #else gpio_set_value_cansleep(pdata->wake, SEC_NFC_WAKE_SLEEP); #endif } break; case SEC_NFC_WAKEUP: if (info->mode != SEC_NFC_MODE_BOOTLOADER) { gpio_set_value_cansleep(pdata->wake, SEC_NFC_WAKE_UP); #ifdef CONFIG_SEC_ESE_COLDRESET mutex_lock(&sleep_wake_mutex); sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] = true; mutex_unlock(&sleep_wake_mutex); #endif if (!wake_lock_active(&info->nfc_wake_lock)) wake_lock(&info->nfc_wake_lock); } break; /*[START] NPT*/ case SEC_NFC_SET_NPT_MODE: NFC_LOG_INFO("NPT: VEN=%d, FIRM:%d\n", gpio_get_value(pdata->ven), gpio_get_value_cansleep(pdata->firm)); if (new == SEC_NFC_NPT_CMD_ON) { NFC_LOG_INFO("NPT: NFC OFF mode NPT - Turn on VEN.\n"); info->mode = SEC_NFC_MODE_FIRMWARE; mutex_lock(&info->i2c_info.read_mutex); info->i2c_info.read_irq = SEC_NFC_SKIP; mutex_unlock(&info->i2c_info.read_mutex); gpio_set_value(pdata->ven, SEC_NFC_PW_ON); sec_nfc_clk_ctl_enable(info); msleep(20); gpio_set_value_cansleep(pdata->firm, SEC_NFC_FW_ON); enable_irq_wake(info->i2c_info.i2c_dev->irq); } else if (new == SEC_NFC_NPT_CMD_OFF) { NFC_LOG_INFO("NPT: NFC OFF mode NPT - Turn off VEN.\n"); info->mode = SEC_NFC_MODE_OFF; gpio_set_value_cansleep(pdata->firm, SEC_NFC_FW_OFF); gpio_set_value(pdata->ven, SEC_NFC_PW_OFF); sec_nfc_clk_ctl_disable(info); disable_irq_wake(info->i2c_info.i2c_dev->irq); } break; /*[END] NPT*/ #ifdef CONFIG_ESE_COLDRESET case SEC_NFC_COLD_RESET: trig_cold_reset_id(DEVICEHOST_ID); break; #endif default: NFC_LOG_ERR("NPT: Unknown ioctl 0x%x\n", cmd); ret = -ENOIOCTLCMD; break; } #ifdef CONFIG_ESE_COLDRESET mutex_unlock(&coldreset_mutex); #endif mutex_unlock(&info->mutex); return ret; } static int sec_nfc_open(struct inode *inode, struct file *file) { struct sec_nfc_info *info = container_of(file->private_data, struct sec_nfc_info, miscdev); int ret = 0; NFC_LOG_INFO("%s\n", __func__); #if defined(CONFIG_ESE_SECURE) && defined(CONFIG_ESE_USE_TZ_API) if (nfc_ese_secured == NOT_CHECKED) { ret = tz_tee_ese_secure_check(); if (ret) { nfc_ese_secured = ESE_NOT_SECURED; NFC_LOG_ERR("eSE spi is not Secured\n"); return -EBUSY; } nfc_ese_secured = ESE_SECURED; } else if (nfc_ese_secured == ESE_NOT_SECURED) { NFC_LOG_ERR("eSE spi is not Secured\n"); return -EBUSY; } #endif mutex_lock(&info->mutex); if (info->mode != SEC_NFC_MODE_OFF) { NFC_LOG_ERR("open() nfc is busy\n"); nfc_state_print(info); ret = -EBUSY; goto out; } sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); out: mutex_unlock(&info->mutex); return ret; } static int sec_nfc_close(struct inode *inode, struct file *file) { struct sec_nfc_info *info = container_of(file->private_data, struct sec_nfc_info, miscdev); if (wake_lock_active(&info->nfc_clk_wake_lock)) wake_unlock(&info->nfc_clk_wake_lock); nfc_state_print(info); NFC_LOG_INFO("%s\n", __func__); mutex_lock(&info->mutex); sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); mutex_unlock(&info->mutex); return 0; } static const struct file_operations sec_nfc_fops = { .owner = THIS_MODULE, .read = sec_nfc_read, .write = sec_nfc_write, .poll = sec_nfc_poll, .open = sec_nfc_open, .release = sec_nfc_close, .unlocked_ioctl = sec_nfc_ioctl, }; #ifdef CONFIG_PM static int sec_nfc_suspend(struct device *dev) { struct sec_nfc_info *info = SEC_NFC_GET_INFO(dev); int ret = 0; NFC_LOG_INFO_WITH_DATE("suspend!\n"); mutex_lock(&info->mutex); if (info->mode == SEC_NFC_MODE_BOOTLOADER) ret = -EPERM; mutex_unlock(&info->mutex); return ret; } static int sec_nfc_resume(struct device *dev) { NFC_LOG_INFO_WITH_DATE("resume!\n"); return 0; } static SIMPLE_DEV_PM_OPS(sec_nfc_pm_ops, sec_nfc_suspend, sec_nfc_resume); #endif /*device tree parsing*/ static int sec_nfc_parse_dt(struct device *dev, struct sec_nfc_platform_data *pdata) { struct device_node *np = dev->of_node; static int retry_count = 3; pdata->ven = of_get_named_gpio(np, "sec-nfc,ven-gpio", 0); pdata->firm = of_get_named_gpio(np, "sec-nfc,firm-gpio", 0); pdata->wake = pdata->firm; pdata->irq = of_get_named_gpio(np, "sec-nfc,irq-gpio", 0); #ifdef CONFIG_ESE_COLDRESET pdata->coldreset = of_get_named_gpio(np, "sec-nfc,coldreset-gpio", 0); NFC_LOG_INFO("parse_dt() coldreset : %d\n", pdata->coldreset); cold_reset_gpio_data.firm_gpio = pdata->firm; cold_reset_gpio_data.coldreset_gpio = pdata->coldreset; #endif if (of_get_property(dev->of_node, "sec-nfc,ldo_control", NULL)) { pdata->nfc_pvdd = regulator_get(dev, "nfc_pvdd"); if (IS_ERR(pdata->nfc_pvdd)) { NFC_LOG_ERR("get nfc_pvdd error\n"); pdata->nfc_pvdd = NULL; if (--retry_count > 0) return -EPROBE_DEFER; else return -ENODEV; } } else { pdata->pvdd = of_get_named_gpio(np, "sec-nfc,pvdd-gpio", 0); NFC_LOG_INFO("parse_dt() pvdd : %d\n", pdata->pvdd); } if (of_find_property(dev->of_node, "sec-nfc,sw-gpio", NULL)) { pdata->i2c_switch = of_get_named_gpio(np, "sec-nfc,sw-gpio", 0); NFC_LOG_INFO("parse_dt() i2c switch : %d\n", pdata->i2c_switch); } pdata->clk_req = of_get_named_gpio(np, "sec-nfc,clk_req-gpio", 0); NFC_LOG_INFO("parse_dt() clk_req : %d\n", pdata->clk_req); pdata->clk_req_wake = of_property_read_bool(np, "sec-nfc,clk_req_wake"); NFC_LOG_INFO("%s : sec-nfc,clk_req_wake: %s\n", __func__, pdata->clk_req_wake ? "true" : "false"); if (of_find_property(np, "clocks", NULL)) { pdata->clk = clk_get(dev, "oscclk_nfc"); if (IS_ERR(pdata->clk)) { NFC_LOG_ERR("probe() clk not found\n"); pdata->clk = NULL; } else { NFC_LOG_INFO("parse_dt() found oscclk_nfc\n"); } } if (of_property_read_bool(np, "sec-nfc,irq_all_trigger")) { pdata->irq_all_trigger = true; NFC_LOG_INFO("irq_all_trigger\n"); } if (!of_property_read_u32(np, "sec-nfc,bootloader_ver", &pdata->bootloader_ver)) NFC_LOG_INFO("bootloader_ver : %d\n", pdata->bootloader_ver); NFC_LOG_INFO("parse_dt() irq : %d, ven : %d, firm : %d\n", pdata->irq, pdata->ven, pdata->firm); return 0; } #ifdef FEATURE_SEC_NFC_TEST static int sec_nfc_i2c_read(char *buf, int count) { struct sec_nfc_info *info = g_nfc_info; int ret = 0; mutex_lock(&info->mutex); if (info->mode == SEC_NFC_MODE_OFF) { NFC_LOG_ERR("NFC_TEST: sec_nfc is not enabled\n"); ret = -ENODEV; goto out; } /* i2c recv */ if (count > info->i2c_info.buflen) count = info->i2c_info.buflen; if (count > SEC_NFC_MSG_MAX_SIZE) { NFC_LOG_ERR("NFC_TEST: user required wrong size :%d\n", (u32)count); ret = -EINVAL; goto out; } mutex_lock(&info->i2c_info.read_mutex); memset(buf, 0, count); ret = i2c_master_recv(info->i2c_info.i2c_dev, buf, (u32)count); NFC_LOG_INFO("NFC_TEST: recv size : %d\n", ret); if (ret == -EREMOTEIO) { ret = -ERESTART; goto read_error; } else if (ret != count) { NFC_LOG_ERR("NFC_TEST: read failed: return: %d count: %d\n", ret, (u32)count); goto read_error; } mutex_unlock(&info->i2c_info.read_mutex); goto out; read_error: info->i2c_info.read_irq = SEC_NFC_NONE; mutex_unlock(&info->i2c_info.read_mutex); out: mutex_unlock(&info->mutex); return ret; } static int sec_nfc_i2c_write(char *buf, int count) { struct sec_nfc_info *info = g_nfc_info; int ret = 0; mutex_lock(&info->mutex); if (info->mode == SEC_NFC_MODE_OFF) { NFC_LOG_ERR("NFC_TEST: sec_nfc is not enabled\n"); ret = -ENODEV; goto out; } if (count > info->i2c_info.buflen) count = info->i2c_info.buflen; if (count > SEC_NFC_MSG_MAX_SIZE) { NFC_LOG_ERR("NFC_TEST: user required wrong size :%d\n", (u32)count); ret = -EINVAL; goto out; } mutex_lock(&info->i2c_info.read_mutex); ret = i2c_master_send(info->i2c_info.i2c_dev, buf, count); mutex_unlock(&info->i2c_info.read_mutex); if (ret == -EREMOTEIO) { NFC_LOG_ERR("NFC_TEST: send failed: return: %d count: %d\n", ret, (u32)count); ret = -ERESTART; goto out; } if (ret != count) { NFC_LOG_ERR("NFC_TEST: send failed: return: %d count: %d\n", ret, (u32)count); ret = -EREMOTEIO; } out: mutex_unlock(&info->mutex); return ret; } static ssize_t test_show(struct class *class, struct class_attribute *attr, char *buf) { enum sec_nfc_mode old_mode = g_nfc_info->mode; int size; int ret = 0; int timeout = 1; on_nfc_test = true; nfc_int_wait = false; NFC_LOG_INFO("NFC_TEST: mode = %d, bootloader ver = %d\n", old_mode, g_nfc_info->pdata->bootloader_ver); sec_nfc_set_mode(g_nfc_info, SEC_NFC_MODE_BOOTLOADER); if (g_nfc_info->pdata->bootloader_ver > 4) { /* SEN4, SN4V, RN4V */ char cmd[9] = {0x0, 0x1, 0x5, 0x0, 0x0, 0x14, 0x1, 0x0, 0x0}; ret = sec_nfc_i2c_write(cmd, 9); } else { char cmd[4] = {0x0, 0x1, 0x0, 0x0}; ret = sec_nfc_i2c_write(cmd, 4); } if (ret < 0) { NFC_LOG_INFO("NFC_TEST: i2c write error %d\n", ret); size = snprintf(buf, PAGE_SIZE, "NFC_TEST: i2c write error %d\n", ret); goto exit; } timeout = wait_event_interruptible_timeout(g_nfc_info->i2c_info.read_wait, nfc_int_wait, 100); ret = sec_nfc_i2c_read(buf, 16); if (ret < 0) { NFC_LOG_INFO("NFC_TEST: i2c read error %d\n", ret); size = snprintf(buf, PAGE_SIZE, "NFC_TEST: i2c read error %d\n", ret); goto exit; } NFC_LOG_INFO("NFC_TEST: BL ver: %02X %02X %02X %02X, INT: %s\n", buf[0], buf[1], buf[2], buf[3], timeout ? "OK":"NOK"); size = snprintf(buf, PAGE_SIZE, "BL ver: %02X.%02X.%02X.%02X, INT: %s\n", buf[0], buf[1], buf[2], buf[3], timeout ? "OK":"NOK"); exit: sec_nfc_set_mode(g_nfc_info, old_mode); on_nfc_test = false; return size; } static CLASS_ATTR_RO(test); #endif #if defined(CONFIG_NFC_PVDD_LATE_ENABLE) static ssize_t pvdd_store(struct class *class, struct class_attribute *attr, const char *buf, size_t size) { int ret = 0; if (!g_nfc_info) { NFC_LOG_ERR("%s nfc drv is NULL!", __func__); return size; } NFC_LOG_INFO("late_pvdd_en %c\n", buf[0]); if (buf[0] == '1') { ret = sec_nfc_regulator_onoff(g_nfc_info->pdata, NFC_I2C_LDO_ON); if (ret < 0) NFC_LOG_ERR("regulator on failed: %d\n", ret); if (of_find_property(g_nfc_info->dev->of_node, "sec-nfc,sw-gpio", NULL)) { if (gpio_is_valid(g_nfc_info->pdata->i2c_switch)) { ret = gpio_request(g_nfc_info->pdata->i2c_switch, "nfc_i2c_sw"); if (ret) NFC_LOG_ERR("probe() i2c_swich gpio request failed\n"); gpio_direction_output(g_nfc_info->pdata->i2c_switch, 1); } } } return size; } static CLASS_ATTR_WO(pvdd); #endif static ssize_t nfc_support_show(struct class *class, struct class_attribute *attr, char *buf) { NFC_LOG_INFO("\n"); return 0; } static CLASS_ATTR_RO(nfc_support); static int __sec_nfc_probe(struct device *dev) { struct sec_nfc_info *info; struct sec_nfc_platform_data *pdata = NULL; int ret = 0; struct class *nfc_class; NFC_LOG_INFO("probe start\n"); if (dev->of_node) { pdata = devm_kzalloc(dev, sizeof(struct sec_nfc_platform_data), GFP_KERNEL); if (!pdata) { NFC_LOG_ERR("probe() Failed to allocate memory\n"); return -ENOMEM; } ret = sec_nfc_parse_dt(dev, pdata); if (ret) return ret; } else { pdata = dev->platform_data; } if (!pdata) { NFC_LOG_ERR("probe() No platform data\n"); ret = -ENOMEM; goto err_pdata; } info = kzalloc(sizeof(struct sec_nfc_info), GFP_KERNEL); if (!info) { NFC_LOG_ERR("probe() failed to allocate memory for sec_nfc_info\n"); ret = -ENOMEM; goto err_info_alloc; } info->dev = dev; info->pdata = pdata; info->mode = SEC_NFC_MODE_OFF; mutex_init(&info->mutex); wake_lock_init(&info->nfc_wake_lock, WAKE_LOCK_SUSPEND, "nfc_wake_lock"); wake_lock_init(&info->nfc_clk_wake_lock, WAKE_LOCK_SUSPEND, "nfc_clk_wake_lock"); dev_set_drvdata(dev, info); /*separate NFC / non NFC using GPIO*/ if (of_find_property(dev->of_node, "sec-nfc,check_nfc", NULL)) { int nfc_support = 0; nfc_support = gpio_get_value(of_get_named_gpio(dev->of_node, "sec-nfc,check_nfc", 0)); if (nfc_support > 0) { NFC_LOG_INFO("nfc support: %d\n", nfc_support); } else { struct pinctrl *pinctrl = NULL; pinctrl = devm_pinctrl_get_select(dev, "nfc_nc"); if (IS_ERR_OR_NULL(pinctrl)) NFC_LOG_ERR("Failed to configure nfc NC pin\n"); else devm_pinctrl_put(pinctrl); NFC_LOG_INFO("nfc not support: %d\n", nfc_support); return -ENXIO; } } info->miscdev.minor = MISC_DYNAMIC_MINOR; info->miscdev.name = SEC_NFC_DRIVER_NAME; info->miscdev.fops = &sec_nfc_fops; info->miscdev.parent = dev; ret = misc_register(&info->miscdev); if (ret < 0) { NFC_LOG_ERR("probe() failed to register Device\n"); goto err_dev_reg; } if (pdata->clk_req_wake || pdata->irq_all_trigger) { unsigned long irq_flag = IRQF_TRIGGER_RISING | IRQF_ONESHOT; if (pdata->irq_all_trigger) irq_flag |= IRQF_TRIGGER_FALLING; ret = gpio_request(pdata->clk_req, "nfc_clk_req"); if (ret) NFC_LOG_ERR("probe() failed to get clk_req\n"); gpio_direction_input(pdata->clk_req); pdata->clk_irq = gpio_to_irq(pdata->clk_req); ret = request_threaded_irq(pdata->clk_irq, NULL, sec_nfc_clk_irq_thread, irq_flag, "sec-nfc_clk", info); if (ret < 0) NFC_LOG_ERR("probe() failed to register CLK REQ IRQ handler\n"); else enable_irq_wake(pdata->clk_irq); } ret = gpio_request(pdata->ven, "nfc_ven"); if (ret) { NFC_LOG_ERR("probe() failed to get gpio ven\n"); goto err_gpio_ven; } gpio_direction_output(pdata->ven, SEC_NFC_PW_OFF); if (pdata->firm) { ret = gpio_request(pdata->firm, "nfc_firm"); if (ret) { NFC_LOG_ERR("probe() failed to get gpio firm\n"); goto err_gpio_firm; } gpio_direction_output(pdata->firm, SEC_NFC_FW_OFF); } #ifdef CONFIG_ESE_COLDRESET init_coldreset_mutex(); init_sleep_wake_mutex(); memset(sleep_wakeup_state, false, sizeof(sleep_wakeup_state)); ret = gpio_request(pdata->coldreset, "nfc_coldreset"); if (ret) { dev_err(dev, "failed to get gpio coldreset(NFC-GPIO3)\n"); goto err_gpio_coldreset; } gpio_direction_output(pdata->coldreset, SEC_NFC_COLDRESET_OFF); #endif g_nfc_info = info; #ifdef FEATURE_SEC_NFC_TEST nfc_class = class_create(THIS_MODULE, "nfc_test"); if (IS_ERR(&nfc_class)) NFC_LOG_ERR("NFC: failed to create nfc_test class\n"); else { ret = class_create_file(nfc_class, &class_attr_test); if (ret) NFC_LOG_ERR("NFC: failed to create attr_test\n"); } #endif nfc_class = class_create(THIS_MODULE, "nfc_sec"); if (IS_ERR(&nfc_class)) NFC_LOG_ERR("NFC: failed to create nfc class\n"); else { ret = class_create_file(nfc_class, &class_attr_nfc_support); if (ret) NFC_LOG_ERR("NFC: failed to create attr_nfc_support\n"); #if defined(CONFIG_NFC_PVDD_LATE_ENABLE) if (nfc_param_lpcharge == LPM_NO_SUPPORT) { ret = class_create_file(nfc_class, &class_attr_pvdd); if (ret) NFC_LOG_ERR("NFC: failed to create attr_pvdd\n"); } #endif } nfc_logger_register_nfc_stauts_func(sec_nfc_print_status); NFC_LOG_INFO("probe() success\n"); return 0; #ifdef CONFIG_ESE_COLDRESET err_gpio_coldreset: gpio_free(pdata->coldreset); #endif err_gpio_firm: gpio_free(pdata->ven); err_gpio_ven: free_irq(pdata->clk_irq, info); misc_deregister(&info->miscdev); err_dev_reg: mutex_destroy(&info->mutex); kfree(info); err_info_alloc: devm_kfree(dev, pdata); err_pdata: return ret; } static int __sec_nfc_remove(struct device *dev) { struct sec_nfc_info *info = dev_get_drvdata(dev); struct i2c_client *client = info->i2c_info.i2c_dev; struct sec_nfc_platform_data *pdata = info->pdata; NFC_LOG_DBG("remove\n"); misc_deregister(&info->miscdev); sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); free_irq(client->irq, info); free_irq(pdata->clk_irq, info); gpio_free(pdata->irq); gpio_set_value_cansleep(pdata->firm, 0); gpio_free(pdata->ven); if (pdata->firm) gpio_free(pdata->firm); wake_lock_destroy(&info->nfc_wake_lock); kfree(info); return 0; } #define SEC_NFC_INIT(driver) i2c_add_driver(driver) #define SEC_NFC_EXIT(driver) i2c_del_driver(driver) static int sec_nfc_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret = 0; nfc_logger_init(); ret = __sec_nfc_probe(&client->dev); if (ret) return ret; if (sec_nfc_i2c_probe(client)) __sec_nfc_remove(&client->dev); return ret; } static int sec_nfc_remove(struct i2c_client *client) { return __sec_nfc_remove(&client->dev); } static struct i2c_device_id sec_nfc_id_table[] = { { SEC_NFC_DRIVER_NAME, 0 }, { } }; MODULE_DEVICE_TABLE(i2c, sec_nfc_id_table); static const struct of_device_id nfc_match_table[] = { { .compatible = SEC_NFC_DRIVER_NAME,}, {}, }; static struct i2c_driver sec_nfc_driver = { .probe = sec_nfc_probe, .id_table = sec_nfc_id_table, .remove = sec_nfc_remove, .driver = { .name = SEC_NFC_DRIVER_NAME, #ifdef CONFIG_PM .pm = &sec_nfc_pm_ops, #endif .of_match_table = nfc_match_table, .suppress_bind_attrs = true, }, }; #if !IS_MODULE(CONFIG_SAMSUNG_NFC) /* * if cmd line(nfc_sec.nfc_param_lpcharge) is not defined in bootloader, * this function is not called and LPM_NO_SUPPORT(-1) is assigned to nfc_param_lpcharge. */ static int __init nfc_lpcharge_func(char *str) { pr_info("nfc_sec.nfc_param_lpcharge %s\n", str); if (str[0] == '1') nfc_param_lpcharge = LPM_TRUE; else nfc_param_lpcharge = LPM_FALSE; return 0; } early_param("nfc_sec.nfc_param_lpcharge", nfc_lpcharge_func); #endif #if IS_MODULE(CONFIG_SAMSUNG_NFC) extern int spip3_dev_init(void); extern void spip3_dev_exit(void); static int __init sec_nfc_init(void) { #if IS_ENABLED(CONFIG_ESE_P3_LSI) spip3_dev_init(); #endif return SEC_NFC_INIT(&sec_nfc_driver); } static void __exit sec_nfc_exit(void) { #if IS_ENABLED(CONFIG_ESE_P3_LSI) spip3_dev_exit(); #endif SEC_NFC_EXIT(&sec_nfc_driver); } #else static int __init sec_nfc_init(void) { pr_info("%s lpcharge %d\n", __func__, nfc_param_lpcharge); if (nfc_param_lpcharge == LPM_TRUE) return 0; return SEC_NFC_INIT(&sec_nfc_driver); } static void __exit sec_nfc_exit(void) { if (nfc_param_lpcharge != LPM_TRUE) SEC_NFC_EXIT(&sec_nfc_driver); } #endif module_init(sec_nfc_init); module_exit(sec_nfc_exit); MODULE_DESCRIPTION("Samsung sec_nfc driver"); MODULE_LICENSE("GPL");