From fb80f7afe2728a2a18210401101f9e9c752fc1df Mon Sep 17 00:00:00 2001 From: Luwei Zhou Date: Wed, 8 Apr 2015 14:05:52 +0800 Subject: [PATCH] MLK-10600-1: mxc: sim: Add SIM IP driver support on the i.MX7d-12x12-arm2 platform. This driver is based on the current code which runs the the EMV test on the i.MX258 platform. Since there are still many cases that can't pass on the i.MX258 and i.MX7d platform. The driver will need to be improved after per-test work. Just check in as a base code. There would be definitly some timing improvement work to do in the future. Signed-off-by: Luwei Zhou (cherry picked from 3ac1ad5b2a68ecb052ccacca4ac7459ead04415e) --- drivers/mxc/sim/imx_sim.c | 1678 +++++++++++++++++++++++++++++ include/linux/mxc_sim_interface.h | 124 +++ 2 files changed, 1802 insertions(+) create mode 100755 drivers/mxc/sim/imx_sim.c create mode 100644 include/linux/mxc_sim_interface.h diff --git a/drivers/mxc/sim/imx_sim.c b/drivers/mxc/sim/imx_sim.c new file mode 100755 index 000000000000..4784efdd2e00 --- /dev/null +++ b/drivers/mxc/sim/imx_sim.c @@ -0,0 +1,1678 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRIVER_NAME "mxc_sim" +#define SIM_INTERNAL_CLK (0) +#define SIM_RFU (-1) + +/* Transmit and receive buffer sizes */ +#define SIM_XMT_BUFFER_SIZE (300) +#define SIM_RCV_BUFFER_SIZE (400) + +#define SIM_TX_FIFO_DEPTH (16) +#define SIM_RX_FIFO_DEPTH (285) + +#define TX_FIFO_THRESHOLD (0x04) +#define RX_FIFO_THRESHOLD (250) + +/* Interface character references */ +#define SIM_IFC_TXI(letter, number) (letter + number * 4) +#define SIM_IFC_TA1 SIM_IFC_TXI(0, 0) +#define SIM_IFC_TB1 SIM_IFC_TXI(0, 1) +#define SIM_IFC_TC1 SIM_IFC_TXI(0, 2) +#define SIM_IFC_TD1 SIM_IFC_TXI(0, 3) +#define SIM_IFC_TA2 SIM_IFC_TXI(1, 0) +#define SIM_IFC_TB2 SIM_IFC_TXI(1, 1) +#define SIM_IFC_TC2 SIM_IFC_TXI(1, 2) +#define SIM_IFC_TD2 SIM_IFC_TXI(1, 3) +#define SIM_IFC_TA3 SIM_IFC_TXI(2, 0) +#define SIM_IFC_TB3 SIM_IFC_TXI(2, 1) +#define SIM_IFC_TC3 SIM_IFC_TXI(2, 2) +#define SIM_IFC_TD3 SIM_IFC_TXI(2, 3) +#define SIM_IFC_TA4 SIM_IFC_TXI(3, 0) +#define SIM_IFC_TB4 SIM_IFC_TXI(3, 1) +#define SIM_IFC_TC4 SIM_IFC_TXI(3, 2) +#define SIM_IFC_TD4 SIM_IFC_TXI(3, 3) + +/* ATR and OPS states */ +#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) + +/* Definitions of the offset of the SIM hardware registers */ +#define PORT1_CNTL (0x00) +#define SETUP (0x04) +#define PORT1_DETECT (0x08) +#define PORT1_XMT_BUF (0x0C) +#define PORT1_RCV_BUF (0x10) +#define PORT0_CNTL (0x14) +#define CNTL (0x18) +#define CLK_PRESCALER (0x1C) +#define RCV_THRESHOLD (0x20) +#define ENABLE (0x24) +#define XMT_STATUS (0x28) +#define RCV_STATUS (0x2C) +#define INT_MASK (0x30) +#define PORTO_XMT_BUF (0x34) +#define PORT0_RCV_BUF (0x38) +#define PORT0_DETECT (0x3C) +#define DATA_FORMAT (0x40) +#define XMT_THRESHOLD (0x44) +#define GUARD_CNTL (0x48) +#define OD_CONFIG (0x4C) +#define RESET_CNTL (0x50) +#define CHAR_WAIT (0x54) +#define GPCNT (0x58) +#define DIVISOR (0x5C) +#define BWT (0x60) +#define BGT (0x64) +#define BWT_H (0x68) +#define XMT_FIFO_STAT (0x6C) +#define RCV_FIFO_CNT (0x70) +#define RCV_FIFO_WPTR (0x74) +#define RCV_FIFO_RPTR (0x78) + +/* SIM port[0|1]_cntl register bits */ +#define SIM_PORT_CNTL_SFPD (1 << 7) +#define SIM_PORT_CNTL_3VOLT (1 << 6) +#define SIM_PORT_CNTL_SCSP (1 << 5) +#define SIM_PORT_CNTL_SCEN (1 << 4) +#define SIM_PORT_CNTL_SRST (1 << 3) +#define SIM_PORT_CNTL_STEN (1 << 2) +#define SIM_PORT_CNTL_SVEN (1 << 1) +#define SIM_PORT_CNTL_SAPD (1 << 0) + +/* SIM od_config register bits */ +#define SIM_OD_CONFIG_OD_P1 (1 << 1) +#define SIM_OD_CONFIG_OD_P0 (1 << 0) + +/* SIM enable register bits */ +#define SIM_ENABLE_XMTEN (1 << 1) +#define SIM_ENABLE_RCVEN (1 << 0) + +/* SIM int_mask register bits */ +#define SIM_INT_MASK_RFEM (1 << 13) +#define SIM_INT_MASK_BGTM (1 << 12) +#define SIM_INT_MASK_BWTM (1 << 11) +#define SIM_INT_MASK_RTM (1 << 10) +#define SIM_INT_MASK_CWTM (1 << 9) +#define SIM_INT_MASK_GPCM (1 << 8) +#define SIM_INT_MASK_TDTFM (1 << 7) +#define SIM_INT_MASK_TFOM (1 << 6) +#define SIM_INT_MASK_XTM (1 << 5) +#define SIM_INT_MASK_TFEIM (1 << 4) +#define SIM_INT_MASK_ETCIM (1 << 3) +#define SIM_INT_MASK_OIM (1 << 2) +#define SIM_INT_MASK_TCIM (1 << 1) +#define SIM_INT_MASK_RIM (1 << 0) + +/* SIM xmt_status register bits */ +#define SIM_XMT_STATUS_GPCNT (1 << 8) +#define SIM_XMT_STATUS_TDTF (1 << 7) +#define SIM_XMT_STATUS_TFO (1 << 6) +#define SIM_XMT_STATUS_TC (1 << 5) +#define SIM_XMT_STATUS_ETC (1 << 4) +#define SIM_XMT_STATUS_TFE (1 << 3) +#define SIM_XMT_STATUS_XTE (1 << 0) + +/* SIM rcv_status register bits */ +#define SIM_RCV_STATUS_BGT (1 << 11) +#define SIM_RCV_STATUS_BWT (1 << 10) +#define SIM_RCV_STATUS_RTE (1 << 9) +#define SIM_RCV_STATUS_CWT (1 << 8) +#define SIM_RCV_STATUS_CRCOK (1 << 7) +#define SIM_RCV_STATUS_LRCOK (1 << 6) +#define SIM_RCV_STATUS_RDRF (1 << 5) +#define SIM_RCV_STATUS_RFD (1 << 4) +#define SIM_RCV_STATUS_RFE (1 << 1) +#define SIM_RCV_STATUS_OEF (1 << 0) + +/* SIM cntl register bits */ +#define SIM_CNTL_BWTEN (1 << 15) +#define SIM_CNTL_XMT_CRC_LRC (1 << 14) +#define SIM_CNTL_CRCEN (1 << 13) +#define SIM_CNTL_LRCEN (1 << 12) +#define SIM_CNTL_CWTEN (1 << 11) +#define SIM_CNTL_SAMPLE12 (1 << 4) +#define SIM_CNTL_ONACK (1 << 3) +#define SIM_CNTL_ANACK (1 << 2) +#define SIM_CNTL_ICM (1 << 1) +#define SIM_CNTL_GPCNT_CLK_SEL(x) ((x&0x03) << 9) +#define SIM_CNTL_GPCNT_CLK_SEL_MASK (0x03 << 9) +#define SIM_CNTL_BAUD_SEL(x) ((x&0x07) << 6) +#define SIM_CNTL_BAUD_SEL_MASK (0x07 << 6) + +/* SIM rcv_threshold register bits */ +#define SIM_RCV_THRESHOLD_RTH(x) ((x&0x0f) << 9) +#define SIM_RCV_THRESHOLD_RTH_MASK (0x0f << 9) +#define SIM_RCV_THRESHOLD_RDT(x) ((x&0x1ff) << 0) +#define SIM_RCV_THRESHOLD_RDT_MASK (0x1ff << 0) + +/* SIM xmt_threshold register bits */ +#define SIM_XMT_THRESHOLD_XTH(x) ((x&0x0f) << 4) +#define SIM_XMT_THRESHOLD_XTH_MASK (0x0f << 4) +#define SIM_XMT_THRESHOLD_TDT(x) ((x&0x0f) << 0) +#define SIM_XMT_THRESHOLD_TDT_MASK (0x0f << 0) + +/* SIM guard_cntl register bits */ +#define SIM_GUARD_CNTL_RCVR11 (1 << 8) +#define SIM_GIARD_CNTL_GETU(x) (x&0xff) +#define SIM_GIARD_CNTL_GETU_MASK (0xff) + +/* SIM port[0|]_detect register bits */ +#define SIM_PORT_DETECT_SPDS (1 << 3) +#define SIM_PORT_DETECT_SPDP (1 << 2) +#define SIM_PORT_DETECT_SDI (1 << 1) +#define SIM_PORT_DETECT_SDIM (1 << 0) + +/* SIM RESET_CNTL register bits*/ +#define SIM_RESET_CNTL_FLUSH_RCV (1 << 0) +#define SIM_RESET_CNTL_FLUSH_XMT (1 << 1) +#define SIM_RESET_CNTL_SOFT_RESET (1 << 2) +#define SIM_RESET_CNTL_KILL_CLOCK (1 << 3) +#define SIM_RESET_CNTL_DOZE (1 << 4) +#define SIM_RESET_CNTL_STOP (1 << 5) +#define SIM_RESET_CNTL_DEBUG (1 << 6) + +/*SIM receive buffer register error status*/ +#define SIM_REC_CWT_ERROR (1 << 10) +#define SIM_REC_FRAME_ERROR (1 << 9) +#define SIM_REC_PARITY_ERROR (1 << 8) + +#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 (5000000) +#define SIM_BLOCK_CLK_FREQ (60000000) + +#define ATR_TIMEOUT (5) +#define TX_TIMEOUT (10) +#define RX_TIMEOUT (100) +#define RESET_RETRY_TIMES (5) + +/* Main SIM driver structure */ +struct sim_t{ + s32 present; + u8 open_cnt; + int state; + struct clk *clk; + struct resource *res; + void __iomem *ioaddr; + int ipb_irq; + int dat_irq; + + /* error code occured during transfer */ + 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; + + /* remaining bytes to transmit for the current transfer */ + u32 xmt_remaining; + /* transmit position */ + u32 xmt_pos; + /* receive position / number of bytes received */ + u32 rcv_count; + u8 rcv_buffer[SIM_RCV_BUFFER_SIZE]; + u8 xmt_buffer[SIM_XMT_BUFFER_SIZE]; + /* transfer completion notifier */ + struct completion xfer_done; + /* async notifier for card and ATR detection */ + struct fasync_struct *fasync; + /* Platform specific data */ + struct mxc_sim_platform_data *plat_data; + bool last_is_tx; + u16 rcv_head; + spinlock_t lock; + u32 clk_rate; +}; + +static struct miscdevice sim_dev; + +static void sim_data_reset(struct sim_t *sim) +{ + sim->errval = SIM_OK; + sim->protocol_type = 0; + sim->timeout = 0; + sim->nack_threshold = SIM_EMV_NACK_THRESHOLD; + sim->nack_enable = 0; + memset(&sim->timing_data, 0, sizeof(sim->timing_data)); + memset(&sim->baud_rate, 0, sizeof(sim->baud_rate)); + + sim->xmt_remaining = 0; + sim->xmt_pos = 0; + sim->rcv_count = 0; + sim->rcv_head = 0; + sim->last_is_tx = false; + memset(sim->rcv_buffer, 0, SIM_RCV_BUFFER_SIZE); + memset(sim->xmt_buffer, 0, SIM_XMT_BUFFER_SIZE); + + init_completion(&sim->xfer_done); +}; + +static void sim_set_nack(struct sim_t *sim, u8 enable) +{ + u32 reg_val; + + reg_val = __raw_readl(sim->ioaddr + CNTL); + /*Disable overrun NACK setting for now*/ + reg_val &= ~(SIM_CNTL_ONACK); + + if (enable) { + reg_val |= SIM_CNTL_ANACK; + __raw_writel(reg_val, sim->ioaddr + CNTL); + reg_val = __raw_readl(sim->ioaddr + XMT_THRESHOLD); + reg_val &= ~(SIM_XMT_THRESHOLD_XTH_MASK); + reg_val |= SIM_XMT_THRESHOLD_XTH(sim->nack_threshold); + __raw_writel(reg_val, sim->ioaddr + XMT_THRESHOLD); + } else { + reg_val &= ~SIM_CNTL_ANACK; + __raw_writel(reg_val, sim->ioaddr + CNTL); + } + + sim->nack_enable = enable; +} + +static void sim_set_tx(struct sim_t *sim, u8 enable) +{ + u32 reg_data; + + reg_data = __raw_readl(sim->ioaddr + ENABLE); + if (enable) + reg_data |= SIM_ENABLE_XMTEN; + else + reg_data &= ~SIM_ENABLE_XMTEN; + + __raw_writel(reg_data, sim->ioaddr + ENABLE); +} + +static void sim_set_rx(struct sim_t *sim, u8 enable) +{ + u32 reg_data; + reg_data = __raw_readl(sim->ioaddr + ENABLE); + if (enable) + reg_data |= SIM_ENABLE_RCVEN; + else + reg_data &= ~SIM_ENABLE_RCVEN; + + __raw_writel(reg_data, sim->ioaddr + ENABLE); +} + +static void sim_set_waiting_timers(struct sim_t *sim, u8 enable) +{ + u32 reg_val; + reg_val = __raw_readl(sim->ioaddr + CNTL); + if (enable) { + if (sim->timing_data.cwt) + reg_val |= (SIM_CNTL_CWTEN); + if (sim->timing_data.bwt || sim->timing_data.bgt) + reg_val |= (SIM_CNTL_BWTEN); + } else { + reg_val &= ~(SIM_CNTL_CWTEN | SIM_CNTL_BWTEN); + } + __raw_writel(reg_val, sim->ioaddr + CNTL); +} + +static int sim_reset_module(struct sim_t *sim) +{ + u32 reg_val; + s8 timeout = RESET_RETRY_TIMES; + + reg_val = __raw_readl(sim->ioaddr + RESET_CNTL); + reg_val |= (SIM_RESET_CNTL_SOFT_RESET); + __raw_writel(reg_val, sim->ioaddr + RESET_CNTL); + + while (__raw_readl(sim->ioaddr + RESET_CNTL) & SIM_RESET_CNTL_SOFT_RESET) { + usleep_range(1, 3); + if (timeout-- <= 0) { + pr_err("SIM module reset timeout\n"); + return -EINVAL; + } + } + return 0; +} + +static void sim_receive_atr_set(struct sim_t *sim) +{ + u32 reg_data; + + /*Enable RX*/ + __raw_writel(SIM_ENABLE_RCVEN, sim->ioaddr + ENABLE); + + /*Receive fifo threshold = 1 to trigger GPC timer in irq handler*/ + reg_data = SIM_RCV_THRESHOLD_RTH(0) | SIM_RCV_THRESHOLD_RDT(1); + __raw_writel(reg_data, sim->ioaddr + RCV_THRESHOLD); + + /* Clear the interrupt status*/ + reg_data = __raw_readl(sim->ioaddr + XMT_STATUS); + reg_data |= SIM_XMT_STATUS_GPCNT; + __raw_writel(reg_data, sim->ioaddr + XMT_STATUS); + + reg_data = __raw_readl(sim->ioaddr + RCV_STATUS); + reg_data |= (SIM_RCV_STATUS_CWT | SIM_RCV_STATUS_RDRF); + __raw_writel(reg_data, sim->ioaddr + RCV_STATUS); + + /*Set the cwt timer.Refer the setting of ATR on EMV4.3 book*/ + __raw_writel(ATR_MAX_CWT, sim->ioaddr + CHAR_WAIT); + + /*Set the baud rate to be 1/372. Refer the setting of ATR on EMV4.3 book*/ + reg_data = __raw_readl(sim->ioaddr + CNTL); + reg_data &= ~SIM_CNTL_BAUD_SEL_MASK; + reg_data |= SIM_CNTL_BAUD_SEL(0); + + /* + *Set the GPT timer disabled. + *KILL_CLOCK is reset to 0 by default, ANACK is disabled by default. + */ + reg_data &= ~SIM_CNTL_GPCNT_CLK_SEL_MASK; + reg_data |= (SIM_CNTL_GPCNT_CLK_SEL(0) | SIM_CNTL_CWTEN); + + /*Enable ICM mode*/ + reg_data |= SIM_CNTL_ICM; + + /*Enable Sample12*/ + reg_data |= SIM_CNTL_SAMPLE12; + __raw_writel(reg_data, sim->ioaddr + CNTL); + + /*Disable NACK*/ + sim_set_nack(sim, 0); + + /*Set 12 ETUS*/ + __raw_writel(0, sim->ioaddr + GUARD_CNTL); + + sim->errval = 0; + sim->rcv_count = 0; + sim->state = SIM_STATE_ATR_RECEIVING; + + /*Enable the Rx threshold interrupt and cwt interrupt,disalbe the GPC interrupt*/ + reg_data = __raw_readl(sim->ioaddr + INT_MASK); + reg_data &= ~(SIM_INT_MASK_CWTM | SIM_INT_MASK_RIM); + reg_data |= SIM_INT_MASK_GPCM; + __raw_writel(reg_data, sim->ioaddr + INT_MASK); +} + +static int32_t sim_check_rec_data(u32 *reg_data) +{ + s32 err = 0; + + if (*reg_data & SIM_REC_CWT_ERROR) + err |= SIM_ERROR_CWT; + + if (*reg_data & SIM_REC_FRAME_ERROR) + err |= SIM_ERROR_FRAME; + + if (*reg_data & SIM_REC_PARITY_ERROR) + err |= SIM_ERROR_PARITY; + + return err; +} + +static void sim_xmt_fill_fifo(struct sim_t *sim) +{ + u32 reg_data; + u32 bytesleft, i; + + reg_data = __raw_readl(sim->ioaddr + XMT_FIFO_STAT); + bytesleft = SIM_TX_FIFO_DEPTH - ((reg_data >> 8) & 0x0F); + + if (bytesleft > sim->xmt_remaining) + bytesleft = sim->xmt_remaining; + + for (i = 0; i < bytesleft; i++) { + __raw_writel(sim->xmt_buffer[sim->xmt_pos], + sim->ioaddr + PORT1_XMT_BUF); + sim->xmt_pos++; + }; + sim->xmt_remaining -= bytesleft; +}; + +static void sim_rcv_read_fifo(struct sim_t *sim) +{ + u16 i, count; + u32 reg_data; + + count = __raw_readl(sim->ioaddr + RCV_FIFO_CNT); + + spin_lock(&sim->lock); + for (i = 0; i < count; i++) { + reg_data = __raw_readl(sim->ioaddr + PORT1_RCV_BUF); + sim->errval |= sim_check_rec_data(®_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 (!(sim->nack_enable && (reg_data & SIM_REC_PARITY_ERROR))) { + sim->rcv_buffer[sim->rcv_head + sim->rcv_count] = (u8)reg_data; + sim->rcv_count++; + } + if (sim->rcv_head + sim->rcv_count >= SIM_RCV_BUFFER_SIZE) { + pr_err("The software fifo is full,head %d, cnt%d\n", sim->rcv_head, sim->rcv_count); + break; + } + } + spin_unlock(&sim->lock); +} + +static void sim_tx_irq_enable(struct sim_t *sim) +{ + u32 reg_val; + /*Clear the status and enable the related interrupt*/ + reg_val = __raw_readl(sim->ioaddr + XMT_STATUS); + __raw_writel(reg_val, sim->ioaddr + XMT_STATUS); + reg_val = __raw_readl(sim->ioaddr + RCV_STATUS); + __raw_writel(reg_val, sim->ioaddr + RCV_STATUS); + + reg_val = __raw_readl(sim->ioaddr + INT_MASK); + /* + *Disable CWT , BWT interrupt when transmitting, it would + *be enabled when rx is enabled just after tx completes + *The timer will be enabled. + */ + reg_val |= SIM_INT_MASK_CWTM | SIM_INT_MASK_BWTM; + reg_val |= SIM_INT_MASK_RIM | SIM_INT_MASK_RTM; + + if (sim->xmt_remaining != 0) + reg_val &= ~SIM_INT_MASK_TDTFM; + else{ + reg_val &= ~SIM_INT_MASK_TCIM; + /*Enable transmit early complete interrupt.*/ + reg_val &= ~SIM_INT_MASK_ETCIM; + } + + /*NACK interrupt is enabled only when T0 mode*/ + if (sim->protocol_type == SIM_PROTOCOL_T0 || sim->nack_enable != 0) + reg_val &= ~SIM_INT_MASK_XTM; + else + reg_val |= SIM_INT_MASK_XTM; + __raw_writel(reg_val, sim->ioaddr + INT_MASK); +} + +static void sim_tx_irq_disable(struct sim_t *sim) +{ + u32 reg_val; + /*Disable the NACK interruptand TX related interrupt*/ + reg_val = __raw_readl(sim->ioaddr + INT_MASK); + reg_val |= (SIM_INT_MASK_TDTFM | SIM_INT_MASK_TCIM | SIM_INT_MASK_XTM | SIM_INT_MASK_ETCIM); + __raw_writel(reg_val, sim->ioaddr + INT_MASK); +} + +static void sim_rx_irq_enable(struct sim_t *sim) +{ + u32 reg_data; + /*Clear status and enable interrupt + *It is suggested by Tengda from IC team. TX may have CWT status so clear it + */ + if (sim->last_is_tx) + __raw_writel(SIM_RCV_STATUS_CWT, sim->ioaddr + RCV_STATUS); + + reg_data = __raw_readl(sim->ioaddr + INT_MASK); + reg_data |= (SIM_INT_MASK_TCIM | SIM_INT_MASK_TDTFM | SIM_INT_MASK_XTM); + reg_data &= ~(SIM_INT_MASK_RIM | SIM_INT_MASK_CWTM | SIM_INT_MASK_BWTM); + + if (sim->protocol_type == SIM_PROTOCOL_T0 || sim->nack_enable != 0) + reg_data &= ~SIM_INT_MASK_RTM; + else + reg_data |= SIM_INT_MASK_RTM; + + __raw_writel(reg_data, sim->ioaddr + INT_MASK); +} + +static void sim_rx_irq_disable(struct sim_t *sim) +{ + u32 reg_val; + reg_val = __raw_readl(sim->ioaddr + INT_MASK); + reg_val |= (SIM_INT_MASK_RIM | SIM_INT_MASK_CWTM | SIM_INT_MASK_BWTM | SIM_INT_MASK_RTM); + __raw_writel(reg_val, sim->ioaddr + INT_MASK); +} + +static irqreturn_t sim_irq_handler(int irq, void *dev_id) +{ + u32 reg_data, tx_status, rx_status; + + struct sim_t *sim = (struct sim_t *) dev_id; + + tx_status = __raw_readl(sim->ioaddr + XMT_STATUS); + rx_status = __raw_readl(sim->ioaddr + RCV_STATUS); + __raw_writel(tx_status, sim->ioaddr + XMT_STATUS); + __raw_writel(rx_status, sim->ioaddr + RCV_STATUS); + + if (sim->state == SIM_STATE_ATR_RECEIVING) { + if ((rx_status & SIM_RCV_STATUS_RDRF) && + (__raw_readl(sim->ioaddr + RCV_THRESHOLD) == 0x01)) { + + /*Enable GPC interrupt and disable the rx full interrupt*/ + reg_data = __raw_readl(sim->ioaddr + INT_MASK); + reg_data &= ~(SIM_INT_MASK_GPCM); + reg_data |= SIM_INT_MASK_RIM; + __raw_writel(reg_data, sim->ioaddr + INT_MASK); + sim_rcv_read_fifo(sim); + + /*ATR each recieved byte will cost 12 ETU, so get the remaining etus*/ + reg_data = ATR_MAX_DURATION - sim->rcv_count * 12; + __raw_writel(reg_data, sim->ioaddr + GPCNT); + + reg_data = __raw_readl(sim->ioaddr + CNTL); + reg_data &= ~SIM_CNTL_GPCNT_CLK_SEL_MASK; + reg_data |= SIM_CNTL_GPCNT_CLK_SEL(3); + __raw_writel(reg_data, sim->ioaddr + CNTL); + + /*Receive fifo threshold set to max value*/ + reg_data = SIM_RCV_THRESHOLD_RTH(0) | SIM_RCV_THRESHOLD_RDT(ATR_THRESHOLD_MAX); + __raw_writel(reg_data, sim->ioaddr + RCV_THRESHOLD); + } + if ((rx_status & SIM_RCV_STATUS_CWT) || + (tx_status & SIM_XMT_STATUS_GPCNT)) { + + /*Disable the GPCNT timer and CWT timer right now*/ + reg_data = __raw_readl(sim->ioaddr + CNTL); + reg_data &= ~(SIM_CNTL_GPCNT_CLK_SEL_MASK | SIM_CNTL_CWTEN); + __raw_writel(reg_data, sim->ioaddr + CNTL); + + reg_data = __raw_readl(sim->ioaddr + INT_MASK); + reg_data |= (SIM_INT_MASK_GPCM | SIM_INT_MASK_CWTM); + __raw_writel(reg_data, sim->ioaddr + INT_MASK); + + if (tx_status & SIM_XMT_STATUS_GPCNT) + sim->errval |= SIM_ERROR_ATR_TIMEROUT; + + if (rx_status & SIM_RCV_STATUS_CWT) + sim->errval |= SIM_ERROR_CWT; + + sim_rcv_read_fifo(sim); + sim->state = SIM_STATE_ATR_RECEIVED; + + complete(&sim->xfer_done); + } + } + + else if (sim->state == SIM_STATE_XMTING) { + /*The CWT BWT expire should not happen when in the transmitting state*/ + if (tx_status & SIM_XMT_STATUS_ETC) { + /*Once the transmit frame is completed, need to enable RX immedially*/ + sim_set_rx(sim, 1); + } + if (tx_status & SIM_XMT_STATUS_XTE) { + /*Disable TX*/ + sim_set_tx(sim, 0); + /*Disalbe the timers*/ + sim_set_waiting_timers(sim, 0); + /*Disable the NACK interruptand TX related interrupt*/ + sim_tx_irq_disable(sim); + + /*Update the state and status*/ + sim->errval |= SIM_ERROR_NACK_THRESHOLD; + sim->state = SIM_STATE_XMT_ERROR; + + complete(&sim->xfer_done); + } else if (tx_status & SIM_XMT_STATUS_TDTF && sim->xmt_remaining != 0) { + sim_xmt_fill_fifo(sim); + if (sim->xmt_remaining == 0) { + /*Disable TX threshold interrupt and enable tx complete interrupt*/ + reg_data = __raw_readl(sim->ioaddr + INT_MASK); + reg_data |= SIM_INT_MASK_TDTFM; + /*Enable transmit complete and early transmit complete interrupt*/ + reg_data &= ~(SIM_INT_MASK_TCIM | SIM_INT_MASK_ETCIM); + __raw_writel(reg_data, sim->ioaddr + INT_MASK); + } + } else if (tx_status & SIM_XMT_STATUS_TC && sim->xmt_remaining == 0) { + /*Disable the NACK interruptand TX related interrupt*/ + sim_tx_irq_disable(sim); + sim_set_rx(sim, 1); + /*Update the state and status*/ + sim->state = SIM_STATE_XMT_DONE; + complete(&sim->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 (sim->state == SIM_STATE_RECEIVING) { + if (rx_status & SIM_RCV_STATUS_RTE) { + /*Disable RX*/ + sim_set_rx(sim, 0); + /*Disable the BWT timer and CWT timer right now*/ + sim_set_waiting_timers(sim, 0); + /*Disable the interrupt right now*/ + sim_rx_irq_disable(sim); + /*Should we read the fifo or just flush the fifo?*/ + sim_rcv_read_fifo(sim); + sim->errval = SIM_ERROR_NACK_THRESHOLD; + sim->state = SIM_STATE_RECEIVE_ERROR; + complete(&sim->xfer_done); + } + + if (rx_status & SIM_RCV_STATUS_RDRF) { + sim_rcv_read_fifo(sim); + if (sim->is_fixed_len_rec && + sim->rcv_count >= sim->expected_rcv_cnt) { + + /*Disable the BWT timer and CWT timer right now*/ + sim_rx_irq_disable(sim); + /*Add the state judgement to ensure the maybe complete has been impletment in the above "if" case*/ + if (sim->state == SIM_STATE_RECEIVING) { + sim->state = SIM_STATE_RECEIVE_DONE; + complete(&sim->xfer_done); + } + } + } + + if ((rx_status & SIM_RCV_STATUS_CWT) || + (rx_status & SIM_RCV_STATUS_BWT) || + (rx_status & SIM_RCV_STATUS_BGT)) { + + /*Disable the BWT timer and CWT timer right now*/ + sim_set_waiting_timers(sim, 0); + sim_rx_irq_disable(sim); + + if (rx_status & SIM_RCV_STATUS_BWT) { + sim->errval |= SIM_ERROR_BWT; + } + if (rx_status & SIM_RCV_STATUS_CWT) + sim->errval |= SIM_ERROR_CWT; + if (rx_status & SIM_RCV_STATUS_BGT) + sim->errval |= SIM_ERROR_BGT; + + sim_rcv_read_fifo(sim); + /*Add the state judgement to ensure the maybe complete has been impletment in the above "if" case*/ + if (sim->state == SIM_STATE_RECEIVING) { + sim->state = SIM_STATE_RECEIVE_DONE; + complete(&sim->xfer_done); + } + } + } + + else if (rx_status & SIM_RCV_STATUS_RDRF) { + pr_err("unexpected status %d\n", sim->state); + sim_rcv_read_fifo(sim); + } + + return IRQ_HANDLED; +}; + +static void sim_start(struct sim_t *sim) +{ + u32 reg_data, clk_rate, clk_div = 0; + pr_debug("%s entering.\n", __func__); + + __raw_writel(0, sim->ioaddr + SETUP); + + /* ~ 5 MHz */ + clk_rate = clk_get_rate(sim->clk); + clk_div = (clk_rate + sim->clk_rate - 1) / sim->clk_rate; + __raw_writel(clk_div, sim->ioaddr + CLK_PRESCALER); + + /*Set the port pin to be open drained*/ + reg_data = __raw_readl(sim->ioaddr + OD_CONFIG); + reg_data |= SIM_OD_CONFIG_OD_P0; + __raw_writel(reg_data, sim->ioaddr + OD_CONFIG); + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + + /*One pin mode*/ + reg_data |= SIM_PORT_CNTL_3VOLT | SIM_PORT_CNTL_STEN; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + + /* presense detect */ + pr_debug("%s p0_det is 0x%x \n", __func__, + __raw_readl(sim->ioaddr + PORT0_DETECT)); + if (__raw_readl(sim->ioaddr + PORT0_DETECT) & SIM_PORT_DETECT_SPDP) { + reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT); + reg_data &= ~SIM_PORT_DETECT_SPDS; + __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT); + sim->present = SIM_PRESENT_REMOVED; + sim->state = SIM_STATE_REMOVED; + } else { + reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT); + reg_data |= SIM_PORT_DETECT_SPDS; + __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT); + sim->present = SIM_PRESENT_DETECTED; + sim->state = SIM_STATE_DETECTED; + }; + + /*enable card interrupt. clear interrupt status*/ + reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT); + reg_data |= SIM_PORT_DETECT_SDI; + reg_data |= SIM_PORT_DETECT_SDIM; + __raw_writel(reg_data, sim->ioaddr + PORT0_DETECT); +}; + +static void sim_activate(struct sim_t *sim) +{ + u32 reg_data; + pr_debug("%s Activate on the sim port.\n", __func__); + /* activate on sequence */ + if (sim->present != SIM_PRESENT_REMOVED) { + /*Disable Reset pin*/ + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data &= ~SIM_PORT_CNTL_SRST; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + + /*Enable VCC pin*/ + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data |= SIM_PORT_CNTL_SVEN; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + msleep(10); + /*Enable clock pin*/ + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data |= SIM_PORT_CNTL_SCEN; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + msleep(10); + } else { + pr_err("No card%s\n", __func__); + } +} + +static void sim_cold_reset_sequency(struct sim_t *sim) +{ + u32 reg_data; + + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data &= ~SIM_PORT_CNTL_SRST; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data |= SIM_PORT_CNTL_SVEN; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + mdelay(9); + + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data |= SIM_PORT_CNTL_SCEN; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + + mdelay(8); + udelay(600); + + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data |= SIM_PORT_CNTL_SRST; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); +}; + +static void sim_deactivate(struct sim_t *sim) +{ + u32 reg_data; + + pr_debug("%s entering.\n", __func__); + /* Auto powdown to implement the deactivate sequence */ + if (sim->present != SIM_PRESENT_REMOVED) { + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data |= SIM_PORT_CNTL_SAPD; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + reg_data |= SIM_PORT_CNTL_SFPD; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + } else { + pr_err("No card%s\n", __func__); + } + + sim->present = SIM_PRESENT_REMOVED; +}; + +static void sim_cold_reset(struct sim_t *sim) +{ + if (sim->present != SIM_PRESENT_REMOVED) { + sim->state = SIM_STATE_DETECTED; + sim->present = SIM_PRESENT_DETECTED; + sim_cold_reset_sequency(sim); + sim_receive_atr_set(sim); + } else { + pr_err("No card%s\n", __func__); + } +}; + +static void sim_warm_reset_sequency(struct sim_t *sim) +{ + u32 reg_data; + + reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL); + reg_data |= (SIM_PORT_CNTL_SRST | SIM_PORT_CNTL_SVEN | SIM_PORT_CNTL_SCEN); + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + + udelay(20); + + reg_data &= ~SIM_PORT_CNTL_SRST; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); + + mdelay(8); + udelay(600); + + reg_data |= SIM_PORT_CNTL_SRST; + __raw_writel(reg_data, sim->ioaddr + PORT0_CNTL); +} + +static void sim_warm_reset(struct sim_t *sim) +{ + if (sim->present != SIM_PRESENT_REMOVED) { + sim_data_reset(sim); + sim_warm_reset_sequency(sim); + sim_receive_atr_set(sim); + } else { + pr_err("No card%s\n", __func__); + } +}; + + +static int sim_card_lock(struct sim_t *sim) +{ + int errval; + + /* place holder for true physcial locking */ + if (sim->present != SIM_PRESENT_REMOVED) + errval = SIM_OK; + else + errval = -SIM_E_NOCARD; + return errval; +}; + +static int sim_card_eject(struct sim_t *sim) +{ + int errval; + + pr_debug("%s entering.\n", __func__); + /* place holder for true physcial locking */ + if (sim->present != SIM_PRESENT_REMOVED) + errval = SIM_OK; + else + errval = -SIM_E_NOCARD; + return errval; +}; + +static int sim_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 sim_set_baud_rate(struct sim_t *sim) +{ + u32 reg_data; + reg_data = __raw_readl(sim->ioaddr + CNTL); + reg_data &= ~(SIM_CNTL_BAUD_SEL_MASK); + + switch (sim->baud_rate.di) { + case 1: + reg_data |= SIM_CNTL_BAUD_SEL(0); + break; + case 2: + reg_data |= SIM_CNTL_BAUD_SEL(1); + break; + case 3: + reg_data |= SIM_CNTL_BAUD_SEL(2); + break; + default: + pr_err("Invalid baud Di, Using default 372 / 1\n"); + reg_data |= SIM_CNTL_BAUD_SEL(0); + break; + } + + __raw_writel(reg_data, sim->ioaddr + CNTL); + + return 0; +} + +static int sim_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) { + /*Check whether the counter is out of the scope of SIM IP*/ + pr_err("The timing value is out of scope of IP\n"); + return -EINVAL; + } + + return 0; +} + +static void sim_set_timer_counter(struct sim_t *sim) +{ + if (sim->timing_data.wwt != 0 && + sim->protocol_type == SIM_PROTOCOL_T0) { + sim->timing_data.cwt = sim->timing_data.wwt; + sim->timing_data.bwt = sim->timing_data.wwt; + } + + + if (sim->timing_data.bgt != 0) { + __raw_writel(sim->timing_data.bgt, sim->ioaddr + BGT); + } + + if (sim->timing_data.cwt != 0) + __raw_writel(sim->timing_data.cwt, sim->ioaddr + CHAR_WAIT); + + if (sim->timing_data.bwt != 0) { + + __raw_writel(sim->timing_data.bwt & 0x0000FFFF, sim->ioaddr + BWT); + __raw_writel((sim->timing_data.bwt >> 16) & 0x0000FFFF, + sim->ioaddr + BWT_H); + } + + if (sim->timing_data.cgt == 0xFF && sim->protocol_type == SIM_PROTOCOL_T0) + /* From EMV4.3 , CGT =0xFF in T0 mode means 12 ETU. + * Set register to be 12 ETU for transmitting and receiving. + */ + __raw_writel(0 , sim->ioaddr + GUARD_CNTL); + else if (sim->timing_data.cgt == 0xFF && sim->protocol_type == SIM_PROTOCOL_T1) + /* From EMV4.3 , CGT =0xFF in T1 mode means 11 ETU. + * Set register to be 12 ETU for transmitting and receiving. + */ + __raw_writel(0x1FF , sim->ioaddr + GUARD_CNTL); + + /*For the T1 mode, use 11ETU to receive.*/ + else if (sim->protocol_type == SIM_PROTOCOL_T1) + __raw_writel((sim->timing_data.cgt | SIM_GUARD_CNTL_RCVR11), sim->ioaddr + GUARD_CNTL); + + else + /*sim->protocol_type == SIM_PROTOCOL_T0*/ + __raw_writel(sim->timing_data.cgt, sim->ioaddr + GUARD_CNTL); +} + +static void sim_xmt_start(struct sim_t *sim) +{ + u32 reg_val; + + /*Set TX threshold if there are remaing data*/ + if (sim->xmt_remaining != 0) { + reg_val = __raw_readl(sim->ioaddr + XMT_THRESHOLD); + reg_val &= ~SIM_XMT_THRESHOLD_TDT_MASK; + reg_val |= SIM_XMT_THRESHOLD_TDT(TX_FIFO_THRESHOLD); + __raw_writel(reg_val, sim->ioaddr + XMT_THRESHOLD); + } + sim_tx_irq_enable(sim); + + /*Enable BWT, CWT timers*/ + sim_set_waiting_timers(sim, 1); + + /*Enable TX*/ + sim_set_tx(sim, 1); + + /*Disalbe RX*/ + sim_set_rx(sim, 0); +} + +static void sim_flush_fifo(struct sim_t *sim, u8 flush_tx, u8 flush_rx) +{ + u32 reg_val; + + reg_val = __raw_readl(sim->ioaddr + RESET_CNTL); + + if (flush_tx) + reg_val |= SIM_RESET_CNTL_FLUSH_XMT; + if (flush_rx) + reg_val |= SIM_RESET_CNTL_FLUSH_RCV; + __raw_writel(reg_val, sim->ioaddr + RESET_CNTL); + + usleep_range(2, 3); + + if (flush_tx) + reg_val &= ~(SIM_RESET_CNTL_FLUSH_XMT); + if (flush_rx) + reg_val &= ~(SIM_RESET_CNTL_FLUSH_RCV); + __raw_writel(reg_val, sim->ioaddr + RESET_CNTL); +} + +static void sim_change_rcv_threshold(struct sim_t *sim) +{ + u32 rx_threshold = 0; + u32 reg_val = 0; + + if (sim->is_fixed_len_rec) { + rx_threshold = sim->expected_rcv_cnt - sim->rcv_count; + reg_val = __raw_readl(sim->ioaddr + RCV_THRESHOLD); + reg_val &= ~(SIM_RCV_THRESHOLD_RDT_MASK); + reg_val |= SIM_RCV_THRESHOLD_RDT(rx_threshold); + __raw_writel(reg_val, sim->ioaddr + RCV_THRESHOLD); + } +} + +static void sim_start_rcv(struct sim_t *sim) +{ + sim_set_baud_rate(sim); + if (sim->protocol_type == SIM_PROTOCOL_T0) + sim_set_nack(sim, 1); + else if (sim->protocol_type == SIM_PROTOCOL_T1) + sim_set_nack(sim, 0); + + /*Set RX threshold*/ + if (sim->protocol_type == SIM_PROTOCOL_T0) + __raw_writel(SIM_RCV_THRESHOLD_RTH(sim->nack_threshold) | + SIM_RCV_THRESHOLD_RDT(RX_FIFO_THRESHOLD), sim->ioaddr + RCV_THRESHOLD); + else + __raw_writel(SIM_RCV_THRESHOLD_RDT(RX_FIFO_THRESHOLD), sim->ioaddr + RCV_THRESHOLD); + + /*Clear status and enable interrupt*/ + sim_rx_irq_enable(sim); + + /*Disalbe TX and Enable Rx*/ + sim_set_rx(sim, 1); + sim_set_tx(sim, 0); +} + +static void sim_polling_delay(struct sim_t *sim, u32 delay) +{ + u32 reg_data; + + /*Reset the timer*/ + reg_data = __raw_readl(sim->ioaddr + CNTL); + reg_data &= ~SIM_CNTL_GPCNT_CLK_SEL_MASK; + reg_data |= SIM_CNTL_GPCNT_CLK_SEL(0); + __raw_writel(reg_data, sim->ioaddr + CNTL); + + /*Clear the interrupt status*/ + __raw_writel(SIM_XMT_STATUS_GPCNT, sim->ioaddr + XMT_STATUS); + + /*Disable timer interrupt*/ + reg_data = __raw_readl(sim->ioaddr + INT_MASK); + reg_data |= SIM_INT_MASK_GPCM; + __raw_writel(reg_data, sim->ioaddr + INT_MASK); + + __raw_writel(delay, sim->ioaddr + GPCNT); + + /*Set the ETU as clock source and start timer*/ + reg_data = __raw_readl(sim->ioaddr + CNTL); + reg_data &= ~SIM_CNTL_GPCNT_CLK_SEL_MASK; + reg_data |= SIM_CNTL_GPCNT_CLK_SEL(3); + __raw_writel(reg_data, sim->ioaddr + CNTL); + + /*Loop for timeout*/ + while (!(__raw_readl(sim->ioaddr + XMT_STATUS) & SIM_XMT_STATUS_GPCNT)) + usleep_range(10, 20); + __raw_writel(SIM_XMT_STATUS_GPCNT, sim->ioaddr + XMT_STATUS); +} + +static long sim_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; + + struct sim_t *sim = (struct sim_t *) file->private_data; + + pr_debug("%s entering.\n", __func__); + switch (cmd) { + + case SIM_IOCTL_GET_ATR: + if (sim->present != SIM_PRESENT_DETECTED) { + pr_err("NO card ...\n"); + errval = -SIM_E_NOCARD; + break; + }; + sim->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(&sim->xfer_done, + sim->timeout); + /*Disalbe the GPCNT timer and CWT timer right now*/ + reg_data = __raw_readl(sim->ioaddr + CNTL); + reg_data &= ~(SIM_CNTL_GPCNT_CLK_SEL_MASK | SIM_CNTL_CWTEN); + __raw_writel(reg_data, sim->ioaddr + CNTL); + + reg_data = __raw_readl(sim->ioaddr + INT_MASK); + reg_data |= (SIM_INT_MASK_GPCM | SIM_INT_MASK_CWTM); + __raw_writel(reg_data, sim->ioaddr + INT_MASK); + + if (timeout == 0) { + pr_err("ATR timeout\n"); + errval = -SIM_E_TIMEOUT; + break; + } + + ret = copy_to_user(&(((sim_atr_t *)arg)->size), &sim->rcv_count, + sizeof(sim->rcv_count)); + if (ret) { + pr_err("ATR ACCESS rcv_count Error, %d\n", ret); + errval = -SIM_E_ACCESS; + break; + } + + ret = copy_to_user(((sim_atr_t *)arg)->atr_buffer, sim->rcv_buffer, + sim->rcv_count); + if (ret) { + pr_err("ATR ACCESS buffer Error %d %d\n", sim->rcv_count, ret); + errval = -SIM_E_ACCESS; + break; + } + + ret = copy_to_user(&(((sim_atr_t *)arg)->errval), &sim->errval, + sizeof(sim->errval)); + if (ret) { + pr_err("ATR ACCESS Error\n"); + errval = -SIM_E_ACCESS; + break; + } + sim->rcv_count = 0; + sim->rcv_head = 0; + sim->errval = 0; + + break; + + case SIM_IOCTL_DEACTIVATE: + + sim_deactivate(sim); + break; + + case SIM_IOCTL_COLD_RESET: + sim->present = SIM_PRESENT_REMOVED; + sim->state = SIM_STATE_REMOVED; + sim_reset_module(sim); + sim_data_reset(sim); + sim_start(sim); + sim_activate(sim); + sim_cold_reset(sim); + + break; + + case SIM_IOCTL_WARM_RESET: + sim_warm_reset(sim); + break; + + case SIM_IOCTL_XMT: + ret = copy_from_user(&sim->xmt_remaining, &(((sim_xmt_t *)arg)->xmt_length), + sizeof(uint32_t)); + if (ret || sim->xmt_remaining > SIM_XMT_BUFFER_SIZE) { + pr_err("copy error or to big buffer\n"); + errval = -EINVAL; + break; + } + ret = copy_from_user(sim->xmt_buffer, (((sim_xmt_t *)arg)->xmt_buffer), + sim->xmt_remaining); + + if (ret) { + pr_err("Copy Error\n"); + errval = ret; + break; + } + /*Flush the tx rx fifo*/ + sim_flush_fifo(sim, 1, 1); + sim->xmt_pos = 0; + sim->rcv_count = 0; + sim->rcv_head = 0; + sim->errval = 0; + + sim_xmt_fill_fifo(sim); + sim_set_baud_rate(sim); + if (sim->protocol_type == SIM_PROTOCOL_T0) + sim_set_nack(sim, 1); + else if (sim->protocol_type == SIM_PROTOCOL_T1) + sim_set_nack(sim, 0); + else { + pr_err("Invalid protocol not T0 or T1\n"); + errval = -EINVAL; + break; + } + + sim_set_timer_counter(sim); + sim_xmt_start(sim); + sim->state = SIM_STATE_XMTING; + + sim->timeout = TX_TIMEOUT * HZ; + timeout = wait_for_completion_interruptible_timeout(&sim->xfer_done, + sim->timeout); + if (timeout == 0) { + /*Disable the NACK interruptand TX related interrupt*/ + sim_tx_irq_disable(sim); + pr_err("tx timeout\n"); + } + + if (timeout == 0 || sim->state == SIM_STATE_XMT_ERROR) { + pr_err("TX error\n"); + /*Disable timers*/ + sim_set_waiting_timers(sim, 0); + /*Disable TX*/ + sim_set_tx(sim, 0); + /*Flush the tx fifos*/ + sim_flush_fifo(sim, 1, 0); + if (timeout == 0) + errval = -SIM_E_TIMEOUT; + else + errval = -SIM_E_NACK; + + ret = copy_to_user(&(((sim_atr_t *)arg)->errval), &sim->errval, + sizeof(sim->errval)); + sim->errval = 0; + break; + } + + /*Copy the error status to user space*/ + ret = copy_to_user(&(((sim_atr_t *)arg)->errval), &sim->errval, + sizeof(sim->errval)); + sim->last_is_tx = true; + /*Start RX*/ + sim->rcv_count = 0; + sim->errval = 0; + sim->state = SIM_STATE_RECEIVING; + sim_start_rcv(sim); + + break; + + case SIM_IOCTL_RCV: + if (sim->present != SIM_PRESENT_DETECTED) { + errval = -SIM_E_NOCARD; + break; + } + sim->is_fixed_len_rec = 0; + val = 0; + ret = copy_from_user(&sim->expected_rcv_cnt, &(((sim_rcv_t *)arg)->rcv_length), + sizeof(sim->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 (sim->expected_rcv_cnt != 0) + sim->is_fixed_len_rec = 1; + + if (sim->is_fixed_len_rec && sim->rcv_count >= sim->expected_rcv_cnt) + goto copy_data; + + if (sim->state != SIM_STATE_RECEIVING) { + sim_set_timer_counter(sim); + /*Enable CWT BWT*/ + sim_set_waiting_timers(sim, 1); + sim->state = SIM_STATE_RECEIVING; + sim_start_rcv(sim); + } + + spin_lock_irqsave(&sim->lock, flags); + if (sim->is_fixed_len_rec && sim->rcv_count < sim->expected_rcv_cnt) + sim_change_rcv_threshold(sim); + spin_unlock_irqrestore(&sim->lock, flags); + sim->timeout = RX_TIMEOUT * HZ; + timeout = wait_for_completion_interruptible_timeout(&sim->xfer_done, + sim->timeout); + + if (timeout == 0) { + pr_err("Receiving timeout\n"); + sim_set_waiting_timers(sim, 0); + sim_rx_irq_disable(sim); + errval = -SIM_E_TIMEOUT; + break; + } + +copy_data: + if (sim->is_fixed_len_rec) + copy_cnt = sim->rcv_count >= sim->expected_rcv_cnt ? sim->expected_rcv_cnt : sim->rcv_count; + else + copy_cnt = sim->rcv_count; + + ret = copy_to_user(&(((sim_rcv_t *)arg)->rcv_length), ©_cnt, + sizeof(copy_cnt)); + if (ret) { + pr_err("ATR ACCESS Error\n"); + errval = -SIM_E_ACCESS; + break; + } + + ret = copy_to_user(((sim_rcv_t *)arg)->rcv_buffer, &sim->rcv_buffer[sim->rcv_head], + copy_cnt); + if (ret) { + pr_err("ATR ACCESS Error\n"); + errval = -SIM_E_ACCESS; + break; + } + + ret = copy_to_user(&(((sim_rcv_t *)arg)->errval), &sim->errval, + sizeof(sim->errval)); + if (ret) { + pr_err("ATR ACCESS Error\n"); + errval = -SIM_E_ACCESS; + break; + } + /*Reset the receiving count and errval*/ + spin_lock_irqsave(&sim->lock, flags); + sim->rcv_head += copy_cnt; + sim->rcv_count -= copy_cnt; + sim->errval = 0; + spin_unlock_irqrestore(&sim->lock, flags); + + sim->last_is_tx = false; + + break; + + case SIM_IOCTL_SET_PROTOCOL: + ret = copy_from_user(&sim->protocol_type, (int *)arg, + sizeof(int)); + if (ret) + errval = -SIM_E_ACCESS; + break; + + case SIM_IOCTL_SET_TIMING: + ret = copy_from_user(&sim->timing_data, (sim_timing_t *)arg, + sizeof(sim_timing_t)); + if (ret) { + pr_err("Copy Error\n"); + errval = ret; + break; + } + + ret = sim_check_timing_data(&sim->timing_data); + + if (ret) + errval = ret; + + break; + + case SIM_IOCTL_SET_BAUD: + ret = copy_from_user(&sim->baud_rate, (sim_baud_t *)arg, + sizeof(sim_baud_t)); + + if (ret) { + pr_err("Copy Error\n"); + errval = ret; + break; + } + + sim_check_baud_rate(&sim->baud_rate); + + break; + case SIM_IOCTL_WAIT: + ret = copy_from_user(&delay, (unsigned int *)arg, + sizeof(unsigned int)); + + if (ret) { + pr_err("\nWait Copy Error\n"); + errval = ret; + break; + } + + sim_polling_delay(sim, delay); + break; + + case SIM_IOCTL_GET_PRESENSE: + if (put_user(sim->present, (int *)arg)) + errval = -SIM_E_ACCESS; + break; + + case SIM_IOCTL_CARD_LOCK: + errval = sim_card_lock(sim); + break; + + case SIM_IOCTL_CARD_EJECT: + errval = sim_card_eject(sim); + break; + + }; + + return errval; +}; + +static int sim_open(struct inode *inode, struct file *file) +{ + int errval = SIM_OK; + struct sim_t *sim = dev_get_drvdata(sim_dev.parent); + + file->private_data = sim; + spin_lock_init(&sim->lock); + + pr_debug("%s entering.\n", __func__); + if (!sim->ioaddr) { + errval = -ENOMEM; + return errval; + } + + if (!sim->open_cnt) + clk_prepare_enable(sim->clk); + + sim->open_cnt = 1; + + errval = sim_reset_module(sim); + sim_data_reset(sim); + + return errval; +}; + +static int sim_release(struct inode *inode, struct file *file) +{ + u32 reg_data; + struct sim_t *sim = (struct sim_t *) file->private_data; + + /* disable presense detection */ + reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT); + __raw_writel(reg_data | SIM_PORT_DETECT_SDIM, + sim->ioaddr + PORT0_DETECT); + + if (sim->present != SIM_PRESENT_REMOVED) + sim_deactivate(sim); + + + if (sim->open_cnt) + clk_disable_unprepare(sim->clk); + + sim->open_cnt = 0; + + pr_err("exit %s\n", __func__); + + return 0; +}; + +static const struct file_operations sim_fops = { + .owner = THIS_MODULE, + .open = sim_open, + .release = sim_release, + .unlocked_ioctl = sim_ioctl, +}; + +static struct miscdevice sim_dev = { + MISC_DYNAMIC_MINOR, + "mxc_sim", + &sim_fops +}; + +static struct platform_device_id imx_sim_devtype[] = { + { + .name = "imx7d-sim", + .driver_data = 0, + }, { + /* sentinel */ + } +}; + +static const struct of_device_id sim_imx_dt_ids[] = { + { .compatible = "fsl,imx7d-sim", + .data = &imx_sim_devtype[0], }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, mlb150_imx_dt_ids); + +static int sim_probe(struct platform_device *pdev) +{ + int ret = 0; + const struct of_device_id *of_id; + struct sim_t *sim = NULL; + + sim = devm_kzalloc(&pdev->dev, sizeof(struct sim_t), + GFP_KERNEL); + if (!sim) { + dev_err(&pdev->dev, "can't allocate enough memory\n"); + return -ENOMEM; + } + + + of_id = of_match_device(sim_imx_dt_ids, &pdev->dev); + if (of_id) + pdev->id_entry = of_id->data; + else + return -EINVAL; + + sim->clk_rate = FCLK_FREQ; + sim->open_cnt = 0; + + sim->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!sim->res) { + pr_err("Can't get the MEMORY\n"); + return -ENOMEM; + } + sim->ioaddr = devm_request_and_ioremap(&pdev->dev, sim->res); + dev_dbg(&pdev->dev, "mapped base address: 0x%08x\n", (u32)sim->ioaddr); + if (IS_ERR(sim->ioaddr)) { + dev_err(&pdev->dev, + "failed to get ioremap base\n"); + ret = PTR_ERR(sim->ioaddr); + return ret; + } + + /* request the sim clk and sim_serial_clk */ + sim->clk = devm_clk_get(&pdev->dev, "sim"); + if (IS_ERR(sim->clk)) { + ret = PTR_ERR(sim->clk); + pr_err("Get CLK ERROR !\n"); + return ret; + } + pr_debug("sim clock:%lu\n", clk_get_rate(sim->clk)); + + sim->ipb_irq = platform_get_irq(pdev, 0); + if (sim->ipb_irq < 0) { + dev_err(&pdev->dev, "No ipb irq line provided\n"); + return -ENOENT; + } + if (devm_request_irq(&pdev->dev, sim->ipb_irq, sim_irq_handler, + 0, "mxc_sim_ipb", sim)) { + dev_err(&pdev->dev, "can't claim irq %d\n", sim->ipb_irq); + return -ENOENT; + } + + platform_set_drvdata(pdev, sim); + + /* + *@todo: Need to figure a better way if possible. + */ + sim_dev.parent = &(pdev->dev); + + misc_register(&sim_dev); + + return 0; +} + +static int sim_remove(struct platform_device *pdev) +{ + struct sim_t *sim = platform_get_drvdata(pdev); + + if (sim->open_cnt) + clk_disable_unprepare(sim->clk); + + misc_deregister(&sim_dev); + + return 0; +} + +#ifdef CONFIG_PM +static int sim_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct sim_t *sim = platform_get_drvdata(pdev); + + if (sim->open_cnt) + clk_disable_unprepare(sim->clk); + + return 0; +} + +static int sim_resume(struct platform_device *pdev) +{ + struct sim_t *sim = platform_get_drvdata(pdev); + + if (sim->open_cnt) + clk_prepare_enable(sim->clk); + + return 0; +} +#else +#define sim_suspend NULL +#define sim_resume NULL +#endif + +static struct platform_driver sim_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = sim_imx_dt_ids, + }, + .probe = sim_probe, + .remove = sim_remove, + .suspend = sim_suspend, + .resume = sim_resume, + .id_table = imx_sim_devtype, +}; + +static int __init sim_drv_init(void) +{ + return platform_driver_register(&sim_driver); +} + +static void __exit sim_drv_exit(void) +{ + platform_driver_unregister(&sim_driver); +} + +module_init(sim_drv_init); +module_exit(sim_drv_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC SIM Driver"); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/mxc_sim_interface.h b/include/linux/mxc_sim_interface.h new file mode 100644 index 000000000000..5eae53a59075 --- /dev/null +++ b/include/linux/mxc_sim_interface.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef MXC_SIM_INTERFACE_H +#define MXC_SIM_INTERFACE_H + +#define SIM_ATR_LENGTH_MAX 32 + +/* Raw ATR SIM_IOCTL_GET_ATR */ +typedef struct { + unsigned int size;/* length of ATR received */ + unsigned char *atr_buffer;/* raw ATR string received */ + int errval;/* The error vale reported to user space after completing ATR*/ +} sim_atr_t; + +/* ISO7816-3 protocols */ +#define SIM_PROTOCOL_T0 1 +#define SIM_PROTOCOL_T1 2 + +/* Transfer types for SIM_IOCTL_XFER */ +#define SIM_XFER_TYPE_TPDU 1 +#define SIM_XFER_TYPE_PTS 2 + +typedef struct { + unsigned int wwt; + unsigned int cwt; + unsigned int bwt; + unsigned int bgt; + unsigned int cgt; +} sim_timing_t; + +/* Transfer data for SIM_IOCTL_XFER */ +typedef struct { + unsigned char *xmt_buffer; /* transmit buffer pointer */ + int xmt_length;/* transmit buffer length */ + int timeout;/* transfer timeout in milliseconds */ + int errval;/* The error vale reported to user space after completing transmitting*/ +} sim_xmt_t; + +typedef struct { + unsigned char *rcv_buffer; /* receive buffer pointer */ + int rcv_length; /* receive buffer length */ + int timeout;/* transfer timeout in milliseconds */ + int errval;/* The error vale reported to user space after receiving*/ +} sim_rcv_t; + +typedef struct { + unsigned char di; + unsigned char fi; +} sim_baud_t; + +/* Interface power states */ +#define SIM_POWER_OFF (0) +#define SIM_POWER_ON (1) + +/* Return values for SIM_IOCTL_GET_PRESENSE */ +#define SIM_PRESENT_REMOVED (0) +#define SIM_PRESENT_DETECTED (1) +#define SIM_PRESENT_OPERATIONAL (2) + +/* The error value */ +#define SIM_OK (0) +#define SIM_ERROR_CWT (1 << 0) +#define SIM_ERROR_BWT (1 << 1) +#define SIM_ERROR_PARITY (1 << 2) +#define SIM_ERROR_INVALID_TS (1 << 3) +#define SIM_ERROR_FRAME (1 << 4) +#define SIM_ERROR_ATR_TIMEROUT (1 << 5) +#define SIM_ERROR_NACK_THRESHOLD (1 << 6) +#define SIM_ERROR_BGT (1 << 7) +#define SIM_ERROR_ATR_DELAY (1 << 8) + +/* Return values for SIM_IOCTL_GET_ERROR */ +#define SIM_E_ACCESS (1) +#define SIM_E_TPDUSHORT (2) +#define SIM_E_PTSEMPTY (3) +#define SIM_E_INVALIDXFERTYPE (4) +#define SIM_E_INVALIDXMTLENGTH (5) +#define SIM_E_INVALIDRCVLENGTH (6) +#define SIM_E_NACK (7) +#define SIM_E_TIMEOUT (8) +#define SIM_E_NOCARD (9) +#define SIM_E_PARAM_FI_INVALID (10) +#define SIM_E_PARAM_DI_INVALID (11) +#define SIM_E_PARAM_FBYD_WITHFRACTION (12) +#define SIM_E_PARAM_FBYD_NOTDIVBY8OR12 (13) +#define SIM_E_PARAM_DIVISOR_RANGE (14) +#define SIM_E_MALLOC (15) +#define SIM_E_IRQ (16) +#define SIM_E_POWERED_ON (17) +#define SIM_E_POWERED_OFF (18) + +/* ioctl encodings */ +#define SIM_IOCTL_BASE (0xc0) +#define SIM_IOCTL_GET_PRESENSE _IOR(SIM_IOCTL_BASE, 1, int) +#define SIM_IOCTL_GET_ATR _IOR(SIM_IOCTL_BASE, 2, sim_atr_t) +#define SIM_IOCTL_XMT _IOR(SIM_IOCTL_BASE, 3, sim_xmt_t) +#define SIM_IOCTL_RCV _IOR(SIM_IOCTL_BASE, 4, sim_rcv_t) +#define SIM_IOCTL_ACTIVATE _IO(SIM_IOCTL_BASE, 5) +#define SIM_IOCTL_DEACTIVATE _IO(SIM_IOCTL_BASE, 6) +#define SIM_IOCTL_WARM_RESET _IO(SIM_IOCTL_BASE, 7) +#define SIM_IOCTL_COLD_RESET _IO(SIM_IOCTL_BASE, 8) +#define SIM_IOCTL_CARD_LOCK _IO(SIM_IOCTL_BASE, 9) +#define SIM_IOCTL_CARD_EJECT _IO(SIM_IOCTL_BASE, 10) +#define SIM_IOCTL_SET_PROTOCOL _IOR(SIM_IOCTL_BASE, 11, unsigned int) +#define SIM_IOCTL_SET_TIMING _IOR(SIM_IOCTL_BASE, 12, sim_timing_t) +#define SIM_IOCTL_SET_BAUD _IOR(SIM_IOCTL_BASE, 13, sim_baud_t) +#define SIM_IOCTL_WAIT _IOR(SIM_IOCTL_BASE, 14, unsigned int) + +#endif