1866 lines
48 KiB
C
Executable File
1866 lines
48 KiB
C
Executable File
/*
|
|
* 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/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;
|
|
}
|
|
|
|
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 + 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);
|
|
|
|
|
|
if (sim->open_cnt)
|
|
clk_disable_unprepare(sim->clk);
|
|
|
|
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->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_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);
|
|
|
|
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);
|
|
|
|
pinctrl_pm_select_sleep_state(&pdev->dev);
|
|
|
|
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);
|
|
|
|
pinctrl_pm_select_default_state(&pdev->dev);
|
|
|
|
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");
|
|
|