mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-09-03 10:33:11 +02:00

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 <b45643@freescale.com>
Signed-off-by: Gao Pan <b45643@freescale.com>
(cherry picked from 3ac1ad5b2a
)
[ Aisheng: squash legacy patches ]
Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
1909 lines
49 KiB
C
1909 lines
49 KiB
C
/*
|
|
* 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 <linux/module.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/io.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/types.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/mxc_sim_interface.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/time.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/io.h>
|
|
|
|
#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)
|
|
#define SIM_STATE_RESET_SEQUENCY (10)
|
|
|
|
/* 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 SETUP register bits */
|
|
#define SIM_SETUP_SPS_PORT0 (0 << 1)
|
|
#define SIM_SETUP_SPS_PORT1 (1 << 1)
|
|
|
|
/* 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)
|
|
#define SIM_ESTOP_EN (1 << 5)
|
|
#define SIM_ESTOP_EXE (1 << 6)
|
|
|
|
/* 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)
|
|
#define SIM_CNTL_GPCNT_CARD_CLK 1
|
|
#define SIM_CNTL_GPCNT_RCV_CLK 2
|
|
#define SIM_CNTL_GPCNT_ETU_CLK 3
|
|
|
|
/* 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 (4000000)
|
|
|
|
#define ATR_TIMEOUT (5)
|
|
#define TX_TIMEOUT (10)
|
|
#define RX_TIMEOUT (100)
|
|
#define RESET_RETRY_TIMES (5)
|
|
#define SIM_QUIRK_TKT259347 (1 << 0)
|
|
#define EMV_RESET_LOW_CYCLES 40000
|
|
#define ATR_MAX_DELAY_CLK 46400
|
|
|
|
/* 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;
|
|
bool sven_low_active;
|
|
u32 port_index;
|
|
u32 port_detect_reg;
|
|
u32 port_ctrl_reg;
|
|
u32 clk_rate;
|
|
u32 quirks;
|
|
u8 checking_ts_timing;
|
|
};
|
|
|
|
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 | SIM_ENABLE_RCVEN;
|
|
if (sim->quirks & SIM_QUIRK_TKT259347)
|
|
reg_data &= ~(SIM_ESTOP_EN | SIM_ESTOP_EXE);
|
|
} 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;
|
|
reg_data &= ~SIM_ENABLE_XMTEN;
|
|
if (sim->quirks & SIM_QUIRK_TKT259347)
|
|
reg_data |= (SIM_ESTOP_EN | SIM_ESTOP_EXE);
|
|
} else {
|
|
reg_data &= ~SIM_ENABLE_RCVEN;
|
|
if (sim->quirks & SIM_QUIRK_TKT259347)
|
|
reg_data &= ~(SIM_ESTOP_EN | SIM_ESTOP_EXE);
|
|
}
|
|
|
|
__raw_writel(reg_data, sim->ioaddr + ENABLE);
|
|
}
|
|
|
|
static void sim_reset_timer(struct sim_t *sim)
|
|
{
|
|
u32 reg_data;
|
|
|
|
reg_data = __raw_readl(sim->ioaddr + CNTL);
|
|
reg_data &= ~SIM_CNTL_GPCNT_CLK_SEL_MASK;
|
|
__raw_writel(reg_data, sim->ioaddr + CNTL);
|
|
}
|
|
|
|
static void sim_start_timer(struct sim_t *sim, u8 clk_source)
|
|
{
|
|
u32 reg_data;
|
|
|
|
reg_data = __raw_readl(sim->ioaddr + CNTL);
|
|
reg_data &= ~SIM_CNTL_GPCNT_CLK_SEL_MASK;
|
|
reg_data |= SIM_CNTL_GPCNT_CLK_SEL(clk_source);
|
|
writel(reg_data, sim->ioaddr + CNTL);
|
|
}
|
|
|
|
static void sim_set_gpc_timer(struct sim_t *sim, u32 val)
|
|
{
|
|
uint32_t reg_data;
|
|
|
|
/*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);
|
|
|
|
/*Set the timer counter*/
|
|
__raw_writel(val, sim->ioaddr + GPCNT);
|
|
|
|
/*First reset the counter*/
|
|
sim_reset_timer(sim);
|
|
|
|
/*Enable GPC timer interrupt*/
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data &= ~SIM_INT_MASK_GPCM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
|
|
/*Set the GPCNT clock source to be Fclk*/
|
|
sim_start_timer(sim, SIM_CNTL_GPCNT_CARD_CLK);
|
|
}
|
|
|
|
static int sim_reset_low_timing(struct sim_t *sim, u32 clock_cycle)
|
|
{
|
|
int errval = 0;
|
|
int timeout = 0;
|
|
u32 fclk_in_khz, delay_in_us, reg_data;
|
|
|
|
fclk_in_khz = sim->clk_rate / MSEC_PER_SEC;
|
|
delay_in_us = EMV_RESET_LOW_CYCLES * USEC_PER_MSEC / fclk_in_khz;
|
|
|
|
sim_set_gpc_timer(sim, clock_cycle);
|
|
|
|
timeout = wait_for_completion_timeout(&sim->xfer_done,
|
|
msecs_to_jiffies(delay_in_us / 1000 * 2));
|
|
if (timeout == 0) {
|
|
pr_err("Reset low GPC timout\n");
|
|
errval = -SIM_E_TIMEOUT;
|
|
}
|
|
|
|
sim_reset_timer(sim);
|
|
/*Disable GPC timer interrupt*/
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data |= SIM_INT_MASK_GPCM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
|
|
return errval;
|
|
}
|
|
|
|
static void sim_set_cwt(struct sim_t *sim, u8 enable)
|
|
{
|
|
u32 reg_val;
|
|
reg_val = __raw_readl(sim->ioaddr + CNTL);
|
|
if (enable && sim->timing_data.cwt)
|
|
reg_val |= SIM_CNTL_CWTEN;
|
|
else
|
|
reg_val &= ~SIM_CNTL_CWTEN;
|
|
__raw_writel(reg_val, sim->ioaddr + CNTL);
|
|
}
|
|
|
|
static void sim_set_bwt(struct sim_t *sim, u8 enable)
|
|
{
|
|
u32 reg_val;
|
|
reg_val = __raw_readl(sim->ioaddr + CNTL);
|
|
if (enable && (sim->timing_data.bwt || sim->timing_data.bgt))
|
|
reg_val |= SIM_CNTL_BWTEN;
|
|
else
|
|
reg_val &= ~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*/
|
|
sim_set_rx(sim, 1);
|
|
|
|
/*Receive fifo threshold = 1*/
|
|
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 + 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
|
|
*Enable the CWT timer during receiving ATR process.
|
|
*/
|
|
reg_data = __raw_readl(sim->ioaddr + CNTL);
|
|
reg_data &= ~SIM_CNTL_BAUD_SEL_MASK;
|
|
reg_data |= SIM_CNTL_BAUD_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->checking_ts_timing = 1;
|
|
sim->state = SIM_STATE_ATR_RECEIVING;
|
|
|
|
/*Enable the RIM and GPC interrupt, disalbe the CWT interrupt*/
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data |= SIM_INT_MASK_CWTM;
|
|
reg_data &= ~(SIM_INT_MASK_RIM | 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;
|
|
/*
|
|
* Ensure the CWT timer is enabled.
|
|
*/
|
|
sim_set_cwt(sim, 1);
|
|
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 &&
|
|
sim->checking_ts_timing == 1) {
|
|
|
|
if ((tx_status & SIM_XMT_STATUS_GPCNT) &&
|
|
!(rx_status & SIM_RCV_STATUS_RDRF)) {
|
|
/*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 | SIM_INT_MASK_RIM);
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
sim->errval = SIM_ERROR_ATR_DELAY;
|
|
complete(&sim->xfer_done);
|
|
sim->checking_ts_timing = 0;
|
|
} else if (rx_status & SIM_RCV_STATUS_RDRF) {
|
|
/*
|
|
* Reset/stop the GPCNT timer first.
|
|
*/
|
|
sim_reset_timer(sim);
|
|
|
|
/*Enable GPC, CWT interrupt and
|
|
*disable the rx full interrupt
|
|
*/
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data &= ~(SIM_INT_MASK_GPCM | SIM_INT_MASK_CWTM);
|
|
reg_data |= SIM_INT_MASK_RIM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
sim_rcv_read_fifo(sim);
|
|
|
|
/*Clear the GPCNT expiring status*/
|
|
__raw_writel(SIM_XMT_STATUS_GPCNT,
|
|
sim->ioaddr + XMT_STATUS);
|
|
|
|
/*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);
|
|
|
|
sim_start_timer(sim, SIM_CNTL_GPCNT_ETU_CLK);
|
|
|
|
/*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);
|
|
sim->checking_ts_timing = 0;
|
|
} else {
|
|
pr_err("Unexpected irq when delay checking\n");
|
|
}
|
|
}
|
|
|
|
else if (sim->state == SIM_STATE_ATR_RECEIVING) {
|
|
if ((rx_status & SIM_RCV_STATUS_CWT) ||
|
|
((tx_status & SIM_XMT_STATUS_GPCNT) &&
|
|
(sim->rcv_count != 0))) {
|
|
|
|
/*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 CWT timer*/
|
|
sim_set_cwt(sim, 1);
|
|
}
|
|
if (tx_status & SIM_XMT_STATUS_XTE) {
|
|
/*Disable TX*/
|
|
sim_set_tx(sim, 0);
|
|
/*Disalbe the timers*/
|
|
sim_set_cwt(sim, 0);
|
|
sim_set_bwt(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_cwt(sim, 0);
|
|
sim_set_bwt(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_cwt(sim, 0);
|
|
sim_set_bwt(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 ((sim->state == SIM_STATE_RESET_SEQUENCY) &&
|
|
(tx_status & SIM_XMT_STATUS_GPCNT))
|
|
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__);
|
|
|
|
if (sim->port_index == 1)
|
|
__raw_writel(SIM_SETUP_SPS_PORT1, sim->ioaddr + SETUP);
|
|
else
|
|
__raw_writel(SIM_SETUP_SPS_PORT0, sim->ioaddr + SETUP);
|
|
|
|
/*1 ~ 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);
|
|
if (sim->port_index == 1)
|
|
reg_data |= SIM_OD_CONFIG_OD_P1;
|
|
else
|
|
reg_data |= SIM_OD_CONFIG_OD_P0;
|
|
|
|
__raw_writel(reg_data, sim->ioaddr + OD_CONFIG);
|
|
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
/*One pin mode*/
|
|
reg_data |= SIM_PORT_CNTL_3VOLT | SIM_PORT_CNTL_STEN;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
/* presense detect */
|
|
pr_debug("%s p0_det is 0x%x \n", __func__,
|
|
__raw_readl(sim->ioaddr + sim->port_detect_reg));
|
|
if (__raw_readl(sim->ioaddr + sim->port_detect_reg)
|
|
& SIM_PORT_DETECT_SPDP) {
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_detect_reg);
|
|
reg_data &= ~SIM_PORT_DETECT_SPDS;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_detect_reg);
|
|
sim->present = SIM_PRESENT_REMOVED;
|
|
sim->state = SIM_STATE_REMOVED;
|
|
} else {
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_detect_reg);
|
|
reg_data |= SIM_PORT_DETECT_SPDS;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_detect_reg);
|
|
sim->present = SIM_PRESENT_DETECTED;
|
|
sim->state = SIM_STATE_DETECTED;
|
|
};
|
|
|
|
/*enable card interrupt. clear interrupt status*/
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_detect_reg);
|
|
reg_data |= SIM_PORT_DETECT_SDI;
|
|
reg_data |= SIM_PORT_DETECT_SDIM;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_detect_reg);
|
|
};
|
|
|
|
static void sim_cold_reset_sequency(struct sim_t *sim)
|
|
{
|
|
u32 reg_data;
|
|
|
|
sim->state = SIM_STATE_RESET_SEQUENCY;
|
|
|
|
/*set VCC*/
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
if (sim->sven_low_active)
|
|
reg_data &= ~SIM_PORT_CNTL_SVEN;
|
|
else
|
|
reg_data |= SIM_PORT_CNTL_SVEN;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
msleep(9);
|
|
|
|
/*enable CLK*/
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data |= SIM_PORT_CNTL_SCEN;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
/*RST low time*/
|
|
sim_reset_low_timing(sim, EMV_RESET_LOW_CYCLES);
|
|
|
|
/*RST high*/
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data |= SIM_PORT_CNTL_SRST;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
/*wait for ATR*/
|
|
sim_set_gpc_timer(sim, ATR_MAX_DELAY_CLK);
|
|
};
|
|
|
|
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) {
|
|
if (sim->sven_low_active) {
|
|
/*Set the RESET to be low*/
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data &= ~SIM_PORT_CNTL_SRST;
|
|
writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
usleep_range(2, 5);
|
|
|
|
/*Set the clock to be low*/
|
|
reg_data &= ~SIM_PORT_CNTL_SCEN;
|
|
writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
usleep_range(2, 5);
|
|
|
|
/*Set the sven to be high*/
|
|
reg_data |= SIM_PORT_CNTL_SVEN;
|
|
writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
} else {
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data |= SIM_PORT_CNTL_SAPD;
|
|
__raw_writel(reg_data,
|
|
sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data |= SIM_PORT_CNTL_SFPD;
|
|
__raw_writel(reg_data,
|
|
sim->ioaddr + sim->port_ctrl_reg);
|
|
}
|
|
} else
|
|
pr_err(">>>No card%s\n", __func__);
|
|
};
|
|
|
|
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;
|
|
|
|
sim->state = SIM_STATE_RESET_SEQUENCY;
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data |= (SIM_PORT_CNTL_SRST | SIM_PORT_CNTL_SCEN);
|
|
if (sim->sven_low_active)
|
|
reg_data &= ~SIM_PORT_CNTL_SVEN;
|
|
else
|
|
reg_data |= SIM_PORT_CNTL_SVEN;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
usleep_range(20, 25);
|
|
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data &= ~SIM_PORT_CNTL_SRST;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
|
|
sim_reset_low_timing(sim, EMV_RESET_LOW_CYCLES);
|
|
|
|
reg_data = __raw_readl(sim->ioaddr + sim->port_ctrl_reg);
|
|
reg_data |= SIM_PORT_CNTL_SRST;
|
|
__raw_writel(reg_data, sim->ioaddr + sim->port_ctrl_reg);
|
|
sim_set_gpc_timer(sim, ATR_MAX_DELAY_CLK);
|
|
}
|
|
|
|
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 and disalbe CWT timers when tx*/
|
|
sim_set_bwt(sim, 1);
|
|
sim_set_cwt(sim, 0);
|
|
|
|
/*Disalbe RX*/
|
|
sim_set_rx(sim, 0);
|
|
|
|
/*Enable TX*/
|
|
sim_set_tx(sim, 1);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void sim_clear_rx_buf(struct sim_t *sim)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < SIM_RCV_BUFFER_SIZE; i++)
|
|
sim->rcv_buffer[i] = 0;
|
|
sim->rcv_count = 0;
|
|
sim->rcv_head = 0;
|
|
}
|
|
|
|
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;
|
|
unsigned char __user *atr_buffer;
|
|
unsigned char __user *xmt_buffer;
|
|
unsigned char __user *rcv_buffer;
|
|
|
|
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;
|
|
}
|
|
|
|
__get_user(atr_buffer, &((sim_atr_t __user *)arg)->atr_buffer);
|
|
ret = copy_to_user(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_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;
|
|
}
|
|
|
|
__get_user(xmt_buffer, &((sim_xmt_t *)arg)->xmt_buffer);
|
|
ret = copy_from_user(sim->xmt_buffer, xmt_buffer, sim->xmt_remaining);
|
|
|
|
if (ret) {
|
|
pr_err("Copy Error\n");
|
|
errval = ret;
|
|
break;
|
|
}
|
|
|
|
sim_clear_rx_buf(sim);
|
|
sim_set_cwt(sim, 0);
|
|
sim_set_bwt(sim, 0);
|
|
/*Flush the tx rx fifo*/
|
|
sim_flush_fifo(sim, 1, 1);
|
|
sim->xmt_pos = 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_cwt(sim, 0);
|
|
sim_set_bwt(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->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_cwt(sim, 1);
|
|
sim_set_bwt(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_cwt(sim, 0);
|
|
sim_set_bwt(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;
|
|
}
|
|
|
|
__get_user(rcv_buffer, &((sim_rcv_t *)arg)->rcv_buffer);
|
|
ret = copy_to_user(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;
|
|
}
|
|
|
|
ret = sim_check_baud_rate(&sim->baud_rate);
|
|
if (ret) {
|
|
pr_err("Invalid baud rate value\n");
|
|
errval = ret;
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
|
|
sim->open_cnt = 1;
|
|
errval = pm_runtime_get_sync(sim_dev.parent);
|
|
if (errval < 0)
|
|
return errval;
|
|
|
|
errval = sim_reset_module(sim);
|
|
if (errval < 0)
|
|
goto out_runtime_put;
|
|
|
|
sim_data_reset(sim);
|
|
|
|
return 0;
|
|
|
|
out_runtime_put:
|
|
pm_runtime_put_sync(sim_dev.parent);
|
|
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 + sim->port_detect_reg);
|
|
__raw_writel(reg_data | SIM_PORT_DETECT_SDIM,
|
|
sim->ioaddr + sim->port_detect_reg);
|
|
|
|
if (sim->present != SIM_PRESENT_REMOVED)
|
|
sim_deactivate(sim);
|
|
|
|
pm_runtime_put_sync(sim_dev.parent);
|
|
sim->open_cnt = 0;
|
|
|
|
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,
|
|
}, {
|
|
.name = "imx6ul-sim",
|
|
.driver_data = SIM_QUIRK_TKT259347,
|
|
}, {
|
|
/* sentinel */
|
|
}
|
|
};
|
|
|
|
enum imx_sim_type {
|
|
IMX7D_SIM = 0,
|
|
IMX6UL_SIM,
|
|
};
|
|
|
|
static const struct of_device_id sim_imx_dt_ids[] = {
|
|
{ .compatible = "fsl,imx7d-sim",
|
|
.data = &imx_sim_devtype[IMX7D_SIM], },
|
|
{ .compatible = "fsl,imx6ul-sim",
|
|
.data = &imx_sim_devtype[IMX6UL_SIM], },
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, sim_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;
|
|
struct device_node *of_node = pdev->dev.of_node;
|
|
|
|
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->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!sim->res) {
|
|
pr_err("Can't get the MEMORY\n");
|
|
return -ENOMEM;
|
|
}
|
|
sim->ioaddr = devm_ioremap_resource(&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;
|
|
}
|
|
|
|
sim->sven_low_active = of_property_read_bool(of_node,
|
|
"sven_low_active");
|
|
|
|
ret = of_property_read_u32(of_node, "port", &sim->port_index);
|
|
if (ret)
|
|
sim->port_index = 0;
|
|
sim->port_ctrl_reg = (sim->port_index == 0) ?
|
|
PORT0_CNTL : PORT1_CNTL;
|
|
sim->port_detect_reg = (sim->port_index == 0) ?
|
|
PORT0_DETECT : PORT1_DETECT;
|
|
sim->quirks = pdev->id_entry->driver_data;
|
|
|
|
platform_set_drvdata(pdev, sim);
|
|
|
|
/*
|
|
*@todo: Need to figure a better way if possible.
|
|
*/
|
|
sim_dev.parent = &(pdev->dev);
|
|
|
|
pm_runtime_get_noresume(&pdev->dev);
|
|
pm_runtime_set_active(&pdev->dev);
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
sim->open_cnt = 1;
|
|
ret = clk_prepare_enable(sim->clk);
|
|
if (ret)
|
|
return ret;
|
|
/* Let pm_runtime_put_snyc() disable the clock.
|
|
* If CONFIG_PM is not enabled, the clock will stay powered.
|
|
*/
|
|
pm_runtime_put_sync(&pdev->dev);
|
|
sim->open_cnt = 0;
|
|
|
|
misc_register(&sim_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sim_remove(struct platform_device *pdev)
|
|
{
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
|
misc_deregister(&sim_dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused sim_suspend(struct device *dev)
|
|
{
|
|
int err;
|
|
|
|
err = pm_runtime_force_suspend(dev);
|
|
if (err)
|
|
return err;
|
|
|
|
pinctrl_pm_select_sleep_state(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused sim_resume(struct device *dev)
|
|
{
|
|
int err;
|
|
|
|
err = pm_runtime_force_resume(dev);
|
|
if (err)
|
|
return err;
|
|
|
|
pinctrl_pm_select_default_state(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused sim_runtime_suspend(struct device *dev)
|
|
{
|
|
struct sim_t *sim = dev_get_drvdata(dev);
|
|
|
|
if (sim->open_cnt)
|
|
clk_disable_unprepare(sim->clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __maybe_unused sim_runtime_resume(struct device *dev)
|
|
{
|
|
int err;
|
|
struct sim_t *sim = dev_get_drvdata(dev);
|
|
|
|
if (sim->open_cnt) {
|
|
err = clk_prepare_enable(sim->clk);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops sim_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(sim_suspend, sim_resume)
|
|
SET_RUNTIME_PM_OPS(sim_runtime_suspend, sim_runtime_resume, NULL)
|
|
};
|
|
|
|
static struct platform_driver sim_driver = {
|
|
.driver = {
|
|
.name = DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
.pm = &sim_pm_ops,
|
|
.of_match_table = sim_imx_dt_ids,
|
|
},
|
|
.probe = sim_probe,
|
|
.remove = sim_remove,
|
|
.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");
|
|
|