mirror of
https://github.com/genesi/linux-legacy.git
synced 2026-04-24 06:41:03 +00:00
This is i.MX BSP 5.0.0 release ported to 2.6.31 Signed-off-by: Rob Herring <r.herring@freescale.com> Signed-off-by: Alan Tull <r80115@freescale.com> Signed-off-by: Xinyu Chen <xinyu.chen@freescale.com>
1498 lines
39 KiB
C
1498 lines
39 KiB
C
/*
|
|
* Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
|
|
*/
|
|
|
|
/*
|
|
* The code contained herein is licensed under the GNU General Public
|
|
* License. You may obtain a copy of the GNU General Public License
|
|
* Version 2 or later at the following locations:
|
|
*
|
|
* http://www.opensource.org/licenses/gpl-license.html
|
|
* http://www.gnu.org/copyleft/gpl.html
|
|
*/
|
|
|
|
/*!
|
|
* @file mxc_sim.c
|
|
*
|
|
* @brief Driver for Freescale IMX SIM interface
|
|
*
|
|
*/
|
|
|
|
#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/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 <asm/io.h>
|
|
#include <mach/hardware.h>
|
|
|
|
#define SIM_INTERNAL_CLK 0
|
|
#define SIM_RFU -1
|
|
|
|
/* Default communication parameters: FI=372, DI=1, PI1=5V, II=50mA, WWT=10 */
|
|
#define SIM_PARAM_DEFAULT { 0, 1, 1, 5, 1, 0, 0, 0, 10 }
|
|
|
|
/* Transmit and receive buffer sizes */
|
|
#define SIM_XMT_BUFFER_SIZE 256
|
|
#define SIM_RCV_BUFFER_SIZE 256
|
|
|
|
/* 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_OPERATIONAL_IDLE 1
|
|
#define SIM_STATE_OPERATIONAL_COMMAND 2
|
|
#define SIM_STATE_OPERATIONAL_RESPONSE 3
|
|
#define SIM_STATE_OPERATIONAL_STATUS1 4
|
|
#define SIM_STATE_OPERATIONAL_STATUS2 5
|
|
#define SIM_STATE_OPERATIONAL_PTS 6
|
|
#define SIM_STATE_DETECTED_ATR_T0 7
|
|
#define SIM_STATE_DETECTED_ATR_TS 8
|
|
#define SIM_STATE_DETECTED_ATR_TXI 9
|
|
#define SIM_STATE_DETECTED_ATR_THB 10
|
|
#define SIM_STATE_DETECTED_ATR_TCK 11
|
|
|
|
/* Definitions of the offset of the SIM hardware registers */
|
|
#define PORT1_CNTL 0x00 /* 00 */
|
|
#define SETUP 0x04 /* 04 */
|
|
#define PORT1_DETECT 0x08 /* 08 */
|
|
#define PORT1_XMT_BUF 0x0C /* 0c */
|
|
#define PORT1_RCV_BUF 0x10 /* 10 */
|
|
#define PORT0_CNTL 0x14 /* 14 */
|
|
#define CNTL 0x18 /* 18 */
|
|
#define CLK_PRESCALER 0x1C /* 1c */
|
|
#define RCV_THRESHOLD 0x20 /* 20 */
|
|
#define ENABLE 0x24 /* 24 */
|
|
#define XMT_STATUS 0x28 /* 28 */
|
|
#define RCV_STATUS 0x2C /* 2c */
|
|
#define INT_MASK 0x30 /* 30 */
|
|
#define PORTO_XMT_BUF 0x34 /* 34 */
|
|
#define PORT0_RCV_BUF 0x38 /* 38 */
|
|
#define PORT0_DETECT 0x3C /* 3c */
|
|
#define DATA_FORMAT 0x40 /* 40 */
|
|
#define XMT_THRESHOLD 0x44 /* 44 */
|
|
#define GUARD_CNTL 0x48 /* 48 */
|
|
#define OD_CONFIG 0x4C /* 4c */
|
|
#define RESET_CNTL 0x50 /* 50 */
|
|
#define CHAR_WAIT 0x54 /* 54 */
|
|
#define GPCNT 0x58 /* 58 */
|
|
#define DIVISOR 0x5C /* 5c */
|
|
#define BWT 0x60 /* 60 */
|
|
#define BGT 0x64 /* 64 */
|
|
#define BWT_H 0x68 /* 68 */
|
|
#define XMT_FIFO_STAT 0x6C /* 6c */
|
|
#define RCV_FIFO_CNT 0x70 /* 70 */
|
|
#define RCV_FIFO_WPTR 0x74 /* 74 */
|
|
#define RCV_FIFO_RPTR 0x78 /* 78 */
|
|
|
|
/* SIM port[0|1]_cntl register bits */
|
|
#define SIM_PORT_CNTL_SFPD (1<<7)
|
|
#define SIM_PORT_CNTL_3VOLT (1<<6)
|
|
#define SIM_PORT_CNTL_SCSP (1<<5)
|
|
#define SIM_PORT_CNTL_SCEN (1<<4)
|
|
#define SIM_PORT_CNTL_SRST (1<<3)
|
|
#define SIM_PORT_CNTL_STEN (1<<2)
|
|
#define SIM_PORT_CNTL_SVEN (1<<1)
|
|
#define SIM_PORT_CNTL_SAPD (1<<0)
|
|
|
|
/* SIM od_config register bits */
|
|
#define SIM_OD_CONFIG_OD_P1 (1<<1)
|
|
#define SIM_OD_CONFIG_OD_P0 (1<<0)
|
|
|
|
/* SIM enable register bits */
|
|
#define SIM_ENABLE_XMTEN (1<<1)
|
|
#define SIM_ENABLE_RCVEN (1<<0)
|
|
|
|
/* SIM int_mask register bits */
|
|
#define SIM_INT_MASK_RFEM (1<<13)
|
|
#define SIM_INT_MASK_BGTM (1<<12)
|
|
#define SIM_INT_MASK_BWTM (1<<11)
|
|
#define SIM_INT_MASK_RTM (1<<10)
|
|
#define SIM_INT_MASK_CWTM (1<<9)
|
|
#define SIM_INT_MASK_GPCM (1<<8)
|
|
#define SIM_INT_MASK_TDTFM (1<<7)
|
|
#define SIM_INT_MASK_TFOM (1<<6)
|
|
#define SIM_INT_MASK_XTM (1<<5)
|
|
#define SIM_INT_MASK_TFEIM (1<<4)
|
|
#define SIM_INT_MASK_ETCIM (1<<3)
|
|
#define SIM_INT_MASK_OIM (1<<2)
|
|
#define SIM_INT_MASK_TCIM (1<<1)
|
|
#define SIM_INT_MASK_RIM (1<<0)
|
|
|
|
/* SIM xmt_status register bits */
|
|
#define SIM_XMT_STATUS_GPCNT (1<<8)
|
|
#define SIM_XMT_STATUS_TDTF (1<<7)
|
|
#define SIM_XMT_STATUS_TFO (1<<6)
|
|
#define SIM_XMT_STATUS_TC (1<<5)
|
|
#define SIM_XMT_STATUS_ETC (1<<4)
|
|
#define SIM_XMT_STATUS_TFE (1<<3)
|
|
#define SIM_XMT_STATUS_XTE (1<<0)
|
|
|
|
/* SIM rcv_status register bits */
|
|
#define SIM_RCV_STATUS_BGT (1<<11)
|
|
#define SIM_RCV_STATUS_BWT (1<<10)
|
|
#define SIM_RCV_STATUS_RTE (1<<9)
|
|
#define SIM_RCV_STATUS_CWT (1<<8)
|
|
#define SIM_RCV_STATUS_CRCOK (1<<7)
|
|
#define SIM_RCV_STATUS_LRCOK (1<<6)
|
|
#define SIM_RCV_STATUS_RDRF (1<<5)
|
|
#define SIM_RCV_STATUS_RFD (1<<4)
|
|
#define SIM_RCV_STATUS_RFE (1<<1)
|
|
#define SIM_RCV_STATUS_OEF (1<<0)
|
|
|
|
/* SIM cntl register bits */
|
|
#define SIM_CNTL_BWTEN (1<<15)
|
|
#define SIM_CNTL_XMT_CRC_LRC (1<<14)
|
|
#define SIM_CNTL_CRCEN (1<<13)
|
|
#define SIM_CNTL_LRCEN (1<<12)
|
|
#define SIM_CNTL_CWTEN (1<<11)
|
|
#define SIM_CNTL_SAMPLE12 (1<<4)
|
|
#define SIM_CNTL_ONACK (1<<3)
|
|
#define SIM_CNTL_ANACK (1<<2)
|
|
#define SIM_CNTL_ICM (1<<1)
|
|
#define SIM_CNTL_GPCNT_CLK_SEL(x) ((x&0x03)<<9)
|
|
#define SIM_CNTL_GPCNT_CLK_SEL_MASK (0x03<<9)
|
|
#define SIM_CNTL_BAUD_SEL(x) ((x&0x07)<<6)
|
|
#define SIM_CNTL_BAUD_SEL_MASK (0x07<<6)
|
|
|
|
/* SIM rcv_threshold register bits */
|
|
#define SIM_RCV_THRESHOLD_RTH(x) ((x&0x0f)<<9)
|
|
#define SIM_RCV_THRESHOLD_RTH_MASK (0x0f<<9)
|
|
#define SIM_RCV_THRESHOLD_RDT(x) ((x&0x1ff)<<0)
|
|
#define SIM_RCV_THRESHOLD_RDT_MASK (0x1ff<<0)
|
|
|
|
/* SIM xmt_threshold register bits */
|
|
#define SIM_XMT_THRESHOLD_XTH(x) ((x&0x0f)<<4)
|
|
#define SIM_XMT_THRESHOLD_XTH_MASK (0x0f<<4)
|
|
#define SIM_XMT_THRESHOLD_TDT(x) ((x&0x0f)<<0)
|
|
#define SIM_XMT_THRESHOLD_TDT_MASK (0x0f<<0)
|
|
|
|
/* SIM guard_cntl register bits */
|
|
#define SIM_GUARD_CNTL_RCVR11 (1<<8)
|
|
#define SIM_GIARD_CNTL_GETU(x) (x&0xff)
|
|
#define SIM_GIARD_CNTL_GETU_MASK (0xff)
|
|
|
|
/* SIM port[0|]_detect register bits */
|
|
#define SIM_PORT_DETECT_SPDS (1<<3)
|
|
#define SIM_PORT_DETECT_SPDP (1<<2)
|
|
#define SIM_PORT_DETECT_SDI (1<<1)
|
|
#define SIM_PORT_DETECT_SDIM (1<<0)
|
|
|
|
/* END of REGS definitions */
|
|
|
|
/* ATR parser data (the parser state is stored in the main device structure) */
|
|
typedef struct {
|
|
uint8_t T0; /* ATR T0 */
|
|
uint8_t TS; /* ATR TS */
|
|
/* ATR TA1, TB1, TC1, TD1, TB1, ... , TD4 */
|
|
uint8_t TXI[16];
|
|
uint8_t THB[15]; /* ATR historical bytes */
|
|
uint8_t TCK; /* ATR checksum */
|
|
uint16_t ifc_valid; /* valid interface characters */
|
|
uint8_t ifc_current_valid; /* calid ifcs in the current batch */
|
|
uint8_t cnt; /* number of current batch */
|
|
uint8_t num_hb; /* number of historical bytes */
|
|
} sim_atrparser_t;
|
|
|
|
/* Main SIM driver structure */
|
|
typedef struct {
|
|
/* card inserted = 1, ATR received = 2, card removed = 0 */
|
|
int present;
|
|
/* current ATR or OPS state */
|
|
int state;
|
|
/* current power state */
|
|
int power;
|
|
/* error code occured during transfer */
|
|
int errval;
|
|
struct clk *clk; /* Clock id */
|
|
uint8_t clk_flag;
|
|
struct resource *res; /* IO map memory */
|
|
void __iomem *ioaddr; /* Mapped address */
|
|
int ipb_irq; /* sim ipb IRQ num */
|
|
int dat_irq; /* sim dat IRQ num */
|
|
/* parser for incoming ATR stream */
|
|
sim_atrparser_t atrparser;
|
|
/* raw ATR stream received */
|
|
sim_atr_t atr;
|
|
/* communication parameters according to ATR */
|
|
sim_param_t param_atr;
|
|
/* current communication parameters */
|
|
sim_param_t param;
|
|
/* current TPDU or PTS transfer */
|
|
sim_xfer_t xfer;
|
|
/* transfer is on the way = 1, idle = 2 */
|
|
int xfer_ongoing;
|
|
/* remaining bytes to transmit for the current transfer */
|
|
int xmt_remaining;
|
|
/* transmit position */
|
|
int xmt_pos;
|
|
/* receive position / number of bytes received */
|
|
int rcv_count;
|
|
uint8_t rcv_buffer[SIM_RCV_BUFFER_SIZE];
|
|
uint8_t 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;
|
|
} sim_t;
|
|
|
|
static int sim_param_F[] = {
|
|
SIM_INTERNAL_CLK, 372, 558, 744, 1116, 1488, 1860, SIM_RFU,
|
|
SIM_RFU, 512, 768, 1024, 1536, 2048, SIM_RFU, SIM_RFU
|
|
};
|
|
|
|
static int sim_param_D[] = {
|
|
SIM_RFU, 64 * 1, 64 * 2, 64 * 4, 64 * 8, 64 * 16, SIM_RFU, SIM_RFU,
|
|
SIM_RFU, SIM_RFU, 64 * 1 / 2, 64 * 1 / 4, 64 * 1 / 8, 64 * 1 / 16,
|
|
64 * 1 / 32, 64 * 1 / 64
|
|
};
|
|
|
|
static struct miscdevice sim_dev;
|
|
|
|
/* Function: sim_calc_param
|
|
*
|
|
* Description: determine register values depending on communication parameters
|
|
*
|
|
* Parameters:
|
|
* uint32_t fi ATR frequency multiplier index
|
|
* uint32_t di ATR frequency divider index
|
|
* uint32_t* ptr_divisor location to store divisor result
|
|
* uint32_t* ptr_sample12 location to store sample12 result
|
|
*
|
|
* Return Values:
|
|
* SIM_OK calculation finished without errors
|
|
* -SIM_E_PARAM_DIVISOR_RANGE calculated divisor > 255
|
|
* -SIM_E_PARAM_FBYD_NOTDIVBY8OR12 F/D not divisable by 12 (as required)
|
|
* -SIM_E_PARAM_FBYD_WITHFRACTION F/D has a remainder
|
|
* -SIM_E_PARAM_DI_INVALID frequency multiplyer index not supported
|
|
* -SIM_E_PARAM_FI_INVALID frequency divider index not supported
|
|
*/
|
|
|
|
static int sim_calc_param(uint32_t fi, uint32_t di, uint32_t *ptr_divisor,
|
|
uint32_t *ptr_sample12)
|
|
{
|
|
int32_t errval = SIM_OK;
|
|
int32_t f = sim_param_F[fi];
|
|
int32_t d = sim_param_D[di];
|
|
int32_t stage2_fra = (64 * f) % d;
|
|
int32_t stage2_div = (64 * f) / d;
|
|
uint32_t sample12 = 1;
|
|
uint32_t divisor = 31;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
if ((f > 0) || (d > 0)) {
|
|
if (stage2_fra == 0) {
|
|
if ((stage2_div % 12) == 0) {
|
|
sample12 = 1;
|
|
divisor = stage2_div / 12;
|
|
} else if ((stage2_div % 8) == 0) {
|
|
sample12 = 0;
|
|
divisor = stage2_div / 8;
|
|
} else
|
|
sample12 = -1;
|
|
if (sample12 >= 0) {
|
|
if (divisor < 256) {
|
|
pr_debug("fi=%i", fi);
|
|
pr_debug("di=%i", di);
|
|
pr_debug("f=%i", f);
|
|
pr_debug("d=%i/64", d);
|
|
pr_debug("div=%i", stage2_div);
|
|
pr_debug("divisor=%i", divisor);
|
|
pr_debug("sample12=%i\n", sample12);
|
|
|
|
*ptr_divisor = divisor;
|
|
*ptr_sample12 = sample12;
|
|
errval = SIM_OK;
|
|
} else
|
|
errval = -SIM_E_PARAM_DIVISOR_RANGE;
|
|
} else
|
|
errval = -SIM_E_PARAM_FBYD_NOTDIVBY8OR12;
|
|
} else
|
|
errval = -SIM_E_PARAM_FBYD_WITHFRACTION;
|
|
} else
|
|
errval = -SIM_E_PARAM_FI_INVALID;
|
|
|
|
return errval;
|
|
};
|
|
|
|
/* Function: sim_set_param
|
|
*
|
|
* Description: apply communication parameters (setup devisor and sample12)
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
* sim_param_t* param pointer to communication parameters
|
|
*
|
|
* Return Values:
|
|
* see function sim_calc_param
|
|
*/
|
|
|
|
static int sim_set_param(sim_t *sim, sim_param_t *param)
|
|
{
|
|
uint32_t divisor, sample12, reg_data;
|
|
int errval;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
errval = sim_calc_param(param->FI, param->DI, &divisor, &sample12);
|
|
if (errval == SIM_OK) {
|
|
__raw_writel(divisor, sim->ioaddr + DIVISOR);
|
|
if (sample12) {
|
|
reg_data = __raw_readl(sim->ioaddr + CNTL);
|
|
reg_data |= SIM_CNTL_SAMPLE12;
|
|
__raw_writel(reg_data, sim->ioaddr + CNTL);
|
|
} else {
|
|
reg_data = __raw_readl(sim->ioaddr + CNTL);
|
|
reg_data &= ~SIM_CNTL_SAMPLE12;
|
|
__raw_writel(reg_data, sim->ioaddr + CNTL);
|
|
}
|
|
}
|
|
|
|
return errval;
|
|
};
|
|
|
|
/* Function: sim_atr_received
|
|
*
|
|
* Description: this function is called whenever a valid ATR has been received.
|
|
* It determines the communication parameters from the ATR received and notifies
|
|
* the user space application with SIGIO.
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_atr_received(sim_t *sim)
|
|
{
|
|
sim_param_t param_default = SIM_PARAM_DEFAULT;
|
|
sim->param_atr = param_default;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TA1))) {
|
|
sim->param_atr.FI = sim->atrparser.TXI[SIM_IFC_TA1] >> 4;
|
|
sim->param_atr.DI = sim->atrparser.TXI[SIM_IFC_TA1] & 0x0f;
|
|
}
|
|
if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TB1))) {
|
|
sim->param_atr.PI1 = (sim->atrparser.TXI[SIM_IFC_TB1] >> 4)
|
|
& 0x07;
|
|
sim->param_atr.II = sim->atrparser.TXI[SIM_IFC_TB1] & 0x07f;
|
|
}
|
|
if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TC1)))
|
|
sim->param_atr.N = sim->atrparser.TXI[SIM_IFC_TC1];
|
|
|
|
if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TD1)))
|
|
sim->param_atr.T = sim->atrparser.TXI[SIM_IFC_TD1] & 0x0f;
|
|
|
|
if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TB2)))
|
|
sim->param_atr.PI2 = sim->atrparser.TXI[SIM_IFC_TB2];
|
|
|
|
if (sim->atrparser.ifc_valid & (1 << (SIM_IFC_TC2)))
|
|
sim->param_atr.WWT = sim->atrparser.TXI[SIM_IFC_TC2];
|
|
|
|
if (sim->fasync)
|
|
kill_fasync(&sim->fasync, SIGIO, POLL_IN);
|
|
|
|
};
|
|
|
|
/* Function: sim_xmt_fill
|
|
*
|
|
* Description: fill the transmit FIFO until the FIFO is full or
|
|
* the end of the transmission has been reached.
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_xmt_fill(sim_t *sim)
|
|
{
|
|
uint32_t reg_data;
|
|
int bytesleft;
|
|
|
|
reg_data = __raw_readl(sim->ioaddr + XMT_FIFO_STAT);
|
|
bytesleft = 16 - ((reg_data >> 8) & 0x0f);
|
|
|
|
pr_debug("txfill: remaining=%i bytesleft=%i\n",
|
|
sim->xmt_remaining, bytesleft);
|
|
if (bytesleft > sim->xmt_remaining)
|
|
bytesleft = sim->xmt_remaining;
|
|
|
|
sim->xmt_remaining -= bytesleft;
|
|
for (; bytesleft > 0; bytesleft--) {
|
|
__raw_writel(sim->xmt_buffer[sim->xmt_pos],
|
|
sim->ioaddr + PORT1_XMT_BUF);
|
|
sim->xmt_pos++;
|
|
};
|
|
/* FIXME: optimization - keep filling until fifo full */
|
|
};
|
|
|
|
/* Function: sim_xmt_start
|
|
*
|
|
* Description: initiate a transfer
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
* int pos position in the xfer transmit buffer
|
|
* int count number of bytes to be transmitted
|
|
*/
|
|
|
|
static void sim_xmt_start(sim_t *sim, int pos, int count)
|
|
{
|
|
uint32_t reg_data;
|
|
|
|
pr_debug("tx\n");
|
|
sim->xmt_remaining = count;
|
|
sim->xmt_pos = pos;
|
|
sim_xmt_fill(sim);
|
|
|
|
if (sim->xmt_remaining) {
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data &= ~SIM_INT_MASK_TDTFM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
} else {
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data &= ~SIM_INT_MASK_TCIM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
__raw_writel(SIM_XMT_STATUS_TC | SIM_XMT_STATUS_TDTF,
|
|
sim->ioaddr + XMT_STATUS);
|
|
reg_data = __raw_readl(sim->ioaddr + ENABLE);
|
|
reg_data |= SIM_ENABLE_XMTEN;
|
|
__raw_writel(reg_data, sim->ioaddr + ENABLE);
|
|
}
|
|
};
|
|
|
|
/* Function: sim_atr_add
|
|
*
|
|
* Description: add a byte to the raw ATR string
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
* uint8_t data byte to be added
|
|
*/
|
|
|
|
static void sim_atr_add(sim_t *sim, uint8_t data)
|
|
{
|
|
pr_debug("%s entering.\n", __func__);
|
|
if (sim->atr.size < SIM_ATR_LENGTH_MAX)
|
|
sim->atr.t[sim->atr.size++] = data;
|
|
else
|
|
printk(KERN_ERR "sim.c: ATR received is too big!\n");
|
|
};
|
|
|
|
/* Function: sim_fsm
|
|
*
|
|
* Description: main finite state machine running in ISR context.
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
* uint8_t data byte received
|
|
*/
|
|
|
|
static void sim_fsm(sim_t *sim, uint16_t data)
|
|
{
|
|
uint32_t temp, i = 0;
|
|
switch (sim->state) {
|
|
|
|
pr_debug("%s stat is %d \n", __func__, sim->state);
|
|
/* OPS FSM */
|
|
|
|
case SIM_STATE_OPERATIONAL_IDLE:
|
|
printk(KERN_INFO "data received unexpectidly (%04x)\n", data);
|
|
break;
|
|
|
|
case SIM_STATE_OPERATIONAL_COMMAND:
|
|
if (data == sim->xmt_buffer[1]) {
|
|
if (sim->xfer.rcv_length) {
|
|
sim->state = SIM_STATE_OPERATIONAL_RESPONSE;
|
|
} else {
|
|
sim->state = SIM_STATE_OPERATIONAL_STATUS1;
|
|
if (sim->xfer.xmt_length > 5)
|
|
sim_xmt_start(sim, 5,
|
|
sim->xfer.xmt_length - 5);
|
|
};
|
|
} else if (((data & 0xf0) == 0x60) | ((data & 0xf0) == 0x90)) {
|
|
sim->xfer.sw1 = data;
|
|
sim->state = SIM_STATE_OPERATIONAL_STATUS2;
|
|
} else {
|
|
sim->errval = -SIM_E_NACK;
|
|
complete(&sim->xfer_done);
|
|
};
|
|
break;
|
|
|
|
case SIM_STATE_OPERATIONAL_RESPONSE:
|
|
sim->rcv_buffer[sim->rcv_count] = data;
|
|
sim->rcv_count++;
|
|
if (sim->rcv_count == sim->xfer.rcv_length)
|
|
sim->state = SIM_STATE_OPERATIONAL_STATUS1;
|
|
break;
|
|
|
|
case SIM_STATE_OPERATIONAL_STATUS1:
|
|
sim->xfer.sw1 = data;
|
|
sim->state = SIM_STATE_OPERATIONAL_STATUS2;
|
|
break;
|
|
|
|
case SIM_STATE_OPERATIONAL_STATUS2:
|
|
sim->xfer.sw2 = data;
|
|
sim->state = SIM_STATE_OPERATIONAL_IDLE;
|
|
complete(&sim->xfer_done);
|
|
break;
|
|
|
|
case SIM_STATE_OPERATIONAL_PTS:
|
|
sim->rcv_buffer[sim->rcv_count] = data;
|
|
sim->rcv_count++;
|
|
if (sim->rcv_count == sim->xfer.rcv_length)
|
|
sim->state = SIM_STATE_OPERATIONAL_IDLE;
|
|
break;
|
|
|
|
/* ATR FSM */
|
|
|
|
case SIM_STATE_DETECTED_ATR_T0:
|
|
sim_atr_add(sim, data);
|
|
pr_debug("T0 %02x\n", data);
|
|
sim->atrparser.T0 = data;
|
|
sim->state = SIM_STATE_DETECTED_ATR_TS;
|
|
break;
|
|
|
|
case SIM_STATE_DETECTED_ATR_TS:
|
|
sim_atr_add(sim, data);
|
|
pr_debug("TS %02x\n", data);
|
|
sim->atrparser.TS = data;
|
|
if (data & 0xf0) {
|
|
sim->atrparser.ifc_current_valid = (data >> 4) & 0x0f;
|
|
sim->atrparser.num_hb = data & 0x0f;
|
|
sim->atrparser.ifc_valid = 0;
|
|
sim->state = SIM_STATE_DETECTED_ATR_TXI;
|
|
sim->atrparser.cnt = 0;
|
|
} else {
|
|
goto sim_fsm_atr_thb;
|
|
};
|
|
break;
|
|
|
|
case SIM_STATE_DETECTED_ATR_TXI:
|
|
sim_atr_add(sim, data);
|
|
i = ffs(sim->atrparser.ifc_current_valid) - 1;
|
|
pr_debug("T%c%i %02x\n", 'A' + i, sim->atrparser.cnt + 1, data);
|
|
sim->atrparser.TXI[SIM_IFC_TXI(i, sim->atrparser.cnt)] = data;
|
|
sim->atrparser.ifc_valid |= 1 << SIM_IFC_TXI(i,
|
|
sim->atrparser.
|
|
cnt);
|
|
sim->atrparser.ifc_current_valid &= ~(1 << i);
|
|
|
|
if (sim->atrparser.ifc_current_valid == 0) {
|
|
if (i == 3) {
|
|
sim->atrparser.ifc_current_valid = (data >> 4)
|
|
& 0x0f;
|
|
sim->atrparser.cnt++;
|
|
|
|
if (sim->atrparser.cnt >= 4) {
|
|
/* error */
|
|
printk(KERN_ERR "ERROR !\n");
|
|
break;
|
|
};
|
|
|
|
if (sim->atrparser.ifc_current_valid == 0)
|
|
goto sim_fsm_atr_thb;
|
|
} else {
|
|
sim_fsm_atr_thb:
|
|
if (sim->atrparser.num_hb) {
|
|
sim->state = SIM_STATE_DETECTED_ATR_THB;
|
|
sim->atrparser.cnt = 0;
|
|
} else {
|
|
goto sim_fsm_atr_tck;
|
|
};
|
|
};
|
|
};
|
|
break;
|
|
|
|
case SIM_STATE_DETECTED_ATR_THB:
|
|
sim_atr_add(sim, data);
|
|
pr_debug("THB%i %02x\n", i, data);
|
|
sim->atrparser.THB[sim->atrparser.cnt] = data;
|
|
sim->atrparser.cnt++;
|
|
|
|
if (sim->atrparser.cnt == sim->atrparser.num_hb) {
|
|
sim_fsm_atr_tck:
|
|
i = sim->atrparser.ifc_valid & (1 << (SIM_IFC_TD1));
|
|
temp = sim->atrparser.TXI[SIM_IFC_TD1] & 0x0f;
|
|
if ((i && temp) == SIM_PROTOCOL_T1)
|
|
sim->state = SIM_STATE_DETECTED_ATR_TCK;
|
|
else
|
|
goto sim_fsm_atr_received;
|
|
};
|
|
break;
|
|
|
|
case SIM_STATE_DETECTED_ATR_TCK:
|
|
sim_atr_add(sim, data);
|
|
/* checksum not required for T=0 */
|
|
sim->atrparser.TCK = data;
|
|
sim_fsm_atr_received:
|
|
sim->state = SIM_STATE_OPERATIONAL_IDLE;
|
|
sim->present = SIM_PRESENT_OPERATIONAL;
|
|
sim_atr_received(sim);
|
|
break;
|
|
};
|
|
};
|
|
|
|
/* Function: sim_irq_handler
|
|
*
|
|
* Description: interrupt service routine.
|
|
*
|
|
* Parameters:
|
|
* int irq interrupt number
|
|
* void *dev_id pointer to SIM device handler
|
|
*
|
|
* Return values:
|
|
* IRQ_HANDLED OS specific
|
|
*/
|
|
|
|
static irqreturn_t sim_irq_handler(int irq, void *dev_id)
|
|
{
|
|
uint32_t reg_data, reg_data0, reg_data1;
|
|
|
|
sim_t *sim = (sim_t *) dev_id;
|
|
|
|
pr_debug("%s entering\n", __func__);
|
|
|
|
reg_data0 = __raw_readl(sim->ioaddr + XMT_STATUS);
|
|
reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
|
|
if ((reg_data0 & SIM_XMT_STATUS_TC)
|
|
&& (!(reg_data1 & SIM_INT_MASK_TCIM))) {
|
|
pr_debug("TC_IRQ\n");
|
|
__raw_writel(SIM_XMT_STATUS_TC, sim->ioaddr + XMT_STATUS);
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data |= SIM_INT_MASK_TCIM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
reg_data = __raw_readl(sim->ioaddr + ENABLE);
|
|
reg_data &= ~SIM_ENABLE_XMTEN;
|
|
__raw_writel(reg_data, sim->ioaddr + ENABLE);
|
|
};
|
|
|
|
reg_data0 = __raw_readl(sim->ioaddr + XMT_STATUS);
|
|
reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
|
|
if ((reg_data0 & SIM_XMT_STATUS_TDTF)
|
|
&& (!(reg_data1 & SIM_INT_MASK_TDTFM))) {
|
|
pr_debug("TDTF_IRQ\n");
|
|
__raw_writel(SIM_XMT_STATUS_TDTF, sim->ioaddr + XMT_STATUS);
|
|
sim_xmt_fill(sim);
|
|
|
|
if (sim->xmt_remaining == 0) {
|
|
__raw_writel(SIM_XMT_STATUS_TC,
|
|
sim->ioaddr + XMT_STATUS);
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data &= ~SIM_INT_MASK_TCIM;
|
|
reg_data |= SIM_INT_MASK_TDTFM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
};
|
|
};
|
|
|
|
reg_data0 = __raw_readl(sim->ioaddr + RCV_STATUS);
|
|
reg_data1 = __raw_readl(sim->ioaddr + INT_MASK);
|
|
if ((reg_data0 & SIM_RCV_STATUS_RDRF)
|
|
&& (!(reg_data1 & SIM_INT_MASK_RIM))) {
|
|
pr_debug("%s RDRF_IRQ\n", __func__);
|
|
__raw_writel(SIM_RCV_STATUS_RDRF, sim->ioaddr + RCV_STATUS);
|
|
|
|
while (__raw_readl(sim->ioaddr + RCV_FIFO_CNT)) {
|
|
uint32_t data;
|
|
data = __raw_readl(sim->ioaddr + PORT1_RCV_BUF);
|
|
pr_debug("RX = %02x state = %i\n", data, sim->state);
|
|
if (data & 0x700) {
|
|
if (sim->xfer_ongoing) {
|
|
/* error */
|
|
printk(KERN_ERR "ERROR !\n");
|
|
return IRQ_HANDLED;
|
|
};
|
|
} else
|
|
sim_fsm(sim, data);
|
|
};
|
|
};
|
|
|
|
reg_data0 = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
if (reg_data0 & SIM_PORT_DETECT_SDI) {
|
|
pr_debug("%s PD_IRQ\n", __func__);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
reg_data |= SIM_PORT_DETECT_SDI;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
|
|
|
reg_data0 = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
if (reg_data0 & SIM_PORT_DETECT_SPDP) {
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
reg_data &= ~SIM_PORT_DETECT_SPDS;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
|
|
|
if (sim->present != SIM_PRESENT_REMOVED) {
|
|
pr_debug("Removed sim card\n");
|
|
sim->present = SIM_PRESENT_REMOVED;
|
|
sim->state = SIM_STATE_REMOVED;
|
|
|
|
if (sim->fasync)
|
|
kill_fasync(&sim->fasync,
|
|
SIGIO, POLL_IN);
|
|
};
|
|
} else {
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
reg_data |= SIM_PORT_DETECT_SPDS;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
|
|
|
if (sim->present == SIM_PRESENT_REMOVED) {
|
|
pr_debug("Inserted sim card\n");
|
|
sim->state = SIM_STATE_DETECTED_ATR_T0;
|
|
sim->present = SIM_PRESENT_DETECTED;
|
|
|
|
if (sim->fasync)
|
|
kill_fasync(&sim->fasync,
|
|
SIGIO, POLL_IN);
|
|
};
|
|
};
|
|
};
|
|
|
|
return IRQ_HANDLED;
|
|
};
|
|
|
|
/* Function: sim_power_on
|
|
*
|
|
* Description: run the power on sequence
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_power_on(sim_t *sim)
|
|
{
|
|
uint32_t reg_data;
|
|
|
|
/* power on sequence */
|
|
pr_debug("%s Powering on the sim port.\n", __func__);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data |= SIM_PORT_CNTL_SVEN;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
msleep(10);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data |= SIM_PORT_CNTL_SCEN;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
msleep(10);
|
|
reg_data = SIM_RCV_THRESHOLD_RTH(0) | SIM_RCV_THRESHOLD_RDT(1);
|
|
__raw_writel(reg_data, sim->ioaddr + RCV_THRESHOLD);
|
|
__raw_writel(SIM_RCV_STATUS_RDRF, sim->ioaddr + RCV_STATUS);
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data &= ~SIM_INT_MASK_RIM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
__raw_writel(31, sim->ioaddr + DIVISOR);
|
|
reg_data = __raw_readl(sim->ioaddr + CNTL);
|
|
reg_data |= SIM_CNTL_SAMPLE12;
|
|
__raw_writel(reg_data, sim->ioaddr + CNTL);
|
|
reg_data = __raw_readl(sim->ioaddr + ENABLE);
|
|
reg_data |= SIM_ENABLE_RCVEN;
|
|
__raw_writel(reg_data, sim->ioaddr + ENABLE);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data |= SIM_PORT_CNTL_SRST;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
pr_debug("%s port0_ctl is 0x%x.\n", __func__,
|
|
__raw_readl(sim->ioaddr + PORT0_CNTL));
|
|
sim->power = SIM_POWER_ON;
|
|
};
|
|
|
|
/* Function: sim_power_off
|
|
*
|
|
* Description: run the power off sequence
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_power_off(sim_t *sim)
|
|
{
|
|
uint32_t reg_data;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
/* sim_power_off sequence */
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data &= ~SIM_PORT_CNTL_SCEN;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
reg_data = __raw_readl(sim->ioaddr + ENABLE);
|
|
reg_data &= ~SIM_ENABLE_RCVEN;
|
|
__raw_writel(reg_data, sim->ioaddr + ENABLE);
|
|
reg_data = __raw_readl(sim->ioaddr + INT_MASK);
|
|
reg_data |= SIM_INT_MASK_RIM;
|
|
__raw_writel(reg_data, sim->ioaddr + INT_MASK);
|
|
__raw_writel(0, sim->ioaddr + RCV_THRESHOLD);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data &= ~SIM_PORT_CNTL_SRST;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data &= ~SIM_PORT_CNTL_SVEN;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
sim->power = SIM_POWER_OFF;
|
|
};
|
|
|
|
/* Function: sim_start
|
|
*
|
|
* Description: ramp up the SIM interface
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_start(sim_t *sim)
|
|
{
|
|
uint32_t reg_data, clk_rate, clk_div = 0;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
/* Configuring SIM for Operation */
|
|
reg_data = SIM_XMT_THRESHOLD_XTH(0) | SIM_XMT_THRESHOLD_TDT(4);
|
|
__raw_writel(reg_data, sim->ioaddr + XMT_THRESHOLD);
|
|
__raw_writel(0, sim->ioaddr + SETUP);
|
|
/* ~ 4 MHz */
|
|
clk_rate = clk_get_rate(sim->clk);
|
|
clk_div = clk_rate / sim->plat_data->clk_rate;
|
|
if (clk_rate % sim->plat_data->clk_rate)
|
|
clk_div++;
|
|
pr_debug("%s prescaler is 0x%x.\n", __func__, clk_div);
|
|
__raw_writel(clk_div, sim->ioaddr + CLK_PRESCALER);
|
|
|
|
reg_data = SIM_CNTL_GPCNT_CLK_SEL(0) | SIM_CNTL_BAUD_SEL(7)
|
|
| SIM_CNTL_SAMPLE12 | SIM_CNTL_ANACK | SIM_CNTL_ICM;
|
|
__raw_writel(reg_data, sim->ioaddr + CNTL);
|
|
__raw_writel(31, sim->ioaddr + DIVISOR);
|
|
reg_data = __raw_readl(sim->ioaddr + OD_CONFIG);
|
|
reg_data |= SIM_OD_CONFIG_OD_P0;
|
|
__raw_writel(reg_data, sim->ioaddr + OD_CONFIG);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data |= SIM_PORT_CNTL_3VOLT | SIM_PORT_CNTL_STEN;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
|
|
/* presense detect */
|
|
pr_debug("%s p0_det is 0x%x \n", __func__,
|
|
__raw_readl(sim->ioaddr + PORT0_DETECT));
|
|
if (__raw_readl(sim->ioaddr + PORT0_DETECT) & SIM_PORT_DETECT_SPDP) {
|
|
pr_debug("%s card removed \n", __func__);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
reg_data &= ~SIM_PORT_DETECT_SPDS;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
|
sim->present = SIM_PRESENT_REMOVED;
|
|
sim->state = SIM_STATE_REMOVED;
|
|
} else {
|
|
pr_debug("%s card inserted \n", __func__);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
reg_data |= SIM_PORT_DETECT_SPDS;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
|
sim->present = SIM_PRESENT_DETECTED;
|
|
sim->state = SIM_STATE_DETECTED_ATR_T0;
|
|
};
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
reg_data |= SIM_PORT_DETECT_SDI;
|
|
reg_data &= ~SIM_PORT_DETECT_SDIM;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
|
|
|
/*
|
|
* Since there is no PD0 layout on MX51, assume
|
|
* that there is a SIM card in slot defaulty.
|
|
* */
|
|
if (0 == (sim->plat_data->detect)) {
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
reg_data |= SIM_PORT_DETECT_SPDS;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_DETECT);
|
|
sim->present = SIM_PRESENT_DETECTED;
|
|
sim->state = SIM_STATE_DETECTED_ATR_T0;
|
|
}
|
|
|
|
if (sim->present == SIM_PRESENT_DETECTED)
|
|
sim_power_on(sim);
|
|
|
|
};
|
|
|
|
/* Function: sim_stop
|
|
*
|
|
* Description: shut down the SIM interface
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_stop(sim_t *sim)
|
|
{
|
|
pr_debug("%s entering.\n", __func__);
|
|
__raw_writel(0, sim->ioaddr + SETUP);
|
|
__raw_writel(0, sim->ioaddr + ENABLE);
|
|
__raw_writel(0, sim->ioaddr + PORT0_CNTL);
|
|
__raw_writel(0x06, sim->ioaddr + CNTL);
|
|
__raw_writel(0, sim->ioaddr + CLK_PRESCALER);
|
|
__raw_writel(0, sim->ioaddr + SETUP);
|
|
__raw_writel(0, sim->ioaddr + OD_CONFIG);
|
|
__raw_writel(0, sim->ioaddr + XMT_THRESHOLD);
|
|
__raw_writel(0xb8, sim->ioaddr + XMT_STATUS);
|
|
__raw_writel(4, sim->ioaddr + RESET_CNTL);
|
|
mdelay(1);
|
|
};
|
|
|
|
/* Function: sim_data_reset
|
|
*
|
|
* Description: reset a SIM structure to default values
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_data_reset(sim_t *sim)
|
|
{
|
|
sim_param_t param_default = SIM_PARAM_DEFAULT;
|
|
sim->present = SIM_PRESENT_REMOVED;
|
|
sim->state = SIM_STATE_REMOVED;
|
|
sim->power = SIM_POWER_OFF;
|
|
sim->errval = SIM_OK;
|
|
memset(&sim->atrparser, 0, sizeof(sim->atrparser));
|
|
memset(&sim->atr, 0, sizeof(sim->atr));
|
|
sim->param_atr = param_default;
|
|
memset(&sim->param, 0, sizeof(sim->param));
|
|
memset(&sim->xfer, 0, sizeof(sim->xfer));
|
|
sim->xfer_ongoing = 0;
|
|
sim->xmt_remaining = 0;
|
|
sim->xmt_pos = 0;
|
|
sim->rcv_count = 0;
|
|
memset(sim->rcv_buffer, 0, SIM_RCV_BUFFER_SIZE);
|
|
memset(sim->xmt_buffer, 0, SIM_XMT_BUFFER_SIZE);
|
|
};
|
|
|
|
/* Function: sim_cold_reset
|
|
*
|
|
* Description: cold reset the SIM interface, including card
|
|
* power down and interface hardware reset.
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_cold_reset(sim_t *sim)
|
|
{
|
|
pr_debug("%s entering.\n", __func__);
|
|
if (sim->present != SIM_PRESENT_REMOVED) {
|
|
sim_power_off(sim);
|
|
sim_stop(sim);
|
|
sim_data_reset(sim);
|
|
sim->state = SIM_STATE_DETECTED_ATR_T0;
|
|
sim->present = SIM_PRESENT_DETECTED;
|
|
msleep(50);
|
|
sim_start(sim);
|
|
sim_power_on(sim);
|
|
};
|
|
};
|
|
|
|
/* Function: sim_warm_reset
|
|
*
|
|
* Description: warm reset the SIM interface: just invoke the
|
|
* reset signal and reset the SIM structure for the interface.
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static void sim_warm_reset(sim_t *sim)
|
|
{
|
|
uint32_t reg_data;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
if (sim->present != SIM_PRESENT_REMOVED) {
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data |= SIM_PORT_CNTL_SRST;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
sim_data_reset(sim);
|
|
msleep(50);
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_CNTL);
|
|
reg_data &= ~SIM_PORT_CNTL_SRST;
|
|
__raw_writel(reg_data, sim->ioaddr + PORT0_CNTL);
|
|
};
|
|
};
|
|
|
|
/* Function: sim_card_lock
|
|
*
|
|
* Description: physically lock the SIM card.
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static int sim_card_lock(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;
|
|
};
|
|
|
|
/* Function: sim_card_eject
|
|
*
|
|
* Description: physically unlock and eject the SIM card.
|
|
*
|
|
* Parameters:
|
|
* sim_t* sim pointer to SIM device handler
|
|
*/
|
|
|
|
static int sim_card_eject(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;
|
|
};
|
|
|
|
/* Function: sim_ioctl
|
|
*
|
|
* Description: handle ioctl calls
|
|
*
|
|
* Parameters: OS specific
|
|
*/
|
|
|
|
static int sim_ioctl(struct inode *inode, struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret, errval = SIM_OK;
|
|
unsigned long timeout;
|
|
|
|
sim_t *sim = (sim_t *) file->private_data;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
switch (cmd) {
|
|
pr_debug("ioctl cmd %d is issued...\n", cmd);
|
|
|
|
case SIM_IOCTL_GET_ATR:
|
|
if (sim->present != SIM_PRESENT_OPERATIONAL) {
|
|
errval = -SIM_E_NOCARD;
|
|
break;
|
|
};
|
|
ret = copy_to_user((sim_atr_t *) arg, &sim->atr,
|
|
sizeof(sim_atr_t));
|
|
if (ret)
|
|
errval = -SIM_E_ACCESS;
|
|
break;
|
|
|
|
case SIM_IOCTL_GET_PARAM_ATR:
|
|
if (sim->present != SIM_PRESENT_OPERATIONAL) {
|
|
errval = -SIM_E_NOCARD;
|
|
break;
|
|
};
|
|
ret = copy_to_user((sim_param_t *) arg, &sim->param_atr,
|
|
sizeof(sim_param_t));
|
|
if (ret)
|
|
errval = -SIM_E_ACCESS;
|
|
break;
|
|
|
|
case SIM_IOCTL_GET_PARAM:
|
|
ret = copy_to_user((sim_param_t *) arg, &sim->param,
|
|
sizeof(sim_param_t));
|
|
if (ret)
|
|
errval = -SIM_E_ACCESS;
|
|
break;
|
|
|
|
case SIM_IOCTL_SET_PARAM:
|
|
ret = copy_from_user(&sim->param, (sim_param_t *) arg,
|
|
sizeof(sim_param_t));
|
|
if (ret)
|
|
errval = -SIM_E_ACCESS;
|
|
else
|
|
errval = sim_set_param(sim, &sim->param);
|
|
break;
|
|
|
|
case SIM_IOCTL_POWER_ON:
|
|
if (sim->power == SIM_POWER_ON) {
|
|
errval = -SIM_E_POWERED_ON;
|
|
break;
|
|
};
|
|
sim_power_on(sim);
|
|
break;
|
|
|
|
case SIM_IOCTL_POWER_OFF:
|
|
if (sim->power == SIM_POWER_OFF) {
|
|
errval = -SIM_E_POWERED_OFF;
|
|
break;
|
|
};
|
|
sim_power_off(sim);
|
|
break;
|
|
|
|
case SIM_IOCTL_COLD_RESET:
|
|
if (sim->power == SIM_POWER_OFF) {
|
|
errval = -SIM_E_POWERED_OFF;
|
|
break;
|
|
};
|
|
sim_cold_reset(sim);
|
|
break;
|
|
|
|
case SIM_IOCTL_WARM_RESET:
|
|
sim_warm_reset(sim);
|
|
if (sim->power == SIM_POWER_OFF) {
|
|
errval = -SIM_E_POWERED_OFF;
|
|
break;
|
|
};
|
|
break;
|
|
|
|
case SIM_IOCTL_XFER:
|
|
if (sim->present != SIM_PRESENT_OPERATIONAL) {
|
|
errval = -SIM_E_NOCARD;
|
|
break;
|
|
};
|
|
|
|
ret = copy_from_user(&sim->xfer, (sim_xfer_t *) arg,
|
|
sizeof(sim_xfer_t));
|
|
if (ret) {
|
|
errval = -SIM_E_ACCESS;
|
|
break;
|
|
};
|
|
|
|
ret = copy_from_user(sim->xmt_buffer, sim->xfer.xmt_buffer,
|
|
sim->xfer.xmt_length);
|
|
if (ret) {
|
|
errval = -SIM_E_ACCESS;
|
|
break;
|
|
};
|
|
|
|
sim->rcv_count = 0;
|
|
sim->xfer.sw1 = 0;
|
|
sim->xfer.sw2 = 0;
|
|
|
|
if (sim->xfer.type == SIM_XFER_TYPE_TPDU) {
|
|
if (sim->xfer.xmt_length < 5) {
|
|
errval = -SIM_E_TPDUSHORT;
|
|
break;
|
|
}
|
|
sim->state = SIM_STATE_OPERATIONAL_COMMAND;
|
|
} else if (sim->xfer.type == SIM_XFER_TYPE_PTS) {
|
|
if (sim->xfer.xmt_length == 0) {
|
|
errval = -SIM_E_PTSEMPTY;
|
|
break;
|
|
}
|
|
sim->state = SIM_STATE_OPERATIONAL_PTS;
|
|
} else {
|
|
errval = -SIM_E_INVALIDXFERTYPE;
|
|
break;
|
|
};
|
|
|
|
if (sim->xfer.xmt_length > SIM_XMT_BUFFER_SIZE) {
|
|
errval = -SIM_E_INVALIDXMTLENGTH;
|
|
break;
|
|
};
|
|
|
|
if (sim->xfer.rcv_length > SIM_XMT_BUFFER_SIZE) {
|
|
errval = -SIM_E_INVALIDRCVLENGTH;
|
|
break;
|
|
};
|
|
|
|
sim->errval = 0;
|
|
sim->xfer_ongoing = 1;
|
|
init_completion(&sim->xfer_done);
|
|
sim_xmt_start(sim, 0, 5);
|
|
timeout =
|
|
wait_for_completion_interruptible_timeout(&sim->xfer_done,
|
|
sim->xfer.
|
|
timeout);
|
|
sim->xfer_ongoing = 0;
|
|
|
|
if (sim->errval) {
|
|
errval = sim->errval;
|
|
break;
|
|
};
|
|
|
|
if (timeout == 0) {
|
|
errval = -SIM_E_TIMEOUT;
|
|
break;
|
|
}
|
|
|
|
ret = copy_to_user(sim->xfer.rcv_buffer, sim->rcv_buffer,
|
|
sim->xfer.rcv_length);
|
|
if (ret) {
|
|
errval = -SIM_E_ACCESS;
|
|
break;
|
|
};
|
|
|
|
ret = copy_to_user((sim_xfer_t *) arg, &sim->xfer,
|
|
sizeof(sim_xfer_t));
|
|
if (ret)
|
|
errval = -SIM_E_ACCESS;
|
|
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;
|
|
};
|
|
|
|
/* Function: sim_fasync
|
|
*
|
|
* Description: async handler
|
|
*
|
|
* Parameters: OS specific
|
|
*/
|
|
|
|
static int sim_fasync(int fd, struct file *file, int mode)
|
|
{
|
|
sim_t *sim = (sim_t *) file->private_data;
|
|
pr_debug("%s entering.\n", __func__);
|
|
return fasync_helper(fd, file, mode, &sim->fasync);
|
|
}
|
|
|
|
/* Function: sim_open
|
|
*
|
|
* Description: ramp up interface when being opened
|
|
*
|
|
* Parameters: OS specific
|
|
*/
|
|
|
|
static int sim_open(struct inode *inode, struct file *file)
|
|
{
|
|
int errval = SIM_OK;
|
|
|
|
sim_t *sim = dev_get_drvdata(sim_dev.parent);
|
|
file->private_data = sim;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
if (!sim->ioaddr) {
|
|
errval = -ENOMEM;
|
|
return errval;
|
|
}
|
|
|
|
if (!(sim->clk_flag)) {
|
|
pr_debug("\n%s enable the clock\n", __func__);
|
|
clk_enable(sim->clk);
|
|
sim->clk_flag = 1;
|
|
}
|
|
|
|
sim_start(sim);
|
|
|
|
return errval;
|
|
};
|
|
|
|
/* Function: sim_release
|
|
*
|
|
* Description: shut down interface when being closed
|
|
*
|
|
* Parameters: OS specific
|
|
*/
|
|
|
|
static int sim_release(struct inode *inode, struct file *file)
|
|
{
|
|
uint32_t reg_data;
|
|
|
|
sim_t *sim = (sim_t *) file->private_data;
|
|
|
|
pr_debug("%s entering.\n", __func__);
|
|
if (sim->clk_flag) {
|
|
pr_debug("\n%s disable the clock\n", __func__);
|
|
clk_disable(sim->clk);
|
|
sim->clk_flag = 0;
|
|
}
|
|
|
|
/* disable presense detection */
|
|
reg_data = __raw_readl(sim->ioaddr + PORT0_DETECT);
|
|
__raw_writel(reg_data | SIM_PORT_DETECT_SDIM,
|
|
sim->ioaddr + PORT0_DETECT);
|
|
|
|
if (sim->present != SIM_PRESENT_REMOVED) {
|
|
sim_power_off(sim);
|
|
if (sim->fasync)
|
|
kill_fasync(&sim->fasync, SIGIO, POLL_IN);
|
|
};
|
|
|
|
sim_stop(sim);
|
|
|
|
sim_fasync(-1, file, 0);
|
|
|
|
pr_debug("exit\n");
|
|
return 0;
|
|
};
|
|
|
|
static const struct file_operations sim_fops = {
|
|
.open = sim_open,
|
|
.ioctl = sim_ioctl,
|
|
.fasync = sim_fasync,
|
|
.release = sim_release
|
|
};
|
|
|
|
static struct miscdevice sim_dev = {
|
|
MISC_DYNAMIC_MINOR,
|
|
"mxc_sim",
|
|
&sim_fops
|
|
};
|
|
|
|
/*****************************************************************************\
|
|
* *
|
|
* Driver init/exit *
|
|
* *
|
|
\*****************************************************************************/
|
|
|
|
static int sim_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct mxc_sim_platform_data *sim_plat = pdev->dev.platform_data;
|
|
|
|
sim_t *sim = kzalloc(sizeof(sim_t), GFP_KERNEL);
|
|
|
|
if (sim == 0) {
|
|
ret = -ENOMEM;
|
|
printk(KERN_ERR "Can't get the MEMORY\n");
|
|
return ret;
|
|
};
|
|
|
|
BUG_ON(pdev == NULL);
|
|
|
|
sim->plat_data = sim_plat;
|
|
sim->clk_flag = 0;
|
|
|
|
sim->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!sim->res) {
|
|
ret = -ENOMEM;
|
|
printk(KERN_ERR "Can't get the MEMORY\n");
|
|
goto out;
|
|
}
|
|
|
|
/* request the sim clk and sim_serial_clk */
|
|
sim->clk = clk_get(NULL, sim->plat_data->clock_sim);
|
|
if (IS_ERR(sim->clk)) {
|
|
ret = PTR_ERR(sim->clk);
|
|
printk(KERN_ERR "Get CLK ERROR !\n");
|
|
goto out;
|
|
}
|
|
pr_debug("sim clock:%lu\n", clk_get_rate(sim->clk));
|
|
|
|
sim->ipb_irq = platform_get_irq(pdev, 0);
|
|
sim->dat_irq = platform_get_irq(pdev, 1);
|
|
if (!(sim->ipb_irq | sim->dat_irq)) {
|
|
ret = -ENOMEM;
|
|
goto out1;
|
|
}
|
|
|
|
if (!request_mem_region(sim->res->start,
|
|
sim->res->end -
|
|
sim->res->start + 1, pdev->name)) {
|
|
printk(KERN_ERR "request_mem_region failed\n");
|
|
ret = -ENOMEM;
|
|
goto out1;
|
|
}
|
|
|
|
sim->ioaddr = (void *)ioremap(sim->res->start, sim->res->end -
|
|
sim->res->start + 1);
|
|
if (sim->ipb_irq)
|
|
ret = request_irq(sim->ipb_irq, sim_irq_handler,
|
|
0, "mxc_sim_ipb", sim);
|
|
if (sim->dat_irq)
|
|
ret |= request_irq(sim->dat_irq, sim_irq_handler,
|
|
0, "mxc_sim_dat", sim);
|
|
|
|
if (ret) {
|
|
printk(KERN_ERR "Can't get the irq\n");
|
|
goto out2;
|
|
};
|
|
|
|
platform_set_drvdata(pdev, sim);
|
|
sim_dev.parent = &(pdev->dev);
|
|
|
|
misc_register(&sim_dev);
|
|
|
|
return ret;
|
|
out2:
|
|
if (sim->ipb_irq)
|
|
free_irq(sim->ipb_irq, sim);
|
|
if (sim->dat_irq)
|
|
free_irq(sim->dat_irq, sim);
|
|
release_mem_region(sim->res->start,
|
|
sim->res->end - sim->res->start + 1);
|
|
out1:
|
|
clk_put(sim->clk);
|
|
out:
|
|
kfree(sim);
|
|
return ret;
|
|
}
|
|
|
|
static int sim_remove(struct platform_device *pdev)
|
|
{
|
|
sim_t *sim = platform_get_drvdata(pdev);
|
|
|
|
clk_put(sim->clk);
|
|
|
|
if (sim->ipb_irq)
|
|
free_irq(sim->ipb_irq, sim);
|
|
if (sim->dat_irq)
|
|
free_irq(sim->dat_irq, sim);
|
|
|
|
iounmap(sim->ioaddr);
|
|
|
|
kfree(sim);
|
|
release_mem_region(sim->res->start,
|
|
sim->res->end - sim->res->start + 1);
|
|
|
|
misc_deregister(&sim_dev);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver sim_driver = {
|
|
.driver = {
|
|
.name = "mxc_sim",
|
|
},
|
|
.probe = sim_probe,
|
|
.remove = sim_remove,
|
|
.suspend = NULL,
|
|
.resume = NULL,
|
|
};
|
|
|
|
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");
|