firmware/br-ext-chip-allwinner/board/v83x/kernel/patches/00000-drivers_char_sunxi-sc...

1485 lines
39 KiB
Diff

diff -drupN a/drivers/char/sunxi-scr/sunxi-scr.c b/drivers/char/sunxi-scr/sunxi-scr.c
--- a/drivers/char/sunxi-scr/sunxi-scr.c 1970-01-01 03:00:00.000000000 +0300
+++ b/drivers/char/sunxi-scr/sunxi-scr.c 2022-06-12 05:28:14.000000000 +0300
@@ -0,0 +1,1480 @@
+/*
+ * drivers/char/sunxi-scr/sunxi-scr.c
+ *
+ * Copyright (C) 2016 Allwinner.
+ * fuzhaoke <fuzhaoke@allwinnertech.com>
+ *
+ * SUNXI SCR Controller Driver
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/major.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include "sunxi-scr.h"
+
+/* ==================== For debug =============================== */
+#define SCR_ENTER() pr_info("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
+#define SCR_EXIT() pr_info("%s()%d - %s\n", __func__, __LINE__, "Exit")
+#define SCR_DBG(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
+#define SCR_INFO(fmt, arg...) pr_info("%s()%d - "fmt, __func__, __LINE__, ##arg)
+#define SCR_WARN(fmt, arg...) pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg)
+#define SCR_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)
+
+static struct sunxi_scr *pscr;
+static int sunxi_scr_major;
+static struct class *scr_dev_class;
+static struct device *scr_device;
+
+struct scr_data {
+ uint8_t buf[SCR_BUF_SIZE];
+ uint16_t cnt; /* valid count of data */
+};
+
+static struct scr_data scr_buf_rx, scr_buf_tx;
+
+static void sunxi_scr_do_atr(struct sunxi_scr *pscr);
+
+/* ================= smart card reader basic interface =================== */
+/* clear control and status register */
+static inline void scr_clear_csr_reg(void __iomem *reg_base)
+{
+ writel(0x0, reg_base + SCR_CSR_OFF);
+}
+
+/* get control and status register config */
+static inline uint32_t scr_get_csr_config(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_CSR_OFF);
+}
+
+/* get detect status 0:card remove, 1:card insert */
+static inline uint8_t scr_get_det_status(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+
+ return (reg_val >> 31) & 0x1;
+}
+
+/* set detect polarity, 0:low active, 1:high active */
+static inline void scr_set_det_polar(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 24);
+ reg_val |= ((config & 0x1) << 24);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* set protocol 0:T0, 1:T1, 2~3:reserved */
+static inline void scr_set_t_protocol(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x3 << 22);
+ reg_val |= ((config & 0x3) << 22);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* when enable(1), both RX&TX FIFO will be flush before ATR start */
+static inline void scr_set_atr_flush(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 21);
+ reg_val |= ((config & 0x1) << 21);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* when enable(1), TS character(first ATR character) will be stored in FIFO */
+static inline void scr_set_ts_recv(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 20);
+ reg_val |= ((config & 0x1) << 20);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* scclk output state during clock stop, 0:low level, 1:high level */
+static inline void scr_set_clk_polar(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 19);
+ reg_val |= ((config & 0x1) << 19);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* parity error character receive enable,
+ * 0:disable, 1:enable store error parity in RX FIFO
+ */
+static inline void scr_set_recv_parity(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 18);
+ reg_val |= ((config & 0x1) << 18);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+static inline uint8_t scr_get_recv_parity(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+
+ return (reg_val >> 18) & 0x1;
+}
+
+/* 0:lsb first, 1:msb first */
+static inline void scr_set_data_order(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 17);
+ reg_val |= ((config & 0x1) << 17);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* when enable(1), invert data level */
+static inline void scr_set_data_invert(void __iomem *reg_base, uint8_t config)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 16);
+ reg_val |= ((config & 0x1) << 16);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* deinit card after stop use and auto clear when finish */
+static inline void scr_set_deactivation(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val |= (0x1 << 11);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* init card before start use and auto clear when finish */
+static inline void scr_set_activation(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val |= (0x1 << 10);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* reset card */
+static inline void scr_set_warmreset(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val |= (0x1 << 9);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+static inline void scr_set_clk_stop(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val |= (0x1 << 8);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+static inline void scr_set_clk_restart(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 8);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+static inline void scr_global_interrupt_enable(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val |= (0x1 << 2);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+static inline void scr_global_interrupt_disable(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 2);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* enable receive data stored in RX FIFO */
+static inline void scr_receive_enable(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val |= (0x1 << 1);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* disable receive data stored in RX FIFO */
+static inline void scr_receive_disable(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 1);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* enable transmit data stored in TX FIFO */
+static inline void scr_transmit_enable(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val |= (0x1 << 0);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* disable transmit data stored in TX FIFO */
+static inline void scr_transmit_disable(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CSR_OFF);
+ reg_val &= ~(0x1 << 0);
+ writel(reg_val, reg_base + SCR_CSR_OFF);
+}
+
+/* set each interrupt bit mask */
+static inline void scr_set_interrupt_enable(void __iomem *reg_base, uint32_t bm)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_INTEN_OFF);
+ reg_val |= bm;
+ writel(reg_val, reg_base + SCR_INTEN_OFF);
+}
+
+/* disable interrupt bit mask */
+static inline void scr_set_interrupt_disable(void __iomem *reg_base,
+ uint32_t bm)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_INTEN_OFF);
+ reg_val &= ~bm;
+ writel(reg_val, reg_base + SCR_INTEN_OFF);
+}
+
+/* get all interrupt status */
+static inline uint32_t scr_get_interrupt_status(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_INTST_OFF);
+}
+
+/* write 1 to clear interrupt flag */
+static inline void scr_clear_interrupt_status(void __iomem *reg_base,
+ uint32_t bm)
+{
+ writel(bm, reg_base + SCR_INTST_OFF);
+}
+
+/* flush RX FIFO and auto clear when finish */
+static inline void scr_flush_rxfifo(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_FCSR_OFF);
+ reg_val |= (0x1 << 10);
+ writel(reg_val, reg_base + SCR_FCSR_OFF);
+}
+
+static inline bool scr_rxfifo_is_full(void __iomem *reg_base)
+{
+ return (readl(reg_base + SCR_FCSR_OFF) >> 9) & 0x1;
+}
+
+static inline bool scr_rxfifo_is_empty(void __iomem *reg_base)
+{
+ return (readl(reg_base + SCR_FCSR_OFF) >> 8) & 0x1;
+}
+
+/* flush TX FIFO and auto clear when finish */
+static inline void scr_flush_txfifo(void __iomem *reg_base)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_FCSR_OFF);
+ reg_val |= (0x1 << 2);
+ writel(reg_val, reg_base + SCR_FCSR_OFF);
+}
+
+static inline bool scr_txfifo_is_full(void __iomem *reg_base)
+{
+ return (readl(reg_base + SCR_FCSR_OFF) >> 1) & 0x1;
+}
+
+static inline bool scr_txfifo_is_empty(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_FCSR_OFF) & 0x1;
+}
+
+static inline void scr_set_rxfifo_threshold(void __iomem *reg_base, uint8_t thh)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_FCNT_OFF);
+ reg_val &= ~(0xffU << 24);
+ reg_val |= thh << 24;
+ writel(reg_val, reg_base + SCR_FCNT_OFF);
+}
+
+static inline void scr_set_txfifo_threshold(void __iomem *reg_base, uint8_t thh)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_FCNT_OFF);
+ reg_val &= ~(0xffU << 16);
+ reg_val |= thh << 16;
+ writel(reg_val, reg_base + SCR_FCNT_OFF);
+}
+
+static inline uint8_t scr_get_rxfifo_count(void __iomem *reg_base)
+{
+ return (readl(reg_base + SCR_FCNT_OFF) >> 8) & 0xff;
+}
+
+static inline uint8_t scr_get_txfifo_count(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_FCNT_OFF) & 0xff;
+}
+
+static inline void scr_set_rx_repeat(void __iomem *reg_base, uint8_t repeat)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_RPT_OFF);
+ reg_val &= ~(0xf << 4);
+ reg_val |= (repeat & 0xf) << 4;
+ writel(reg_val, reg_base + SCR_RPT_OFF);
+}
+
+static inline void scr_set_tx_repeat(void __iomem *reg_base, uint8_t repeat)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_RPT_OFF);
+ reg_val &= ~(0xf << 0);
+ reg_val |= (repeat & 0xf) << 0;
+ writel(reg_val, reg_base + SCR_RPT_OFF);
+}
+
+/* baud = F_sysclk/(2*(BAUDDIV+1)), BAUDDIV=bit[31:16] */
+static inline void scr_set_baud_divisor(void __iomem *reg_base,
+ uint16_t divisor)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_DIV_OFF);
+ reg_val &= ~(0xffffU << 16);
+ reg_val |= divisor << 16;
+ writel(reg_val, reg_base + SCR_DIV_OFF);
+}
+
+static inline uint16_t scr_get_baud_divisor(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_DIV_OFF) >> 16 & 0xffff;
+}
+
+/* F_scclk = F_sysclk/(2*(SCCLK+1)), SCCLK=bit[15:0] */
+static inline void scr_set_scclk_divisor(void __iomem *reg_base,
+ uint16_t divisor)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_DIV_OFF);
+ reg_val &= ~0xffffU;
+ reg_val |= divisor;
+ writel(reg_val, reg_base + SCR_DIV_OFF);
+}
+
+static inline uint16_t scr_get_scclk_divisor(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_DIV_OFF) & 0xffff;
+}
+
+/* ATR start time limit, it define the maximum time of ATR response
+ * limit_time = 128*ATR*T_scclk, ATR=bit[23:16], T_scclk=1/F_scclk
+ */
+static inline void scr_set_atr_time(void __iomem *reg_base, uint8_t scclk)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_LTIM_OFF);
+ reg_val &= ~(0xff << 16);
+ reg_val |= scclk << 16;
+ writel(reg_val, reg_base + SCR_LTIM_OFF);
+}
+
+/* reset duration, dura = 128*RST*T_scclk, RST=bit[15:8], T_scclk=1/F_scclk */
+static inline void scr_set_reset_time(void __iomem *reg_base, uint8_t scclk)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_LTIM_OFF);
+ reg_val &= ~(0xff << 8);
+ reg_val |= scclk << 8;
+ writel(reg_val, reg_base + SCR_LTIM_OFF);
+}
+
+/* reset duration, dura = 128*ACT*T_scclk, ACT=bit[7:0], T_scclk=1/F_scclk */
+static inline void scr_set_activation_time(void __iomem *reg_base,
+ uint8_t scclk)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_LTIM_OFF);
+ reg_val &= ~0xff;
+ reg_val |= scclk;
+ writel(reg_val, reg_base + SCR_LTIM_OFF);
+}
+
+static inline uint32_t scr_get_line_time(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_LTIM_OFF);
+}
+
+/* character limit, maximum time of two consecutive character, ETU as unit */
+static inline void scr_set_chlimit_time(void __iomem *reg_base, uint16_t etu)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CTIM_OFF);
+ reg_val &= ~(0xffffU << 16);
+ reg_val |= etu << 16;
+ writel(reg_val, reg_base + SCR_CTIM_OFF);
+}
+
+/* character guard time, delay time of each character, ETU as unit */
+static inline void scr_set_guard_time(void __iomem *reg_base, uint8_t etu)
+{
+ uint32_t reg_val;
+
+ reg_val = readl(reg_base + SCR_CTIM_OFF);
+ reg_val &= ~0xff;
+ reg_val |= etu;
+ writel(reg_val, reg_base + SCR_CTIM_OFF);
+}
+
+static inline uint32_t scr_get_character_time(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_CTIM_OFF);
+}
+
+static inline uint32_t scr_get_fsm(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_FSM_OFF);
+}
+
+static inline void scr_write_fifo(void __iomem *reg_base, uint8_t data)
+{
+ writel(data, reg_base + SCR_FIFO_OFF);
+}
+
+static inline uint8_t scr_read_fifo(void __iomem *reg_base)
+{
+ return readl(reg_base + SCR_FIFO_OFF) & 0xff;
+}
+
+/* ========================= end =================================== */
+
+/* IRQ interrupt handler */
+static irqreturn_t sunxi_scr_interrupt(int irqno, void *dev_id)
+{
+ struct sunxi_scr *pscr = (struct sunxi_scr *)dev_id;
+ uint32_t rx_cnt = 0, i = 0;
+ u32 irq_status = 0;
+
+ irq_status = scr_get_interrupt_status(pscr->reg_base);
+ scr_clear_interrupt_status(pscr->reg_base, irq_status);
+
+ SCR_DBG("irq_status = 0x%08x\n", irq_status);
+ if (irq_status & SCR_INTSTA_INS) {
+ SCR_DBG("SmartCard Inserted!!\n");
+ scr_set_activation(pscr->reg_base);
+ pscr->card_in = true;
+ /* avoid error multi trigger */
+ mod_timer(&pscr->poll_timer, jiffies + HZ/100); /* 10ms */
+ }
+
+ if (irq_status & SCR_INTSTA_REM) {
+ SCR_DBG("SmartCard Removed!!\n\n");
+ scr_set_deactivation(pscr->reg_base);
+ pscr->card_in = false;
+ /* avoid error multi trigger */
+ mod_timer(&pscr->poll_timer, jiffies + HZ/100); /* 10ms */
+ }
+
+ if (irq_status & SCR_INTSTA_ACT) {
+ SCR_DBG("SmartCard Activated!!\n");
+ memset(&scr_buf_rx, 0, sizeof(struct scr_data));
+ pscr->card_in = true;
+ }
+
+ if (irq_status & SCR_INTSTA_DEACT) {
+ SCR_DBG("SmartCard Deactivated!!\n");
+ pscr->card_in = false;
+ }
+
+ if ((irq_status & SCR_INTSTA_RXDONE) ||
+ (irq_status & SCR_INTSTA_RXFTH) ||
+ (irq_status & SCR_INTSTA_RXFFULL)) {
+ SCR_DBG("SmartCard Rx interrupt!!\n");
+ rx_cnt = scr_get_rxfifo_count(pscr->reg_base);
+ SCR_DBG("rx_cnt=%d\n", rx_cnt);
+ if (rx_cnt > (SCR_BUF_SIZE - scr_buf_rx.cnt)) {
+ SCR_ERR("There are not more space filled in RX buffer");
+ } else {
+ spin_lock(&pscr->rx_lock);
+ for (i = 0; i < rx_cnt; i++) {
+ scr_buf_rx.buf[scr_buf_rx.cnt] =
+ scr_read_fifo(pscr->reg_base);
+ scr_buf_rx.cnt++;
+ }
+ spin_unlock(&pscr->rx_lock);
+ }
+ }
+
+ if (irq_status & SCR_INTSTA_RXPERR) {
+ SCR_DBG("SmartCard Rx Parity Error!!\n");
+ }
+
+ if (irq_status & SCR_INTSTA_ATRFAIL) {
+ SCR_DBG("SmartCard ATR Fail!!\n");
+ pscr->atr_resp = SCR_ATR_RESP_FAIL;
+ /* set activation again */
+ scr_set_activation(pscr->reg_base);
+ }
+
+ if (irq_status & SCR_INTSTA_ATRDONE) {
+ SCR_DBG("SmartCard ATR Done!!\n");
+ memcpy(pscr->scr_atr_des.atr_data, scr_buf_rx.buf,
+ scr_buf_rx.cnt);
+ pscr->scr_atr_des.atr_len = scr_buf_rx.cnt;
+ pscr->atr_resp = SCR_ATR_RESP_OK;
+ /* parse ATR data to reconfig smart card */
+ sunxi_scr_do_atr(pscr);
+ }
+ if (irq_status & SCR_INTSTA_CHTO) {
+ SCR_DBG("character timeout!!\n");
+ pscr->rx_transmit_status = SCR_RX_TRANSMIT_TMOUT;
+ }
+
+ if (irq_status & SCR_INTSTA_TXFEMPTY)
+ SCR_DBG("SmartCard TX Empty!!\n");
+
+ if (irq_status & SCR_INTSTA_TXDONE)
+ SCR_DBG("SmartCard TX Done!!\n");
+
+ if (irq_status & SCR_INTSTA_TXPERR)
+ SCR_DBG("SmartCard TX Error!!\n");
+
+ if (irq_status & SCR_INTSTA_TXFDONE)
+ SCR_DBG("SmartCard TX FIFO Done!!\n");
+
+ return IRQ_HANDLED;
+}
+
+static int scr_request_gpio(struct sunxi_scr *pscr)
+{
+ int ret = 0;
+ struct pinctrl_state *pctrl_state = NULL;
+
+ pscr->scr_pinctrl = devm_pinctrl_get(&(pscr->scr_device->dev));
+ if (IS_ERR_OR_NULL(pscr->scr_pinctrl)) {
+ SCR_ERR("request pinctrl handle fail!\n");
+ return -EINVAL;
+ }
+
+ pctrl_state = pinctrl_lookup_state(pscr->scr_pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(pctrl_state)) {
+ SCR_ERR("pinctrl_lookup_state fail! return %p\n", pctrl_state);
+ return -EINVAL;
+ }
+
+ ret = pinctrl_select_state(pscr->scr_pinctrl, pctrl_state);
+ if (ret < 0)
+ SCR_ERR("pinctrl_select_state fail! return %d\n", ret);
+
+ return ret;
+}
+
+static void scr_release_gpio(struct sunxi_scr *pscr)
+{
+ if (!IS_ERR_OR_NULL(pscr->scr_pinctrl))
+ devm_pinctrl_put(pscr->scr_pinctrl);
+ pscr->scr_pinctrl = NULL;
+}
+
+static uint32_t scr_init_reg(struct sunxi_scr *pscr)
+{
+ scr_global_interrupt_disable(pscr->reg_base);
+ scr_set_interrupt_disable(pscr->reg_base, 0xffffffff);
+ scr_clear_interrupt_status(pscr->reg_base, 0xffffffff);
+
+ scr_flush_txfifo(pscr->reg_base);
+ scr_flush_rxfifo(pscr->reg_base);
+
+ scr_set_txfifo_threshold(pscr->reg_base, pscr->txfifo_thh);
+ scr_set_rxfifo_threshold(pscr->reg_base, pscr->rxfifo_thh);
+
+ scr_set_tx_repeat(pscr->reg_base, pscr->tx_repeat);
+ scr_set_rx_repeat(pscr->reg_base, pscr->rx_repeat);
+
+ scr_set_scclk_divisor(pscr->reg_base, pscr->scclk_div);
+ scr_set_baud_divisor(pscr->reg_base, pscr->baud_div);
+ scr_set_activation_time(pscr->reg_base, pscr->act_time);
+ scr_set_reset_time(pscr->reg_base, pscr->rst_time);
+ scr_set_atr_time(pscr->reg_base, pscr->atr_time);
+ scr_set_guard_time(pscr->reg_base, pscr->guard_time);
+ scr_set_chlimit_time(pscr->reg_base, pscr->chlimit_time);
+ scr_set_atr_flush(pscr->reg_base, 1);
+ scr_set_ts_recv(pscr->reg_base, 1);
+ scr_set_t_protocol(pscr->reg_base, pscr->card_para.protocol_type);
+
+ scr_receive_enable(pscr->reg_base);
+ scr_transmit_enable(pscr->reg_base);
+
+ scr_set_interrupt_enable(pscr->reg_base, pscr->inten_bm);
+ scr_global_interrupt_enable(pscr->reg_base);
+
+ scr_set_recv_parity(pscr->reg_base,
+ pscr->card_para.recv_no_parity);
+
+ return 0;
+}
+
+static void sunxi_scr_param_init(struct sunxi_scr *pscr)
+{
+ /* init register parameters */
+ pscr->inten_bm = 0xfffffff0;
+ pscr->txfifo_thh = SCR_FIFO_DEPTH;
+ pscr->rxfifo_thh = SCR_FIFO_DEPTH;
+ pscr->tx_repeat = 0x3;
+ pscr->rx_repeat = 0x3;
+ /* (APB1CLK/4000000) PCLK/14, <175, && SCCLK >= 1M && =<4M */
+ pscr->scclk_div = 0;
+ pscr->baud_div = 0; /* ETU = 372*SCCLK */
+ pscr->act_time = 2; /* =1*256, 100 */
+ pscr->rst_time = 0xff; /* 2*256, >=400 */
+ /* scr.atr_time = (40000>>8)+1; //=256*256, 400~40000 */
+ pscr->atr_time = 0xff;
+ pscr->guard_time = 2; /* =2*ETUs */
+ /* interval time (400-1) characters */
+ pscr->chlimit_time = 100 * (10 + pscr->guard_time);
+ pscr->atr_resp = SCR_ATR_RESP_INVALID;
+ pscr->rx_transmit_status = SCR_RX_TRANSMIT_NOYET;
+
+ /* init card parameters */
+ pscr->card_para.f = 372;
+ pscr->card_para.d = 1;
+ /* 3.579MHz, unit is KHz, expect baud=9600bps*/
+ pscr->card_para.freq = 3579;
+ pscr->card_para.recv_no_parity = 1;
+ pscr->card_para.protocol_type = 0;
+
+ /* init atr data */
+ pscr->smc_atr_para.TS = 0x3B;
+ pscr->smc_atr_para.TK_NUM = 0x00;
+
+ pscr->smc_atr_para.T = 0; /* T=0 Protocol */
+ pscr->smc_atr_para.FMAX = 4; /* 4MHz, unit is MHz */
+ pscr->smc_atr_para.F = 372;
+ pscr->smc_atr_para.D = 1;
+ pscr->smc_atr_para.I = 50; /* 50mA */
+ pscr->smc_atr_para.P = 5; /* 5V */
+ pscr->smc_atr_para.N = 2;
+
+ /* pscr->clk_freq'unit is hz but pscr->card_para.freq'unit is khz */
+ pscr->scclk_div = pscr->clk_freq / pscr->card_para.freq / 2000 - 1;
+ pscr->baud_div = (pscr->scclk_div + 1) *
+ (pscr->card_para.f / pscr->card_para.d) - 1;
+
+ SCR_DBG("clk_freq=%d, scclk_div=%d, baud_div=%d\n",
+ pscr->clk_freq, pscr->scclk_div, pscr->baud_div);
+
+ /* init registers */
+ scr_init_reg(pscr);
+}
+
+/* use ATR data to reconfig smart card control register */
+static void sunxi_scr_do_atr(struct sunxi_scr *pscr)
+{
+ struct scr_atr *pscr_atr_des = &pscr->scr_atr_des;
+ struct smc_atr_para *psmc_atr_para = &pscr->smc_atr_para;
+ struct smc_pps_para *psmc_pps_para = &pscr->smc_pps_para;
+
+ SCR_DBG("\nBefore Decode:\n"
+ "psmc_atr_para->TS = 0x%x\n"
+ "psmc_atr_para->T = %d\n"
+ "psmc_atr_para->FMAX = %d(MHz), Current: %d(KHz)\n"
+ "psmc_atr_para->F = %d\n"
+ "psmc_atr_para->D = %d\n",
+ psmc_atr_para->TS,
+ psmc_atr_para->T,
+ psmc_atr_para->FMAX,
+ pscr->card_para.freq, psmc_atr_para->F, psmc_atr_para->D);
+
+ smartcard_atr_decode(psmc_atr_para, psmc_pps_para,
+ (uint8_t *)&pscr_atr_des->atr_data, 1);
+
+
+ SCR_DBG("\nAfter Decode:\n"
+ "psmc_atr_para->TS = 0x%x\n"
+ "psmc_atr_para->T = %d\n"
+ "psmc_atr_para->FMAX = %d(MHz), Current: %d(KHz)\n"
+ "psmc_atr_para->F = %d\n"
+ "psmc_atr_para->D = %d\n",
+ psmc_atr_para->TS,
+ psmc_atr_para->T,
+ psmc_atr_para->FMAX,
+ pscr->card_para.freq, psmc_atr_para->F, psmc_atr_para->D);
+
+
+ /* use default F&D or set by up layer
+ pscr->card_para.f = pscr->smc_atr_para.F;
+ pscr->card_para.d = pscr->smc_atr_para.D;
+ pscr->card_para.freq = psmc_atr_para->FMAX * 1000;
+
+ pscr->scclk_div = pscr->clk_freq/pscr->card_para.freq/2000-1;
+ pscr->baud_div = (pscr->scclk_div + 1) *
+ (pscr->card_para.f/pscr->card_para.d)-1;
+
+ scr_set_scclk_divisor(pscr->reg_base, pscr->scclk_div);
+ scr_set_baud_divisor(pscr->reg_base, pscr->baud_div);
+ */
+ pscr->card_para.protocol_type = psmc_atr_para->T;
+ scr_set_t_protocol(pscr->reg_base, pscr->card_para.protocol_type);
+
+ if (0x3f == psmc_atr_para->TS) {
+ /* 0x3b:direct convention, 0x3f:inverse convention */
+ scr_set_data_order(pscr->reg_base, 1);
+ scr_set_data_invert(pscr->reg_base, 1);
+ }
+}
+
+static uint32_t sunxi_scr_clk_init(struct sunxi_scr *pscr)
+{
+ struct platform_device *pdev = pscr->scr_device;
+ struct device_node *node = pdev->dev.of_node;
+
+ if (NULL == pdev || !of_device_is_available(node)) {
+ SCR_ERR("platform_device invalid!\n");
+ return -EINVAL;
+ }
+
+ pscr->scr_clk = of_clk_get(node, 0);
+ if (!pscr->scr_clk || IS_ERR(pscr->scr_clk)) {
+ SCR_ERR("try to get scr clock fail!\n");
+ return -EINVAL;
+ }
+
+ pscr->scr_clk_source = of_clk_get(node, 1);
+ if (!pscr->scr_clk_source || IS_ERR(pscr->scr_clk_source)) {
+ SCR_ERR("err: try to get scr_clk_source clock fail!\n");
+ return -EINVAL;
+ }
+
+ if (clk_set_parent(pscr->scr_clk, pscr->scr_clk_source)) {
+ SCR_ERR("set scr_clk parent to scr_clk_source fail!\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "clock-frequency", &pscr->clk_freq)) {
+ SCR_INFO("get clock-frequency fail! use default 24Mhz\n");
+ pscr->clk_freq = 24000000;
+ }
+
+ if (clk_set_rate(pscr->scr_clk, pscr->clk_freq)) {
+ SCR_ERR("set ir scr_clk freq failed!\n");
+ return -EINVAL;
+ }
+
+ if (clk_prepare_enable(pscr->scr_clk)) {
+ SCR_ERR("try to enable scr_clk failed!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static uint32_t sunxi_scr_clk_exit(struct sunxi_scr *pscr)
+{
+ if (NULL == pscr->scr_clk || IS_ERR(pscr->scr_clk)) {
+ SCR_ERR("scr_clk handle is invalid, just return!\n");
+ return -EINVAL;
+ } else {
+ clk_disable_unprepare(pscr->scr_clk);
+ clk_put(pscr->scr_clk);
+ pscr->scr_clk = NULL;
+ }
+ if (NULL == pscr->scr_clk_source || IS_ERR(pscr->scr_clk_source)) {
+ SCR_ERR("scr_clk_source handle is invalid, just return!\n");
+ return -EINVAL;
+ } else {
+ clk_put(pscr->scr_clk_source);
+ pscr->scr_clk_source = NULL;
+ }
+
+ return 0;
+}
+
+static int sunxi_scr_open(struct inode *inode, struct file *file)
+{
+ file->private_data = pscr;
+
+ if (pscr->open_cnt > 0) {
+ SCR_DBG("smart card opened already\n");
+ pscr->open_cnt++;
+ return 0;
+ }
+
+ sunxi_scr_param_init(pscr);
+ pscr->card_in = scr_get_det_status(pscr->reg_base) ? true : false;
+ pscr->card_last = pscr->card_in;
+ if (true == pscr->card_in)
+ scr_set_activation(pscr->reg_base);
+
+ pscr->open_cnt++;
+
+ return 0;
+}
+
+static int sunxi_scr_release(struct inode *inode, struct file *file)
+{
+ struct sunxi_scr *pscr = file->private_data;
+
+ if (--pscr->open_cnt) {
+ SCR_DBG("There is not really close, just return!\n");
+ return 0;
+ }
+
+ scr_set_deactivation(pscr->reg_base);
+ scr_clear_csr_reg(pscr->reg_base);
+
+ return 0;
+}
+
+static ssize_t
+sunxi_scr_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
+{
+ struct sunxi_scr *pscr = file->private_data;
+ uint32_t rx_size = 0;
+ int try_num = 100;
+
+ while ((SCR_RX_TRANSMIT_NOYET == pscr->rx_transmit_status) && try_num--)
+ msleep(50);
+ if (try_num < 0) {
+ SCR_ERR("read timeout\n");
+ return -EAGAIN;
+ }
+
+ rx_size = scr_buf_rx.cnt;
+ if (rx_size > size)
+ rx_size = size;
+
+ if (copy_to_user(buf, scr_buf_rx.buf, rx_size))
+ return -EFAULT;
+ scr_flush_rxfifo(pscr->reg_base);
+
+ return rx_size;
+}
+
+static int scr_write(struct sunxi_scr *pscr, char *buf, int size)
+{
+ int try_num = 100;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ while (scr_txfifo_is_full(pscr->reg_base) && try_num--)
+ msleep(50);
+ if (try_num < 0) {
+ SCR_ERR("TX FIFO full, write timeout\n");
+ break;
+ }
+ scr_write_fifo(pscr->reg_base, buf[i]);
+ try_num = 100;
+ }
+ SCR_DBG("writed %d byte\n", i);
+
+ return i;
+}
+
+static ssize_t sunxi_scr_write(struct file *file, const char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct sunxi_scr *pscr = file->private_data;
+
+ if (copy_from_user(scr_buf_tx.buf, buf, size))
+ return -EFAULT;
+
+ scr_flush_txfifo(pscr->reg_base);
+ memset(&scr_buf_rx, 0, sizeof(struct scr_data));
+ pscr->rx_transmit_status = SCR_RX_TRANSMIT_NOYET;
+
+ return scr_write(pscr, scr_buf_tx.buf, size);
+}
+
+static void scr_timer_handler(unsigned long data)
+{
+ (void)data;
+ wake_up(&pscr->scr_poll);
+}
+
+unsigned int sunxi_scr_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct sunxi_scr *pscr = file->private_data;
+ unsigned int mask = 0;
+
+ /* add wait_queue to poll_table */
+ poll_wait(file, &pscr->scr_poll, wait);
+
+ /* using Edge Triggered instand of Level Triggered */
+ if (pscr->card_last^pscr->card_in) {
+ if (pscr->card_in)
+ mask |= POLLIN;
+ else
+ mask |= POLLOUT;
+
+ pscr->card_last = pscr->card_in;
+ }
+
+ return mask;
+}
+
+static long sunxi_scr_ioctl(struct file *file, uint32_t cmd, unsigned long arg)
+{
+ struct sunxi_scr *pscr = file->private_data;
+ uint32_t tmp = 0, ret = 0;
+ int try_num = 300;
+
+ SCR_ENTER();
+
+ switch (cmd) {
+ /* get smart card status 0:SCR_CARD_OUT, 1:SCR_CARD_IN */
+ case SCR_IOCGSTATUS:
+ tmp = scr_get_det_status(pscr->reg_base);
+ ret = put_user(tmp, (int __user *)arg);
+ break;
+
+ /* reset card and store ATR data immediately */
+ case SCR_IOCRESET:
+ scr_set_activation(pscr->reg_base);
+ mdelay(10);
+ break;
+
+ /* get ATR data, the arg type is struct scr_atr */
+ case SCR_IOCGATR:
+ while (SCR_ATR_RESP_OK != pscr->atr_resp && try_num--) {
+ msleep(10);
+ };
+ if (try_num < 0) {
+ SCR_ERR("SCR_IOCGATR timeout!\n");
+ ret = -EAGAIN;
+ break;
+ }
+ ret = copy_to_user((void __user *)arg, &pscr->scr_atr_des,
+ sizeof(struct scr_atr)) ? -EFAULT : 0;
+ break;
+
+ /* get current card parameters & status */
+ case SCR_IOCGPARA:
+ ret = copy_to_user((void __user *)arg, &pscr->card_para,
+ sizeof(struct scr_card_para)) ? -EFAULT : 0;
+ break;
+
+ /* set current card parameters & status */
+ case SCR_IOCSPARA:
+ if (copy_from_user(&pscr->card_para, (void __user *)arg,
+ sizeof(struct scr_card_para))) {
+ SCR_ERR("get card para from user error!\n");
+ ret = -EFAULT;
+ break;
+ }
+ pscr->scclk_div = pscr->clk_freq / pscr->card_para.freq / 2000
+ - 1;
+ pscr->baud_div = (pscr->scclk_div + 1) *
+ (pscr->card_para.f / pscr->card_para.d) - 1;
+ scr_set_scclk_divisor(pscr->reg_base, pscr->scclk_div);
+ scr_set_baud_divisor(pscr->reg_base, pscr->baud_div);
+ scr_set_recv_parity(pscr->reg_base,
+ pscr->card_para.recv_no_parity);
+ scr_set_t_protocol(pscr->reg_base,
+ pscr->card_para.protocol_type);
+ break;
+
+ /* get the parse parameters come from ATR data */
+ case SCR_IOCGATRPARA:
+ ret = copy_to_user((void __user *)arg, &pscr->smc_atr_para,
+ sizeof(struct smc_atr_para)) ? -EFAULT : 0;
+ break;
+
+ /* get the pps parse parameters come from ATR data */
+ case SCR_IOCGPPSPARA:
+ ret = copy_to_user((void __user *)arg, &pscr->smc_pps_para,
+ sizeof(struct smc_pps_para)) ? -EFAULT : 0;
+ break;
+
+ /* write cmd and read data immediately */
+ case SCR_IOCWRDATA: {
+ int rtn_data_len;
+ struct scr_wr_data wr_data;
+
+ if (copy_from_user(&wr_data, (void __user *)arg,
+ sizeof(struct scr_wr_data))) {
+ SCR_ERR("get wr_data from user error!\n");
+ ret = -EFAULT;
+ break;
+ }
+ if (copy_from_user(scr_buf_tx.buf,
+ (void __user *)wr_data.cmd_buf,
+ wr_data.cmd_len)) {
+ SCR_ERR("get wr_data cmd_buf from user error!\n");
+ ret = -EFAULT;
+ break;
+ }
+ scr_buf_tx.cnt = wr_data.cmd_len;
+
+ scr_flush_txfifo(pscr->reg_base);
+ scr_flush_rxfifo(pscr->reg_base);
+ memset(&scr_buf_rx, 0, sizeof(struct scr_data));
+
+ /* APDU, smart card cammand format */
+ /* type1: CLS + INS + P1 + P2 + le -> only read, le=read size*/
+ if (5 == scr_buf_tx.cnt) {
+ scr_write(pscr, scr_buf_tx.buf, 5);
+ /* respond:
+ * INS(=buf[1]) + valid_data(=buf[4]) + SW1 + SW2
+ */
+ rtn_data_len = scr_buf_tx.buf[4] + 3;
+ while ((rtn_data_len > scr_buf_rx.cnt) && try_num--) {
+ msleep(10);
+ };
+
+ if (try_num < 0) {
+ SCR_ERR("read timeout\n");
+ ret = -EFAULT;
+ break;
+ }
+ ret = copy_to_user((void __user *)wr_data.rtn_data,
+ &scr_buf_rx.buf[1],
+ scr_buf_tx.buf[4]) ? -EFAULT : 0;
+ put_user(scr_buf_tx.buf[4], wr_data.rtn_len);
+ put_user(scr_buf_rx.buf[rtn_data_len - 2],
+ wr_data.psw1);
+ put_user(scr_buf_rx.buf[rtn_data_len - 1],
+ wr_data.psw2);
+
+ /* type2: CLS + INS + P1 + P2 + lc + data
+ * only lc, write data, lc=data size
+ */
+ } else if (scr_buf_tx.buf[4]+5 == scr_buf_tx.cnt) {
+ scr_write(pscr, scr_buf_tx.buf, 5);
+ while ((0 == scr_buf_rx.cnt) && try_num--) {
+ msleep(10);
+ };
+ if (try_num < 0) {
+ SCR_ERR("timeout: there is not any data\n");
+ ret = -EFAULT;
+ break;
+ }
+ if (scr_buf_rx.buf[0] != scr_buf_tx.buf[1]) {
+ SCR_ERR("do not support this instruction\n");
+ ret = -EFAULT;
+ break;
+ }
+ scr_write(pscr, &scr_buf_tx.buf[5], scr_buf_tx.buf[4]);
+ try_num = 300;
+ while ((scr_buf_rx.cnt < 3) && try_num--) {
+ msleep(10);
+ };
+ if (try_num < 0) {
+ SCR_ERR("timeout: get sw1,sw2 fail\n");
+ ret = -EFAULT;
+ break;
+ }
+ put_user(scr_buf_rx.buf[1], wr_data.psw1);
+ put_user(scr_buf_rx.buf[2], wr_data.psw2);
+
+ /* type3: CLS + INS + P1 + P2 + lc + data +le -> le+lc */
+ } else if (scr_buf_tx.buf[4]+6 == scr_buf_tx.cnt) {
+ scr_write(pscr, scr_buf_tx.buf, 5);
+ while ((0 == scr_buf_rx.cnt) && try_num--) {
+ msleep(10);
+ };
+ if (try_num < 0) {
+ SCR_ERR("timeout: there is not any data\n");
+ ret = -EFAULT;
+ break;
+ }
+ if (scr_buf_rx.buf[0] != scr_buf_tx.buf[1]) {
+ SCR_ERR("do not support this instruction\n");
+ ret = -EFAULT;
+ break;
+ }
+ scr_write(pscr, &scr_buf_tx.buf[5],
+ scr_buf_tx.buf[4]+1);
+ try_num = 300;
+ /* respond: INS + valid_data + SW1 + SW2 */
+ rtn_data_len = scr_buf_tx.buf[scr_buf_tx.cnt-1] + 3;
+ while ((rtn_data_len > scr_buf_rx.cnt) && try_num--) {
+ msleep(10);
+ };
+
+ if (try_num < 0) {
+ SCR_ERR("read timeout\n");
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = copy_to_user((void __user *)wr_data.rtn_data,
+ &scr_buf_rx.buf[1],
+ scr_buf_tx.buf[scr_buf_tx.cnt-1]) ? -EFAULT : 0;
+ put_user(scr_buf_tx.buf[scr_buf_tx.cnt-1],
+ wr_data.rtn_len);
+ put_user(scr_buf_rx.buf[rtn_data_len - 2],
+ wr_data.psw1);
+ put_user(scr_buf_rx.buf[rtn_data_len - 1],
+ wr_data.psw2);
+
+ } else {
+ SCR_ERR("invalid command format\n");
+ ret = -EFAULT;
+ break;
+ }
+ break;
+ }
+
+ default:
+ SCR_ERR("Invalid iocontrol command!\n");
+ break;
+ }
+
+ return ret;
+}
+
+static const struct file_operations sunxi_scr_fops = {
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .read = sunxi_scr_read,
+ .write = sunxi_scr_write,
+ .unlocked_ioctl = sunxi_scr_ioctl,
+ .open = sunxi_scr_open,
+ .release = sunxi_scr_release,
+ .poll = sunxi_scr_poll,
+};
+
+static int sunxi_scr_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct resource *mem_res = NULL;
+ int ret = 0;
+
+ SCR_ENTER();
+
+ pscr = kzalloc(sizeof(struct sunxi_scr), GFP_KERNEL);
+ if (!pscr) {
+ SCR_ERR("kzalloc struct sunxi_scr fail!\n");
+ return -ENOMEM;
+ }
+
+ pscr->scr_device = pdev;
+ if (!of_device_is_available(node)) {
+ SCR_ERR("invalid node!\n");
+ ret = -EINVAL;
+ goto emloc;
+ }
+
+ if (sunxi_scr_clk_init(pscr)) {
+ SCR_ERR("sunxi_scr_clk_init fail!\n");
+ ret = -EINVAL;
+ goto eclk;
+ }
+
+ pscr->irq_no = platform_get_irq(pdev, 0);
+ if (pscr->irq_no < 0) {
+ SCR_ERR("get irq number fail!\n");
+ ret = -EINVAL;
+ goto eclk;
+ }
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem_res == NULL) {
+ SCR_ERR("failed to get MEM res\n");
+ ret = -ENXIO;
+ goto eclk;
+ }
+
+ if (!request_mem_region(mem_res->start,
+ resource_size(mem_res), mem_res->name)) {
+ SCR_ERR("failed to request mem region\n");
+ ret = -EINVAL;
+ goto eclk;
+ }
+
+ pscr->reg_base = ioremap(mem_res->start, resource_size(mem_res));
+ if (!pscr->reg_base) {
+ SCR_ERR("failed to io remap\n");
+ ret = -EIO;
+ goto eiomem;
+ }
+ pscr->mem_res = mem_res;
+
+ if (request_irq(pscr->irq_no, sunxi_scr_interrupt,
+ IRQF_TRIGGER_NONE, "scr", pscr)) {
+ SCR_ERR("request irq fail!\n");
+ ret = -EINVAL;
+ goto eiomap;
+ }
+
+ if (scr_request_gpio(pscr)) {
+ SCR_ERR("failed to request gpio\n");
+ ret = -EINVAL;
+ goto eirq;
+ }
+
+ spin_lock_init(&pscr->rx_lock);
+ sunxi_scr_param_init(pscr);
+ init_waitqueue_head(&pscr->scr_poll);
+ pscr->poll_timer.expires = jiffies + HZ/100; /* 10ms */
+ pscr->poll_timer.function = scr_timer_handler;
+ init_timer(&pscr->poll_timer);
+ add_timer(&pscr->poll_timer);
+
+ /* creat character device */
+ sunxi_scr_major = register_chrdev(0, SCR_MODULE_NAME, &sunxi_scr_fops);
+ if (sunxi_scr_major < 0) {
+ SCR_ERR("register_chrdev fail!\n");
+ ret = -ENODEV;
+ goto eirq;
+ }
+ scr_dev_class = class_create(THIS_MODULE, SCR_MODULE_NAME);
+ if (IS_ERR(scr_dev_class)) {
+ SCR_ERR("class_create fail!\n");
+ ret = -ENODEV;
+ goto edev;
+ }
+ scr_device = device_create(scr_dev_class, NULL,
+ MKDEV(sunxi_scr_major, 0),
+ NULL, SCR_MODULE_NAME);
+ if (IS_ERR(scr_device)) {
+ SCR_ERR("device_create fail!\n");
+ ret = -ENODEV;
+ goto ecla;
+ }
+
+ platform_set_drvdata(pdev, pscr);
+
+ return 0;
+
+ecla:
+ class_destroy(scr_dev_class);
+
+edev:
+ unregister_chrdev(sunxi_scr_major, SCR_MODULE_NAME);
+
+eirq:
+ free_irq(pscr->irq_no, pscr);
+
+eiomap:
+ iounmap(pscr->reg_base);
+
+eiomem:
+ release_mem_region(mem_res->start, resource_size(mem_res));
+
+eclk:
+ sunxi_scr_clk_exit(pscr);
+
+emloc:
+ kfree(pscr);
+
+ return ret;
+
+}
+
+static int sunxi_scr_remove(struct platform_device *pdev)
+{
+ struct sunxi_scr *pscr = platform_get_drvdata(pdev);
+
+ device_destroy(scr_dev_class, MKDEV(sunxi_scr_major, 0));
+ class_destroy(scr_dev_class);
+ unregister_chrdev(sunxi_scr_major, SCR_MODULE_NAME);
+ free_irq(pscr->irq_no, pscr);
+ iounmap(pscr->reg_base);
+ release_mem_region(pscr->mem_res->start,
+ resource_size(pscr->mem_res));
+ scr_release_gpio(pscr);
+ sunxi_scr_clk_exit(pscr);
+
+ del_timer(&pscr->poll_timer);
+
+ SCR_EXIT();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sunxi_scr_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sunxi_scr *pscr = platform_get_drvdata(pdev);
+ struct pinctrl_state *pctrl_state = NULL;
+
+ pscr->suspended = true;
+
+ if (sunxi_scr_clk_exit(pscr)) {
+ SCR_ERR("SCR suspend failed !\n");
+ pscr->suspended = false;
+ return -1;
+ }
+
+ if (!IS_ERR_OR_NULL(pscr->scr_pinctrl)) {
+ pctrl_state = pinctrl_lookup_state(pscr->scr_pinctrl,
+ PINCTRL_STATE_SLEEP);
+ if (IS_ERR(pctrl_state)) {
+ SCR_ERR("SCR pinctrl lookup sleep fail\n");
+ return -1;
+ }
+
+ if (pinctrl_select_state(pscr->scr_pinctrl, pctrl_state) < 0) {
+ SCR_ERR("SCR pinctrl select sleep fail\n");
+ return -1;
+ }
+ }
+
+ disable_irq_nosync(pscr->irq_no);
+
+ SCR_DBG("SCR suspend okay\n");
+ return 0;
+}
+
+static int sunxi_scr_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sunxi_scr *pscr = platform_get_drvdata(pdev);
+ struct pinctrl_state *pctrl_state = NULL;
+
+
+ pscr->suspended = false;
+
+ if (sunxi_scr_clk_init(pscr)) {
+ SCR_ERR("SCR resume failed !\n");
+ return -1;
+ }
+
+ if (!IS_ERR_OR_NULL(pscr->scr_pinctrl)) {
+ pctrl_state = pinctrl_lookup_state(pscr->scr_pinctrl,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(pctrl_state)) {
+ SCR_ERR("SCR pinctrl lookup default fail\n");
+ return -1;
+ }
+
+ if (pinctrl_select_state(pscr->scr_pinctrl, pctrl_state) < 0) {
+ SCR_ERR("SCR pinctrl select default fail\n");
+ return -1;
+ }
+ }
+
+ enable_irq(pscr->irq_no);
+
+ SCR_DBG("SCR resume okay\n");
+ return 0;
+}
+
+static const struct dev_pm_ops sunxi_scr_dev_pm_ops = {
+ .suspend = sunxi_scr_suspend,
+ .resume = sunxi_scr_resume,
+};
+
+#define SUNXI_SCR_DEV_PM_OPS (&sunxi_scr_dev_pm_ops)
+#else
+#define SUNXI_SCR_DEV_PM_OPS NULL
+#endif
+
+static const struct of_device_id sunxi_scr_match[] = {
+ {.compatible = "allwinner,sunxi-scr",},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, sunxi_scr_match);
+
+static struct platform_driver scr_platform_driver = {
+ .probe = sunxi_scr_probe,
+ .remove = sunxi_scr_remove,
+ .driver = {
+ .name = SCR_MODULE_NAME,
+ .owner = THIS_MODULE,
+ .pm = SUNXI_SCR_DEV_PM_OPS,
+ .of_match_table = sunxi_scr_match,
+ },
+};
+
+static int __init sunxi_scr_init(void)
+{
+ return platform_driver_register(&scr_platform_driver);
+}
+
+static void __exit sunxi_scr_exit(void)
+{
+ platform_driver_unregister(&scr_platform_driver);
+}
+
+module_init(sunxi_scr_init);
+module_exit(sunxi_scr_exit);
+MODULE_DESCRIPTION("Smart Card Driver");
+MODULE_AUTHOR("fuzhaoke");
+MODULE_LICENSE("GPL");