From: Aiyoujun Date: Fri, 18 Mar 2016 09:03:07 +0000 (+0800) Subject: misc: rockchip-scr: add rockchip SOC smart card reader controller driver. X-Git-Tag: firefly_0821_release~3093 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=1834384898d4f4f294bd9249264a6e0f32a0cf7c;p=firefly-linux-kernel-4.4.55.git misc: rockchip-scr: add rockchip SOC smart card reader controller driver. Change-Id: I8d3ab66bc6fa7cbb4e8d9b2f2c5c2feee94a045b Signed-off-by: Aiyoujun --- diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 98c020b560ac..9afddaf6a23c 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -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" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 24483a6caa6b..5ded4471223a 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -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 index 000000000000..63d5d4cfe100 --- /dev/null +++ b/drivers/misc/rk_scr.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(""); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/rk_scr.h b/drivers/misc/rk_scr.h new file mode 100644 index 000000000000..01e0ae406ae4 --- /dev/null +++ b/drivers/misc/rk_scr.h @@ -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 index 000000000000..535e83a9183c --- /dev/null +++ b/include/misc/rk_scr_api.h @@ -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__ */