| /* |
| * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/fs.h> |
| #include <linux/slab.h> |
| #include <linux/init.h> |
| #include <linux/list.h> |
| #include <linux/i2c.h> |
| #include <linux/clk.h> |
| #include <linux/irq.h> |
| #include <linux/jiffies.h> |
| #include <linux/uaccess.h> |
| #include <linux/delay.h> |
| #include <linux/interrupt.h> |
| #include <linux/io.h> |
| #include <linux/platform_device.h> |
| #include <linux/gpio.h> |
| #include <linux/miscdevice.h> |
| #include <linux/spinlock.h> |
| #include "pn547.h" |
| #include <linux/wakelock.h> |
| #include <linux/of_gpio.h> |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| #include <mach/msm_xo.h> |
| #include <linux/workqueue.h> |
| #endif |
| #include <linux/clk.h> |
| #include <linux/regulator/consumer.h> |
| |
| #include <asm/siginfo.h> |
| #include <linux/rcupdate.h> |
| #include <linux/sched.h> |
| #include <linux/signal.h> |
| |
| #define SIG_NFC 44 |
| #define MAX_BUFFER_SIZE 512 |
| #define NFC_DEBUG 0 |
| #define MAX_TRY_I2C_READ 10 |
| #define I2C_ADDR_READ_L 0x51 |
| #define I2C_ADDR_READ_H 0x57 |
| #define FEATURE_SEC_NFC_TEST |
| |
| struct pn547_dev { |
| wait_queue_head_t read_wq; |
| struct mutex read_mutex; |
| struct i2c_client *client; |
| struct miscdevice pn547_device; |
| void (*conf_gpio) (void); |
| int ven_gpio; |
| int firm_gpio; |
| int irq_gpio; |
| int pvdd; /*pvdd-gpio number*/ |
| void __iomem *clkctrl; |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| int ese_pwr_req; |
| struct mutex p61_state_mutex; /* used to make p61_current_state flag secure */ |
| p61_access_state_t p61_current_state; /* stores the current P61 state */ |
| #endif |
| bool spi_ven_enabled; /* stores the VEN pin state powered by Spi */ |
| bool nfc_ven_enabled; /* stores the VEN pin state powered by Nfc */ |
| |
| atomic_t irq_enabled; |
| atomic_t read_flag; |
| bool cancel_read; |
| struct wake_lock nfc_wake_lock; |
| struct wake_lock nfc_clk_wake_lock; |
| struct clk *nfc_clock; |
| int clk_req_gpio; |
| int clk_req_irq; |
| |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| struct msm_xo_voter *nfc_clock; |
| struct work_struct work_nfc_clock; |
| struct workqueue_struct *wq_clock; |
| bool clock_state; |
| #endif |
| long nfc_service_pid; /*used to signal the nfc the nfc service */ |
| }; |
| |
| static struct pn547_dev *pn547_dev; |
| static atomic_t s_Device_opened = ATOMIC_INIT(1); |
| |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| static struct semaphore ese_access_sema; |
| static void release_ese_lock(p61_access_state_t p61_current_state); |
| static struct semaphore svdd_sync_onoff_sema; |
| int get_ese_lock(p61_access_state_t p61_current_state, int timeout); |
| static unsigned char svdd_sync_wait; |
| #endif |
| |
| static void set_pd(struct pn547_dev *info, int power) |
| { |
| if (info->ven_gpio > 0) { |
| gpio_direction_output(info->ven_gpio, power); |
| } |
| else { |
| unsigned int val = 0; |
| int pd_active=0; |
| |
| val = readl(info->clkctrl); |
| pd_active = (val & PN547_NFC_CLKCTRL_PD_POLA); |
| if (!pd_active) { |
| if (power) |
| val &= ~PN547_NFC_CLKCTRL_PD; |
| else |
| val |= PN547_NFC_CLKCTRL_PD; |
| } else { |
| if (power) |
| val |= PN547_NFC_CLKCTRL_PD; |
| else |
| val &= ~PN547_NFC_CLKCTRL_PD; |
| } |
| writel(val, info->clkctrl); |
| pr_info("%s , val : %x\n", __func__,readl(info->clkctrl)); |
| } |
| } |
| |
| static irqreturn_t pn547_dev_irq_handler(int irq, void *dev_id) |
| { |
| struct pn547_dev *pn547_dev = dev_id; |
| |
| if (!gpio_get_value(pn547_dev->irq_gpio)) { |
| #if NFC_DEBUG |
| pr_err("%s, irq_gpio = %d\n", __func__, |
| gpio_get_value(pn547_dev->irq_gpio)); |
| #endif |
| return IRQ_HANDLED; |
| } |
| |
| /* Wake up waiting readers */ |
| atomic_set(&pn547_dev->read_flag, 1); |
| wake_up(&pn547_dev->read_wq); |
| |
| |
| #if NFC_DEBUG |
| pr_info("pn547 : call\n"); |
| #endif |
| wake_lock_timeout(&pn547_dev->nfc_wake_lock, 2*HZ); |
| return IRQ_HANDLED; |
| } |
| |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| static void nfc_work_func_clock(struct work_struct *work) |
| { |
| struct pn547_dev *pn547_dev = container_of(work, struct pn547_dev, |
| work_nfc_clock); |
| int ret = 0; |
| |
| if (gpio_get_value(pn547_dev->clk_req_gpio)) { |
| if (pn547_dev->clock_state == false) { |
| ret = msm_xo_mode_vote(pn547_dev->nfc_clock, |
| MSM_XO_MODE_ON); |
| if (ret < 0) { |
| pr_err("%s: Failed to vote for TCX0_A1 ON (%d)\n", |
| __func__, ret); |
| } |
| pn547_dev->clock_state = true; |
| } |
| } else { |
| if (pn547_dev->clock_state == true) { |
| ret = msm_xo_mode_vote(pn547_dev->nfc_clock, |
| MSM_XO_MODE_OFF); |
| if (ret < 0) { |
| pr_err("%s: Failed to vote for TCX0_A1 OFF (%d)\n", |
| __func__, ret); |
| } |
| pn547_dev->clock_state = false; |
| } |
| } |
| } |
| |
| static irqreturn_t pn547_dev_clk_req_irq_handler(int irq, void *dev_id) |
| { |
| struct pn547_dev *pn547_dev = dev_id; |
| queue_work(pn547_dev->wq_clock, &pn547_dev->work_nfc_clock); |
| return IRQ_HANDLED; |
| } |
| #endif |
| static irqreturn_t pn547_nfc_clk_irq(int irq, void *dev_id) |
| { |
| struct pn547_dev *pn547_dev = dev_id; |
| |
| #if 0 |
| if (gpio_get_value(pn547_dev->clk_req_gpio)) { |
| if(!wake_lock_active(&pn547_dev->nfc_clk_wake_lock)) |
| wake_lock(&pn547_dev->nfc_clk_wake_lock); |
| } else { |
| if (wake_lock_active(&pn547_dev->nfc_clk_wake_lock)) |
| wake_unlock(&pn547_dev->nfc_clk_wake_lock); |
| } |
| #else |
| wake_lock_timeout(&pn547_dev->nfc_clk_wake_lock, 2*HZ); |
| |
| #endif |
| |
| return IRQ_HANDLED; |
| } |
| |
| static ssize_t pn547_dev_read(struct file *filp, char __user *buf, |
| size_t count, loff_t *offset) |
| { |
| struct pn547_dev *pn547_dev = filp->private_data; |
| char tmp[MAX_BUFFER_SIZE] = {0, }; |
| int ret = 0; |
| #ifdef CONFIG_NFC_PN544 |
| int readingWatchdog = 0; |
| #endif |
| |
| if (count > MAX_BUFFER_SIZE) |
| count = MAX_BUFFER_SIZE; |
| |
| pr_debug("%s : reading %zu bytes. irq=%s\n", __func__, count, |
| gpio_get_value(pn547_dev->irq_gpio) ? "1" : "0"); |
| |
| #if NFC_DEBUG |
| pr_info("pn547 : + r\n"); |
| #endif |
| |
| mutex_lock(&pn547_dev->read_mutex); |
| |
| #ifdef CONFIG_NFC_PN544 |
| wait_irq: |
| #endif |
| |
| if (!gpio_get_value(pn547_dev->irq_gpio)) { |
| atomic_set(&pn547_dev->read_flag, 0); |
| if (filp->f_flags & O_NONBLOCK) { |
| pr_info("%s : O_NONBLOCK\n", __func__); |
| ret = -EAGAIN; |
| goto fail; |
| } |
| |
| #if NFC_DEBUG |
| pr_info("pn547: wait_event_interruptible : in\n"); |
| #endif |
| if (!gpio_get_value(pn547_dev->irq_gpio)) |
| ret = wait_event_interruptible(pn547_dev->read_wq, |
| atomic_read(&pn547_dev->read_flag)); |
| |
| #if NFC_DEBUG |
| pr_info("pn547 : h\n"); |
| #endif |
| |
| if (pn547_dev->cancel_read) { |
| pn547_dev->cancel_read = false; |
| ret = -1; |
| goto fail; |
| } |
| |
| if (ret) |
| goto fail; |
| |
| } |
| |
| /* Read data */ |
| ret = i2c_master_recv(pn547_dev->client, tmp, count); |
| |
| #ifdef CONFIG_NFC_PN544 |
| /* If bad frame(from 0x51 to 0x57) is received from pn65n, |
| * we need to read again after waiting that IRQ is down. |
| * if data is not ready, pn65n will send from 0x51 to 0x57. */ |
| if ((I2C_ADDR_READ_L <= tmp[0] && tmp[0] <= I2C_ADDR_READ_H) |
| && readingWatchdog < MAX_TRY_I2C_READ) { |
| pr_warn("%s: data is not ready yet.data = 0x%x, cnt=%d\n", |
| __func__, tmp[0], readingWatchdog); |
| usleep_range(2000, 2000); /* sleep 2ms to wait for IRQ */ |
| readingWatchdog++; |
| goto wait_irq; |
| } |
| #endif |
| |
| #if NFC_DEBUG |
| pr_info("pn547: i2c_master_recv\n"); |
| #endif |
| mutex_unlock(&pn547_dev->read_mutex); |
| if (ret < 0) { |
| pr_err("%s: i2c_master_recv returned %d\n", __func__, |
| ret); |
| return ret; |
| } |
| |
| if (ret > count) { |
| pr_err("%s: received too many bytes from i2c (%d)\n", |
| __func__, ret); |
| return -EIO; |
| } |
| |
| if (copy_to_user(buf, tmp, ret)) { |
| pr_err("%s : failed to copy to user space\n", __func__); |
| return -EFAULT; |
| } |
| return ret; |
| |
| fail: |
| mutex_unlock(&pn547_dev->read_mutex); |
| return ret; |
| } |
| |
| static ssize_t pn547_dev_write(struct file *filp, const char __user *buf, |
| size_t count, loff_t *offset) |
| { |
| struct pn547_dev *pn547_dev; |
| char tmp[MAX_BUFFER_SIZE] = {0, }; |
| int ret = 0, retry = 2; |
| |
| pn547_dev = filp->private_data; |
| |
| #if NFC_DEBUG |
| pr_info("pn547 : + w\n"); |
| #endif |
| |
| if (count > MAX_BUFFER_SIZE) |
| count = MAX_BUFFER_SIZE; |
| |
| if (copy_from_user(tmp, buf, count)) { |
| pr_err("%s : failed to copy from user space\n", __func__); |
| return -EFAULT; |
| } |
| |
| pr_debug("%s : writing %zu bytes.\n", __func__, count); |
| /* Write data */ |
| do { |
| retry--; |
| ret = i2c_master_send(pn547_dev->client, tmp, count); |
| if (ret == count) |
| break; |
| usleep_range(6000, 10000); /* Retry, chip was in standby */ |
| #if NFC_DEBUG |
| pr_debug("%s : retry = %d\n", __func__, retry); |
| #endif |
| } while (retry); |
| |
| #if NFC_DEBUG |
| pr_info("pn547 : - w\n"); |
| #endif |
| |
| if (ret != count) { |
| pr_err("%s : i2c_master_send returned %d\n", __func__, ret); |
| ret = -EIO; |
| } |
| |
| return ret; |
| } |
| |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| static void p61_update_access_state(struct pn547_dev *pn547_dev, p61_access_state_t current_state, bool set) |
| { |
| pr_info("%s: Enter current_state = 0x%x\n", __func__, pn547_dev->p61_current_state); |
| if (current_state) |
| { |
| if(set){ |
| if(pn547_dev->p61_current_state == P61_STATE_IDLE) |
| pn547_dev->p61_current_state = P61_STATE_INVALID; |
| pn547_dev->p61_current_state |= current_state; |
| } |
| else{ |
| pn547_dev->p61_current_state ^= current_state; |
| if(!pn547_dev->p61_current_state) |
| pn547_dev->p61_current_state = P61_STATE_IDLE; |
| } |
| } |
| pr_info("%s: Exit current_state = 0x%x\n", __func__, pn547_dev->p61_current_state); |
| } |
| |
| static void p61_get_access_state(struct pn547_dev *pn547_dev, p61_access_state_t *current_state) |
| { |
| |
| if (current_state == NULL) { |
| pr_err("%s : invalid state of p61_access_state_t current state \n", __func__); |
| } else { |
| *current_state = pn547_dev->p61_current_state; |
| } |
| } |
| static void p61_access_lock(struct pn547_dev *pn547_dev) |
| { |
| pr_info("%s: Enter\n", __func__); |
| mutex_lock(&pn547_dev->p61_state_mutex); |
| pr_info("%s: Exit\n", __func__); |
| } |
| static void p61_access_unlock(struct pn547_dev *pn547_dev) |
| { |
| pr_info("%s: Enter\n", __func__); |
| mutex_unlock(&pn547_dev->p61_state_mutex); |
| pr_info("%s: Exit\n", __func__); |
| } |
| |
| static int signal_handler(p61_access_state_t state, long nfc_pid) |
| { |
| struct siginfo sinfo; |
| pid_t pid; |
| struct task_struct *task; |
| int sigret = 0; |
| int ret = 0; |
| |
| pr_info("%s: Enter\n", __func__); |
| |
| memset(&sinfo, 0, sizeof(struct siginfo)); |
| sinfo.si_signo = SIG_NFC; |
| sinfo.si_code = SI_QUEUE; |
| sinfo.si_int = state; |
| pid = nfc_pid; |
| |
| task = pid_task(find_vpid(pid), PIDTYPE_PID); |
| if(task) |
| { |
| pr_info("%s.\n", task->comm); |
| sigret = send_sig_info(SIG_NFC, &sinfo, task); |
| if(sigret < 0){ |
| pr_info("send_sig_info failed..... sigret %d.\n", sigret); |
| return -1; |
| } |
| } |
| else{ |
| pr_info("finding task from PID failed\r\n"); |
| return -1; |
| } |
| pr_info("%s: Exit\n", __func__); |
| |
| return ret; |
| } |
| |
| static void svdd_sync_onoff(long nfc_service_pid, p61_access_state_t origin) |
| { |
| int timeout = 100; //100 ms timeout |
| unsigned long tempJ = msecs_to_jiffies(timeout); |
| pr_info("%s: Enter nfc_service_pid: %ld\n", __func__, nfc_service_pid); |
| if(nfc_service_pid) |
| { |
| if (0 == signal_handler(origin, nfc_service_pid)) |
| { |
| sema_init(&svdd_sync_onoff_sema, 0); |
| svdd_sync_wait = 1; |
| pr_info("Waiting for svdd protection response"); |
| if(down_timeout(&svdd_sync_onoff_sema, tempJ) != 0) |
| { |
| pr_info("svdd wait protection: Timeout"); |
| } |
| pr_info("svdd wait protection : released"); |
| svdd_sync_wait = 0; |
| } |
| } |
| pr_info("%s: Exit\n", __func__); |
| } |
| |
| static int release_svdd_wait(void) |
| { |
| unsigned char i = 0; |
| pr_info("%s: Enter \n", __func__); |
| |
| for(i=0;i<9;i++) |
| { |
| if(svdd_sync_wait) |
| { |
| up(&svdd_sync_onoff_sema); |
| svdd_sync_wait = 0; |
| break; |
| } else msleep(10); |
| } |
| pr_info("%s: Exit\n", __func__); |
| return 0; |
| } |
| |
| #endif |
| |
| static int pn547_dev_open(struct inode *inode, struct file *filp) |
| { |
| struct pn547_dev *pn547_dev = container_of(filp->private_data, |
| struct pn547_dev, |
| pn547_device); |
| if (!atomic_dec_and_test(&s_Device_opened)) { |
| atomic_inc(&s_Device_opened); |
| pr_err("%s: already opened!\n", __func__); |
| return -EBUSY; |
| } |
| filp->private_data = pn547_dev; |
| |
| pr_debug("%s : %d,%d\n", __func__, imajor(inode), iminor(inode)); |
| |
| return 0; |
| } |
| |
| static int pn547_dev_release(struct inode *inode, struct file *filp) |
| { |
| atomic_inc(&s_Device_opened); |
| return 0; |
| } |
| |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| static unsigned char p61_trans_acc_on = 0; |
| #endif |
| long pn547_dev_ioctl(struct file *filp, |
| unsigned int cmd, unsigned long arg) |
| { |
| /*struct pn547_dev *pn547_dev = filp->private_data;*/ |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| if (cmd == P547_GET_ESE_ACCESS) |
| { |
| return get_ese_lock(P61_STATE_WIRED, arg); |
| } |
| else if(cmd == P547_REL_SVDD_WAIT) |
| { |
| return release_svdd_wait(); |
| } |
| |
| p61_access_lock(pn547_dev); |
| switch (cmd) { |
| case PN547_SET_PWR: |
| { |
| p61_access_state_t current_state = P61_STATE_INVALID; |
| p61_get_access_state(pn547_dev, ¤t_state); |
| |
| if (arg == 2) { |
| if (current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) |
| { |
| /* NFCC fw/download should not be allowed if p61 is used |
| * by SPI |
| */ |
| pr_info("%s NFCC should not be allowed to reset/FW download \n", __func__); |
| p61_access_unlock(pn547_dev); |
| return -EBUSY; /* Device or resource busy */ |
| } |
| pn547_dev->nfc_ven_enabled = true; |
| if (pn547_dev->spi_ven_enabled == false) |
| { |
| /* power on with firmware download (requires hw reset) |
| */ |
| p61_update_access_state(pn547_dev, P61_STATE_DWNLD, true); |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| gpio_set_value(pn547_dev->firm_gpio, 1); |
| usleep_range(4900, 5000); |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| usleep_range(4900, 5000); |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| usleep_range(4900, 5000); |
| if (atomic_read(&pn547_dev->irq_enabled) == 0) { |
| atomic_set(&pn547_dev->irq_enabled, 1); |
| enable_irq(pn547_dev->client->irq); |
| enable_irq_wake(pn547_dev->client->irq); |
| } |
| pr_info("%s power on with firmware, irq=%d\n", __func__, |
| atomic_read(&pn547_dev->irq_enabled)); |
| |
| if (pn547_dev->ven_gpio > 0) |
| pr_info("VEN=%d FIRM=%d\n", gpio_get_value(pn547_dev->ven_gpio), |
| gpio_get_value(pn547_dev->firm_gpio)); |
| } |
| } else if (arg == 1) { |
| /* power on */ |
| if (pn547_dev->conf_gpio) |
| pn547_dev->conf_gpio(); |
| if ((current_state & (P61_STATE_WIRED|P61_STATE_SPI|P61_STATE_SPI_PRIO))== 0){ |
| p61_update_access_state(pn547_dev, P61_STATE_IDLE, true); |
| } |
| gpio_set_value(pn547_dev->firm_gpio, 0); |
| |
| pn547_dev->nfc_ven_enabled = true; |
| if (pn547_dev->spi_ven_enabled == false) { |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| } |
| usleep_range(4900, 5000); |
| if (atomic_read(&pn547_dev->irq_enabled) == 0) { |
| atomic_set(&pn547_dev->irq_enabled, 1); |
| enable_irq(pn547_dev->client->irq); |
| enable_irq_wake(pn547_dev->client->irq); |
| } |
| |
| svdd_sync_wait = 0; |
| |
| pr_info("%s power on, irq=%d\n", __func__, |
| atomic_read(&pn547_dev->irq_enabled)); |
| } else if (arg == 0) { |
| /* power off */ |
| if (atomic_read(&pn547_dev->irq_enabled) == 1) { |
| atomic_set(&pn547_dev->irq_enabled, 0); |
| disable_irq_wake(pn547_dev->client->irq); |
| disable_irq_nosync(pn547_dev->client->irq); |
| } |
| if(current_state & P61_STATE_DWNLD) |
| p61_update_access_state(pn547_dev, P61_STATE_DWNLD, false); |
| |
| if ((current_state & (P61_STATE_WIRED|P61_STATE_SPI|P61_STATE_SPI_PRIO))== 0){ |
| p61_update_access_state(pn547_dev, P61_STATE_IDLE, true); |
| } |
| pr_info("%s power off, irq=%d\n", __func__, |
| atomic_read(&pn547_dev->irq_enabled)); |
| gpio_set_value(pn547_dev->firm_gpio, 0); |
| |
| pn547_dev->nfc_ven_enabled = false; |
| /* Don't change Ven state if spi made it high */ |
| if (pn547_dev->spi_ven_enabled == false) { |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| } |
| usleep_range(4900, 5000); |
| } else if (arg == 3) { |
| pr_info("%s Read Cancel\n", __func__); |
| pn547_dev->cancel_read = true; |
| atomic_set(&pn547_dev->read_flag, 1); |
| wake_up(&pn547_dev->read_wq); |
| } else { |
| pr_err("%s bad arg %lu\n", __func__, arg); |
| /* changed the p61 state to idle*/ |
| p61_access_unlock(pn547_dev); |
| return -EINVAL; |
| } |
| } |
| break; |
| |
| case P61_SET_SPI_PWR: |
| { |
| p61_access_state_t current_state = P61_STATE_INVALID; |
| p61_get_access_state(pn547_dev, ¤t_state); |
| if (arg == 1) { |
| pr_info("%s : PN61_SET_SPI_PWR - power on ese, cur=0x%x\n", __func__, current_state); |
| |
| if ((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO|P61_STATE_DWNLD)) == 0) |
| { |
| p61_update_access_state(pn547_dev, P61_STATE_SPI, true); |
| /*To handle triple mode protection signal |
| NFC service when SPI session started*/ |
| if (current_state & P61_STATE_WIRED){ |
| if(pn547_dev->nfc_service_pid){ |
| pr_info("nfc service pid %s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| signal_handler(P61_STATE_SPI, pn547_dev->nfc_service_pid); |
| } |
| else{ |
| pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| } |
| } |
| pn547_dev->spi_ven_enabled = true; |
| if (pn547_dev->nfc_ven_enabled == false) { |
| /* provide power to NFCC if, NFC service not provided */ |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| msleep(10); |
| } |
| /* pull the gpio to high once NFCC is power on*/ |
| gpio_set_value(pn547_dev->ese_pwr_req, 1); |
| msleep(10); |
| } else { |
| pr_info("%s : PN61_SET_SPI_PWR - power on ese failed \n", __func__); |
| p61_access_unlock(pn547_dev); |
| return -EBUSY; /* Device or resource busy */ |
| } |
| } else if (arg == 0) { |
| pr_info("%s : PN61_SET_SPI_PWR - power off ese\n", __func__); |
| if(current_state & P61_STATE_SPI_PRIO){ |
| p61_update_access_state(pn547_dev, P61_STATE_SPI_PRIO, false); |
| if (current_state & P61_STATE_WIRED) |
| { |
| if(pn547_dev->nfc_service_pid){ |
| pr_info("nfc service pid %s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| signal_handler(P61_STATE_SPI_PRIO_END, pn547_dev->nfc_service_pid); |
| }else{ |
| pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| } |
| } |
| if (!(current_state & P61_STATE_WIRED)) { |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); |
| gpio_set_value(pn547_dev->ese_pwr_req, 0); |
| msleep(60); |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); |
| } |
| |
| pn547_dev->spi_ven_enabled = false; |
| |
| if (pn547_dev->nfc_ven_enabled == false) { |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| msleep(10); |
| } |
| } |
| |
| else if(current_state & P61_STATE_SPI){ |
| p61_update_access_state(pn547_dev, P61_STATE_SPI, false); |
| if (!(current_state & P61_STATE_WIRED)) |
| { |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); |
| gpio_set_value(pn547_dev->ese_pwr_req, 0); |
| msleep(60); |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); |
| } |
| /*If JCOP3.2 or 3.3 for handling triple mode protection signal NFC service */ |
| else |
| { |
| if (current_state & P61_STATE_WIRED) |
| { |
| if(pn547_dev->nfc_service_pid){ |
| pr_info("nfc service pid %s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| signal_handler(P61_STATE_SPI_END, pn547_dev->nfc_service_pid); |
| } |
| else{ |
| pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| } |
| } |
| } |
| pn547_dev->spi_ven_enabled = false; |
| if (pn547_dev->nfc_ven_enabled == false) { |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| msleep(10); |
| } |
| } else { |
| pr_err("%s : PN61_SET_SPI_PWR - failed, current_state = 0x%x \n", |
| __func__, pn547_dev->p61_current_state); |
| p61_access_unlock(pn547_dev); |
| return -EPERM; /* Operation not permitted */ |
| } |
| }else if (arg == 2) { |
| pr_info("%s : PN61_SET_SPI_PWR - reset\n", __func__); |
| if (current_state & (P61_STATE_IDLE|P61_STATE_SPI|P61_STATE_SPI_PRIO)) { |
| if (pn547_dev->spi_ven_enabled == false) |
| { |
| pn547_dev->spi_ven_enabled = true; |
| if (pn547_dev->nfc_ven_enabled == false) { |
| /* provide power to NFCC if, NFC service not provided */ |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| msleep(10); |
| } |
| } |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_START); |
| gpio_set_value(pn547_dev->ese_pwr_req, 0); |
| msleep(60); |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_SPI_SVDD_SYNC_END); |
| gpio_set_value(pn547_dev->ese_pwr_req, 1); |
| msleep(10); |
| } else { |
| pr_info("%s : PN61_SET_SPI_PWR - reset failed \n", __func__); |
| p61_access_unlock(pn547_dev); |
| return -EBUSY; /* Device or resource busy */ |
| } |
| } |
| |
| else if (arg == 3) { |
| pr_info("%s : PN61_SET_SPI_PWR - Prio Session Start power on ese\n", __func__); |
| if ((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO|P61_STATE_DWNLD)) == 0) { |
| p61_update_access_state(pn547_dev, P61_STATE_SPI_PRIO, true); |
| if (current_state & P61_STATE_WIRED){ |
| if(pn547_dev->nfc_service_pid){ |
| pr_info("nfc service pid %s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| signal_handler(P61_STATE_SPI_PRIO, pn547_dev->nfc_service_pid); |
| } |
| else{ |
| pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| } |
| } |
| pn547_dev->spi_ven_enabled = true; |
| if (pn547_dev->nfc_ven_enabled == false) { |
| /* provide power to NFCC if, NFC service not provided */ |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| msleep(10); |
| } |
| /* pull the gpio to high once NFCC is power on*/ |
| gpio_set_value(pn547_dev->ese_pwr_req, 1); |
| msleep(10); |
| }else { |
| pr_info("%s : Prio Session Start power on ese failed 0x%x\n", __func__, current_state); |
| p61_access_unlock(pn547_dev); |
| return -EBUSY; /* Device or resource busy */ |
| } |
| }else if (arg == 4) { |
| if (current_state & P61_STATE_SPI_PRIO) |
| { |
| pr_info("%s : PN61_SET_SPI_PWR - Prio Session Ending...\n", __func__); |
| p61_update_access_state(pn547_dev, P61_STATE_SPI_PRIO, false); |
| /*after SPI prio timeout, the state is changing from SPI prio to SPI */ |
| p61_update_access_state(pn547_dev, P61_STATE_SPI, true); |
| if (current_state & P61_STATE_WIRED) |
| { |
| if(pn547_dev->nfc_service_pid){ |
| pr_info("nfc service pid %s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| signal_handler(P61_STATE_SPI_PRIO_END, pn547_dev->nfc_service_pid); |
| } |
| else{ |
| pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| } |
| } |
| } |
| else |
| { |
| pr_info("%s : PN61_SET_SPI_PWR - Prio Session End failed 0x%x\n", __func__, current_state); |
| p61_access_unlock(pn547_dev); |
| return -EBADRQC; /* Device or resource busy */ |
| } |
| } else if(arg == 5){ |
| release_ese_lock(P61_STATE_SPI); |
| } |
| else { |
| pr_info("%s bad ese pwr arg %lu\n", __func__, arg); |
| p61_access_unlock(pn547_dev); |
| return -EBADRQC; /* Invalid request code */ |
| } |
| } |
| break; |
| |
| case P61_GET_PWR_STATUS: |
| { |
| p61_access_state_t current_state = P61_STATE_INVALID; |
| p61_get_access_state(pn547_dev, ¤t_state); |
| pr_info("%s: P61_GET_PWR_STATUS = %x",__func__, current_state); |
| put_user(current_state, (int __user *)arg); |
| } |
| break; |
| |
| case P61_SET_WIRED_ACCESS: |
| { |
| p61_access_state_t current_state = P61_STATE_INVALID; |
| p61_get_access_state(pn547_dev, ¤t_state); |
| if (arg == 1) { |
| if (current_state) |
| { |
| pr_info("%s : P61_SET_WIRED_ACCESS - enabling\n", __func__); |
| p61_update_access_state(pn547_dev, P61_STATE_WIRED, true); |
| if (current_state & P61_STATE_SPI_PRIO) |
| { |
| if(pn547_dev->nfc_service_pid){ |
| pr_info("nfc service pid %s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| signal_handler(P61_STATE_SPI_PRIO, pn547_dev->nfc_service_pid); |
| } |
| else{ |
| pr_info(" invalid nfc service pid....signalling failed%s ---- %ld", __func__, pn547_dev->nfc_service_pid); |
| } |
| } |
| if((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0) { |
| gpio_set_value(pn547_dev->ese_pwr_req, 1); |
| msleep(10); |
| } |
| |
| } else { |
| pr_info("%s : P61_SET_WIRED_ACCESS - enabling failed \n", __func__); |
| p61_access_unlock(pn547_dev); |
| return -EBUSY; /* Device or resource busy */ |
| } |
| } else if (arg == 0) { |
| pr_info("%s : P61_SET_WIRED_ACCESS - disabling \n", __func__); |
| if (current_state & P61_STATE_WIRED){ |
| p61_update_access_state(pn547_dev, P61_STATE_WIRED, false); |
| if((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0) { |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_DWP_SVDD_SYNC_START); |
| gpio_set_value(pn547_dev->ese_pwr_req, 0); |
| msleep(60); |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_DWP_SVDD_SYNC_END); |
| } |
| } else { |
| pr_err("%s : P61_SET_WIRED_ACCESS - failed, current_state = %x \n", |
| __func__, pn547_dev->p61_current_state); |
| p61_access_unlock(pn547_dev); |
| return -EPERM; /* Operation not permitted */ |
| } |
| } |
| else if(arg == 2) |
| { |
| pr_info("%s : P61 ESE POWER REQ LOW \n", __func__); |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_DWP_SVDD_SYNC_START); |
| gpio_set_value(pn547_dev->ese_pwr_req, 0); |
| msleep(60); |
| svdd_sync_onoff(pn547_dev->nfc_service_pid, P61_STATE_DWP_SVDD_SYNC_END); |
| } |
| else if(arg == 3) |
| { |
| pr_info("%s : P61 ESE POWER REQ HIGH \n", __func__); |
| gpio_set_value(pn547_dev->ese_pwr_req, 1); |
| msleep(10); |
| } |
| else if(arg == 4) |
| { |
| release_ese_lock(P61_STATE_WIRED); |
| } |
| else { |
| pr_info("%s P61_SET_WIRED_ACCESS - bad arg %lu\n", __func__, arg); |
| p61_access_unlock(pn547_dev); |
| return -EBADRQC; /* Invalid request code */ |
| } |
| } |
| break; |
| |
| case P547_SET_NFC_SERVICE_PID: |
| { |
| p61_access_state_t current_state = P61_STATE_INVALID; |
| p61_get_access_state(pn547_dev, ¤t_state); |
| if((p61_trans_acc_on == 1) && ((current_state & (P61_STATE_SPI|P61_STATE_SPI_PRIO)) == 0)) |
| release_ese_lock(P61_STATE_WIRED); |
| |
| pr_info("%s : The NFC Service PID is %ld\n", __func__, arg); |
| pn547_dev->nfc_service_pid = arg; |
| } |
| break; |
| |
| default: |
| pr_err("%s bad ioctl %u\n", __func__, cmd); |
| p61_access_unlock(pn547_dev); |
| pr_info("%s :exit cmd = %u, arg = %ld\n", __func__, cmd, arg); |
| return -EINVAL; |
| } |
| p61_access_unlock(pn547_dev); |
| #else |
| switch (cmd) { |
| case PN547_SET_PWR: |
| if (arg == 2) { |
| /* power on with firmware download (requires hw reset) |
| */ |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| gpio_set_value(pn547_dev->firm_gpio, 1); |
| usleep_range(4900, 5000); |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| usleep_range(4900, 5000); |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| usleep_range(4900, 5000); |
| if (atomic_read(&pn547_dev->irq_enabled) == 0) { |
| atomic_set(&pn547_dev->irq_enabled, 1); |
| enable_irq(pn547_dev->client->irq); |
| enable_irq_wake(pn547_dev->client->irq); |
| } |
| pr_info("%s power on with firmware, irq=%d\n", __func__, |
| atomic_read(&pn547_dev->irq_enabled)); |
| } else if (arg == 1) { |
| /* power on */ |
| if (pn547_dev->conf_gpio) |
| pn547_dev->conf_gpio(); |
| gpio_set_value(pn547_dev->firm_gpio, 0); |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| usleep_range(4900, 5000); |
| if (atomic_read(&pn547_dev->irq_enabled) == 0) { |
| atomic_set(&pn547_dev->irq_enabled, 1); |
| enable_irq(pn547_dev->client->irq); |
| enable_irq_wake(pn547_dev->client->irq); |
| } |
| pr_info("%s power on, irq=%d\n", __func__, |
| atomic_read(&pn547_dev->irq_enabled)); |
| } else if (arg == 0) { |
| /* power off */ |
| if (atomic_read(&pn547_dev->irq_enabled) == 1) { |
| atomic_set(&pn547_dev->irq_enabled, 0); |
| disable_irq_wake(pn547_dev->client->irq); |
| disable_irq_nosync(pn547_dev->client->irq); |
| } |
| pr_info("%s power off, irq=%d\n", __func__, |
| atomic_read(&pn547_dev->irq_enabled)); |
| gpio_set_value(pn547_dev->firm_gpio, 0); |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| usleep_range(4900, 5000); |
| } else if (arg == 3) { |
| pr_info("%s Read Cancel\n", __func__); |
| pn547_dev->cancel_read = true; |
| atomic_set(&pn547_dev->read_flag, 1); |
| wake_up(&pn547_dev->read_wq); |
| } else { |
| pr_err("%s bad arg %lu\n", __func__, arg); |
| return -EINVAL; |
| } |
| break; |
| default: |
| pr_err("%s bad ioctl %u\n", __func__, cmd); |
| return -EINVAL; |
| } |
| #endif |
| return 0; |
| } |
| |
| EXPORT_SYMBOL(pn547_dev_ioctl); |
| |
| #ifdef CONFIG_COMPAT |
| long pn547_compat_ioctl(struct file *filp, |
| unsigned int cmd, unsigned long arg) |
| |
| { |
| pr_info("%s : cmd = %u, arg = %ld\n", __func__, cmd, arg); |
| arg = (unsigned long) compat_ptr(arg); |
| |
| switch (cmd) { |
| case PN547_SET_PWR: |
| case P61_SET_SPI_PWR: |
| case P61_GET_PWR_STATUS: |
| case P61_SET_WIRED_ACCESS: |
| case P547_SET_NFC_SERVICE_PID: |
| return pn547_dev_ioctl(filp, cmd, arg); |
| } |
| return -ENOIOCTLCMD; |
| } |
| #endif |
| |
| |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| int get_ese_lock(p61_access_state_t p61_current_state, int timeout) |
| { |
| unsigned long tempJ = msecs_to_jiffies(timeout); |
| pr_info("get_ese_lock: enter p61_current_state =(0x%x), timeout = %d, jiffies = %lu\n" |
| , p61_current_state, timeout, tempJ); |
| if(down_timeout(&ese_access_sema, tempJ) != 0) |
| { |
| pr_err("get_ese_lock: timeout p61_current_state = %d\n", p61_current_state); |
| return -EBUSY; |
| } |
| p61_trans_acc_on = 1; |
| pr_info("get_ese_lock: exit p61_trans_acc_on =%d, timeout = %d\n" |
| , p61_trans_acc_on, timeout); |
| return 0; |
| } |
| EXPORT_SYMBOL(get_ese_lock); |
| |
| static void release_ese_lock(p61_access_state_t p61_current_state) |
| { |
| pr_info("%s: enter p61_current_state = (0x%x)\n", __func__, p61_current_state); |
| up(&ese_access_sema); |
| p61_trans_acc_on = 0; |
| pr_info("%s: p61_trans_acc_on =%d exit\n", __func__, p61_trans_acc_on); |
| } |
| #endif |
| |
| |
| static const struct file_operations pn547_dev_fops = { |
| .owner = THIS_MODULE, |
| .llseek = no_llseek, |
| .read = pn547_dev_read, |
| .write = pn547_dev_write, |
| .open = pn547_dev_open, |
| .release = pn547_dev_release, |
| .unlocked_ioctl = pn547_dev_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = pn547_compat_ioctl, |
| #endif |
| |
| }; |
| |
| static int pn547_parse_dt(struct device *dev, |
| struct pn547_i2c_platform_data *pdata) |
| { |
| struct device_node *np = dev->of_node; |
| int ret; |
| |
| pdata->irq_gpio = of_get_named_gpio_flags(np, "pn547,irq-gpio", |
| 0, &pdata->irq_gpio_flags); |
| |
| pdata->ven_gpio = of_get_named_gpio_flags(np, "pn547,ven-gpio", |
| 0, &pdata->ven_gpio_flags); |
| if (pdata->ven_gpio < 0) |
| pdata->ven_gpio = of_get_named_gpio_flags(np, "pn547,ven-expander-gpio", |
| 0, &pdata->ven_gpio_flags); |
| if(pdata->ven_gpio < 0) { |
| if (of_property_read_u32(np, "clkctrl-reg", (u32 *)&pdata->clkctrl_addr)) { |
| pr_err("%s: no ven_gpio at dt file\n", __func__); |
| return -ENOENT; |
| } |
| } |
| pdata->firm_gpio = of_get_named_gpio_flags(np, "pn547,firm-gpio", |
| 0, &pdata->firm_gpio_flags); |
| if (pdata->firm_gpio < 0) |
| of_property_read_u32(np, "pn547,firm-expander-gpio", |
| &pdata->firm_gpio); |
| |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| pdata->ese_pwr_req = of_get_named_gpio_flags(np, "pn547,pwr_req", |
| 0, &pdata->ese_pwr_req_flags); |
| #endif |
| |
| if (of_get_property(dev->of_node, "pn547,nfc_pm_clk", NULL)) { |
| pdata->clk = clk_get(dev, "rf_clk"); |
| if (IS_ERR(pdata->clk)) { |
| pr_err("%s: Couldn't get rf_clk\n", __func__); |
| } else { |
| pr_info("%s : enable rf_clk\n", __func__); |
| clk_prepare_enable(pdata->clk); |
| } |
| ret = of_get_named_gpio(np, "pn547,clk_req-gpio", 0); |
| if (ret < 0) { |
| pr_info("%s : clk_req-gpio is not set", __func__); |
| } else |
| pdata->clk_req_gpio = ret; |
| } |
| |
| if (of_find_property(np, "pn547,nfc_clkreq_int", NULL)) |
| pdata->clk_req_gpio = of_get_named_gpio(np, "pn547,nfc_clkreq_int", 0); |
| |
| pr_info("%s: irq : %d, ven : %d, firm : %d\n", |
| __func__, pdata->irq_gpio, pdata->ven_gpio, |
| pdata->firm_gpio); |
| |
| return 0; |
| } |
| |
| static int pn547_regulator_onoff(struct i2c_client *client, int onoff) |
| { |
| int rc = 0; |
| struct regulator *regulator_i2c_1p8; |
| |
| pr_err("%s - onoff = %d\n", __func__, onoff); |
| regulator_i2c_1p8 = regulator_get(&client->dev, "pn547,i2c_1p8"); |
| if (IS_ERR(regulator_i2c_1p8) || regulator_i2c_1p8 == NULL) { |
| pr_err("%s - i2c_1p8 regulator_get fail\n", __func__); |
| return -ENODEV; |
| } |
| |
| pr_info("%s - onoff = %d\n", __func__, onoff); |
| |
| if (onoff == NFC_I2C_LDO_ON) { |
| rc = regulator_enable(regulator_i2c_1p8); |
| if (rc) { |
| pr_err("%s - enable i2c_1p8 failed, rc=%d\n", |
| __func__, rc); |
| goto done; |
| } |
| } else { |
| rc = regulator_disable(regulator_i2c_1p8); |
| if (rc) { |
| pr_err("%s - disable i2c_1p8 failed, rc=%d\n", |
| __func__, rc); |
| goto done; |
| } |
| } |
| |
| done: |
| regulator_put(regulator_i2c_1p8); |
| |
| return rc; |
| } |
| |
| #ifdef FEATURE_SEC_NFC_TEST |
| static ssize_t pn547_class_show(struct class * class, struct class_attribute *attr, char *buf) { |
| |
| int size; |
| |
| int ret = 0; |
| int count = 4; |
| char tmp[128] = {0x20, 0x00, 0x01, 0x00, }; |
| int retry; |
| bool old_ven, old_irq; |
| int old_read_value; |
| |
| pr_err("%s : start\n", __func__); |
| |
| /*TODO : nfc_ven_enabled should be capsulated with ESE_SUPPORT */ |
| old_ven = pn547_dev->nfc_ven_enabled; |
| pr_info("%s : old_ven is %d\n", __func__, old_ven); |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| p61_access_lock(pn547_dev); |
| #endif |
| retry = 20; |
| if(!old_ven) { /* if nfc status is off */ |
| pn547_dev->nfc_ven_enabled = true; |
| |
| if (pn547_dev->spi_ven_enabled == false) { |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| } |
| usleep_range(4900, 5000); |
| } |
| else { /* if nfc status is on */ |
| //wake up device |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| usleep_range(4900, 5000); |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| usleep_range(4900, 5000); |
| //intercept i2c_master_recv |
| pn547_dev->cancel_read = 1; |
| atomic_set(&pn547_dev->read_flag, 1); |
| } |
| wake_up_all(&pn547_dev->read_wq); |
| while(!mutex_trylock(&pn547_dev->read_mutex) && --retry) { |
| usleep_range(15, 20); |
| } |
| if(!retry) { |
| pr_err("%s : mutex_trylock failed. check pn547_dev_read()\n", __func__); |
| ret = sprintf(buf, "test failed : device in use\n"); |
| goto fail_lock; |
| } |
| |
| pr_info("%s : read_mutex locked. retry : %d\n", __func__, retry); |
| |
| atomic_set(&pn547_dev->read_flag, 0); |
| pn547_dev->cancel_read = 0; |
| old_irq = atomic_read(&pn547_dev->irq_enabled); |
| pr_info("%s : old_irq is %d\n", __func__, old_irq); |
| if(!old_irq) { |
| atomic_set(&pn547_dev->irq_enabled, 1); |
| enable_irq(pn547_dev->client->irq); |
| enable_irq_wake(pn547_dev->client->irq); |
| pr_info("%s power on, irq=%d\n", __func__, atomic_read(&pn547_dev->irq_enabled)); |
| } |
| retry = 2; |
| do { |
| ret = i2c_master_send(pn547_dev->client, tmp, count); |
| if(count == ret) { |
| break; |
| } |
| pr_info("%s : i2c_master_send error. ret: %d, retry: %d\n", __func__, ret, retry); |
| usleep_range(6000, 10000); /* Retry, chip was in standby */ |
| } while(retry--); |
| |
| if(ret != count) { |
| pr_err("%s : failed. count error. send=%d, recv=%d\n", __func__, count, ret); |
| ret = 0; |
| goto fail; |
| } |
| |
| pr_err("%s : send success. returned: %d\n", __func__, ret); |
| |
| //wait for reply |
| |
| |
| #if NFC_DEBUG |
| pr_info("pn547: wait_event_interruptible : in\n"); |
| #endif |
| ret = 0; |
| old_read_value = atomic_read(&pn547_dev->read_flag); |
| pr_info("%s: read_flag %d, cancel_read %d", __func__, old_read_value, pn547_dev->cancel_read); |
| if(!old_read_value) { |
| ret = wait_event_interruptible(pn547_dev->read_wq, |
| atomic_read(&pn547_dev->read_flag)); |
| } |
| |
| #if NFC_DEBUG |
| pr_info("pn547 : h\n"); |
| #endif |
| |
| if (pn547_dev->cancel_read) { |
| pn547_dev->cancel_read = false; |
| ret = 0; |
| //todo : old_ven and old_irq rollback needed |
| goto fail; |
| } |
| |
| if (ret) { |
| ret = 0; |
| goto fail; |
| } |
| |
| /* Read data */ |
| count = 6; |
| ret = i2c_master_recv(pn547_dev->client, tmp, count); |
| |
| #if NFC_DEBUG |
| pr_info("pn547: i2c_master_recv\n"); |
| #endif |
| mutex_unlock(&pn547_dev->read_mutex); |
| |
| if(!old_ven) { /* if nfc status is off */ |
| if (pn547_dev->spi_ven_enabled == false) { |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| } |
| usleep_range(4900, 5000); |
| pn547_dev->nfc_ven_enabled = false; |
| } |
| if(!old_irq) { |
| atomic_set(&pn547_dev->irq_enabled, 0); |
| disable_irq_wake(pn547_dev->client->irq); |
| disable_irq_nosync(pn547_dev->client->irq); |
| } |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| p61_access_unlock(pn547_dev); |
| #endif |
| if (ret < 0 || ret > count) { |
| pr_err("%s: i2c_master_recv returned %d. count : %d\n", __func__, ret, count); |
| return 0; |
| } |
| size = sprintf(buf, "test completed!! size: %d, data: %X %X %X %X %X %X\n", ret, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]); |
| pr_err("%s : recv success.\n", __func__); |
| |
| msleep(10); |
| |
| return size; |
| |
| fail: |
| mutex_unlock(&pn547_dev->read_mutex); |
| fail_lock: |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| p61_access_unlock(pn547_dev); |
| #endif |
| return ret; |
| } |
| static ssize_t pn547_class_store(struct class * class, struct class_attribute *attr, const char *buf, size_t size) { |
| return size; |
| } |
| static CLASS_ATTR(test, 0664, pn547_class_show, pn547_class_store); |
| |
| #endif //FEATURE_SEC_NFC_TEST |
| |
| static ssize_t pn547_support_show(struct class * class, struct class_attribute *attr, char *buf) |
| { |
| pr_info("\n"); |
| return 0; |
| } |
| static CLASS_ATTR(nfc_support, 0444, pn547_support_show, NULL); |
| |
| static int pn547_probe(struct i2c_client *client, |
| const struct i2c_device_id *id) |
| { |
| int ret; |
| int err; |
| int addr; |
| char tmp[4] = {0x20, 0x00, 0x01, 0x01}; |
| int addrcnt; |
| struct device_node *np = client->dev.of_node; |
| struct pn547_i2c_platform_data *platform_data; |
| int nfc_support = 0; |
| struct property *prop; |
| #ifdef FEATURE_SEC_NFC_TEST |
| struct class *nfc_class; |
| #endif |
| /*separate NFC / non NFC using GPIO*/ |
| prop = of_find_property(np, "pn547,check_nfc", NULL); |
| if (prop) { |
| nfc_support = gpio_get_value(of_get_named_gpio(np, "pn547,check_nfc", 0)); |
| if (nfc_support > 0) { |
| pr_info("%s : nfc support model : %d\n", __func__, nfc_support); |
| }else{ |
| pr_info("%s : nfc not support model : %d\n", __func__, nfc_support); |
| return -ENXIO; |
| } |
| } |
| /*struct pn547_dev *pn547_dev;*/ |
| #ifdef FEATURE_SEC_NFC_TEST |
| nfc_class = class_create(THIS_MODULE, "nfc_test"); |
| if (IS_ERR(&nfc_class)) |
| { |
| |
| pr_err("NFC: failed to create nfc_test class\n"); |
| } |
| else |
| { |
| ret = class_create_file(nfc_class, &class_attr_test); |
| if (ret) |
| pr_err("NFC: failed to create attr_test\n"); |
| } |
| #endif //FEATURE_SEC_NFC_TEST |
| nfc_class = class_create(THIS_MODULE, "nfc"); |
| if (IS_ERR(&nfc_class)) |
| { |
| |
| pr_err("NFC: failed to create nfc class\n"); |
| } |
| else |
| { |
| ret = class_create_file(nfc_class, &class_attr_nfc_support); |
| if (ret) |
| pr_err("NFC: failed to create attr_nfc_support\n"); |
| } |
| |
| pr_info("%s entered\n", __func__); |
| |
| if (client->dev.of_node) { |
| platform_data = devm_kzalloc(&client->dev, |
| sizeof(struct pn547_i2c_platform_data), GFP_KERNEL); |
| if (!platform_data) { |
| dev_err(&client->dev, "Failed to allocate memory\n"); |
| return -ENOMEM; |
| } |
| err = pn547_parse_dt(&client->dev, platform_data); |
| if (err) |
| return err; |
| } else { |
| platform_data = client->dev.platform_data; |
| } |
| |
| if (platform_data == NULL) { |
| pr_err("%s : nfc probe fail\n", __func__); |
| return -ENODEV; |
| } |
| |
| if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { |
| pr_err("%s : need I2C_FUNC_I2C\n", __func__); |
| return -ENODEV; |
| } |
| |
| ret = gpio_request(platform_data->irq_gpio, "nfc_int"); |
| if (ret) |
| return -ENODEV; |
| if (platform_data->ven_gpio > 0) { |
| ret = gpio_request(platform_data->ven_gpio, "nfc_ven"); |
| if (ret) |
| goto err_ven; |
| } |
| ret = gpio_request(platform_data->firm_gpio, "nfc_firm"); |
| if (ret) |
| goto err_firm; |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| ret = gpio_request(platform_data->ese_pwr_req, "ese_pwr"); |
| if (ret) |
| goto err_ese; |
| #endif |
| ret = gpio_request(platform_data->clk_req_gpio, "nfc_clk_req"); |
| if (ret) |
| goto err_clk_req; |
| |
| pn547_dev = kzalloc(sizeof(*pn547_dev), GFP_KERNEL); |
| if (pn547_dev == NULL) { |
| dev_err(&client->dev, |
| "failed to allocate memory for module data\n"); |
| ret = -ENOMEM; |
| goto err_exit; |
| } |
| |
| if(platform_data->ven_gpio < 0) { |
| if (platform_data->clkctrl_addr != 0) { |
| unsigned int val = 0; |
| |
| pn547_dev->clkctrl = ioremap_nocache(platform_data->clkctrl_addr, 0x4); |
| if (!pn547_dev->clkctrl) { |
| dev_err(&client->dev, "cannot remap register\n"); |
| ret = -ENXIO; |
| goto err_ioremap; |
| } |
| val = readl(pn547_dev->clkctrl); |
| val |= (PN547_NFC_CLKCTRL_PD_POLA | PN547_NFC_CLKCTRL_REQ_POLA | PN547_NFC_CLKCTRL_CLK_ENABLE); |
| writel(val, pn547_dev->clkctrl); |
| pr_info("%s: clkctrl=0x%X\n", __func__, val); |
| } |
| } |
| if (of_get_property(client->dev.of_node, "pn547,ldo_control", NULL)) { |
| ret = pn547_regulator_onoff(client, NFC_I2C_LDO_ON); |
| if (ret < 0) |
| pr_err("%s pn547 regulator_on fail err = %d\n", |
| __func__, ret); |
| usleep_range(1000, 1100); |
| } else { |
| ret = of_get_named_gpio(client->dev.of_node, "pn547,pvdd-gpio", 0); |
| if (ret < 0) { |
| pr_err("%s : pvdd-gpio is not set", __func__); |
| } else { |
| pn547_dev->pvdd = ret; |
| |
| ret = gpio_request(pn547_dev->pvdd, "pvdd-gpio"); |
| if (ret) { |
| dev_err(&client->dev, "%s failed to get gpio pvdd-gpio\n", __func__); |
| gpio_free(pn547_dev->pvdd); |
| goto err_pvdd; |
| } |
| gpio_direction_output(pn547_dev->pvdd, 1); |
| pr_info("%s pvdd-gpio:%d",__func__, pn547_dev->pvdd); |
| } |
| } |
| |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| pn547_dev->nfc_clock = msm_xo_get(MSM_XO_TCXO_A1, "nfc"); |
| if (IS_ERR(pn547_dev->nfc_clock)) { |
| ret = PTR_ERR(pn547_dev->nfc_clock); |
| printk(KERN_ERR "%s: Couldn't get TCXO_A1 vote for NFC (%d)\n", |
| __func__, ret); |
| ret = -ENODEV; |
| goto err_get_clock; |
| } |
| pn547_dev->clock_state = false; |
| #endif |
| #ifdef CONFIG_NFC_PM_BBCLK2 |
| pn547_dev->nfc_clock = clk_get(&client->dev, "nfc_clock"); |
| if (IS_ERR(pn547_dev->nfc_clock)) { |
| ret = PTR_ERR(pn547_dev->nfc_clock); |
| printk(KERN_ERR "%s: Couldn't get D1 (%d)\n", |
| __func__, ret); |
| } else { |
| if (clk_prepare_enable(pn547_dev->nfc_clock)) |
| printk(KERN_ERR "%s: Couldn't prepare D1\n", |
| __func__); |
| } |
| #endif |
| #if defined(CONFIG_NFC_PN547_CLK_BBCLK2) |
| #if defined(CONFIG_NFC_I2C_OVERWRITE) || defined(CONFIG_NFC_PN547_8084_USE_BBCLK2) |
| pn547_dev->nfc_clk = clk_get(NULL, "nfc_clk"); |
| #else |
| pn547_dev->nfc_clk = clk_get(&client->dev, "nfc_clk"); |
| #endif |
| if (IS_ERR(pn547_dev->nfc_clk)) { |
| ret = PTR_ERR(pn547_dev->nfc_clk); |
| printk(KERN_ERR "%s: Couldn't get D1 (%d)\n", |
| __func__, ret); |
| } else { |
| if (clk_prepare_enable(pn547_dev->nfc_clk)) |
| printk(KERN_ERR "%s: Couldn't prepare D1\n", |
| __func__); |
| } |
| #endif |
| #if defined(CONFIG_GPIO_PCAL6416A) |
| client->irq = gpio_to_irq(platform_data->irq_gpio); |
| #endif |
| pr_info("%s : IRQ num %d\n", __func__, client->irq); |
| |
| pn547_dev->irq_gpio = platform_data->irq_gpio; |
| pn547_dev->ven_gpio = platform_data->ven_gpio; |
| pn547_dev->firm_gpio = platform_data->firm_gpio; |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| pn547_dev->ese_pwr_req = platform_data->ese_pwr_req; |
| #endif |
| pn547_dev->conf_gpio = platform_data->conf_gpio; |
| pn547_dev->clk_req_gpio = platform_data->clk_req_gpio; |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| pn547_dev->clk_req_irq = platform_data->clk_req_irq; |
| #endif |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| pn547_dev->p61_current_state = P61_STATE_IDLE; |
| pn547_dev->nfc_ven_enabled = false; |
| pn547_dev->spi_ven_enabled = false; |
| #endif |
| pn547_dev->client = client; |
| |
| /* init mutex and queues */ |
| init_waitqueue_head(&pn547_dev->read_wq); |
| mutex_init(&pn547_dev->read_mutex); |
| |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| sema_init(&ese_access_sema, 1); |
| mutex_init(&pn547_dev->p61_state_mutex); |
| #endif |
| |
| pn547_dev->pn547_device.minor = MISC_DYNAMIC_MINOR; |
| #ifdef CONFIG_NFC_PN547 |
| pn547_dev->pn547_device.name = "pn547"; |
| #else |
| pn547_dev->pn547_device.name = "pn544"; |
| #endif |
| pn547_dev->pn547_device.fops = &pn547_dev_fops; |
| |
| ret = misc_register(&pn547_dev->pn547_device); |
| if (ret) { |
| pr_err("%s : misc_register failed\n", __FILE__); |
| goto err_misc_register; |
| } |
| |
| /* request irq. the irq is set whenever the chip has data available |
| * for reading. it is cleared when all data has been read. |
| */ |
| pr_info("%s : requesting IRQ %d\n", __func__, client->irq); |
| gpio_direction_input(pn547_dev->irq_gpio); |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| gpio_direction_output(pn547_dev->firm_gpio, 0); |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| gpio_direction_output(pn547_dev->ese_pwr_req, 0); |
| #endif |
| #if defined(CONFIG_NFC_PN547_CLOCK_REQUEST) || defined(CONFIG_NFC_PM_BBCLK2) |
| gpio_direction_input(pn547_dev->clk_req_gpio); |
| #endif |
| |
| i2c_set_clientdata(client, pn547_dev); |
| wake_lock_init(&pn547_dev->nfc_wake_lock, |
| WAKE_LOCK_SUSPEND, "nfc_wake_lock"); |
| wake_lock_init(&pn547_dev->nfc_clk_wake_lock, |
| WAKE_LOCK_SUSPEND, "nfc_clk_wake_lock"); |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| pn547_dev->wq_clock = create_singlethread_workqueue("nfc_wq"); |
| if (!pn547_dev->wq_clock) { |
| ret = -ENOMEM; |
| pr_err("%s: could not create workqueue\n", __func__); |
| goto err_create_workqueue; |
| } |
| INIT_WORK(&pn547_dev->work_nfc_clock, nfc_work_func_clock); |
| #endif |
| ret = request_irq(client->irq, pn547_dev_irq_handler, |
| IRQF_TRIGGER_RISING, "pn547", pn547_dev); |
| if (ret) { |
| dev_err(&client->dev, "request_irq failed\n"); |
| goto err_request_irq_failed; |
| } |
| disable_irq_nosync(pn547_dev->client->irq); |
| atomic_set(&pn547_dev->irq_enabled, 0); |
| |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| ret = request_irq(pn547_dev->clk_req_irq, pn547_dev_clk_req_irq_handler, |
| IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
| , "pn547_clk_req", pn547_dev); |
| if (ret) { |
| dev_err(&client->dev, "request_irq(clk_req) failed\n"); |
| goto err_request_irq_failed; |
| } |
| |
| enable_irq_wake(pn547_dev->clk_req_irq); |
| #else |
| |
| if(platform_data->clk_req_gpio > 0) { |
| pn547_dev->clk_req_irq = gpio_to_irq(platform_data->clk_req_gpio); |
| ret = request_irq(pn547_dev->clk_req_irq, pn547_nfc_clk_irq, |
| IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "nfc_clk",pn547_dev); |
| if (ret < 0) { |
| dev_err(&client->dev, "failed to register IRQ handler\n"); |
| goto err_request_irq_failed; |
| } |
| ret = enable_irq_wake(pn547_dev->clk_req_irq); |
| if (ret < 0) |
| dev_err(&client->dev, |
| "%s: Failed to Enable Wakeup Source(%d)\n", __func__, ret); |
| } |
| #endif |
| |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| gpio_set_value(pn547_dev->firm_gpio, 1); /* add firmware pin */ |
| usleep_range(4900, 5000); |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| usleep_range(4900, 5000); |
| set_pd(pn547_dev, PN547_NFC_PW_ON); |
| usleep_range(4900, 5000); |
| |
| for (addr = 0x2B; addr > 0x27; addr--) { |
| client->addr = addr; |
| addrcnt = 2; |
| |
| do { |
| ret = i2c_master_send(client, tmp, 4); |
| if (ret > 0) { |
| pr_info("%s : i2c addr=0x%X\n", |
| __func__, client->addr); |
| break; |
| } |
| } while (addrcnt--); |
| |
| if (ret > 0) |
| break; |
| } |
| |
| if(ret <= 0) |
| client->addr = 0x2B; |
| |
| set_pd(pn547_dev, PN547_NFC_PW_OFF); |
| gpio_set_value(pn547_dev->firm_gpio, 0); /* add */ |
| |
| if (ret < 0) |
| pr_err("%s : fail to get i2c addr\n", __func__); |
| /* goto err_request_irq_failed; */ |
| else |
| pr_info("%s : success\n", __func__); |
| return 0; |
| |
| err_request_irq_failed: |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| err_create_workqueue: |
| #endif |
| misc_deregister(&pn547_dev->pn547_device); |
| wake_lock_destroy(&pn547_dev->nfc_wake_lock); |
| wake_lock_destroy(&pn547_dev->nfc_clk_wake_lock); |
| err_misc_register: |
| mutex_destroy(&pn547_dev->read_mutex); |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| mutex_destroy(&pn547_dev->p61_state_mutex); |
| #endif |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| msm_xo_put(pn547_dev->nfc_clock); |
| err_get_clock: |
| #endif |
| err_ioremap: |
| if(!pn547_dev->clkctrl) |
| iounmap(pn547_dev->clkctrl); |
| err_pvdd: |
| kfree(pn547_dev); |
| err_exit: |
| gpio_free(platform_data->clk_req_gpio); |
| err_clk_req: |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| gpio_free(platform_data->ese_pwr_req); |
| err_ese: |
| #endif |
| gpio_free(platform_data->firm_gpio); |
| err_firm: |
| gpio_free(platform_data->ven_gpio); |
| err_ven: |
| gpio_free(platform_data->irq_gpio); |
| pr_err("[pn547] pn547_probe fail!\n"); |
| return ret; |
| } |
| |
| static int pn547_remove(struct i2c_client *client) |
| { |
| struct pn547_dev *pn547_dev; |
| |
| pn547_dev = i2c_get_clientdata(client); |
| #ifdef CONFIG_NFC_PM_BBCLK2 |
| if(pn547_dev->nfc_clock) |
| clk_unprepare(pn547_dev->nfc_clock); |
| #endif |
| #if defined(CONFIG_NFC_PN547_CLK_BBCLK2) |
| if (pn547_dev->nfc_clk) |
| clk_unprepare(pn547_dev->nfc_clk); |
| #endif |
| wake_lock_destroy(&pn547_dev->nfc_wake_lock); |
| wake_lock_destroy(&pn547_dev->nfc_clk_wake_lock); |
| free_irq(client->irq, pn547_dev); |
| misc_deregister(&pn547_dev->pn547_device); |
| mutex_destroy(&pn547_dev->read_mutex); |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| mutex_destroy(&pn547_dev->p61_state_mutex); |
| #endif |
| gpio_free(pn547_dev->irq_gpio); |
| if(pn547_dev->ven_gpio > 0) |
| gpio_free(pn547_dev->ven_gpio); |
| else |
| iounmap(pn547_dev->clkctrl); |
| gpio_free(pn547_dev->firm_gpio); |
| gpio_free(pn547_dev->clk_req_gpio); |
| #ifdef CONFIG_NFC_PN547_CLOCK_REQUEST |
| msm_xo_put(pn547_dev->nfc_clock); |
| #endif |
| #ifdef CONFIG_NFC_PN547_ESE_SUPPORT |
| pn547_dev->p61_current_state = P61_STATE_INVALID; |
| pn547_dev->nfc_ven_enabled = false; |
| pn547_dev->spi_ven_enabled = false; |
| #endif |
| kfree(pn547_dev); |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id pn547_id[] = { |
| {"pn547", 0}, |
| {} |
| }; |
| |
| #ifdef CONFIG_OF |
| static struct of_device_id nfc_match_table[] = { |
| { .compatible = "pn547",}, |
| {}, |
| }; |
| #else |
| #define nfc_match_table NULL |
| #endif |
| |
| static struct i2c_driver pn547_driver = { |
| .id_table = pn547_id, |
| .probe = pn547_probe, |
| .remove = pn547_remove, |
| .driver = { |
| .owner = THIS_MODULE, |
| .name = "pn547", |
| .of_match_table = nfc_match_table, |
| }, |
| }; |
| |
| /* |
| * module load/unload record keeping |
| */ |
| |
| /*extern unsigned int system_rev;*/ |
| static int __init pn547_dev_init(void) |
| { |
| pr_info("Loading pn547 driver\n"); |
| |
| #if CONFIG_ARCH_EXYNOS |
| if(lpcharge) |
| #else |
| if(poweroff_charging) |
| #endif |
| return 0; |
| else |
| return i2c_add_driver(&pn547_driver); |
| |
| } |
| |
| module_init(pn547_dev_init); |
| |
| static void __exit pn547_dev_exit(void) |
| { |
| pr_info("Unloading pn547 driver\n"); |
| i2c_del_driver(&pn547_driver); |
| } |
| |
| module_exit(pn547_dev_exit); |
| |
| MODULE_AUTHOR("Sylvain Fonteneau"); |
| MODULE_DESCRIPTION("NFC PN547 driver"); |
| MODULE_LICENSE("GPL"); |