misc: rockchip-scr: add rockchip SOC smart card reader controller driver.
authorAiyoujun <ayj@rock-chips.com>
Fri, 18 Mar 2016 09:03:07 +0000 (17:03 +0800)
committerGerrit Code Review <gerrit@rock-chips.com>
Mon, 21 Mar 2016 03:03:15 +0000 (11:03 +0800)
Change-Id: I8d3ab66bc6fa7cbb4e8d9b2f2c5c2feee94a045b
Signed-off-by: Aiyoujun <ayj@rock-chips.com>
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/rk_scr.c [new file with mode: 0644]
drivers/misc/rk_scr.h [new file with mode: 0644]
include/misc/rk_scr_api.h [new file with mode: 0644]

index 98c020b560ac914f74345d5a24c88f4e2af4150e..9afddaf6a23ce80af7f4fcd337ef82f5559778be 100644 (file)
@@ -535,6 +535,13 @@ config UID_CPUTIME
        help
          Per UID based cpu time statistics exported to /proc/uid_cputime
 
+config ROCKCHIP_SCR
+       tristate "Rockchip Smartcard Reader Controller support"
+       default n
+       help
+         say Y here to enable Rockchip Smartcard Reader Controller driver
+         for Soc such as RK3128,RK322x,RK3288,RK3368,RK3366 and etc.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
index 24483a6caa6b2dce5e02d33166b27394aba9a7f8..5ded4471223a64b67982e62fc78e0d20d1f56536 100644 (file)
@@ -58,3 +58,4 @@ obj-$(CONFIG_ECHO)            += echo/
 obj-$(CONFIG_VEXPRESS_SYSCFG)  += vexpress-syscfg.o
 obj-$(CONFIG_CXL_BASE)         += cxl/
 obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o
+obj-$(CONFIG_ROCKCHIP_SCR)     += rk_scr.o
diff --git a/drivers/misc/rk_scr.c b/drivers/misc/rk_scr.c
new file mode 100644 (file)
index 0000000..63d5d4c
--- /dev/null
@@ -0,0 +1,1323 @@
+/*
+ * Driver for Rockchip Smart Card Reader Controller
+ *
+ * Copyright (C) 2012-2016 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/kthread.h>
+
+#include "rk_scr.h"
+
+#undef DEBUG_RK_SCR
+#define DEBUG_RK_SCR 1
+
+#if DEBUG_RK_SCR
+#define DAL_LOGV(x...) pr_info("RK_SCR: "x)
+#else
+#define DAL_LOGV(x...) do { } while (0)
+#endif
+
+#define SMC_DEFAULT_TIMEOUT            2000 /*ms*/
+#define SMC_RECEIVE_BUF_LEN            (64 * 1024)
+
+struct rk_scr_device {
+       int irq;
+       struct clk *clk_scr;
+       void __iomem *regs;
+       struct scr_chip_info chip_info[RK_SCR_NUM];
+       struct rk_scr scr[RK_SCR_NUM];
+       struct completion is_done;
+       struct mutex scr_mutex; /* mutex for scr operation */
+       unsigned char *recv_buffer;
+       unsigned recv_data_count;
+       unsigned recv_data_offset;
+       unsigned char atr_buffer[SMC_ATR_MAX_LENGTH];
+       unsigned char atr_length;
+};
+
+static struct rk_scr_device *rk_scr;
+
+static struct rk_scr *to_rk_scr(int id)
+{
+       if (id < RK_SCR_NUM)
+               return &rk_scr->scr[id];
+
+       return NULL;
+}
+
+static struct rk_scr *to_opened_rk_scr(int id)
+{
+       struct rk_scr *scr;
+
+       scr = to_rk_scr(id);
+
+       if (scr && scr->is_open)
+               return scr;
+
+       return NULL;
+}
+
+static void rk_scr_deactive(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+
+       DAL_LOGV("Deactive card\n");
+       scr_reg->CTRL2 |= DEACT;
+       scr_reg->CTRL1 = 0;
+       scr->is_active = false;
+}
+
+static void rk_scr_set_clk(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+       unsigned int freq_mhz;
+
+       freq_mhz = clk_get_rate(scr->clk) / 1000 / 1000;
+       DAL_LOGV("freq_mhz = %d\n", freq_mhz);
+       scr_reg->CGSCDIV = ((2 * freq_mhz / 13 - 1)
+                               + (freq_mhz / 8 - 1) + 1) / 2;
+       DAL_LOGV("scr_reg->CGSCDIV = %d\n", scr_reg->CGSCDIV);
+}
+
+static void rk_scr_set_work_waitingtime(struct rk_scr *scr,
+                                       unsigned char wi)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+       unsigned int wt;
+
+       DAL_LOGV("WI: %d\n", wi);
+       wt = 960 * wi * scr->D;
+       scr_reg->C2CLIM = (wt > 0x0FFFF) ? 0x0FFFF : wt;
+}
+
+static void rk_scr_set_etu_duration(struct rk_scr *scr,        unsigned int F,
+                                   unsigned int D)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+
+       DAL_LOGV("Set Etu F: %d D: %d\n", F, D);
+
+       scr->F = F;
+       scr->D = D;
+       scr_reg->CGBITDIV = (scr_reg->CGSCDIV + 1) * (F / D) - 1;
+       DAL_LOGV("scr_reg->CGBITDIV = %d\n", scr_reg->CGBITDIV);
+       scr_reg->CGBITTUNE = 0;
+
+       rk_scr_set_work_waitingtime(scr, 10);
+}
+
+static void rk_scr_set_scr_voltage(struct rk_scr *scr,
+                                  enum hal_scr_voltage_e level)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+
+       scr_reg->CTRL2 = 0;
+
+       switch (level) {
+       case HAL_SCR_VOLTAGE_CLASS_A:
+               scr_reg->CTRL2 |= VCC50;
+               break;
+
+       case HAL_SCR_VOLTAGE_CLASS_B:
+               scr_reg->CTRL2 |= VCC33;
+               break;
+
+       case HAL_SCR_VOLTAGE_CLASS_C:
+               scr_reg->CTRL2 |= VCC18;
+               break;
+
+       case HAL_SCR_VOLTAGE_NULL:
+               break;
+       }
+}
+
+static void rk_scr_powerdown(struct rk_scr *scr)
+{
+       rk_scr_set_scr_voltage(scr, HAL_SCR_VOLTAGE_NULL);
+}
+
+static void rk_scr_set_clockstop_mode(struct rk_scr *scr,
+                                     enum hal_scr_clock_stop_mode_e mode)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+
+       if (mode == HAL_SCR_CLOCK_STOP_L)
+               scr_reg->CTRL1 &= ~CLKSTOPVAL;
+       else if (mode == HAL_SCR_CLOCK_STOP_H)
+               scr_reg->CTRL1 |= CLKSTOPVAL;
+}
+
+static void rk_scr_clock_start(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+       int time_out = 10000;
+
+#ifdef SCR_DEBUG
+       scr_reg->INTEN1 = CLKSTOPRUN;
+#endif
+       scr_reg->CTRL1 &= ~CLKSTOP;
+#ifdef SCR_DEBUG
+       if (scr_reg->CTRL1 & CLKSTOP)
+               DAL_LOGV("Before clock is Stopped\n");
+       else
+               DAL_LOGV("Before clock is running\n");
+#endif
+       while ((scr_reg->CTRL1 & CLKSTOP) && (time_out-- > 0))
+               usleep_range(100, 110);
+}
+
+static void rk_scr_clock_stop(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+       int time_out = 10000;
+
+#ifdef SCR_DEBUG
+       scr_reg->INTEN1 = CLKSTOPRUN;
+#endif
+       scr_reg->CTRL1 |= CLKSTOP;
+       DAL_LOGV("Stop Clock\n");
+       if (scr->is_active) {
+               while ((!(scr_reg->CTRL1 & CLKSTOP)) && (time_out-- > 0))
+                       usleep_range(100, 110);
+       }
+}
+
+static void rk_scr_reset(struct rk_scr *scr, unsigned char *rx_buffer)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+
+       if (!rx_buffer)
+               DAL_LOGV("_scr_reset: invalid argument\n");
+
+       /*
+        * must disable all SCR interrupts.
+        * It will protect the global data.
+        */
+       scr_reg->INTEN1 = 0;
+
+       scr->rx_buf = rx_buffer;
+       scr->rx_expected = 0xff;
+       scr->rx_cnt = 0;
+
+       /*
+        * must in the critical section. If we don't, when we have written CTRL2
+        * before enable expected interrupts, other interrupts occurred,
+        * we may miss expected interrupts.
+        */
+       if (scr->is_active) {
+               DAL_LOGV("Warm Reset\n");
+               scr_reg->CTRL2 |= WARMRST;
+       } else {
+               DAL_LOGV("Active & Cold Reset\n");
+               scr->is_active = true;
+               scr_reg->CTRL1 = TXEN | RXEN | TS2FIFO | ATRSTFLUSH | GINTEN;
+               scr_reg->CTRL2 |= ACT;
+       }
+
+       /*
+        * If we enable the interrupts before write CTRL2, we may get
+        * expected interrupts which belong to the last transfer not
+        * for the reset.This may damage the global data.
+        */
+       scr_reg->RXFIFOTH = MAX_RXTHR;
+       scr_reg->TXFIFOTH = MAX_TXTHR;
+       scr_reg->INTEN1 = RXTHRESHOLD | RXFIFULL | RXPERR |
+                       C2CFULL | ATRFAIL | ATRDONE;
+       DAL_LOGV("Start Rx\n");
+}
+
+static void rk_scr_write_bytes(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+       int     count = FIFO_DEPTH - scr_reg->TXFIFOCNT;
+       int     remainder = scr->tx_expected - scr->tx_cnt;
+       int i = 0;
+
+       if (remainder < count)
+               count = remainder;
+
+       while (i++ < count)
+               scr_reg->FIFODATA = scr->tx_buf[scr->tx_cnt++];
+}
+
+static void rk_scr_read_bytes(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+       int count = scr_reg->RXFIFOCNT;
+       int remainder = scr->rx_expected - scr->rx_cnt;
+       int i = 0;
+
+       if (remainder < count)
+               count = remainder;
+
+       while (i++ < count)
+               scr->rx_buf[scr->rx_cnt++] = (unsigned char)scr_reg->FIFODATA;
+}
+
+static irqreturn_t rk_scr_irqhandler(int irq, void *priv)
+{
+       struct rk_scr *scr = (struct rk_scr *)priv;
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+       enum hal_scr_irq_cause_e user_cause = HAL_SCR_IRQ_INVALID;
+       unsigned int stat;
+
+       stat = (unsigned int)scr_reg->INTSTAT1;
+       if (!stat)
+               return 0;
+
+       if (stat & TXFIEMPTY) {
+               scr_reg->INTSTAT1 |= TXFIEMPTY;
+
+               /* during this period, TXFIEMPTY may occurred. */
+               rk_scr_write_bytes(scr);
+
+               if (scr->tx_cnt == scr->tx_expected) {
+                       scr_reg->INTEN1 &= ~TXFIEMPTY;
+                       scr_reg->INTSTAT1 |= TXFIEMPTY;
+               }
+       }
+#ifdef SCR_DEBUG
+       else if (stat & CLKSTOPRUN) {
+               scr_reg->INTSTAT1 |= CLKSTOPRUN;
+
+               if (scr_reg->CTRL1 & CLKSTOP)
+                       DAL_LOGV("Clock is stopped\n");
+               else
+                       DAL_LOGV("Clock is started\n");
+       }
+#endif
+       else if ((stat & RXTHRESHOLD) || (stat & RXFIFULL)) {
+               unsigned int threshold;
+
+               scr_reg->INTEN1 &= ~RXTHRESHOLD;
+               scr_reg->INTSTAT1 |= RXTHRESHOLD | RXFIFULL;
+
+               if (scr->rx_cnt < scr->rx_expected) {
+                       rk_scr_read_bytes(scr);
+                       if (scr->rx_cnt < scr->rx_expected) {
+                               unsigned int remainder =
+                                               scr->rx_expected - scr->rx_cnt;
+
+                               threshold = (remainder < MAX_RXTHR)
+                                               ? remainder : MAX_RXTHR;
+                       } else {
+                               scr_reg->INTEN1 &= ~C2CFULL;
+                               threshold = 1;
+                               if (scr->user_mask.rx_success)
+                                       user_cause = HAL_SCR_RX_SUCCESS;
+                       }
+               } else {
+                       threshold = 1;
+                       scr->rx_buf[scr->rx_cnt++] =
+                                       (unsigned char)scr_reg->FIFODATA;
+                       if (scr->user_mask.extra_rx)
+                               user_cause = HAL_SCR_EXTRA_RX;
+               }
+               scr_reg->INTEN1 |= RXTHRESHOLD;
+               /*
+                * when RX FIFO now is FULL,
+                * that will not generate RXTHRESHOLD interrupt.
+                * But it will generate RXFIFULL interrupt.
+                */
+               scr_reg->RXFIFOTH = FIFO_DEPTH;
+               scr_reg->RXFIFOTH = threshold;
+       } else if (stat & ATRDONE) {
+               DAL_LOGV("ATR Done\n");
+               scr_reg->INTSTAT1 |= ATRDONE;
+               scr_reg->INTEN1 = 0;
+               rk_scr_read_bytes(scr);
+               if (scr->user_mask.atr_success)
+                       user_cause = HAL_SCR_ATR_SUCCESS;
+       } else if (stat & ATRFAIL) {
+               DAL_LOGV("ATR Fail\n");
+
+               scr_reg->INTSTAT1 |= ATRFAIL;
+               scr_reg->INTEN1 = 0;
+
+               if (scr->user_mask.reset_timeout)
+                       user_cause = HAL_SCR_RESET_TIMEOUT;
+       } else if (stat & TXPERR) {
+               DAL_LOGV("TXPERR\n");
+
+               scr_reg->INTSTAT1 |= TXPERR;
+               scr_reg->INTEN1 = 0;
+
+               if (scr->user_mask.parity_error)
+                       user_cause = HAL_SCR_PARITY_ERROR;
+       } else if (stat & RXPERR) {
+               DAL_LOGV("RXPERR\n");
+               scr_reg->INTSTAT1 |= RXPERR;
+               scr_reg->INTEN1 = 0;
+               rk_scr_read_bytes(scr);
+               if (scr->user_mask.parity_error)
+                       user_cause = HAL_SCR_PARITY_ERROR;
+       } else if (stat & C2CFULL) {
+               DAL_LOGV("Timeout\n");
+               scr_reg->INTSTAT1 |= C2CFULL;
+               scr_reg->INTEN1 = 0;
+               rk_scr_read_bytes(scr);
+
+               if (scr->user_mask.wwt_timeout)
+                       user_cause = HAL_SCR_WWT_TIMEOUT;
+       }
+
+       if (user_cause != HAL_SCR_IRQ_INVALID) {
+               scr->in_process = false;
+               if (scr->user_handler)
+                       scr->user_handler(user_cause);
+       }
+       return 0;
+}
+
+static void _rk_scr_init(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+
+       rk_scr_deactive(scr);
+       rk_scr_set_clk(scr);
+       rk_scr_set_etu_duration(scr, 372, 1);
+
+       /* TXREPEAT = 3 & RXREPEAT = 3 */
+       scr_reg->REPEAT = 0x33;
+
+       /*
+        * Character LeadEdge to Character LeadEdge minimum waiting time
+        * in terms of ETUs. (GT)
+        */
+       scr_reg->SCGT = 12;
+
+       /*
+        * Character LeadEdge to Character LeadEdge maximum waiting time
+        * in terms of ETUs. (WT)
+        */
+       scr_reg->C2CLIM = 9600;
+
+       /*
+        * If no Vpp is necessary, the activation and deactivation part of Vpp
+        * can be omitted by clearing the AUTOADEAVPP bit in SCPADS register.
+        */
+       scr_reg->SCPADS = 0;
+
+       /*
+        * Activation / deactivation step time
+        * in terms of SmartCard Clock Cycles
+        */
+       scr_reg->ADEATIME = 0;
+
+       /*
+        * Duration of low state during Smart Card reset sequence
+        * in terms of smart card clock cycles
+        * require >
+        */
+       scr_reg->LOWRSTTIME = 1000;
+
+       /*
+        * ATR start limit - in terms of SmartCard Clock Cycles
+        * require 400 ~ 40000
+        */
+       scr_reg->ATRSTARTLIMIT = 40000;
+
+       /* enable the detect interrupt */
+       scr_reg->INTEN1 = SCINS;
+       scr_reg->INTEN2 = 0;
+
+       scr_reg->INTSTAT1 = 0xffff;
+       scr_reg->INTSTAT2 = 0xffff;
+
+       scr_reg->FIFOCTRL = FC_TXFIFLUSH | FC_RXFIFLUSH;
+       scr_reg->TXFIFOTH = 0;
+       scr_reg->RXFIFOTH = 0;
+
+       scr_reg->CTRL1 = 0;
+       scr_reg->CTRL2 = 0;
+}
+
+static void _rk_scr_deinit(struct rk_scr *scr)
+{
+       struct scr_reg_t *scr_reg = scr->hw->reg_base;
+
+       /* disable all interrupt */
+       scr_reg->INTEN1 = 0;
+       scr_reg->INTEN2 = 0;
+
+       rk_scr_deactive(scr);
+       rk_scr_powerdown(scr);
+}
+
+int rk_scr_freqchanged_notifiy(struct notifier_block *nb,
+                              unsigned long action, void *data, int len)
+{
+       int idx;
+       struct rk_scr *scr = NULL;
+       /*alter by xieshufa not sure*/
+       struct clk_notifier_data *msg;
+
+       switch (action) {
+       /*case ABORT_RATE_CHANGE:*/
+       case POST_RATE_CHANGE:
+               break;
+       default:
+               return 0;
+       }
+
+       msg = data;
+       for (idx = 0; idx < RK_SCR_NUM; idx++) {
+               struct rk_scr *p = to_rk_scr(idx);
+
+               if (msg->clk == p->clk) {
+                       scr = p;
+                       break;
+               }
+       }
+
+       if (scr) {
+               rk_scr_set_clk(scr);
+               rk_scr_set_etu_duration(scr, scr->F, scr->D);
+       }
+
+       return 0;
+}
+
+static int rk_scr_open(int id)
+{
+       struct rk_scr_device *rk_scr_dev = rk_scr;
+       struct rk_scr *scr = to_rk_scr(id);
+       struct hal_scr_irq_status_t
+               default_scr_user_mask = {1, 1, 1, 1, 1, 1, 1, 1};
+       int result = 0;
+
+       if (!scr)
+               return -1;
+
+       rk_scr_dev->chip_info[id].reg_base = rk_scr_dev->regs;
+       rk_scr_dev->chip_info[id].irq = rk_scr_dev->irq;
+
+       scr->hw = &rk_scr_dev->chip_info[id];
+       scr->clk = rk_scr_dev->clk_scr;
+
+       result = clk_prepare_enable(scr->clk);
+       DAL_LOGV("scr clk_enable result = %d\n", result);
+
+       (&scr->freq_changed_notifier)->priority = 0;
+       clk_notifier_register(scr->clk, &scr->freq_changed_notifier);
+       scr->user_mask = default_scr_user_mask;
+
+       _rk_scr_init(scr);
+
+       scr->is_open = true;
+
+       return 0;
+}
+
+static void rk_scr_close(int id)
+{
+       struct rk_scr *scr;
+
+       scr = to_opened_rk_scr(id);
+       if (!scr)
+               return;
+
+       scr->is_open = false;
+
+       _rk_scr_deinit(scr);
+
+       if (scr->clk) {
+               clk_disable(scr->clk);
+               clk_notifier_unregister(scr->clk, &scr->freq_changed_notifier);
+       }
+}
+
+static int rk_scr_read(int id, unsigned int n_rx_byte,
+                      unsigned char *p_rx_byte)
+{
+       struct rk_scr *scr;
+       struct scr_reg_t *scr_reg;
+       unsigned int inten1 = 0;
+
+       scr = to_opened_rk_scr(id);
+       if (!scr)
+               return -1;
+
+       if (!((n_rx_byte != 0) && (p_rx_byte))) {
+               DAL_LOGV("rk_scr_read: invalid argument\n");
+               return -1;
+       }
+
+       scr_reg = scr->hw->reg_base;
+
+       /*
+        * must disable all SCR interrupts.
+        * It will protect the global data.
+        */
+       scr_reg->INTEN1 = 0;
+
+       scr->rx_buf = p_rx_byte;
+       scr->rx_expected = n_rx_byte;
+       scr->rx_cnt = 0;
+
+       scr_reg->RXFIFOTH = (scr->rx_expected < MAX_RXTHR)
+                               ? scr->rx_expected : MAX_RXTHR;
+       inten1 = RXTHRESHOLD | RXFIFULL | RXPERR | C2CFULL;
+
+       scr_reg->INTEN1 = inten1;
+
+       return 0;
+}
+
+static int rk_scr_write(int id, unsigned int n_tx_byte,
+                       const unsigned char *p_tx_byte)
+{
+       struct rk_scr *scr;
+       struct scr_reg_t *scr_reg;
+       unsigned int inten1 = 0;
+       unsigned timeout_count = 1500;
+       unsigned long udelay = 0;
+
+       timeout_count = 1500;
+       udelay = msecs_to_jiffies(timeout_count) + jiffies;
+
+       scr = to_opened_rk_scr(id);
+       if (!scr)
+               return -1;
+
+       if (!((n_tx_byte != 0) && (p_tx_byte))) {
+               DAL_LOGV("rk_scr_write: invalid argument\n");
+               return -1;
+       }
+
+       scr_reg = scr->hw->reg_base;
+
+       /*
+        * must disable all SCR interrupts.
+        * It will protect the global data.
+        */
+       scr_reg->INTEN1 = 0;
+
+       scr->tx_buf = p_tx_byte;
+       scr->tx_expected = n_tx_byte;
+       scr->tx_cnt = 0;
+
+       scr_reg->FIFOCTRL = FC_TXFIFLUSH | FC_RXFIFLUSH;
+
+       /* send data until FIFO full or send over. */
+       while ((scr->tx_cnt < scr->tx_expected) &&
+              (time_before(jiffies, udelay))) {
+               if (!(scr_reg->FIFOCTRL & FC_TXFIFULL))
+                       scr_reg->FIFODATA = scr->tx_buf[scr->tx_cnt++];
+       }
+       /* need enable tx interrupt to continue */
+       if (scr->tx_cnt < scr->tx_expected) {
+               pr_err("\n@rk_scr_write: FC_TXFIFULL@\n");
+               inten1 |= TXFIEMPTY | TXPERR;
+       }
+
+       scr_reg->INTEN1 = inten1;
+
+       return 0;
+}
+
+int rk_scr_transfer(int id,
+                   unsigned int n_tx_byte, unsigned char *p_tx_byte,
+                   unsigned int n_rx_byte, unsigned char *p_rx_byte)
+{
+       struct rk_scr *scr;
+       struct scr_reg_t *scr_reg;
+       unsigned int inten1;
+
+       scr = to_opened_rk_scr(id);
+       if (!scr)
+               return -1;
+
+       if (!((n_tx_byte != 0) &&
+             (p_tx_byte) &&
+             (n_rx_byte != 0) &&
+             (p_rx_byte))) {
+               DAL_LOGV("rk_scr_transfer: invalid argument\n");
+               return -1;
+       }
+
+       if (scr->in_process)
+               return -1;
+
+       scr->in_process = true;
+       scr_reg = scr->hw->reg_base;
+
+       /*
+        * must disable all SCR interrupts.
+        * It will protect the global data.
+        */
+       scr_reg->INTEN1 = 0;
+       rk_scr_clock_start(scr);
+
+       scr->tx_buf = p_tx_byte;
+       scr->tx_expected = n_tx_byte;
+       scr->tx_cnt = 0;
+
+       scr->rx_buf = p_rx_byte;
+       scr->rx_expected = n_rx_byte;
+       scr->rx_cnt = 0;
+
+       scr_reg->FIFOCTRL = FC_TXFIFLUSH | FC_RXFIFLUSH;
+
+       scr_reg->RXFIFOTH = (scr->rx_expected < MAX_RXTHR)
+                               ? scr->rx_expected : MAX_RXTHR;
+       scr_reg->TXFIFOTH = MAX_TXTHR;
+
+       inten1 = RXTHRESHOLD | RXFIFULL | RXPERR | C2CFULL;
+
+       /* send data until FIFO full or send over. */
+       while ((scr->tx_cnt < scr->tx_expected) &&
+              !(scr_reg->FIFOCTRL & FC_TXFIFULL)) {
+               scr_reg->FIFODATA = scr->tx_buf[scr->tx_cnt++];
+       }
+
+       /* need enable tx interrupt to continue */
+       if (scr->tx_cnt < scr->tx_expected)
+               inten1 |= TXFIEMPTY | TXPERR;
+
+       scr_reg->INTEN1 = inten1;
+
+       return 0;
+}
+
+static enum hal_scr_id_e g_curr_sur_id = HAL_SCR_ID0;
+void _scr_init(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+
+       rk_scr_open(id);
+}
+
+void _scr_close(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+
+       rk_scr_close(id);
+}
+
+bool _scr_set_voltage(enum hal_scr_voltage_e level)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr) {
+               rk_scr_set_scr_voltage(scr, level);
+               return true;
+       }
+
+       return false;
+}
+
+void _scr_reset(unsigned char *rx_bytes)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               rk_scr_reset(scr, rx_bytes);
+}
+
+void _scr_set_etu_duration(unsigned int F, unsigned int D)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               rk_scr_set_etu_duration(scr, F, D);
+}
+
+void _scr_set_clock_stopmode(enum hal_scr_clock_stop_mode_e mode)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               rk_scr_set_clockstop_mode(scr, mode);
+}
+
+void _scr_set_work_waitingtime(unsigned char wi)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               rk_scr_set_work_waitingtime(scr, wi);
+}
+
+void _scr_clock_start(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               rk_scr_clock_start(scr);
+}
+
+void _scr_clock_stop(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               rk_scr_clock_stop(scr);
+}
+
+bool _scr_tx_byte(unsigned int n_tx_byte,
+                 const unsigned char *p_tx_byte)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       int ret = 0;
+
+       ret = rk_scr_write(id, n_tx_byte, p_tx_byte);
+       if (ret)
+               return false;
+       return true;
+}
+
+bool _scr_rx_byte(unsigned int n_rx_byte, unsigned char *p_rx_byte)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       int ret = 0;
+
+       ret = rk_scr_read(id, n_rx_byte, p_rx_byte);
+       if (ret)
+               return false;
+       return true;
+}
+
+bool _scr_tx_byte_rx_byte(unsigned int n_tx_byte,
+                         unsigned char *p_tx_byte,
+                         unsigned int n_rx_byte,
+                         unsigned char *p_rx_byte)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       int ret;
+
+       ret = rk_scr_transfer(id, n_tx_byte, p_tx_byte, n_rx_byte, p_rx_byte);
+       if (ret)
+               return false;
+
+       return true;
+}
+
+unsigned int _scr_get_num_rx_bytes(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               return scr->rx_cnt;
+
+       return 0;
+}
+
+unsigned int _scr_get_num_tx_bytes(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               return scr->tx_cnt;
+
+       return 0;
+}
+
+void _scr_powerdown(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_opened_rk_scr(id);
+
+       if (scr)
+               rk_scr_powerdown(scr);
+}
+
+void _scr_irq_set_handler(hal_scr_irq_handler_t handler)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_rk_scr(id);
+
+       if (scr)
+               scr->user_handler = handler;
+}
+
+void _scr_irq_set_mask(struct hal_scr_irq_status_t mask)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_rk_scr(id);
+
+       if (scr)
+               scr->user_mask = mask;
+}
+
+struct hal_scr_irq_status_t _scr_irq_get_mask(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_rk_scr(id);
+       struct hal_scr_irq_status_t user_mask = {0};
+
+       if (scr)
+               return scr->user_mask;
+
+       return user_mask;
+}
+
+enum hal_scr_detect_status_e _scr_irq_get_detect_status(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_rk_scr(id);
+
+       if (scr && (scr->hw->reg_base->SCPADS & SCPRESENT)) {
+               DAL_LOGV("\n scr_check_card_insert: yes.\n");
+               return SMC_DRV_INT_CARDIN;
+       }
+
+       DAL_LOGV("\n scr_check_card_insert: no.\n");
+       return SMC_DRV_INT_CARDOUT;
+}
+
+unsigned char _scr_rx_done(void)
+{
+       enum hal_scr_id_e id = g_curr_sur_id;
+       struct rk_scr *scr = to_rk_scr(id);
+
+       if (scr->hw->reg_base->INTSTAT1 & RXDONE)
+               return 1;
+       else
+               return 0;
+}
+
+void scr_set_etu_duration_struct(int f_and_d)
+{
+       switch (f_and_d) {
+       case HAL_SCR_ETU_F_372_AND_D_1:
+               _scr_set_etu_duration(372, 1);
+               break;
+       case HAL_SCR_ETU_F_512_AND_D_8:
+               _scr_set_etu_duration(512, 8);
+               break;
+       case HAL_SCR_ETU_F_512_AND_D_4:
+               _scr_set_etu_duration(512, 4);
+               break;
+       }
+}
+
+int scr_check_card_insert(void)
+{
+       int card_detect = -1;
+
+       card_detect = _scr_irq_get_detect_status();
+       if (card_detect)
+               return SMC_DRV_INT_CARDIN;
+       else
+               return SMC_DRV_INT_CARDOUT;
+}
+
+static void scr_activate_card(void)
+{
+       _scr_init();
+       _scr_set_voltage(HAL_SCR_VOLTAGE_CLASS_B);
+}
+
+static void scr_deactivate_card(void)
+{
+       _scr_close();
+}
+
+static void scr_isr_callback(enum hal_scr_irq_cause_e cause)
+{
+       complete(&rk_scr->is_done);
+}
+
+ssize_t scr_write(unsigned char *buf,
+                 unsigned int write_cnt, unsigned int *to_read_cnt)
+{
+       unsigned time_out = SMC_DEFAULT_TIMEOUT;
+       unsigned long udelay = 0;
+       int ret;
+
+       mutex_lock(&rk_scr->scr_mutex);
+       if (scr_check_card_insert() == SMC_DRV_INT_CARDOUT) {
+               mutex_unlock(&rk_scr->scr_mutex);
+               return SMC_ERROR_CARD_NOT_INSERT;
+       }
+
+       udelay = msecs_to_jiffies(time_out) + jiffies;
+
+       init_completion(&rk_scr->is_done);
+       rk_scr->recv_data_count = 0;
+       rk_scr->recv_data_offset = 0;
+       _scr_clock_start();
+
+       _scr_tx_byte(write_cnt, buf);
+       if (*to_read_cnt != 0) {
+               /* Set registers, ready to receive.*/
+               _scr_rx_byte(*to_read_cnt, rk_scr->recv_buffer);
+
+               ret = wait_for_completion_timeout(&rk_scr->is_done,
+                                                 msecs_to_jiffies(time_out));
+               rk_scr->recv_data_count = _scr_get_num_rx_bytes();
+               if (ret == 0) {
+                       _scr_clock_stop();
+                       mutex_unlock(&rk_scr->scr_mutex);
+                       return TIMEOUT;
+               }
+       }
+       _scr_clock_stop();
+       mutex_unlock(&rk_scr->scr_mutex);
+       return SUCCESSFUL;
+}
+
+ssize_t scr_read(unsigned char *buf, unsigned int to_read_cnt,
+                unsigned int *have_read_cnt)
+{
+       unsigned data_len = 0;
+       unsigned data_remain = 0;
+       unsigned data_available = 0;
+       unsigned data_offset = 0;
+       unsigned time_out_ms = SMC_DEFAULT_TIMEOUT;
+       unsigned data_count = 0;
+       unsigned char data_remain_flag = 0;
+       unsigned long udelay = 0;
+
+       if (!rk_scr->recv_buffer)
+               return SMC_ERROR_RX_ERR;
+
+       mutex_lock(&rk_scr->scr_mutex);
+       if (scr_check_card_insert() == SMC_DRV_INT_CARDOUT) {
+               mutex_unlock(&rk_scr->scr_mutex);
+               return SMC_ERROR_CARD_NOT_INSERT;
+       }
+
+       udelay = msecs_to_jiffies(time_out_ms) + jiffies;
+       data_remain = to_read_cnt;
+       data_count = 0;
+       _scr_clock_start();
+
+       if (data_remain != 0xffffff)
+               data_remain_flag = 1;
+
+       while (time_before(jiffies, udelay)) {
+               data_available = rk_scr->recv_data_count
+                               - rk_scr->recv_data_offset;
+               if (data_available) {
+                       if (data_remain_flag)
+                               data_len = (data_available > data_remain)
+                                       ? (data_remain) : (data_available);
+                       else
+                               data_len = data_available;
+                       data_offset = rk_scr->recv_data_offset;
+                       memcpy(&buf[data_count],
+                              &rk_scr->recv_buffer[data_offset],
+                              data_len);
+                       data_count += data_len;
+                       rk_scr->recv_data_offset += data_len;
+                       if (data_remain_flag)
+                               data_remain -= data_len;
+               }
+
+               if (data_remain_flag && (data_remain == 0))
+                       break;
+               msleep(50);
+       }
+       _scr_clock_stop();
+       *have_read_cnt = data_count;
+       mutex_unlock(&rk_scr->scr_mutex);
+
+       return SUCCESSFUL;
+}
+
+int scr_open(void)
+{
+       mutex_lock(&rk_scr->scr_mutex);
+       _scr_irq_set_handler(scr_isr_callback);
+       if (!rk_scr->recv_buffer) {
+               rk_scr->recv_buffer = kmalloc(SMC_RECEIVE_BUF_LEN, GFP_DMA);
+               if (!rk_scr->recv_buffer)
+                       return NO_MEMORY;
+       }
+       memset(rk_scr->recv_buffer, 0, SMC_RECEIVE_BUF_LEN);
+       init_completion(&rk_scr->is_done);
+       rk_scr->recv_data_count = 0;
+       rk_scr->recv_data_offset = 0;
+       scr_activate_card();
+       mutex_unlock(&rk_scr->scr_mutex);
+
+       return SUCCESSFUL;
+}
+
+int scr_close(void)
+{
+       mutex_lock(&rk_scr->scr_mutex);
+       scr_deactivate_card();
+       kfree(rk_scr->recv_buffer);
+       rk_scr->recv_buffer = NULL;
+       mutex_unlock(&rk_scr->scr_mutex);
+
+       return SUCCESSFUL;
+}
+
+int scr_reset(void)
+{
+       unsigned long timeout_ms = SMC_DEFAULT_TIMEOUT;
+       int i = 0;
+       int ret;
+
+       DAL_LOGV("-----------------scr_reset------------------\n");
+       mutex_lock(&rk_scr->scr_mutex);
+       if (scr_check_card_insert() == SMC_DRV_INT_CARDOUT) {
+               mutex_unlock(&rk_scr->scr_mutex);
+               return SMC_ERROR_CARD_NOT_INSERT;
+       }
+
+       init_completion(&rk_scr->is_done);
+
+       rk_scr->recv_data_count = 0;
+       rk_scr->recv_data_offset = 0;
+       memset(rk_scr->atr_buffer, 0, SMC_ATR_MAX_LENGTH);
+       rk_scr->atr_length = 0;
+
+       _scr_clock_start();
+       _scr_reset(rk_scr->recv_buffer);
+
+       ret = wait_for_completion_timeout(&rk_scr->is_done,
+                                         msecs_to_jiffies(timeout_ms));
+       rk_scr->recv_data_count = _scr_get_num_rx_bytes();
+
+       _scr_clock_stop();
+
+       if ((rk_scr->recv_data_count <= SMC_ATR_MAX_LENGTH) &&
+           (rk_scr->recv_data_count > 0)) {
+               memcpy(rk_scr->atr_buffer, rk_scr->recv_buffer,
+                      rk_scr->recv_data_count);
+               rk_scr->atr_length = rk_scr->recv_data_count;
+       } else {
+               DAL_LOGV("ATR error: rk_scr->recv_data_count = %d.\n",
+                        rk_scr->recv_data_count);
+               mutex_unlock(&rk_scr->scr_mutex);
+               return SMC_ERROR_ATR_ERR;
+       }
+
+       DAL_LOGV("\n--------ATR start-----------\n");
+       DAL_LOGV("rk_scr->atr_length = %d\n", rk_scr->atr_length);
+       for (i = 0; i < rk_scr->recv_data_count; i++)
+               DAL_LOGV("0x%2x\n", rk_scr->atr_buffer[i]);
+       DAL_LOGV("\n--------ATR end-----------\n");
+       mutex_unlock(&rk_scr->scr_mutex);
+
+       return SUCCESSFUL;
+}
+
+int scr_get_atr_data(unsigned char *atr_buf, unsigned char *atr_len)
+{
+       if ((!atr_buf) || (!atr_len))
+               return SMC_ERROR_BAD_PARAMETER;
+
+       mutex_lock(&rk_scr->scr_mutex);
+       if ((rk_scr->atr_length < SMC_ATR_MIN_LENGTH) ||
+           (rk_scr->atr_length > SMC_ATR_MAX_LENGTH)) {
+               mutex_unlock(&rk_scr->scr_mutex);
+               return SMC_ERROR_ATR_ERR;
+       }
+
+       memcpy(atr_buf, &rk_scr->atr_buffer[0], rk_scr->atr_length);
+       *atr_len = rk_scr->atr_length;
+       mutex_unlock(&rk_scr->scr_mutex);
+
+       return SUCCESSFUL;
+}
+
+void scr_set_etu_duration(unsigned int F, unsigned int D)
+{
+       mutex_lock(&rk_scr->scr_mutex);
+       _scr_set_etu_duration(F, D);
+       mutex_unlock(&rk_scr->scr_mutex);
+}
+
+void scr_set_work_waitingtime(unsigned char wi)
+{
+       mutex_lock(&rk_scr->scr_mutex);
+       _scr_set_work_waitingtime(wi);
+       mutex_unlock(&rk_scr->scr_mutex);
+}
+
+static int scr_sysfs_value;
+
+static ssize_t scr_sysfs_show(struct kobject *kobj,
+                             struct kobj_attribute *attr,
+                             char *buf)
+{
+       scr_open();
+       scr_reset();
+       scr_close();
+
+       return sprintf(buf, "%d\n", scr_sysfs_value);
+}
+
+static ssize_t scr_sysfs_store(struct kobject *kobj,
+                              struct kobj_attribute *attr,
+                              const char *buf, size_t count)
+{
+       int ret;
+
+       ret = sscanf(buf, "%du", &scr_sysfs_value);
+       if (ret != 1)
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct kobj_attribute scr_sysfs_attribute =
+               __ATTR(scr_sysfs, 0664, scr_sysfs_show, scr_sysfs_store);
+
+struct attribute *rockchip_smartcard_attributes[] = {
+       &scr_sysfs_attribute.attr,
+       NULL
+};
+
+static const struct attribute_group rockchip_smartcard_group = {
+       .attrs = rockchip_smartcard_attributes,
+};
+
+/* #define CONFIG_SMARTCARD_MUX_SEL_T0 */
+/* #define CONFIG_SMARTCARD_MUX_SEL_T1 */
+#define RK_SCR_CLK_NAME "g_pclk_sim_card"
+static int rk_scr_probe(struct platform_device *pdev)
+{
+       struct rk_scr_device *rk_scr_dev = NULL;
+       struct resource *res = NULL;
+       struct device *dev = NULL;
+       int ret = 0;
+
+       dev = &pdev->dev;
+       rk_scr_dev = devm_kzalloc(dev, sizeof(*rk_scr_dev), GFP_KERNEL);
+       if (!rk_scr_dev) {
+               dev_err(dev, "failed to allocate scr_device\n");
+               return -ENOMEM;
+       }
+       rk_scr = rk_scr_dev;
+       mutex_init(&rk_scr->scr_mutex);
+
+       rk_scr_dev->irq = platform_get_irq(pdev, 0);
+       if (rk_scr_dev->irq < 0) {
+               dev_err(dev, "failed to get scr irq\n");
+               return -ENOENT;
+       }
+
+       ret = devm_request_irq(dev, rk_scr_dev->irq, rk_scr_irqhandler,
+                              0, "rockchip-scr",
+                              (void *)&rk_scr->scr[g_curr_sur_id]);
+       if (ret < 0) {
+               dev_err(dev, "failed to attach scr irq\n");
+               return ret;
+       }
+
+       rk_scr_dev->clk_scr = devm_clk_get(dev, RK_SCR_CLK_NAME);
+       if (IS_ERR(rk_scr_dev->clk_scr)) {
+               dev_err(dev, "failed to get scr clock\n");
+               return PTR_ERR(rk_scr_dev->clk_scr);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       rk_scr_dev->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(rk_scr_dev->regs))
+               return PTR_ERR(rk_scr_dev->regs);
+
+#ifdef CONFIG_SMARTCARD_MUX_SEL_T0
+       writel_relaxed(((0x1 << 22) | (0x1 << 6)),
+                      RK_GRF_VIRT + RK3288_GRF_SOC_CON2);
+#endif
+
+#ifdef CONFIG_SMARTCARD_MUX_SEL_T1
+       pinctrl_select_state(dev->pins->p,
+                            pinctrl_lookup_state(dev->pins->p, "sc_t1"));
+       writel_relaxed(((0x1 << 22) | (0x0 << 6)),
+                      (RK_GRF_VIRT + RK3288_GRF_SOC_CON2));
+#endif
+
+       dev_set_drvdata(dev, rk_scr_dev);
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &rockchip_smartcard_group);
+       if (ret < 0)
+               dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", ret);
+       DAL_LOGV("rk_scr_pdev->name = %s\n", pdev->name);
+       DAL_LOGV("rk_scr_dev->irq = 0x%x\n", rk_scr_dev->irq);
+
+       return ret;
+}
+
+#ifdef CONFIG_PM
+static int rk_scr_suspend(struct device *dev)
+{
+       struct rk_scr_device *rk_scr_dev = dev_get_drvdata(dev);
+
+       disable_irq(rk_scr_dev->irq);
+       clk_disable(rk_scr_dev->clk_scr);
+
+       return 0;
+}
+
+static int rk_scr_resume(struct device *dev)
+{
+       struct rk_scr_device *rk_scr_dev = dev_get_drvdata(dev);
+
+       clk_enable(rk_scr_dev->clk_scr);
+       enable_irq(rk_scr_dev->irq);
+
+       return 0;
+}
+#else
+#define rk_scr_suspend NULL
+#define rk_scr_resume NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id rockchip_scr_dt_match[] = {
+       { .compatible = "rockchip-scr",},
+       {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_scr_dt_match);
+#endif /* CONFIG_OF */
+
+static const struct dev_pm_ops scr_pm_ops = {
+       .suspend        = rk_scr_suspend,
+       .resume         = rk_scr_resume,
+};
+
+static struct platform_driver rk_scr_driver = {
+       .driver         = {
+               .name   = "rockchip-scr",
+               .owner  = THIS_MODULE,
+               .pm     = &scr_pm_ops,
+               .of_match_table = of_match_ptr(rockchip_scr_dt_match),
+       },
+       .probe          = rk_scr_probe,
+};
+
+module_platform_driver(rk_scr_driver);
+
+MODULE_DESCRIPTION("rockchip Smart Card controller driver");
+MODULE_AUTHOR("<rockchip>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/rk_scr.h b/drivers/misc/rk_scr.h
new file mode 100644 (file)
index 0000000..01e0ae4
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Driver for Rockchip Smart Card Reader Controller
+ *
+ * Copyright (C) 2012-2016 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __RK_SCR_H__
+#define __RK_SCR_H__
+
+/* CTRL1 bit fields */
+#define INVLEV         BIT(0)
+#define INVORD         BIT(1)
+#define PECH2FIFO      BIT(2)
+#define CLKSTOP                BIT(6)
+#define CLKSTOPVAL     BIT(7)
+#define TXEN           BIT(8)
+#define RXEN           BIT(9)
+#define TS2FIFO                BIT(10)
+#define T0T1           BIT(11)
+#define ATRSTFLUSH     BIT(12)
+#define TCKEN          BIT(13)
+#define GINTEN         BIT(15)
+
+/* CTRL2 bit fields */
+#define WARMRST                BIT(2)
+#define ACT            BIT(3)
+#define DEACT          BIT(4)
+#define VCC18          BIT(5)
+#define VCC33          BIT(6)
+#define VCC50          BIT(7)
+
+/* SCPADS bit fields */
+#define DIRACCPADS             BIT(0)
+#define DSCIO                  BIT(1)
+#define DSCCLK                 BIT(2)
+#define DSCRST                 BIT(3)
+#define DSCVCC                 BIT(4)
+#define AUTOADEAVPP            BIT(5)
+#define DSCVPPEN               BIT(6)
+#define DSCVPPP                        BIT(7)
+#define DSCFCB                 BIT(8)
+#define SCPRESENT              BIT(9)
+
+/* INTEN1 & INTSTAT1 bit fields */
+#define TXFIDONE       BIT(0)
+#define TXFIEMPTY      BIT(1)
+#define RXFIFULL       BIT(2)
+#define CLKSTOPRUN     BIT(3)
+#define TXDONE         BIT(4)
+#define RXDONE         BIT(5)
+#define TXPERR         BIT(6)
+#define RXPERR         BIT(7)
+#define C2CFULL                BIT(8)
+#define RXTHRESHOLD    BIT(9)
+#define ATRFAIL                BIT(10)
+#define ATRDONE                BIT(11)
+#define SCREM          BIT(12)
+#define SCINS          BIT(13)
+#define SCACT          BIT(14)
+#define SCDEACT                BIT(15)
+
+/* INTEN2 & INTSTAT2 bit fields */
+#define TXTHRESHOLD    BIT(0)
+#define TCLKERR                BIT(1)
+
+/* FIFOCTRL bit fields */
+#define FC_TXFIEMPTY   BIT(0)
+#define FC_TXFIFULL    BIT(1)
+#define FC_TXFIFLUSH   BIT(2)
+#define FC_RXFIEMPTY   BIT(8)
+#define FC_RXFIFULL    BIT(9)
+#define FC_RXFIFLUSH   BIT(10)
+
+/* FIFO_DEPTH must >= 2 */
+#define FIFO_DEPTH     32
+#define MAX_RXTHR      (3 * FIFO_DEPTH / 4)
+#define MAX_TXTHR      (256) /* at least, one less than FIFO_DEPTH */
+
+#define RK_SCR_NUM             (2)
+#define SMC_ATR_MAX_LENGTH     (512)
+#define SMC_ATR_MIN_LENGTH     (2)
+
+#define SMC_SUCCESSFUL                         (0)
+#define SMC_ERROR_CARD_NOT_INSERT              BIT(0)
+#define SMC_ERROR_NO_ANSWER                    BIT(1)
+#define SMC_ERROR_TX_ERR                       BIT(2)
+#define SMC_ERROR_RX_ERR                       BIT(3)
+#define SMC_ERROR_CONFLICT_ERR                 BIT(4)
+#define SMC_ERROR_WRITE_FULL_RECV_FIFO_ERR     BIT(5)
+#define SMC_ERROR_BWT_ERR                      BIT(6)
+#define SMC_ERROR_CWT_ERR                      BIT(7)
+#define SMC_ERROR_BAD_PARAMETER                        BIT(8)
+#define SMC_ERROR_ATR_ERR                      BIT(9)
+#define SMC_ERROR_NO_MEMERY                    BIT(10)
+#define SMC_ERROR_TIMEOUT                      BIT(11)
+
+enum {
+       SC_DRV_INT_CARDOUT = 0,
+       SC_DRV_INT_CARDIN
+};
+
+/* card convention */
+enum {
+       SC_CONV_DIRECT = 0,
+       SC_CONV_INVERSE = 1
+};
+
+enum {
+       SC_CARD_INDEX_0 = 0,
+       SC_CARD_INDEX_1 = 1
+};
+
+/* card protocol */
+enum {
+       SC_PROTOCOL_INVALID = -1,
+       SC_PROTOCOL_T0 = 0,
+       SC_PROTOCOL_T1 = 1,
+       SC_PROTOCOL_T14 = 14
+};
+
+/* enumerated constants */
+enum status_code_e {
+       SUCCESSFUL              = 0, /* successful completion           */
+       TASK_EXITTED            = 1, /* returned from a thread          */
+       MP_NOT_CONFIGURED       = 2, /* multiprocessing not configured  */
+       INVALID_NAME            = 3, /* invalid object name             */
+       INVALID_ID              = 4, /* invalid object id               */
+       TOO_MANY                = 5, /* too many                        */
+       TIMEOUT                 = 6, /* timed out waiting               */
+       OBJECT_WAS_DELETED      = 7, /* object deleted while waiting    */
+       INVALID_SIZE            = 8, /* specified size was invalid      */
+       INVALID_ADDRESS         = 9, /* address specified is invalid    */
+       INVALID_NUMBER          = 10, /* number was invalid             */
+       NOT_DEFINED             = 11, /* item has not been initialized  */
+       RESOURCE_IN_USE         = 12, /* resources still outstanding    */
+       UNSATISFIED             = 13, /* request not satisfied          */
+       INCORRECT_STATE         = 14, /* thread is in wrong state       */
+       ALREADY_SUSPENDED       = 15, /* thread already in state        */
+       ILLEGAL_ON_SELF         = 16, /* illegal on calling thread      */
+       ILLEGAL_ON_REMOTE_OBJECT = 17, /* illegal for remote object     */
+       CALLED_FROM_ISR         = 18, /* called from wrong environment  */
+       INVALID_PRIORITY        = 19, /* invalid thread priority        */
+       INVALID_CLOCK           = 20, /* invalid date/time              */
+       INVALID_NODE            = 21, /* invalid node id                */
+       NOT_CONFIGURED          = 22, /* directive not configured       */
+       NOT_OWNER_OF_RESOURCE   = 23, /* not owner of resource          */
+       NOT_IMPLEMENTED         = 24, /* directive not implemented      */
+       INTERNAL_ERROR          = 25, /* inconsistency detected         */
+       NO_MEMORY               = 26, /* could not get enough memory    */
+       IO_ERROR                = 27, /* driver IO error                */
+       PROXY_BLOCKING          = 28 /* internal error only             */
+};
+
+struct scr_reg_t {
+       unsigned int CTRL1;             /* Control Reg 1                */
+       unsigned int CTRL2;             /* Control Reg 2                */
+       unsigned int SCPADS;            /* Direct access to Smart Card pads*/
+       unsigned int INTEN1;            /* Interrupt Enable Reg 1       */
+       unsigned int INTSTAT1;          /* Interrupt Status Reg 1       */
+       unsigned int FIFOCTRL;          /* FIFO control register        */
+       unsigned int LGCYCNT;           /* Legacy TX & RX FIFO Counter  */
+       unsigned int RXFIFOTH;          /* RXFIFO threshold             */
+       unsigned int REPEAT;            /*
+                                        * number of repeating after
+                                        * unsuccessful transaction
+                                        */
+       unsigned int CGSCDIV;           /* SmartCard clock divisor      */
+       unsigned int CGBITDIV;          /* Bit clock divisor            */
+       unsigned int SCGT;              /* SmartCard GuardTime          */
+       unsigned int ADEATIME;          /* Activation/deactivation time (cc)*/
+       unsigned int LOWRSTTIME;        /*
+                                        * Duration of low state during
+                                        * Smart Card reset sequence
+                                        */
+       unsigned int ATRSTARTLIMIT;     /* ATR start limit              */
+       unsigned int C2CLIM;            /*
+                                        * leading edge to leading edge of two
+                                        * consecutive characters delay limit
+                                        */
+       unsigned int INTEN2;            /* Interrupt Enable Reg 2       */
+       unsigned int INTSTAT2;          /* Interrupt Status R           */
+       unsigned int TXFIFOTH;          /* TXFIFO threshold             */
+       unsigned int TXFIFOCNT;         /* TXFIFO counter               */
+       unsigned int RXFIFOCNT;         /* RXFIFO counter               */
+       unsigned int CGBITTUNE;         /* Bit tune register            */
+       unsigned int reserved[0x200 / 4];
+       unsigned int FIFODATA;          /*
+                                        * FIFODATA space start
+                                        * - RX FIFO and TX FIFO
+                                        */
+};
+
+enum hal_scr_id_e {
+       HAL_SCR_ID0 = 0,
+       HAL_SCR_ID1,
+       HAL_SCR_ID_MAX
+};
+
+enum hal_scr_clock_stop_mode_e {
+       /* Continuous clock mode, the autostop is disabled */
+       HAL_SCR_CLOCK_NO_STOP,
+       /* Automatic clock stop mode, stopped at low-level */
+       HAL_SCR_CLOCK_STOP_L,
+       /* Automatic clock stop mode, stopped at high-level */
+       HAL_SCR_CLOCK_STOP_H
+};
+
+enum hal_scr_etu_duration_e {
+       /* F and D to default value F=372, D=1 */
+       HAL_SCR_ETU_F_372_AND_D_1,
+       /* F=512 and D=8 */
+       HAL_SCR_ETU_F_512_AND_D_8,
+       /* F=512 and D=4 */
+       HAL_SCR_ETU_F_512_AND_D_4
+};
+
+struct hal_scr_irq_status_t {
+       /* When the reset time-outs.            */
+       unsigned char reset_timeout;
+       /* When a parity error occurs.          */
+       unsigned char parity_error;
+       /* When a bad ts character is received. */
+       unsigned char bad_ts;
+       /* When the auto-reset is successful.   */
+       unsigned char atr_success;
+       /* When a rx transfer has been finished */
+       unsigned char rx_success;
+       /* When an auto-reset has been started. */
+       unsigned char atr_start;
+       /* When a work waiting time factor time-outs. */
+       unsigned char wwt_timeout;
+       /*
+        * When the number of received character exceeds the
+        * number of awaited bytes:1; (set in the SCI Rx counter register)
+        */
+       unsigned char extra_rx;
+};
+
+/*check card is in or out*/
+enum hal_scr_detect_status_e {
+       SMC_DRV_INT_CARDOUT = 0,
+       SMC_DRV_INT_CARDIN
+};
+
+enum hal_scr_irq_cause_e {
+       HAL_SCR_RESET_TIMEOUT,
+       HAL_SCR_PARITY_ERROR,
+       HAL_SCR_BAD_TS,
+       HAL_SCR_ATR_SUCCESS,
+       HAL_SCR_RX_SUCCESS,
+       HAL_SCR_WWT_TIMEOUT,
+       HAL_SCR_EXTRA_RX,
+       HAL_SCR_IRQ_INVALID = 0x0fffffff
+};
+
+enum hal_scr_voltage_e {
+       /* 5V */
+       HAL_SCR_VOLTAGE_CLASS_A,
+       /* 3V */
+       HAL_SCR_VOLTAGE_CLASS_B,
+       /* 1.8V */
+       HAL_SCR_VOLTAGE_CLASS_C,
+       /* 0V */
+       HAL_SCR_VOLTAGE_NULL
+};
+
+/* card protocol */
+enum {
+       SMC_PROTOCOL_INVALID = -1,
+       SMC_PROTOCOL_T0 = 0,
+       SMC_PROTOCOL_T1 = 1,
+       SMC_PROTOCOL_T14 = 14
+};
+
+/* card convention */
+enum {
+       SMC_CONV_DIRECT = 0,
+       SMC_CONV_INVERSE = 1
+};
+
+/*card index*/
+enum {
+       SMC_CARD_INDEX_0 = 0,
+       SMC_CARD_INDEX_1 = 1
+};
+
+typedef void (*hal_scr_irq_handler_t) (enum hal_scr_irq_cause_e);
+
+struct scr_chip_info {
+       struct scr_reg_t *reg_base;
+       int irq;
+       const char *clk_name;
+};
+
+struct rk_scr {
+       const struct scr_chip_info *hw;
+       struct clk *clk;
+       hal_scr_irq_handler_t user_handler;
+       struct hal_scr_irq_status_t user_mask;
+       bool is_open;
+       bool is_active;
+       bool in_process;
+
+       unsigned char *rx_buf;
+       unsigned int rx_expected;
+       unsigned int rx_cnt;
+       const unsigned char *tx_buf;
+       unsigned int tx_expected;
+       unsigned int tx_cnt;
+       unsigned int F;
+       unsigned int D;
+       struct notifier_block freq_changed_notifier;
+};
+
+#endif /* __RK_SCR_H__ */
diff --git a/include/misc/rk_scr_api.h b/include/misc/rk_scr_api.h
new file mode 100644 (file)
index 0000000..535e83a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Driver for Rockchip Smart Card Reader Controller
+ *
+ * Copyright (C) 2012-2016 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __RK_SCR_API_H__
+#define __RK_SCR_API_H__
+
+int scr_open(void);
+int scr_close(void);
+int scr_check_card_insert(void);
+int scr_reset(void);
+
+int scr_get_atr_data(unsigned char *atr_buf, unsigned char *atr_len);
+ssize_t scr_write(unsigned char *buf, unsigned int write_cnt,
+                 unsigned int *to_read_cnt);
+ssize_t scr_read(unsigned char *buf, unsigned int to_read_cnt,
+                unsigned int *have_read_cnt);
+
+void scr_set_etu_duration(unsigned int F, unsigned int D);
+void scr_set_work_waitingtime(unsigned char wi);
+
+#endif /* __RK_SCR_API_H__ */