Logo Search packages:      
Sourcecode: linux-fsl-imx51 version File versions  Download package

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

Generated by  Doxygen 1.6.0   Back to index