summaryrefslogtreecommitdiff
path: root/drivers/mxc/sim/imx_emvsim.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mxc/sim/imx_emvsim.c')
-rw-r--r--drivers/mxc/sim/imx_emvsim.c1638
1 files changed, 1638 insertions, 0 deletions
diff --git a/drivers/mxc/sim/imx_emvsim.c b/drivers/mxc/sim/imx_emvsim.c
new file mode 100644
index 000000000000..59f54dcd533e
--- /dev/null
+++ b/drivers/mxc/sim/imx_emvsim.c
@@ -0,0 +1,1638 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mxc_sim_interface.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#define DRIVER_NAME "mxc_emvsim"
+
+/* Definitions of the offset of the SIM hardware registers */
+#define EMV_SIM_VER_ID 0X00
+#define EMV_SIM_PARAM 0X04
+#define EMV_SIM_CLKCFG 0X08
+#define EMV_SIM_DIVISOR 0X0C
+#define EMV_SIM_CTRL 0X10
+#define EMV_SIM_INT_MASK 0X14
+#define EMV_SIM_RX_THD 0X18
+#define EMV_SIM_TX_THD 0X1C
+#define EMV_SIM_RX_STATUS 0X20
+#define EMV_SIM_TX_STATUS 0X24
+#define EMV_SIM_PCSR 0X28
+#define EMV_SIM_RX_BUF 0X2C
+#define EMV_SIM_TX_BUF 0X30
+#define EMV_SIM_TX_GETU 0X34
+#define EMV_SIM_CWT_VAL 0X38
+#define EMV_SIM_BWT_VAL 0X3C
+#define EMV_SIM_BGT_VAL 0X40
+#define EMV_SIM_GPCNT0_VAL 0X44
+#define EMV_SIM_GPCNT1_VAL 0X48
+
+#define SIM_XMT_BUFFER_SIZE 300
+#define SIM_RCV_BUFFER_SIZE 400
+
+#define SIM_TX_FIFO_DEPTH 16
+#define SIM_RX_FIFO_DEPTH 16
+#define TX_FIFO_THRESHOLD 4
+
+#define SIM_STATE_REMOVED 0
+#define SIM_STATE_DETECTED 1
+#define SIM_STATE_ATR_RECEIVING 2
+#define SIM_STATE_ATR_RECEIVED 3
+#define SIM_STATE_XMTING 4
+#define SIM_STATE_XMT_DONE 5
+#define SIM_STATE_XMT_ERROR 6
+#define SIM_STATE_RECEIVING 7
+#define SIM_STATE_RECEIVE_DONE 8
+#define SIM_STATE_RECEIVE_ERROR 9
+#define SIM_STATE_RESET_SEQUENCY 10
+
+#define SIM_CNTL_GPCNT_RESET 0
+#define SIM_CNTL_GPCNT_CARD_CLK 1
+#define SIM_CNTL_GPCNT_RCV_CLK 2
+#define SIM_CNTL_GPCNT_ETU_CLK 3
+#define SIM_EMV_NACK_THRESHOLD 5
+#define EMV_T0_BGT 16
+#define EMV_T1_BGT 22
+#define ATR_THRESHOLD_MAX 100
+#define ATR_MAX_CWT 10080
+#define ATR_MAX_DURATION 20160
+#define FCLK_FREQ 4000000
+
+#define ATR_TIMEOUT 5
+#define TX_TIMEOUT 10
+#define RX_TIMEOUT 100
+#define RESET_RETRY_TIMES 5
+#define EMV_RESET_LOW_CYCLES 40000
+#define ATR_MAX_DELAY_CLK 46400
+#define DIVISOR_VALUE 372
+
+#define SIM_CNTL_GPCNT0_CLK_SEL_MASK (3 << 10)
+#define SIM_CNTL_GPCNT0_CLK_SEL(x) ((x & 3) << 10)
+#define SIM_CNTL_GPCNT1_CLK_SEL_MASK (3 << 8)
+#define SIM_CNTL_GPCNT1_CLK_SEL(x) ((x & 3) << 8)
+
+/* EMV_SIM_CTRL */
+#define IC (1 << 0)
+#define ICM (1 << 1)
+#define ANACK (1 << 2)
+#define ONACK (1 << 3)
+#define FLSH_RX (1 << 8)
+#define FLSH_TX (1 << 9)
+#define SW_RST (1 << 10)
+#define KILL_CLOCKS (1 << 11)
+#define RCV_EN (1 << 16)
+#define XMT_EN (1 << 17)
+#define RCVR_11 (1 << 18)
+#define CWT_EN (1 << 27)
+#define BWT_EN (1 << 31)
+
+/* EMV_SIM_INT_MASK */
+#define RDT_IM (1 << 0)
+#define TC_IM (1 << 1)
+#define ETC_IM (1 << 3)
+#define TNACK_IM (1 << 5)
+#define TDT_IM (1 << 7)
+#define GPCNT0_IM (1 << 8)
+#define CWT_ERR_IM (1 << 9)
+#define RNACK_IM (1 << 10)
+#define BWT_ERR_IM (1 << 11)
+#define GPCNT1_IM (1 << 13)
+#define RX_DATA_IM (1 << 14)
+
+/* EMV_SIM_RX_THD */
+#define SIM_RCV_THRESHOLD_RDT_MASK (0x0f << 0)
+#define SIM_RCV_THRESHOLD_RDT(x) ((x & 0x0f) << 0)
+#define SIM_RCV_THRESHOLD_RTH_MASK (0x0f << 8)
+#define SIM_RCV_THRESHOLD_RTH(x) ((x & 0x0f) << 8)
+
+/* EMV_SIM_TX_THD */
+#define SIM_XMT_THRESHOLD_TDT_MASK (0x0f << 0)
+#define SIM_XMT_THRESHOLD_TDT(x) ((x & 0x0f) << 0)
+#define SIM_XMT_THRESHOLD_XTH_MASK (0x0f << 8)
+#define SIM_XMT_THRESHOLD_XTH(x) ((x & 0x0f) << 8)
+
+/* EMV_SIM_RX_STATUS */
+#define RX_DATA (1 << 4)
+#define RDTF (1 << 5)
+#define CWT_ERR (1 << 8)
+#define RTE (1 << 9)
+#define BWT_ERR (1 << 10)
+#define BGT_ERR (1 << 11)
+#define PEF (1 << 12)
+#define FEF (1 << 13)
+
+/* EMV_SIM_TX_STATUS */
+#define TNTE (1 << 0)
+#define ETCF (1 << 4)
+#define TCF (1 << 5)
+#define TDTF (1 << 7)
+#define GPCNT0_TO (1 << 8)
+#define GPCNT1_TO (1 << 9)
+
+/* EMV_SIM_PCSR */
+#define SAPD (1 << 0)
+#define SVCC_EN (1 << 1)
+#define VCCENP (1 << 2)
+#define SRST (1 << 3)
+#define SCEN (1 << 4)
+#define SPD (1 << 7)
+#define SPDIM (1 << 24)
+#define SPDIF (1 << 25)
+#define SPDP (1 << 26)
+#define SPDES (1 << 27)
+
+struct emvsim_t {
+ s32 present;
+ u8 open_cnt;
+ int state;
+ struct clk *clk;
+ struct clk *ipg;
+ struct resource *res;
+ void __iomem *ioaddr;
+ int irq;
+
+ int errval;
+ int protocol_type;
+ sim_timing_t timing_data;
+ sim_baud_t baud_rate;
+ int timeout;
+ u8 nack_threshold;
+ u8 nack_enable;
+ u32 expected_rcv_cnt;
+ u8 is_fixed_len_rec;
+ u32 xmt_remaining;
+ u32 xmt_pos;
+ u32 rcv_count;
+ u8 rcv_buffer[SIM_RCV_BUFFER_SIZE];
+ u8 xmt_buffer[SIM_XMT_BUFFER_SIZE];
+ struct completion xfer_done;
+ u16 rcv_head;
+ spinlock_t lock;
+ u32 clk_rate;
+ u8 checking_ts_timing;
+};
+
+static struct miscdevice emvsim_dev;
+
+static void emvsim_data_reset(struct emvsim_t *emvsim)
+{
+ emvsim->errval = SIM_OK;
+ emvsim->protocol_type = 0;
+ emvsim->timeout = 0;
+ emvsim->nack_threshold = SIM_EMV_NACK_THRESHOLD;
+ emvsim->nack_enable = 0;
+ memset(&emvsim->timing_data, 0, sizeof(emvsim->timing_data));
+ memset(&emvsim->baud_rate, 0, sizeof(emvsim->baud_rate));
+
+ emvsim->xmt_remaining = 0;
+ emvsim->xmt_pos = 0;
+ emvsim->rcv_count = 0;
+ emvsim->rcv_head = 0;
+ memset(emvsim->rcv_buffer, 0, SIM_RCV_BUFFER_SIZE);
+ memset(emvsim->xmt_buffer, 0, SIM_XMT_BUFFER_SIZE);
+
+ reinit_completion(&emvsim->xfer_done);
+};
+
+static void emvsim_set_nack(struct emvsim_t *emvsim, u8 enable)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ /*Disable overrun NACK setting for now*/
+ reg_val &= ~ONACK;
+
+ if (enable) {
+ reg_val |= ANACK;
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_CTRL);
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_THD);
+ reg_val &= ~(SIM_XMT_THRESHOLD_XTH_MASK);
+ reg_val |= SIM_XMT_THRESHOLD_XTH(emvsim->nack_threshold);
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_TX_THD);
+ } else {
+ reg_val &= ~ANACK;
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_CTRL);
+ }
+
+ emvsim->nack_enable = enable;
+}
+
+static void emvsim_set_tx(struct emvsim_t *emvsim, u8 enable)
+{
+ u32 reg_data;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ if (enable) {
+ reg_data |= XMT_EN;
+ reg_data &= ~RCV_EN;
+ } else {
+ reg_data &= ~XMT_EN;
+ }
+
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_CTRL);
+}
+
+static void emvsim_set_rx(struct emvsim_t *emvsim, u8 enable)
+{
+ u32 reg_data;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ if (enable) {
+ reg_data |= RCV_EN;
+ reg_data &= ~XMT_EN;
+ } else {
+ reg_data &= ~RCV_EN;
+ }
+
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_CTRL);
+}
+
+static void emvsim_mask_timer0_int(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data |= GPCNT0_IM;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ reg_data |= GPCNT0_TO;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_TX_STATUS);
+}
+
+static void emvsim_mask_timer1_int(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data |= GPCNT1_IM;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ reg_data |= GPCNT1_TO;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_TX_STATUS);
+}
+
+static void emvsim_set_gpctimer0_clk(struct emvsim_t *emvsim, u8 clk_source)
+{
+ u32 reg_data;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CLKCFG);
+ reg_data &= ~SIM_CNTL_GPCNT0_CLK_SEL_MASK;
+ reg_data |= SIM_CNTL_GPCNT0_CLK_SEL(clk_source);
+ writel(reg_data, emvsim->ioaddr + EMV_SIM_CLKCFG);
+}
+
+static void emvsim_set_gpctimer1_clk(struct emvsim_t *emvsim, u8 clk_source)
+{
+ u32 reg_data;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CLKCFG);
+ reg_data &= ~SIM_CNTL_GPCNT1_CLK_SEL_MASK;
+ reg_data |= SIM_CNTL_GPCNT1_CLK_SEL(clk_source);
+ writel(reg_data, emvsim->ioaddr + EMV_SIM_CLKCFG);
+}
+
+static void emvsim_reset_gpctimer(struct emvsim_t *emvsim)
+{
+ emvsim_set_gpctimer0_clk(emvsim, SIM_CNTL_GPCNT_RESET);
+ emvsim_set_gpctimer1_clk(emvsim, SIM_CNTL_GPCNT_RESET);
+
+ /* need a tx_en posedge to update gpctimer0 clk */
+ emvsim_set_tx(emvsim, 0);
+ emvsim_set_tx(emvsim, 1);
+ emvsim_set_tx(emvsim, 0);
+}
+
+static int emvsim_reset_low_timing(struct emvsim_t *emvsim, u32 clock_cycle)
+{
+ int errval = 0;
+ int timeout = 0;
+ u32 fclk_in_khz, delay_in_us, reg_data;
+
+ fclk_in_khz = emvsim->clk_rate / MSEC_PER_SEC;
+ delay_in_us = EMV_RESET_LOW_CYCLES * USEC_PER_MSEC / fclk_in_khz;
+
+ emvsim_mask_timer0_int(emvsim);
+ __raw_writel(clock_cycle, emvsim->ioaddr + EMV_SIM_GPCNT0_VAL);
+ emvsim_set_gpctimer0_clk(emvsim, SIM_CNTL_GPCNT_CARD_CLK);
+ emvsim_set_tx(emvsim, 1);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data &= ~GPCNT0_IM;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+
+ timeout = wait_for_completion_timeout(
+ &emvsim->xfer_done,
+ msecs_to_jiffies(delay_in_us / 1000 * 2));
+ if (timeout == 0) {
+ dev_err(emvsim_dev.parent, "Reset low GPC timout\n");
+ errval = -SIM_E_TIMEOUT;
+ }
+
+ return errval;
+}
+
+static void emvsim_set_cwt(struct emvsim_t *emvsim, u8 enable)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ if (enable && emvsim->timing_data.cwt)
+ reg_val |= CWT_EN;
+ else
+ reg_val &= ~CWT_EN;
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_CTRL);
+}
+
+static void emvsim_set_bwt(struct emvsim_t *emvsim, u8 enable)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ if (enable && (emvsim->timing_data.bwt || emvsim->timing_data.bgt))
+ reg_val |= BWT_EN;
+ else
+ reg_val &= ~BWT_EN;
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_CTRL);
+}
+
+static int emvsim_reset_module(struct emvsim_t *emvsim)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ reg_val |= SW_RST;
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_CTRL);
+
+ /* Software should allow a minimum of 4 Protocol clock cycles(4MHz)*/
+ usleep_range(1, 3);
+
+ return 0;
+}
+
+static void emvsim_receive_atr_set(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ __raw_writel(0x0, emvsim->ioaddr + EMV_SIM_GPCNT1_VAL);
+ emvsim_set_gpctimer1_clk(emvsim, SIM_CNTL_GPCNT_ETU_CLK);
+ emvsim_set_rx(emvsim, 1);
+
+ /*Set the cwt timer.Refer the setting of ATR on EMV4.3 book*/
+ __raw_writel(ATR_MAX_CWT, emvsim->ioaddr + EMV_SIM_CWT_VAL);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ reg_data |= CWT_EN;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_CTRL);
+
+ emvsim_set_nack(emvsim, 0);
+ emvsim->errval = 0;
+ emvsim->rcv_count = 0;
+ emvsim->checking_ts_timing = 1;
+ emvsim->state = SIM_STATE_ATR_RECEIVING;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data |= CWT_ERR_IM;
+ reg_data &= ~(RX_DATA_IM | GPCNT0_IM);
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+}
+
+static int32_t emvsim_check_rec_data(u32 *reg_data)
+{
+ s32 err = 0;
+
+ if (*reg_data & CWT_ERR)
+ err |= SIM_ERROR_CWT;
+
+ if (*reg_data & FEF)
+ err |= SIM_ERROR_FRAME;
+
+ if (*reg_data & PEF)
+ err |= SIM_ERROR_PARITY;
+
+ return err;
+}
+
+static void emvsim_xmt_fill_fifo(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+ u32 bytesleft, i;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ bytesleft = SIM_TX_FIFO_DEPTH - ((reg_data >> 24) & 0x1F);
+
+ if (bytesleft > emvsim->xmt_remaining)
+ bytesleft = emvsim->xmt_remaining;
+
+ for (i = 0; i < bytesleft; i++) {
+ __raw_writel(emvsim->xmt_buffer[emvsim->xmt_pos],
+ emvsim->ioaddr + EMV_SIM_TX_BUF);
+ emvsim->xmt_pos++;
+ };
+ emvsim->xmt_remaining -= bytesleft;
+};
+
+static void emvsim_rcv_read_fifo(struct emvsim_t *emvsim)
+{
+ u16 i, count;
+ u32 reg_data;
+ u8 data;
+
+ count = __raw_readl(emvsim->ioaddr + EMV_SIM_RX_STATUS) >> 24;
+
+ spin_lock(&emvsim->lock);
+ for (i = 0; i < count; i++) {
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_RX_STATUS);
+ emvsim->errval |= emvsim_check_rec_data(&reg_data);
+
+ /* T1 mode and t0 mode no parity error, T1 mode SIM module
+ * will not produce NACK be NACK is disabled. T0 mode to
+ * ensure there is no parity error for the current byte
+ */
+ if (!(emvsim->nack_enable && (reg_data & PEF))) {
+ data = __raw_readb(emvsim->ioaddr + EMV_SIM_RX_BUF);
+ emvsim->rcv_buffer[emvsim->rcv_head + emvsim->rcv_count] = data;
+ emvsim->rcv_count++;
+ }
+
+ if (emvsim->rcv_head + emvsim->rcv_count >=
+ SIM_RCV_BUFFER_SIZE) {
+ dev_err(emvsim_dev.parent,
+ "The software fifo is full,head %d, cnt%d\n",
+ emvsim->rcv_head, emvsim->rcv_count);
+ break;
+ }
+ }
+ spin_unlock(&emvsim->lock);
+}
+
+static void emvsim_tx_irq_enable(struct emvsim_t *emvsim)
+{
+ u32 reg_val;
+
+ /*Clear the TX&RX status, W1C */
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_RX_STATUS);
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_RX_STATUS);
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_val |= CWT_ERR_IM | BWT_ERR_IM | RX_DATA_IM | RX_DATA_IM;
+
+ if (emvsim->xmt_remaining != 0) {
+ reg_val &= ~TDT_IM;
+ } else {
+ reg_val &= ~TC_IM;
+ reg_val &= ~ETC_IM;
+ }
+
+ /* NACK interrupt is enabled only when T0 mode*/
+ if (emvsim->protocol_type == SIM_PROTOCOL_T0 ||
+ emvsim->nack_enable != 0)
+ reg_val &= ~TNACK_IM;
+ else
+ reg_val |= TNACK_IM;
+
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_INT_MASK);
+}
+
+static void emvsim_tx_irq_disable(struct emvsim_t *emvsim)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_val |= (TDT_IM | TC_IM | TNACK_IM | ETC_IM);
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_INT_MASK);
+}
+
+static void emvsim_rx_irq_enable(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ /* Ensure the CWT timer is enabled */
+ emvsim_set_cwt(emvsim, 1);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data |= (TC_IM | TDT_IM | TNACK_IM);
+ reg_data &= ~(RX_DATA_IM | CWT_ERR_IM | BWT_ERR_IM);
+
+ if (emvsim->protocol_type == SIM_PROTOCOL_T0 ||
+ emvsim->nack_enable != 0)
+ reg_data &= ~RNACK_IM;
+ else
+ reg_data |= RNACK_IM;
+
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+}
+
+static void emvsim_rx_irq_disable(struct emvsim_t *emvsim)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_val |= (RX_DATA_IM | CWT_ERR_IM | BWT_ERR_IM | RNACK_IM);
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_INT_MASK);
+}
+
+static irqreturn_t emvsim_irq_handler(int irq, void *dev_id)
+{
+ u32 reg_data, tx_status, rx_status;
+ struct emvsim_t *emvsim = (struct emvsim_t *)dev_id;
+
+ /* clear TX/RX interrupt status, W1C*/
+ tx_status = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ rx_status = __raw_readl(emvsim->ioaddr + EMV_SIM_RX_STATUS);
+ __raw_writel(tx_status, emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ __raw_writel(rx_status, emvsim->ioaddr + EMV_SIM_RX_STATUS);
+
+ if (emvsim->state == SIM_STATE_ATR_RECEIVING &&
+ emvsim->checking_ts_timing == 1) {
+ if ((tx_status & GPCNT0_TO) && !(rx_status & RX_DATA)) {
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ reg_data &= ~CWT_EN;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_CTRL);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data |= (GPCNT0_IM | CWT_ERR_IM | RX_DATA_IM);
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+
+ emvsim->errval = SIM_ERROR_ATR_DELAY;
+ complete(&emvsim->xfer_done);
+ emvsim->checking_ts_timing = 0;
+ } else if (rx_status & RX_DATA) {
+ u8 rdt = 1;
+
+ emvsim_mask_timer0_int(emvsim);
+
+ /* ATR each received byte will cost 12 ETU */
+ reg_data = ATR_MAX_DURATION - emvsim->rcv_count * 12;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_GPCNT1_VAL);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data &= ~(GPCNT1_IM | CWT_ERR_IM | RX_DATA_IM);
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+ emvsim_rcv_read_fifo(emvsim);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_STATUS);
+ reg_data |= GPCNT1_TO;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_TX_STATUS);
+
+ reg_data = SIM_RCV_THRESHOLD_RTH(0) | SIM_RCV_THRESHOLD_RDT(rdt);
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_RX_THD);
+
+ /* ATR has arrived as EMV demands */
+ emvsim->checking_ts_timing = 0;
+ } else {
+ dev_err(emvsim_dev.parent,
+ "Unexpected irq when delay checking\n");
+ }
+ }
+
+ else if (emvsim->state == SIM_STATE_ATR_RECEIVING) {
+ /*CWT ERROR OR ATR_MAX_DURATION TIMEOUT */
+ if ((rx_status & CWT_ERR) ||
+ ((tx_status & GPCNT1_TO) && (emvsim->rcv_count != 0))) {
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ reg_data &= ~CWT_EN;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_CTRL);
+
+ reg_data = __raw_readl(emvsim->ioaddr +
+ EMV_SIM_INT_MASK);
+ reg_data |= (GPCNT1_IM | CWT_ERR_IM | RX_DATA_IM | GPCNT0_IM);
+ __raw_writel(reg_data, emvsim->ioaddr +
+ EMV_SIM_INT_MASK);
+
+ if (tx_status & GPCNT1_TO)
+ emvsim->errval |= SIM_ERROR_ATR_TIMEROUT;
+
+ if (rx_status & CWT_ERR)
+ emvsim->errval |= SIM_ERROR_CWT;
+
+ emvsim_rcv_read_fifo(emvsim);
+ emvsim->state = SIM_STATE_ATR_RECEIVED;
+
+ complete(&emvsim->xfer_done);
+ } else if (rx_status & RX_DATA) {
+ emvsim_rcv_read_fifo(emvsim);
+ }
+ }
+
+ else if (emvsim->state == SIM_STATE_XMTING) {
+ /* need to enable CWT timer */
+ if (tx_status & ETCF)
+ emvsim_set_cwt(emvsim, 1);
+
+ if (tx_status & TNTE) {
+ emvsim_set_tx(emvsim, 0);
+
+ /*Disalbe the timers*/
+ emvsim_set_cwt(emvsim, 0);
+ emvsim_set_bwt(emvsim, 0);
+
+ /*Disable the NACK interruptand TX related interrupt*/
+ emvsim_tx_irq_disable(emvsim);
+
+ /*Update the state and status*/
+ emvsim->errval |= SIM_ERROR_NACK_THRESHOLD;
+ emvsim->state = SIM_STATE_XMT_ERROR;
+
+ complete(&emvsim->xfer_done);
+ } else if (tx_status & TDTF && emvsim->xmt_remaining != 0) {
+ emvsim_xmt_fill_fifo(emvsim);
+ if (emvsim->xmt_remaining == 0) {
+ reg_data = __raw_readl(emvsim->ioaddr +
+ EMV_SIM_INT_MASK);
+ reg_data |= TDT_IM;
+ reg_data &= ~(TC_IM | ETC_IM);
+ __raw_writel(reg_data, emvsim->ioaddr +
+ EMV_SIM_INT_MASK);
+ }
+ } else if ((tx_status & TCF) && !emvsim->xmt_remaining) {
+ emvsim_tx_irq_disable(emvsim);
+ emvsim_set_rx(emvsim, 1);
+ emvsim->state = SIM_STATE_XMT_DONE;
+ complete(&emvsim->xfer_done);
+ }
+ }
+
+ /*
+ * It takes some time to change from SIM_STATE_XMT_DONE to
+ * SIM_STATE_RECEIVING RX would only be enabled after state
+ * becomes SIM_STATE_RECEIVING
+ */
+ else if (emvsim->state == SIM_STATE_RECEIVING) {
+ if (rx_status & RTE) {
+ emvsim_set_rx(emvsim, 0);
+
+ /* Disable the BWT timer and CWT timer right now */
+ emvsim_set_cwt(emvsim, 0);
+ emvsim_set_bwt(emvsim, 0);
+
+ /* Disable the interrupt right now */
+ emvsim_rx_irq_disable(emvsim);
+
+ /* Should we read the fifo or just flush the fifo? */
+ emvsim_rcv_read_fifo(emvsim);
+ emvsim->errval = SIM_ERROR_NACK_THRESHOLD;
+ emvsim->state = SIM_STATE_RECEIVE_ERROR;
+ complete(&emvsim->xfer_done);
+ }
+
+ if (rx_status & RX_DATA) {
+ emvsim_rcv_read_fifo(emvsim);
+ if (emvsim->is_fixed_len_rec &&
+ emvsim->rcv_count >= emvsim->expected_rcv_cnt) {
+ emvsim_rx_irq_disable(emvsim);
+
+ if (emvsim->state == SIM_STATE_RECEIVING) {
+ emvsim->state = SIM_STATE_RECEIVE_DONE;
+ complete(&emvsim->xfer_done);
+ }
+ }
+ }
+
+ if (rx_status & (CWT_ERR | BWT_ERR | BGT_ERR)) {
+ emvsim_set_cwt(emvsim, 0);
+ emvsim_set_bwt(emvsim, 0);
+ emvsim_rx_irq_disable(emvsim);
+
+ if (rx_status & BWT_ERR)
+ emvsim->errval |= SIM_ERROR_BWT;
+ if (rx_status & CWT_ERR)
+ emvsim->errval |= SIM_ERROR_CWT;
+ if (rx_status & BGT_ERR)
+ emvsim->errval |= SIM_ERROR_BGT;
+
+ emvsim_rcv_read_fifo(emvsim);
+
+ if (emvsim->state == SIM_STATE_RECEIVING) {
+ emvsim->state = SIM_STATE_RECEIVE_DONE;
+ complete(&emvsim->xfer_done);
+ }
+ }
+ }
+
+ else if ((emvsim->state == SIM_STATE_RESET_SEQUENCY) &&
+ (tx_status & GPCNT0_TO)) {
+ complete(&emvsim->xfer_done);
+ emvsim_mask_timer0_int(emvsim);
+ } else if (rx_status & RX_DATA) {
+ dev_err(emvsim_dev.parent,
+ "unexpected status %d\n", emvsim->state);
+ emvsim_rcv_read_fifo(emvsim);
+ }
+
+ return IRQ_HANDLED;
+};
+
+static void emvsim_start(struct emvsim_t *emvsim)
+{
+ u32 reg_data, clk_rate, clk_div = 0;
+
+ clk_rate = clk_get_rate(emvsim->clk);
+ clk_div = (clk_rate + emvsim->clk_rate - 1) / emvsim->clk_rate;
+ __raw_writel(clk_div, emvsim->ioaddr + EMV_SIM_CLKCFG);
+
+ usleep_range(90, 100);
+ /* SPDP=0: SIM Presence Detect pin is low, default PRESENT status */
+ if (__raw_readl(emvsim->ioaddr + EMV_SIM_PCSR) & SPDP) {
+ emvsim->present = SIM_PRESENT_REMOVED;
+ emvsim->state = SIM_STATE_REMOVED;
+ } else {
+ emvsim->present = SIM_PRESENT_DETECTED;
+ emvsim->state = SIM_STATE_DETECTED;
+ };
+
+ /* disabled card interrupt. clear interrupt status*/
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data |= SPDIM | SPDIF;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+};
+
+static void emvsim_cold_reset_sequency(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ emvsim->state = SIM_STATE_RESET_SEQUENCY;
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data &= ~VCCENP;
+ reg_data |= SVCC_EN;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+
+ msleep(20);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data |= SCEN;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+
+ emvsim_reset_low_timing(emvsim, EMV_RESET_LOW_CYCLES);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data |= SRST;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+
+ emvsim_mask_timer0_int(emvsim);
+ __raw_writel(ATR_MAX_DELAY_CLK, emvsim->ioaddr +
+ EMV_SIM_GPCNT0_VAL);
+};
+
+static void emvsim_deactivate(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ /* Auto powdown to implement the deactivate sequence */
+ if (emvsim->present != SIM_PRESENT_REMOVED) {
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data |= SAPD | SPD;
+ writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+ } else {
+ dev_err(emvsim_dev.parent, ">>>No card%s\n", __func__);
+ }
+};
+
+static void emvsim_cold_reset(struct emvsim_t *emvsim)
+{
+ if (emvsim->present != SIM_PRESENT_REMOVED) {
+ emvsim->state = SIM_STATE_DETECTED;
+ emvsim->present = SIM_PRESENT_DETECTED;
+ emvsim_cold_reset_sequency(emvsim);
+ emvsim_receive_atr_set(emvsim);
+ } else {
+ dev_err(emvsim_dev.parent, "No card%s\n", __func__);
+ }
+};
+
+static void emvsim_warm_reset_sequency(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ /*enable power/clk, deassert rst*/
+ emvsim->state = SIM_STATE_RESET_SEQUENCY;
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data |= (SRST | SCEN);
+ reg_data &= ~VCCENP;
+ reg_data |= SVCC_EN;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+
+ usleep_range(20, 25);
+
+ /* assert rst */
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data &= ~SRST;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+
+ /* rst keep low */
+ emvsim_reset_low_timing(emvsim, EMV_RESET_LOW_CYCLES);
+
+ /* deassert rst */
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ reg_data |= SRST;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_PCSR);
+
+ emvsim_mask_timer0_int(emvsim);
+ __raw_writel(ATR_MAX_DELAY_CLK, emvsim->ioaddr + EMV_SIM_GPCNT0_VAL);
+}
+
+static void emvsim_warm_reset(struct emvsim_t *emvsim)
+{
+ if (emvsim->present != SIM_PRESENT_REMOVED) {
+ emvsim_data_reset(emvsim);
+ emvsim_reset_gpctimer(emvsim);
+ emvsim_warm_reset_sequency(emvsim);
+ emvsim_receive_atr_set(emvsim);
+ } else {
+ dev_err(emvsim_dev.parent, "No card%s\n", __func__);
+ }
+};
+
+static int emvsim_card_lock(struct emvsim_t *emvsim)
+{
+ int errval;
+
+ /* place holder for true physcial locking */
+ if (emvsim->present != SIM_PRESENT_REMOVED)
+ errval = SIM_OK;
+ else
+ errval = -SIM_E_NOCARD;
+
+ return errval;
+};
+
+static int emvsim_card_eject(struct emvsim_t *emvsim)
+{
+ int errval;
+
+ /* place holder for true physcial locking */
+ if (emvsim->present != SIM_PRESENT_REMOVED)
+ errval = SIM_OK;
+ else
+ errval = -SIM_E_NOCARD;
+
+ return errval;
+};
+
+static int emvsim_check_baud_rate(sim_baud_t *baud_rate)
+{
+ /* The valid value is decribed in the 8.3.3.1 in EMV 4.3 */
+ if (baud_rate->fi == 1 && (baud_rate->di == 1 ||
+ baud_rate->di == 2 || baud_rate->di == 3))
+ return 0;
+
+ return -EINVAL;
+}
+
+static int emvsim_set_baud_rate(struct emvsim_t *emvsim)
+{
+ u32 reg_data;
+
+ switch (emvsim->baud_rate.di) {
+ case 1:
+ reg_data = 372;
+ break;
+ case 2:
+ reg_data = 372 >> 1;
+ break;
+ case 3:
+ reg_data = 372 >> 2;
+ break;
+ default:
+ dev_err(emvsim_dev.parent,
+ "Invalid baud Di, Using default 372 / 1\n");
+ reg_data = 372;
+ break;
+ }
+
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_DIVISOR);
+
+ return 0;
+}
+
+static int emvsim_check_timing_data(sim_timing_t *timing_data)
+{
+ if (timing_data->wwt > 0xFFFF || timing_data->cwt > 0xFFFF ||
+ timing_data->bgt > 0xFFFF || timing_data->cgt > 0xFF) {
+ dev_err(emvsim_dev.parent,
+ "The timing value is out of scope of IP\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void emvsim_set_timer_counter(struct emvsim_t *emvsim)
+{
+ u32 reg;
+
+ if (emvsim->timing_data.wwt != 0 &&
+ emvsim->protocol_type == SIM_PROTOCOL_T0) {
+ emvsim->timing_data.cwt = emvsim->timing_data.wwt;
+ emvsim->timing_data.bwt = emvsim->timing_data.wwt;
+ }
+
+ if (emvsim->timing_data.bgt != 0)
+ __raw_writel(emvsim->timing_data.bgt,
+ emvsim->ioaddr + EMV_SIM_BGT_VAL);
+
+ if (emvsim->timing_data.cwt != 0)
+ __raw_writel(emvsim->timing_data.cwt,
+ emvsim->ioaddr + EMV_SIM_CWT_VAL);
+
+ if (emvsim->timing_data.bwt != 0)
+ __raw_writel(emvsim->timing_data.bwt,
+ emvsim->ioaddr + EMV_SIM_BWT_VAL);
+
+ /* 11 etu and 12 etu, T0: 12ETU; T1: 11ETU */
+ if (emvsim->protocol_type == SIM_PROTOCOL_T0) {
+ /*
+ * From EMV4.3 , T0 mode means 12 ETU. TotalETU=12+CGT.
+ * If cgt equals 0xFF, TotalETU = 12
+ */
+ reg = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ reg &= ~RCVR_11;
+ __raw_writel(reg, emvsim->ioaddr + EMV_SIM_CTRL);
+
+ /* set Transmitter Guard Time Value in ETU */
+ if (emvsim->timing_data.cgt == 0xFF)
+ __raw_writel(0, emvsim->ioaddr + EMV_SIM_TX_GETU);
+ else
+ __raw_writel(emvsim->timing_data.cgt,
+ emvsim->ioaddr + EMV_SIM_TX_GETU);
+ } else if (emvsim->protocol_type == SIM_PROTOCOL_T1) {
+ /* From EMV4.3 , T1 mode means 11 ETU. TotalETU=11+CGT */
+ reg = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ reg |= RCVR_11;
+ __raw_writel(reg, emvsim->ioaddr + EMV_SIM_CTRL);
+ __raw_writel(emvsim->timing_data.cgt,
+ emvsim->ioaddr + EMV_SIM_TX_GETU);
+ }
+}
+
+static int emvsim_xmt_start(struct emvsim_t *emvsim)
+{
+ u32 reg_val;
+
+ emvsim_set_baud_rate(emvsim);
+ if (emvsim->protocol_type == SIM_PROTOCOL_T0) {
+ emvsim_set_nack(emvsim, 1);
+ } else if (emvsim->protocol_type == SIM_PROTOCOL_T1) {
+ emvsim_set_nack(emvsim, 0);
+ } else {
+ dev_err(emvsim_dev.parent, "Invalid protocol not T0 or T1\n");
+ return -EINVAL;
+ }
+
+ emvsim_set_timer_counter(emvsim);
+
+ if (emvsim->xmt_remaining != 0) {
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_TX_THD);
+ reg_val &= ~SIM_XMT_THRESHOLD_TDT_MASK;
+ reg_val |= SIM_XMT_THRESHOLD_TDT(TX_FIFO_THRESHOLD);
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_TX_THD);
+ }
+
+ emvsim_set_bwt(emvsim, 1);
+ emvsim_set_cwt(emvsim, 0);
+
+ emvsim_set_tx(emvsim, 1);
+ emvsim_xmt_fill_fifo(emvsim);
+ emvsim_tx_irq_enable(emvsim);
+ emvsim->state = SIM_STATE_XMTING;
+
+ return 0;
+}
+
+static void emvsim_flush_fifo(struct emvsim_t *emvsim, u8 flush_tx, u8 flush_rx)
+{
+ u32 reg_val;
+
+ reg_val = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+
+ if (flush_tx)
+ reg_val |= FLSH_TX;
+ if (flush_rx)
+ reg_val |= FLSH_RX;
+ __raw_writel(reg_val, emvsim->ioaddr + EMV_SIM_CTRL);
+}
+
+static void emvsim_start_rcv(struct emvsim_t *emvsim)
+{
+ int rdt = 1;
+
+ emvsim->state = SIM_STATE_RECEIVING;
+
+ emvsim_set_rx(emvsim, 1);
+ emvsim_set_baud_rate(emvsim);
+ emvsim_set_timer_counter(emvsim);
+ emvsim_set_cwt(emvsim, 1);
+ emvsim_set_bwt(emvsim, 1);
+
+ if (emvsim->protocol_type == SIM_PROTOCOL_T0)
+ emvsim_set_nack(emvsim, 1);
+ else if (emvsim->protocol_type == SIM_PROTOCOL_T1)
+ emvsim_set_nack(emvsim, 0);
+
+ /*Set RX threshold*/
+ if (emvsim->protocol_type == SIM_PROTOCOL_T0)
+ __raw_writel(SIM_RCV_THRESHOLD_RTH(emvsim->nack_threshold) |
+ SIM_RCV_THRESHOLD_RDT(rdt),
+ emvsim->ioaddr + EMV_SIM_RX_THD);
+ else
+ __raw_writel(SIM_RCV_THRESHOLD_RDT(rdt),
+ emvsim->ioaddr + EMV_SIM_RX_THD);
+
+ /*Clear status and enable interrupt*/
+ emvsim_rx_irq_enable(emvsim);
+}
+
+static void emvsim_polling_delay(struct emvsim_t *emvsim, u32 delay)
+{
+ u32 reg_data;
+ unsigned long orig_jiffies = jiffies;
+
+ emvsim_mask_timer1_int(emvsim);
+
+ __raw_writel(delay, emvsim->ioaddr + EMV_SIM_GPCNT0_VAL);
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data &= ~GPCNT1_IM;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+
+ /* Loop for timeout, add timeout mechanism to avoid dead loop */
+ while (!(__raw_readl(emvsim->ioaddr + EMV_SIM_TX_STATUS) & GPCNT0_TO)) {
+ if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
+ dev_err(emvsim_dev.parent, "polling delay timeout\n");
+ break;
+ }
+
+ usleep_range(10, 20);
+ }
+
+ emvsim_mask_timer1_int(emvsim);
+}
+
+void emvsim_clear_rx_buf(struct emvsim_t *emvsim)
+{
+ unsigned int i;
+
+ for (i = 0; i < SIM_RCV_BUFFER_SIZE; i++)
+ emvsim->rcv_buffer[i] = 0;
+ emvsim->rcv_count = 0;
+ emvsim->rcv_head = 0;
+}
+
+static long emvsim_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret, errval = SIM_OK;
+ unsigned long timeout;
+ u32 reg_data;
+ u32 delay;
+ u32 copy_cnt, val;
+ unsigned long flags;
+ unsigned char __user *atr_buffer;
+ unsigned char __user *xmt_buffer;
+ unsigned char __user *rcv_buffer;
+
+ struct emvsim_t *emvsim = (struct emvsim_t *)file->private_data;
+
+ switch (cmd) {
+ case SIM_IOCTL_GET_ATR:
+ if (emvsim->present != SIM_PRESENT_DETECTED) {
+ dev_err(emvsim_dev.parent, "NO card ...\n");
+ errval = -SIM_E_NOCARD;
+ break;
+ }
+
+ emvsim->timeout = ATR_TIMEOUT * HZ;
+ val = 0;
+ ret = copy_to_user(&(((sim_atr_t *)arg)->size), &val,
+ sizeof((((sim_atr_t *)arg)->size)));
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &emvsim->xfer_done, emvsim->timeout);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_CTRL);
+ reg_data &= ~CWT_EN;
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_CTRL);
+
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_INT_MASK);
+ reg_data |= (GPCNT0_IM | CWT_ERR_IM);
+ __raw_writel(reg_data, emvsim->ioaddr + EMV_SIM_INT_MASK);
+
+ if (timeout == 0) {
+ dev_err(emvsim_dev.parent, "ATR timeout\n");
+ errval = -SIM_E_TIMEOUT;
+ break;
+ }
+
+ ret = copy_to_user(&(((sim_atr_t *)arg)->size),
+ &emvsim->rcv_count,
+ sizeof(emvsim->rcv_count));
+ if (ret) {
+ dev_err(emvsim_dev.parent,
+ "ATR ACCESS rcv_count Error, %d\n", ret);
+ errval = -SIM_E_ACCESS;
+ break;
+ }
+
+ __get_user(atr_buffer, &((sim_atr_t __user *)arg)->atr_buffer);
+ ret = copy_to_user(atr_buffer,
+ emvsim->rcv_buffer, emvsim->rcv_count);
+ if (ret) {
+ dev_err(emvsim_dev.parent,
+ "ATR ACCESS buffer Error %d %d\n",
+ emvsim->rcv_count, ret);
+ errval = -SIM_E_ACCESS;
+ break;
+ }
+
+ ret = copy_to_user(&(((sim_atr_t *)arg)->errval),
+ &emvsim->errval, sizeof(emvsim->errval));
+ if (ret) {
+ dev_err(emvsim_dev.parent, "ATR ACCESS Error\n");
+ errval = -SIM_E_ACCESS;
+ break;
+ }
+ emvsim->rcv_count = 0;
+ emvsim->rcv_head = 0;
+ emvsim->errval = 0;
+
+ break;
+
+ case SIM_IOCTL_DEACTIVATE:
+ emvsim_deactivate(emvsim);
+ break;
+
+ case SIM_IOCTL_COLD_RESET:
+ emvsim->present = SIM_PRESENT_REMOVED;
+ emvsim->state = SIM_STATE_REMOVED;
+ emvsim_reset_module(emvsim);
+ emvsim_data_reset(emvsim);
+ emvsim_start(emvsim);
+ emvsim_cold_reset(emvsim);
+ break;
+
+ case SIM_IOCTL_WARM_RESET:
+ emvsim_warm_reset(emvsim);
+ break;
+
+ case SIM_IOCTL_XMT:
+ ret = copy_from_user(&emvsim->xmt_remaining,
+ &(((sim_xmt_t *)arg)->xmt_length),
+ sizeof(uint32_t));
+ if (ret || emvsim->xmt_remaining > SIM_XMT_BUFFER_SIZE) {
+ dev_err(emvsim_dev.parent,
+ "copy error or to big buffer\n");
+ errval = -EINVAL;
+ break;
+ }
+
+ __get_user(xmt_buffer, &((sim_xmt_t *)arg)->xmt_buffer);
+ ret = copy_from_user(emvsim->xmt_buffer, xmt_buffer,
+ emvsim->xmt_remaining);
+ if (ret) {
+ dev_err(emvsim_dev.parent, "Copy Error\n");
+ errval = ret;
+ break;
+ }
+
+ emvsim_clear_rx_buf(emvsim);
+ emvsim_set_cwt(emvsim, 0);
+ emvsim_set_bwt(emvsim, 0);
+ /*Flush the tx rx fifo*/
+ emvsim_flush_fifo(emvsim, 1, 1);
+ emvsim->xmt_pos = 0;
+ emvsim->errval = 0;
+
+ errval = emvsim_xmt_start(emvsim);
+ if (errval)
+ break;
+
+ emvsim->timeout = TX_TIMEOUT * HZ;
+ timeout = wait_for_completion_interruptible_timeout(
+ &emvsim->xfer_done, emvsim->timeout);
+ if (timeout == 0) {
+ /*Disable the NACK interruptand TX related interrupt*/
+ emvsim_tx_irq_disable(emvsim);
+ dev_err(emvsim_dev.parent, "tx timeout\n");
+ }
+
+ if (timeout == 0 || emvsim->state == SIM_STATE_XMT_ERROR) {
+ dev_err(emvsim_dev.parent, "TX error\n");
+ /*Disable timers*/
+ emvsim_set_cwt(emvsim, 0);
+ emvsim_set_bwt(emvsim, 0);
+ /*Disable TX*/
+ emvsim_set_tx(emvsim, 0);
+ /*Flush the tx fifos*/
+ emvsim_flush_fifo(emvsim, 1, 0);
+ if (timeout == 0)
+ errval = -SIM_E_TIMEOUT;
+ else
+ errval = -SIM_E_NACK;
+
+ ret = copy_to_user(&(((sim_atr_t *)arg)->errval),
+ &emvsim->errval,
+ sizeof(emvsim->errval));
+ emvsim->errval = 0;
+ break;
+ }
+
+ /*Copy the error status to user space*/
+ ret = copy_to_user(&(((sim_atr_t *)arg)->errval),
+ &emvsim->errval, sizeof(emvsim->errval));
+ emvsim->errval = 0;
+
+ emvsim_start_rcv(emvsim);
+
+ break;
+
+ case SIM_IOCTL_RCV:
+ if (emvsim->present != SIM_PRESENT_DETECTED) {
+ errval = -SIM_E_NOCARD;
+ break;
+ }
+
+ val = 0;
+ emvsim->is_fixed_len_rec = 0;
+ ret = copy_from_user(&emvsim->expected_rcv_cnt,
+ &(((sim_rcv_t *)arg)->rcv_length),
+ sizeof(emvsim->expected_rcv_cnt));
+
+ /*Set the length to be 0 at first*/
+ ret = copy_to_user(&(((sim_rcv_t *)arg)->rcv_length), &val,
+ sizeof(val));
+
+ /*Set error value to be 0 at first*/
+ ret = copy_to_user(&(((sim_rcv_t *)arg)->errval), &val,
+ sizeof(val));
+
+ if (emvsim->expected_rcv_cnt != 0)
+ emvsim->is_fixed_len_rec = 1;
+
+ if (emvsim->is_fixed_len_rec &&
+ emvsim->rcv_count >= emvsim->expected_rcv_cnt)
+ goto copy_data;
+
+ if (emvsim->state != SIM_STATE_RECEIVING)
+ emvsim_start_rcv(emvsim);
+
+ spin_lock_irqsave(&emvsim->lock, flags);
+ spin_unlock_irqrestore(&emvsim->lock, flags);
+ emvsim->timeout = RX_TIMEOUT * HZ;
+ timeout = wait_for_completion_interruptible_timeout(
+ &emvsim->xfer_done, emvsim->timeout);
+ if (timeout == 0) {
+ dev_err(emvsim_dev.parent, "Receiving timeout\n");
+ emvsim_set_cwt(emvsim, 0);
+ emvsim_set_bwt(emvsim, 0);
+ emvsim_rx_irq_disable(emvsim);
+ errval = -SIM_E_TIMEOUT;
+ break;
+ }
+copy_data:
+ if (emvsim->is_fixed_len_rec)
+ copy_cnt = emvsim->rcv_count > emvsim->expected_rcv_cnt
+ ? emvsim->expected_rcv_cnt
+ : emvsim->rcv_count;
+ else
+ copy_cnt = emvsim->rcv_count;
+
+ ret = copy_to_user(&(((sim_rcv_t *)arg)->rcv_length),
+ &copy_cnt, sizeof(copy_cnt));
+ if (ret) {
+ dev_err(emvsim_dev.parent, "ATR ACCESS Error\n");
+ errval = -SIM_E_ACCESS;
+ break;
+ }
+
+ __get_user(rcv_buffer, &((sim_rcv_t *)arg)->rcv_buffer);
+ ret = copy_to_user(rcv_buffer,
+ &emvsim->rcv_buffer[emvsim->rcv_head],
+ copy_cnt);
+ if (ret) {
+ dev_err(emvsim_dev.parent, "ATR ACCESS Error\n");
+ errval = -SIM_E_ACCESS;
+ break;
+ }
+
+ ret = copy_to_user(&(((sim_rcv_t *)arg)->errval),
+ &emvsim->errval, sizeof(emvsim->errval));
+ if (ret) {
+ dev_err(emvsim_dev.parent, "ATR ACCESS Error\n");
+ errval = -SIM_E_ACCESS;
+ break;
+ }
+ /*Reset the receiving count and errval*/
+ spin_lock_irqsave(&emvsim->lock, flags);
+ emvsim->rcv_head += copy_cnt;
+ emvsim->rcv_count -= copy_cnt;
+ emvsim->errval = 0;
+ spin_unlock_irqrestore(&emvsim->lock, flags);
+
+ break;
+
+ case SIM_IOCTL_SET_PROTOCOL:
+ ret = copy_from_user(&emvsim->protocol_type, (int *)arg,
+ sizeof(int));
+ if (ret)
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_SET_TIMING:
+ ret = copy_from_user(&emvsim->timing_data, (sim_timing_t *)arg,
+ sizeof(sim_timing_t));
+ if (ret) {
+ dev_err(emvsim_dev.parent, "Copy Error\n");
+ errval = ret;
+ break;
+ }
+
+ ret = emvsim_check_timing_data(&emvsim->timing_data);
+ if (ret)
+ errval = ret;
+
+ break;
+
+ case SIM_IOCTL_SET_BAUD:
+ ret = copy_from_user(&emvsim->baud_rate, (sim_baud_t *)arg,
+ sizeof(sim_baud_t));
+ if (ret) {
+ dev_err(emvsim_dev.parent, "Copy Error\n");
+ errval = ret;
+ break;
+ }
+
+ emvsim_check_baud_rate(&emvsim->baud_rate);
+
+ break;
+ case SIM_IOCTL_WAIT:
+ ret = copy_from_user(&delay, (unsigned int *)arg,
+ sizeof(unsigned int));
+ if (ret) {
+ dev_err(emvsim_dev.parent, "\nWait Copy Error\n");
+ errval = ret;
+ break;
+ }
+
+ emvsim_polling_delay(emvsim, delay);
+ break;
+
+ case SIM_IOCTL_GET_PRESENSE:
+ if (put_user(emvsim->present, (int *)arg))
+ errval = -SIM_E_ACCESS;
+ break;
+
+ case SIM_IOCTL_CARD_LOCK:
+ errval = emvsim_card_lock(emvsim);
+ break;
+
+ case SIM_IOCTL_CARD_EJECT:
+ errval = emvsim_card_eject(emvsim);
+ break;
+ };
+
+ return errval;
+};
+
+static int emvsim_open(struct inode *inode, struct file *file)
+{
+ int errval = SIM_OK;
+ struct emvsim_t *emvsim = dev_get_drvdata(emvsim_dev.parent);
+
+ file->private_data = emvsim;
+ spin_lock_init(&emvsim->lock);
+
+ if (!emvsim->ioaddr) {
+ errval = -ENOMEM;
+ return errval;
+ }
+
+ if (!emvsim->open_cnt) {
+ clk_prepare_enable(emvsim->ipg);
+ clk_prepare_enable(emvsim->clk);
+ }
+
+ emvsim->open_cnt = 1;
+ init_completion(&emvsim->xfer_done);
+ errval = emvsim_reset_module(emvsim);
+ emvsim_data_reset(emvsim);
+
+ return errval;
+};
+
+static int emvsim_release(struct inode *inode, struct file *file)
+{
+ u32 reg_data;
+ struct emvsim_t *emvsim = (struct emvsim_t *)file->private_data;
+
+ /* disable presense detection interrupt */
+ reg_data = __raw_readl(emvsim->ioaddr + EMV_SIM_PCSR);
+ __raw_writel(reg_data | SPDIM, emvsim->ioaddr + EMV_SIM_PCSR);
+
+ if (emvsim->present != SIM_PRESENT_REMOVED)
+ emvsim_deactivate(emvsim);
+
+ if (emvsim->open_cnt) {
+ clk_disable_unprepare(emvsim->clk);
+ clk_disable_unprepare(emvsim->ipg);
+ }
+
+ emvsim->open_cnt = 0;
+
+ return 0;
+};
+
+static const struct file_operations emvsim_fops = {
+ .owner = THIS_MODULE,
+ .open = emvsim_open,
+ .release = emvsim_release,
+ .unlocked_ioctl = emvsim_ioctl,
+};
+
+static struct miscdevice emvsim_dev = {
+ MISC_DYNAMIC_MINOR,
+ "mxc_sim",
+ &emvsim_fops
+};
+
+static const struct of_device_id emvsim_imx_dt_ids[] = {
+ { .compatible = "fsl,imx8-emvsim" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, emvsim_imx_dt_ids);
+
+static int emvsim_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ const struct of_device_id *of_id;
+ struct emvsim_t *emvsim = NULL;
+
+ emvsim = devm_kzalloc(&pdev->dev, sizeof(struct emvsim_t),
+ GFP_KERNEL);
+ if (!emvsim)
+ return -ENOMEM;
+
+ of_id = of_match_device(emvsim_imx_dt_ids, &pdev->dev);
+ if (of_id)
+ pdev->id_entry = of_id->data;
+ else
+ return -EINVAL;
+
+ emvsim->clk_rate = FCLK_FREQ;
+ emvsim->open_cnt = 0;
+
+ emvsim->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!emvsim->res) {
+ dev_err(emvsim_dev.parent, "Can't get the MEMORY\n");
+ return -ENOMEM;
+ }
+ emvsim->ioaddr = devm_ioremap_resource(&pdev->dev, emvsim->res);
+ if (IS_ERR(emvsim->ioaddr)) {
+ dev_err(&pdev->dev,
+ "failed to get ioremap base\n");
+ ret = PTR_ERR(emvsim->ioaddr);
+ return ret;
+ }
+
+ /* request the emvsim per clk and ipg clk */
+ emvsim->clk = devm_clk_get(&pdev->dev, "sim");
+ if (IS_ERR(emvsim->clk)) {
+ ret = PTR_ERR(emvsim->clk);
+ dev_err(emvsim_dev.parent, "Get PER CLK ERROR !\n");
+ return ret;
+ }
+
+ emvsim->ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(emvsim->ipg)) {
+ ret = PTR_ERR(emvsim->ipg);
+ dev_err(emvsim_dev.parent, "Get IPG CLK ERROR !\n");
+ return ret;
+ }
+
+ emvsim->irq = platform_get_irq(pdev, 0);
+ if (emvsim->irq < 0) {
+ dev_err(&pdev->dev, "No irq line provided\n");
+ return -ENOENT;
+ }
+
+ if (devm_request_irq(&pdev->dev, emvsim->irq, emvsim_irq_handler,
+ 0, "mxc_emvsim_irq", emvsim)) {
+ dev_err(&pdev->dev, "can't claim irq %d\n", emvsim->irq);
+ return -ENOENT;
+ }
+
+ platform_set_drvdata(pdev, emvsim);
+ emvsim_dev.parent = &pdev->dev;
+
+ ret = misc_register(&emvsim_dev);
+ dev_info(&pdev->dev, "emvsim register %s\n", ret ? "fail" : "success");
+
+ return ret;
+}
+
+static int emvsim_remove(struct platform_device *pdev)
+{
+ struct emvsim_t *emvsim = platform_get_drvdata(pdev);
+
+ if (emvsim->open_cnt) {
+ clk_disable_unprepare(emvsim->clk);
+ clk_disable_unprepare(emvsim->ipg);
+ }
+
+ misc_deregister(&emvsim_dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int emvsim_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct emvsim_t *emvsim = platform_get_drvdata(pdev);
+
+ if (emvsim->open_cnt) {
+ clk_disable_unprepare(emvsim->clk);
+ clk_disable_unprepare(emvsim->ipg);
+ }
+
+ pinctrl_pm_select_sleep_state(&pdev->dev);
+
+ return 0;
+}
+
+static int emvsim_resume(struct platform_device *pdev)
+{
+ struct emvsim_t *emvsim = platform_get_drvdata(pdev);
+
+ if (!emvsim->open_cnt) {
+ clk_prepare_enable(emvsim->ipg);
+ clk_prepare_enable(emvsim->clk);
+ }
+
+ pinctrl_pm_select_default_state(&pdev->dev);
+
+ return 0;
+}
+
+#else
+#define emvsim_suspend NULL
+#define emvsim_resume NULL
+#endif
+
+static struct platform_driver emvsim_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = emvsim_imx_dt_ids,
+ },
+ .probe = emvsim_probe,
+ .remove = emvsim_remove,
+ .suspend = emvsim_suspend,
+ .resume = emvsim_resume,
+};
+
+static int __init emvsim_drv_init(void)
+{
+ return platform_driver_register(&emvsim_driver);
+}
+
+static void __exit emvsim_drv_exit(void)
+{
+ platform_driver_unregister(&emvsim_driver);
+}
+
+module_init(emvsim_drv_init);
+module_exit(emvsim_drv_exit);
+
+MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
+MODULE_DESCRIPTION("NXP EMVSIM Driver");
+MODULE_LICENSE("GPL");