Files
linux-legacy/drivers/char/imx_sim.c
Rob Herring be0524d38f ENGR00117389 Port 5.0.0 release to 2.6.31
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>
2010-08-10 11:44:41 -05:00

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");