Changes from v1:
* RFC(v1) comments are fixed
** removed "gpio_to_irq" as GPIO controller process cell from DT and
give it to DT node
** comments on documentation
** few other comments on null checks are resolved
* BWT timing configuration is added to ti-usim driver
v1 cover letter link#
https://lkml.org/lkml/2014/1/6/250
Satish Patel (5):
sc_phy:SmartCard(SC) PHY interface to SC controller
misc: tda8026: Add NXP TDA8026 PHY driver
char: ti-usim: Add driver for USIM module on AM43xx
ARM: dts: AM43xx: DT entries added for ti-usim
ARM: dts: AM43xx-epos-evm: DT entries for ti-usim and phy
Documentation/devicetree/bindings/misc/tda8026.txt | 19 +
.../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
Documentation/sc_phy.txt | 171 ++
arch/arm/boot/dts/am4372.dtsi | 10 +
arch/arm/boot/dts/am43x-epos-evm.dts | 43 +
drivers/char/Kconfig | 7 +
drivers/char/Makefile | 1 +
drivers/char/ti-usim-hw.h | 863 +++++++++
drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/tda8026.c | 1255 +++++++++++++
include/linux/sc_phy.h | 132 ++
include/linux/ti-usim.h | 98 +
14 files changed, 4497 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
create mode 100644 Documentation/sc_phy.txt
create mode 100644 drivers/char/ti-usim-hw.h
create mode 100644 drivers/char/ti-usim.c
create mode 100644 drivers/misc/tda8026.c
create mode 100644 include/linux/sc_phy.h
create mode 100644 include/linux/ti-usim.h
TDA8026 is a SmartCard PHY from NXP.
The PHY interfaces with the main processor over the
I2C interface and acts as a slave device.
The driver also exposes the phy interface
(defined@include/linux/sc_phy.h) for SmartCard controller.
Controller uses this interface to communicate with smart card
inserted to the phy's slot.
Note: gpio irq is not validated as I do not have device with that.
I have validated interrupt with dedicated interrupt line on my device.
Signed-off-by: Satish Patel <[email protected]>
---
Documentation/devicetree/bindings/misc/tda8026.txt | 19 +
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/tda8026.c | 1255 ++++++++++++++++++++
4 files changed, 1282 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
create mode 100644 drivers/misc/tda8026.c
diff --git a/Documentation/devicetree/bindings/misc/tda8026.txt b/Documentation/devicetree/bindings/misc/tda8026.txt
new file mode 100644
index 0000000..f115c9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/tda8026.txt
@@ -0,0 +1,19 @@
+TDA8026 smart card slot interface
+
+This is an i2c based smart card interface device forming the electrical
+interface between a microcontroller and smart cards. This device supports
+asynchronous cards (micro controller-based IC cards) as well as synchronous
+cards (mainly memory cards)
+
+Required properties:
+- compatible: "nxp,tda8026"
+- shutdown-gpio = GPIO pin mapping for SDWNN pin
+- reg = i2c interface address
+
+
+Example:
+tda8026: tda8026@48 {
+ compatible = "nxp,tda8026";
+ reg = <0x48>;
+ shutdown-gpio = <&gpio5 19 GPIO_ACTIVE_HIGH>;/* Bank5, pin19 */
+ };
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index a3e291d..bb94a6f 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,13 @@ config SRAM
the genalloc API. It is supposed to be used for small on-chip SRAM
areas found on many SoCs.
+config NXP_TDA8026_PHY
+ tristate "NXP PHY Driver for Smart Card PHY"
+ depends on I2C=y
+ help
+ If you say yes here you get support for the TDA8026 Smart card PHY
+ with I2C interface.
+
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 f45473e..24374dd 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
+obj-$(CONFIG_NXP_TDA8026_PHY) += tda8026.o
diff --git a/drivers/misc/tda8026.c b/drivers/misc/tda8026.c
new file mode 100644
index 0000000..6a77686
--- /dev/null
+++ b/drivers/misc/tda8026.c
@@ -0,0 +1,1255 @@
+/*
+ * tda8026.c - TDA8026 PHY driver for NXP Smart card PHY
+ *
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/notifier.h>
+#include <linux/sc_phy.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/delay.h>
+
+#define TDA8026_MAX_SLOTS (5)
+#define TDA8026_NUM_SAM_SLOTS (4)
+#define TDA8026_USERCARD_SLOT (1)
+
+#define TDA8026_CSB_ADDR (0x24)
+#define TDA8026_REG0_ADDR (0x20)
+#define TDA8026_REG1_ADDR (0x21)
+#define TDA8026_SLEWRATE_ADDR (0x20)
+#define TDA8026_PRODVER_ADDR (0x20)
+#define TDA8026_INTSTAT_ADDR (0x21)
+
+#define TDA8026_PHY_PRODUCT_VERSION (0xC2)
+
+/* CSB register values */
+#define TDA8026_CSB_PV_INTSTAT_VAL (0x0)
+#define TDA8026_CSB_SLEWRATE_VAL (0x6)
+
+/* Slot REG0 read mode bit fields */
+#define TDA8026_REG0_ACTIVE_MASK (0x80)
+#define TDA8026_REG0_EARLY_MASK (0x40)
+#define TDA8026_REG0_MUTE_MASK (0x20)
+#define TDA8026_REG0_PROT_MASK (0x10)
+#define TDA8026_REG0_SUPL_MASK (0x08)
+#define TDA8026_REG0_CLKSW_MASK (0x04)
+#define TDA8026_REG0_PREL_MASK (0x02)
+#define TDA8026_REG0_PRES_MASK (0x01)
+
+/* Slot REG0 write mode bit fields */
+#define TDA8026_REG0_VCC1V8_MASK (0x80)
+#define TDA8026_REG0_IOEN_MASK (0x40)
+
+#define TDA8026_REG0_REG10_MASK (0x30)
+#define TDA8026_REG0_REG10_SHIFT (4)
+#define TDA8026_REG0_REG10_CFG_VAL (0x0)
+#define TDA8026_REG0_REG10_D_VAL (0x1)
+#define TDA8026_REG0_REG10_CMSB_VAL (0x2)
+#define TDA8026_REG0_REG10_CLSB_VAL (0x3)
+
+#define TDA8026_REG0_PDWN_MASK (0x08)
+#define TDA8026_REG0_5V3VN_MASK (0x04)
+#define TDA8026_REG0_WARM_RESET_MASK (0x02)
+#define TDA8026_REG0_START_MASK (0x01)
+
+/* Slot REG1 CFG bit fields REG[1:0] = 00b */
+#define TDA8026_REG1CFG_CFGP2_MASK (0x80)
+#define TDA8026_REG1CFG_RSTIN_MASK (0x40)
+#define TDA8026_REG1CFG_C8_MASK (0x20)
+#define TDA8026_REG1CFG_C4_MASK (0x10)
+#define TDA8026_REG1CFG_CLKPD_MASK (0x0C)
+#define TDA8026_REG1CFG_CLKPD_SHIFT (2)
+#define TDA8026_REG1CFG_CLKDIV_MASK (0x03)
+#define TDA8026_REG1CFG_CLKDIV_SHIFT (0)
+
+/* Slew rate register bit fields */
+#define TDA8026_SR_CLKSR_SLOT0_MASK 0x0C
+#define TDA8026_SR_CLKSR_SLOT0_SHIFT 0x2
+#define TDA8026_SR_IOSR_SLOT0_MASK 0x3
+#define TDA8026_SR_IOSR_SLOT0_SHIFT 0x0
+
+#define TDA8026_SR_CLKSR_SLOT2TO5_MASK 0xC0
+#define TDA8026_SR_CLKSR_SLOT2TO5_SHIFT 0x6
+#define TDA8026_SR_IOSR_SLOT2TO5_MASK 0x30
+#define TDA8026_SR_IOSR_SLOT2TO5_SHIFT 0x4
+
+#define TDA8026_MIN_EARLY_CYCLE (200)
+
+struct tda8026 {
+ /* device pointer */
+ struct device *dev;
+
+ /* For device IO interfaces: I2C or SPI */
+ void *control_data;
+ /* Store a shadow of Slot Register 0 as it cannot be read */
+ u8 reg0[TDA8026_MAX_SLOTS];
+ int irq;
+ int notify;
+ int shutdown_gpio;
+ int enable;
+};
+
+static BLOCKING_NOTIFIER_HEAD(tda8026_notifier_list);
+
+static int tda8026_i2c_read(struct tda8026 *tda8026, u8 reg,
+ int bytes, u8 *dest)
+{
+ struct i2c_client *i2c = tda8026->control_data;
+ struct i2c_msg xfer;
+ int ret;
+
+ /* We need to read from the slave address itself, as there
+ * is no separate register to be accessed in TDA8026
+ */
+
+ xfer.addr = reg;
+ xfer.flags = I2C_M_RD;
+ xfer.len = bytes;
+ xfer.buf = dest;
+
+ ret = i2c_transfer(i2c->adapter, &xfer, 1);
+ if (ret < 0)
+ dev_err(tda8026->dev, "Read [0x%x] Error %d\n", reg, ret);
+
+ return ret;
+}
+
+static int tda8026_i2c_write(struct tda8026 *tda8026, u8 reg,
+ int bytes, u8 *src)
+{
+ struct i2c_client *i2c = tda8026->control_data;
+ struct i2c_msg xfer;
+ int ret;
+
+ /* We have to write to the slave address itself, as
+ * there is no separate register to be accessed in TDA8026
+ */
+ xfer.addr = reg;
+ xfer.flags = 0;
+ xfer.len = bytes;
+ xfer.buf = src;
+
+ ret = i2c_transfer(i2c->adapter, &xfer, 1);
+ if (ret < 0)
+ dev_err(tda8026->dev, "Write [0x%x] Error %d\n", reg, ret);
+
+ return ret;
+}
+
+/* put the phy in shutdown mode, which in turn deactivate
+ * all the cards in the slot and card pins are forced to 0V
+ */
+static inline void tda8026_disable(struct tda8026 *tda8026)
+{
+ dev_dbg(tda8026->dev, "tda8026_disable!!!!");
+ tda8026->enable = 0;
+ if (gpio_is_valid(tda8026->shutdown_gpio))
+ gpio_set_value(tda8026->shutdown_gpio, 0);
+}
+
+/* exit from shutdown mode */
+static inline void tda8026_enable(struct tda8026 *tda8026)
+{
+ dev_dbg(tda8026->dev, "tda8026_enable!!!!");
+ tda8026->enable = 1;
+ if (gpio_is_valid(tda8026->shutdown_gpio))
+ gpio_set_value(tda8026->shutdown_gpio, 1);
+ /* Added dealy to stabilized phy state after pulling shutdown line */
+ mdelay(100);
+}
+
+/*
+ * Select the card slot to communicate with the card
+ * Note that card slots are numbered from 0 in software.
+ * However TDA8026 PHY numbers the slot starting 1.
+ */
+static inline int tda8026_select_slot(struct tda8026 *tda8026, u8 slot)
+{
+ int ret = 0;
+
+ /* In SW slot starts from 0, in TDA8026 it starts from 1 */
+ slot = slot + 1;
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &slot);
+
+ return ret;
+}
+
+static int tda8026_clear_interrupts(struct tda8026 *tda8026)
+{
+ u8 val;
+ u8 status;
+ int ret = 0;
+
+ /* Select the Interrupt register bank */
+ val = TDA8026_CSB_PV_INTSTAT_VAL;
+ tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+
+ /* Read the interrupt status which will tell us the slot */
+ ret = tda8026_i2c_read(tda8026, TDA8026_INTSTAT_ADDR, 1, &status);
+ if (ret < 0)
+ return ret;
+
+ for (val = 1; val > TDA8026_MAX_SLOTS; val++) {
+ /* Program the slot number to the CSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG0_ADDR, 1, &status);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
+/*
+ * TDA8026 PHY IRQ handler
+ */
+static irqreturn_t tda8026_irq(int irq, void *irq_data)
+{
+ struct sc_phy *phy_tda8026 = (struct sc_phy *)irq_data;
+ struct tda8026 *tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ u8 slot;
+ u8 val;
+ u8 status;
+ int ret = 0;
+ int action = 0;
+
+ dev_dbg(tda8026->dev, "tda8026_irq!!");
+
+ if (tda8026->enable == 0) {
+ dev_dbg(tda8026->dev, "phy is disable not serving interrputs!!");
+ /* when, phy is in shutdown mode, it can detect the card insert
+ * event. But if phy is not enable (i.e.) there is no consumer
+ * of phy then just enable phy, clear the interrupt and disable
+ * again
+ */
+ tda8026_enable(tda8026);
+ tda8026_clear_interrupts(tda8026);
+ tda8026_disable(tda8026);
+ return IRQ_HANDLED;
+ }
+
+ /* Select the Interrupt register bank */
+ val = TDA8026_CSB_PV_INTSTAT_VAL;
+ tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+
+ /* Read the interrupt status which will tell us the slot */
+ ret = tda8026_i2c_read(tda8026, TDA8026_INTSTAT_ADDR, 1, &status);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+
+ /* find out for which slot interrupt has occur */
+ slot = 0;
+ while (val == 0 && slot < TDA8026_MAX_SLOTS) {
+ val = status & (1 << slot);
+ slot++;
+ }
+
+ if (slot > TDA8026_MAX_SLOTS) {
+ dev_err(tda8026->dev, "invalid slot interrput");
+ return IRQ_HANDLED;
+ }
+
+ /* Program the slot number to the CSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &slot);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ /* Now read the slot reg0 to find out the cause of interrupt
+ * Note that IRQ is raised only when one of the SUPL, PROT,
+ * MUTE and EARLY bits are set to logic 1.
+ */
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG0_ADDR, 1, &status);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (slot < 3) {
+ /* slot 1 and slot 2 can be used for user card, it can raise
+ * interrupt for card insert and remove.Other slot are for SAM
+ * modules. They are either always present or alwyas absent
+ */
+ if (status & TDA8026_REG0_PREL_MASK) {
+ if (status & TDA8026_REG0_PRES_MASK) {
+ dev_dbg(tda8026->dev, "card is inserted");
+ action |= SC_PHY_CARD_INSERTED;
+ } else {
+ dev_dbg(tda8026->dev, "card is removed");
+ action |= SC_PHY_CARD_REMOVED;
+ }
+ }
+ }
+
+ if (status & (TDA8026_REG0_EARLY_MASK | TDA8026_REG0_MUTE_MASK)) {
+ dev_dbg(tda8026->dev, "CARD EARLY INTERRUPT\n");
+ action |= SC_PHY_CARD_ATR_TIMEOUT;
+ }
+
+ if (status & TDA8026_REG0_PROT_MASK) {
+ dev_dbg(tda8026->dev, "CARD OVERHEAT/OVERLOAD INTERRUPT\n");
+ action |= SC_PHY_CARD_OVERHEAT;
+ }
+
+ if (action != 0x0 && tda8026->notify) {
+ /* add slot information. Pass slot-1 as for controller slot
+ * starts from 0,1,2.. */
+ action |= ((slot-1) << SC_PHY_NOTIFICATION_SLOT_SHIFT);
+
+ /* notify action */
+ blocking_notifier_call_chain(&tda8026_notifier_list, action,
+ phy_tda8026->notify_data);
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * PDWN bit is set/cleared to apply CLKPD[1:0] bit clock settings to
+ * the clock pin for the selected card slot.
+ */
+static int tda8026_pwdn(struct tda8026 *tda8026, u8 slot, int state)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+
+ if (state)
+ reg0 |= TDA8026_REG0_PDWN_MASK;
+ else
+ reg0 &= ~(TDA8026_REG0_PDWN_MASK);
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Set the card supply voltage.
+ * TDA PHY supports supply voltage of 1.8V, 3V and 5V.
+ */
+static int tda8026_set_voltage(struct tda8026 *tda8026, u8 slot, int voltage)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+
+ reg0 &= ~(TDA8026_REG0_VCC1V8_MASK | TDA8026_REG0_5V3VN_MASK);
+
+ switch (voltage) {
+ case SC_PHY_1_8V:
+ reg0 |= TDA8026_REG0_VCC1V8_MASK;
+ break;
+
+ case SC_PHY_5V:
+ reg0 |= TDA8026_REG0_5V3VN_MASK;
+ break;
+
+ case SC_PHY_3V:
+ default:
+ break;
+ }
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Enable the I/O line by setting I/OEN bit of slot's main address register.
+ * The I/O line should be enabled prior to card activation.
+ */
+static int tda8026_io_enable(struct tda8026 *tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_IOEN_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Disable the I/O line by clearing I/OEN bit of slot's main address register.
+ * The I/O line can be disabled post card activation.
+ */
+static int tda8026_io_disable(struct tda8026 *tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~(TDA8026_REG0_IOEN_MASK);
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Sets the mute counter in C[15:8] and C[7:0] register
+ * Write Reg[1:0] = 11 to select C[7:0] register
+ * Write Reg[1:0] = 10 to select C[15:8] register
+ */
+static int tda8026_set_atr_mute_time(struct tda8026 *tda8026, u8 slot,
+ int mute_counter)
+{
+ u8 reg0;
+ u8 mute_counter_high = (mute_counter & 0xFF00) >> 8;
+ u8 mute_counter_low = mute_counter & 0xFF;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_REG10_CLSB_VAL << TDA8026_REG0_REG10_SHIFT;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Write the mute counter value in C[7:0] LSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1,
+ &mute_counter_low);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_REG10_CMSB_VAL << TDA8026_REG0_REG10_SHIFT;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Write the mute counter value in C[15:8] MSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1,
+ &mute_counter_high);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Sets the ATR early time counter.
+ * Write Reg[1:0] = 01 to select D register
+ */
+static int tda8026_set_atr_early_time(struct tda8026 *tda8026, u8 slot,
+ int early_counter)
+{
+ u8 reg0;
+ int ret = 0;
+ u8 counter = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_REG10_D_VAL << TDA8026_REG0_REG10_SHIFT;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Write the early atr counter value in D register */
+ counter = (early_counter - TDA8026_MIN_EARLY_CYCLE) & 0xFF;
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, &counter);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static int tda8026_set_rstpin(struct tda8026 *tda8026, u8 slot, int state)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ /* Write Reg[1:0] = 00 to select CONFIG register */
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Read the Reg1 value */
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+
+ if (state)
+ reg1 |= TDA8026_REG1CFG_RSTIN_MASK;
+ else
+ reg1 &= ~TDA8026_REG1CFG_RSTIN_MASK;
+
+ /* Write the Reset value to the register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Activate the card by setting START bit of slot's main register.
+ * Voltage selction and enabling of I/O lines should be done prior
+ * to activating the card.
+ */
+static int tda8026_activate_card(struct sc_phy *phy_tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ /* if PHY is used then RSTIN should be 1 for async cards,
+ * currently this is applicable only for TDA8026
+ */
+ tda8026_set_rstpin(tda8026, slot, 1);
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_START_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Deactivate the card by clearing the START bit of slot's main register
+ * We implement normal de-activation here.
+ * On clearing the START bit with normal deactivation, automatic
+ * deactivation is initiated and performaed by TDA8026.
+ */
+static int tda8026_deactivate_card(struct sc_phy *phy_tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~(TDA8026_REG0_START_MASK);
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Warm reset is initiated by setting the WARM bit of slot's main register
+ */
+static int tda8026_warm_reset(struct sc_phy *phy_tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ /* See section 6.5 in TDA app note */
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_WARM_RESET_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Read and return the TDA8026 PHY product version
+ */
+static int tda8026_get_provider_version(struct tda8026 *tda8026)
+{
+ u8 val;
+ u8 version = 0;
+ int ret = 0;
+
+ /* Select Product version bank i.e Write CSB = 00 */
+ val = TDA8026_CSB_PV_INTSTAT_VAL;
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_PRODVER_ADDR, 1, &version);
+ if (ret < 0)
+ return ret;
+ else
+ dev_info(tda8026->dev, "Product Version = %x\n", version);
+
+ return version;
+}
+
+
+/*
+ * Set/reset the C4/C8 Pin
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_set_c4c8(struct tda8026 *tda8026, u8 slot, int state, int pin)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+ int mask = 0;
+
+ if (slot != 0) {
+ /* C4/C8 pin value valid only for slot 1 */
+ return -EINVAL;
+ } else {
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL <<
+ TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+ if (pin == SC_PHY_PIN_C8)
+ mask = TDA8026_REG1CFG_C8_MASK;
+ else if (pin == SC_PHY_PIN_C4)
+ mask = TDA8026_REG1CFG_C4_MASK;
+ else
+ return -EINVAL;
+
+ if (state)
+ reg1 |= mask;
+ else
+ reg1 &= ~mask;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Get the state of C4/C8 pin (high/low)
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_get_c4c8(struct tda8026 *tda8026, u8 slot, int pin)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+ int mask = 0;
+
+ if (slot != 0) {
+ /* C4/C8 pin value valid only for slot 1 */
+ return -EINVAL;
+ } else {
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL <<
+ TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+
+ if (pin == SC_PHY_PIN_C8)
+ mask = TDA8026_REG1CFG_C8_MASK;
+ else if (pin == SC_PHY_PIN_C4)
+ mask = TDA8026_REG1CFG_C4_MASK;
+ else
+ return -EINVAL;
+
+ return (reg1 &= mask) ? 1 : 0;
+ }
+ return 0;
+}
+
+/*
+ * Set card clock
+ * Applicable only for synchronous mode
+ */
+static int tda8026_set_cardclk(struct tda8026 *tda8026, u8 slot,
+ int config)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+
+ reg1 &= ~(TDA8026_REG1CFG_CLKPD_MASK);
+ reg1 |= (config << TDA8026_REG1CFG_CLKPD_SHIFT) &
+ TDA8026_REG1CFG_CLKPD_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Set the CLKDIV[1:0] bits.
+ * CLKDIV[1:0] bits define the card clock frequency
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_set_clkdiv(struct tda8026 *tda8026, u8 slot, int div)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+
+ reg1 &= ~(TDA8026_REG1CFG_CLKDIV_MASK);
+ reg1 |= (div << TDA8026_REG1CFG_CLKDIV_SHIFT) &
+ TDA8026_REG1CFG_CLKDIV_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Get the CLKDIV[1:0] bits.
+ * CLKDIV[1:0] bits define the card clock frequency
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_get_clkdiv(struct tda8026 *tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+
+ ret = reg1 & TDA8026_REG1CFG_CLKDIV_MASK;
+
+ return ret;
+}
+
+/*
+ * Check if card is present in the slot.
+ */
+static int tda8026_card_present(struct tda8026 *tda8026, u8 slot)
+{
+ int present = 0;
+ int ret = 0;
+ u8 status = 0;
+
+ if (tda8026->enable == 0)
+ return 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG0_ADDR, 1, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status & TDA8026_REG0_PRES_MASK)
+ present = 1;
+
+ return present;
+}
+
+/**
+ * tda8026_register_notify - register a notifier callback whenever a card
+ * event happens
+ * @nb: pointer to the notifier block for the callback events.
+ */
+static int tda8026_register_notify(struct sc_phy *phy_tda8026,
+ struct notifier_block *nb, void *data)
+{
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ phy_tda8026->notify_data = data;
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ blocking_notifier_chain_register(&tda8026_notifier_list, nb);
+ tda8026->notify = 1;
+ return 0;
+}
+
+/**
+ * tda8026_unregister_notify - unregister a notifier callback
+ * event happens
+ * @nb: pointer to the notifier block for the callback events.
+ */
+static int tda8026_unregister_notify(struct sc_phy *phy_tda8026,
+ struct notifier_block *nb)
+{
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ blocking_notifier_chain_unregister(&tda8026_notifier_list, nb);
+ tda8026->notify = 0;
+ return 0;
+}
+
+static int tda8026_set_config(struct sc_phy *phy_tda8026, u8 slot, enum
+ sc_phy_config attr, int value)
+{
+ int ret = 0;
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ switch (attr) {
+ case SC_PHY_CARD_SUPPLY_VOLTAGE:
+ ret = tda8026_set_voltage(tda8026, slot, value);
+ break;
+
+ case SC_PHY_ATR_MUTE_TIME:
+ ret = tda8026_set_atr_mute_time(tda8026, slot, value);
+ break;
+
+ case SC_PHY_ATR_EARLY_TIME:
+ ret = tda8026_set_atr_early_time(tda8026, slot, value);
+ break;
+
+ case SC_PHY_CARD_MODE:
+ if (value == SC_PHY_SYNC) {
+ /* set clkdiv to zero, rst pin to low and pwdn bit to
+ * logic 0
+ */
+ tda8026_set_clkdiv(tda8026, slot, 0);
+ tda8026_set_rstpin(tda8026, slot, 0);
+ tda8026_pwdn(tda8026, slot, 0);
+ } else {
+ /* Nothing to do, default mode is async */
+ }
+ break;
+
+ case SC_PHY_IO:
+ if (value)
+ ret = tda8026_io_enable(tda8026, slot);
+ else
+ ret = tda8026_io_disable(tda8026, slot);
+ break;
+
+ case SC_PHY_PIN_RST:
+ ret = tda8026_set_rstpin(tda8026, slot, value);
+ break;
+
+ case SC_PHY_CLKDIV:
+ ret = tda8026_set_clkdiv(tda8026, slot, value);
+ break;
+
+ case SC_PHY_MODE:
+ if (value == SC_PHY_ACTIVE)
+ tda8026_enable(tda8026);
+ else if (value == SC_PHY_SHUTDOWN)
+ tda8026_disable(tda8026);
+ else
+ ret = -EINVAL;
+ break;
+
+ case SC_PHY_PIN_C4:
+ ret = tda8026_set_c4c8(tda8026, slot, value, SC_PHY_PIN_C4);
+ break;
+
+ case SC_PHY_PIN_C8:
+ ret = tda8026_set_c4c8(tda8026, slot, value, SC_PHY_PIN_C8);
+ break;
+
+ case SC_PHY_PIN_CLK:
+ ret = tda8026_set_cardclk(tda8026, slot, value);
+ break;
+
+ default:
+ ret = -EINVAL;
+ dev_err(phy_tda8026->dev, "operation not supported:%d", attr);
+ break;
+ }
+ return ret;
+}
+
+static int tda8026_get_config(struct sc_phy *phy_tda8026, u8 slot, enum
+ sc_phy_config attr)
+{
+ int ret = -1;
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ switch (attr) {
+ case SC_PHY_CARD_PRESENCE:
+ ret = tda8026_card_present(tda8026, slot);
+ break;
+
+ case SC_PHY_VERSION:
+ ret = tda8026_get_provider_version(tda8026);
+ break;
+
+ case SC_PHY_CLKDIV:
+ ret = tda8026_get_clkdiv(tda8026, slot);
+ break;
+
+ case SC_PHY_PIN_C4:
+ ret = tda8026_get_c4c8(tda8026, slot, SC_PHY_PIN_C4);
+ break;
+
+ case SC_PHY_PIN_C8:
+ ret = tda8026_get_c4c8(tda8026, slot, SC_PHY_PIN_C8);
+ break;
+
+ default:
+ ret = -EINVAL;
+ dev_err(phy_tda8026->dev, "operation not supported:%d", attr);
+ break;
+ }
+ return ret;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tda8026_id_table[];
+#endif
+
+static int tda8026_parse_dt(struct device *dev, struct tda8026 *pdata)
+{
+ struct device_node *np = dev->of_node;
+ int ret = 0;
+
+ pdata->shutdown_gpio = of_get_named_gpio(np, "shutdown-gpio", 0);
+ if (!gpio_is_valid(pdata->shutdown_gpio)) {
+ dev_err(dev, "Failed to get shutdown gpio\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(dev, pdata->shutdown_gpio,
+ GPIOF_DIR_OUT, "shutdown_gpio");
+ if (ret) {
+ dev_err(dev, "Failed to request shutdown_gpio\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int tda8026_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct sc_phy *phy_tda8026;
+ struct tda8026 *pdata;
+
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
+
+ if (np == NULL)
+ return -EINVAL;
+
+ pdata = devm_kzalloc(dev, sizeof(struct tda8026), GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+
+ phy_tda8026 = devm_kzalloc(dev, sizeof(struct sc_phy), GFP_KERNEL);
+ if (phy_tda8026 == NULL)
+ return -ENOMEM;
+
+ ret = tda8026_parse_dt(dev, pdata);
+ if (ret != 0)
+ return ret;
+
+ i2c_set_clientdata(i2c, phy_tda8026);
+ phy_tda8026->dev = &i2c->dev;
+
+ phy_tda8026->pdata = (void *)pdata;
+ pdata->control_data = i2c;
+ pdata->irq = i2c->irq;
+ pdata->dev = phy_tda8026->dev;
+ pdata->notify = 0;
+
+ if (pdata->irq == 0) {
+ return -EINVAL;
+ }
+ ret = devm_request_threaded_irq(dev, pdata->irq, NULL, tda8026_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "tda8026", phy_tda8026);
+ if (ret < 0) {
+ dev_err(phy_tda8026->dev, "can't get irq %d err %d\n",
+ pdata->irq, ret);
+ return ret;
+ }
+
+ /* enable phy */
+ tda8026_enable(pdata);
+
+ tda8026_clear_interrupts(pdata);
+ phy_tda8026->set_config = tda8026_set_config;
+ phy_tda8026->get_config = tda8026_get_config;
+ phy_tda8026->activate_card = tda8026_activate_card;
+ phy_tda8026->deactivate_card = tda8026_deactivate_card;
+ phy_tda8026->warm_reset = tda8026_warm_reset;
+ phy_tda8026->register_notify = tda8026_register_notify;
+ phy_tda8026->unregister_notify = tda8026_unregister_notify;
+
+ /* disable phy */
+ tda8026_disable(pdata);
+
+ return 0;
+}
+
+static int tda8026_i2c_remove(struct i2c_client *i2c)
+{
+ struct sc_phy *phy_tda8026;
+ struct tda8026 *tda8026;
+ int action = 0;
+
+ phy_tda8026 = i2c_get_clientdata(i2c);
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ /* notify action */
+ action = SC_PHY_REMOVED;
+ blocking_notifier_call_chain(&tda8026_notifier_list, action,
+ phy_tda8026->notify_data);
+ tda8026->notify = 0;
+
+ /* enable shutdown mode */
+ tda8026_disable(tda8026);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tda8026_id_table[] = {
+ { .compatible = "nxp,tda8026" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tda8026_id_table);
+#endif
+
+static const struct i2c_device_id tda8026_i2c_id[] = {
+ {"tda8026", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda8026_i2c_id);
+
+static struct i2c_driver tda8026_i2c_driver = {
+ .driver = {
+ .name = "tda8026",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tda8026_id_table),
+ },
+ .probe = tda8026_i2c_probe,
+ .remove = tda8026_i2c_remove,
+ .id_table = tda8026_i2c_id,
+};
+static int __init tda8026_i2c_init(void)
+{
+ int ret;
+ ret = i2c_add_driver(&tda8026_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register TDA8026 I2C driver: %d\n", ret);
+ return ret;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tda8026_i2c_init);
+
+static void __exit tda8026_i2c_exit(void)
+{
+ i2c_del_driver(&tda8026_i2c_driver);
+}
+module_exit(tda8026_i2c_exit);
+
+MODULE_AUTHOR("Maulik Mankad <[email protected]>");
+MODULE_DESCRIPTION("TDA8026 Smart Card NXP PHY driver");
+MODULE_LICENSE("GPL");
--
1.7.1
- Board specific DT entries for TI's USIM - smart card controller of AM43xx
platfrom.These entries are used by USIM driver for various configurations.
- Shutdown line of NXP phy is maped to GPIO5. So enabling same to have support
for NXP phy.
- i2c2 pinmux configuration - NxP tda8026 phy is connected to i2c2 lines
Signed-off-by: Satish Patel <[email protected]>
---
arch/arm/boot/dts/am43x-epos-evm.dts | 43 ++++++++++++++++++++++++++++++++++
1 files changed, 43 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index fbf9c4c..38ef2b8 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -79,6 +79,24 @@
0x18c (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* i2c0_scl.i2c0_scl */
>;
};
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0x1c0 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE8) /* i2c2_sda.i2c2_sda */
+ 0x1c4 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE8) /* i2c2_scl.i2c2_scl */
+ >;
+ };
+
+ usim0_default: usim0_default {
+ pinctrl-single,pins = <
+ /* USIM 0 */
+ 0x1B4 (SLEWCTRL_FAST | PULL_DISABLE | MUX_MODE8) /* CLK0 */
+ 0x1B0 (SLEWCTRL_FAST | PULL_DISABLE | MUX_MODE8) /* CLK1 */
+ 0x1B8 (SLEWCTRL_FAST | INPUT_EN | PULL_DISABLE | MUX_MODE8) /* DATA0 */
+ 0x1BC (SLEWCTRL_FAST | INPUT_EN | PULL_DISABLE | MUX_MODE8) /* DATA1 */
+ 0x1C8 (SLEWCTRL_FAST | INPUT_EN | PULL_UP | MUX_MODE8) /* IRQn */
+ >;
+ };
};
matrix_keypad: matrix_keypad@0 {
@@ -169,6 +187,27 @@
};
};
+&usim0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&usim0_default>;
+ phy = <&tda8026>;
+ phy-slots = <1>;
+ status = "okay";
+};
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+ status = "okay";
+
+ tda8026: tda8026@48 {
+ compatible = "nxp,tda8026";
+ reg = <0x48>;
+ shutdown-gpio = <&gpio5 19 GPIO_ACTIVE_HIGH>; /* Bank5, pin19 */
+ interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+ };
+};
+
&gpio0 {
status = "okay";
};
@@ -184,3 +223,7 @@
&gpio3 {
status = "okay";
};
+
+&gpio5 {
+ status = "okay";
+};
--
1.7.1
TI-USIM driver is a platform driver that provides a character
driver interface to user applications.
It allows user applications to call IOCTL's to
perform smart card operations.
Driver currently supports
- ATR
- T=0 & T=1 protocol
- clock stop mode
- smart card clock configuration
- Tx/Rx application data units (APDU) to smart card
- Interface to PHY using DT & phy interface
Validation is done with ACOS3 smart cards
Signed-off-by: Satish Patel <[email protected]>
---
.../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
drivers/char/Kconfig | 7 +
drivers/char/Makefile | 1 +
drivers/char/ti-usim-hw.h | 863 +++++++++
drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
include/linux/ti-usim.h | 98 +
6 files changed, 2859 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
create mode 100644 drivers/char/ti-usim-hw.h
create mode 100644 drivers/char/ti-usim.c
create mode 100644 include/linux/ti-usim.h
diff --git a/Documentation/devicetree/bindings/ti-usim/ti-usim.txt b/Documentation/devicetree/bindings/ti-usim/ti-usim.txt
new file mode 100644
index 0000000..6dc5d9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/ti-usim/ti-usim.txt
@@ -0,0 +1,31 @@
+ti-usim: USIM - Smart Card Controller
+
+Required Properties:
+- compatible: Should be "ti,usim"
+- reg: Specifies base physical address and size of the USIM registers
+- interrupts: Interrupt number for the USIM controller
+- ti,hwmods: Name of the hwmod associated to the USIM controller
+
+- clocks : list of clock specifiers, corresponding to entries in the
+ clock-names property
+- clock-names : should contain "opt_fck" and "opt_fck32" entries, matching
+ entries in the clocks property.
+
+Optional properties:
+- pinctrl-0: Should specify pin control group used for this controller.
+- pinctrl-names: Should contain only one value - "default", for more details
+ please refer to pinctrl-bindings.txt
+
+- phy : Should specify <smart card phy> reference connected to controller
+- phy-slots : No of slots to which controller will communicate
+
+Example:
+
+usim0: usim@48034000 {
+ compatible = "ti,usim";
+ reg = <0x48034000 0x1000>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "usim0";
+ clocks = <&usim0_opt_fck>, <&usim0_opt_fck32>;
+ clock-names = "opt_fck", "opt_fck32";
+ };
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index fa3243d..dee0209 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -599,5 +599,12 @@ config TILE_SROM
device appear much like a simple EEPROM, and knows
how to partition a single ROM for multiple purposes.
+config TI_USIM
+ tristate "Character device access to TI's USIM module on AM43X"
+ depends on SOC_AM43XX
+ help
+ This device creates a character device interface that enables
+ user applications to exchange data with TI's USIM module.
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7ff1d0d..763fd3d 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
obj-$(CONFIG_TILE_SROM) += tile-srom.o
+obj-$(CONFIG_TI_USIM) += ti-usim.o
diff --git a/drivers/char/ti-usim-hw.h b/drivers/char/ti-usim-hw.h
new file mode 100644
index 0000000..b6d69ff
--- /dev/null
+++ b/drivers/char/ti-usim-hw.h
@@ -0,0 +1,863 @@
+/*
+ * ti-usim-hw.h - Header file for USIM smart card interface
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __TI_USIM_HW_H__
+#define __TI_USIM_HW_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/sc_phy.h>
+#include <linux/ti-usim.h>
+
+
+#define USIM_MAX_SLOTS 0x2
+
+/* WWT Work Wait Time */
+#define USIM_EMV_WI (10)
+#define USIM_EMV_WWT ((960 * USIM_EMV_WI) + (480))
+/* CGT Character Guard Time */
+#define USIM_EMV_CGT (12)
+
+#define USIM_ATR_TIMEOUT_EMV (20160)
+#define USIM_EMV_ATR_EARLY_TO (370)
+#define USIM_EMV_ATR_MUTE_TO (42000)
+
+#define USIM_MAX_RX_FIFO_SIZE (260)
+#define USIM_MAX_TX_FIFO_SIZE (260)
+#define USIM_MAX_PARITY_RETRIES (7)
+
+#define USIM_IRQ_NATR (0x00000001)
+#define USIM_IRQ_WT (0x00000002)
+#define USIM_IRQ_RXFULL (0x00000004)
+#define USIM_IRQ_TX (0x00000008)
+#define USIM_IRQ_RX (0x00000010)
+#define USIM_IRQ_CD (0x00000020)
+#define USIM_IRQ_EOB (0x00000040)
+#define USIM_IRQ_TOC (0x00000080)
+#define USIM_IRQ_TOB (0x00000100)
+#define USIM_IRQ_RESENT (0x00000200)
+#define USIM_IRQ_TS_ERR (0x00000400)
+#define USIM_IRQ_EMV_ATR_LENGTH_TIME_OUT (0x00000800)
+#define USIM_IRQ_STOP (0x00001000)
+#define USIM_IRQ_PAR_ERR_LEVEL_REACHED (0x00002000)
+#define USIM_IRQ_FRAME_ERR (0x00004000)
+#define USIM_IRQ_RXDMA_RDY (0x00008000)
+#define USIM_IRQ_ATR_START (0x00010000)
+#define USIM_IRQ_ACT_DONE (0x00020000)
+#define USIM_IRQ_DEACT_DONE (0x00040000)
+#define USIM_IRQ_TX_BLOCK_DONE (0x00080000)
+#define USIM_IRQ_TX_BLOCK_REQ (0x00100000)
+
+#define USIM_CONFSCLKMODE_LEGACY 0x0
+#define USIM_CONFSCLKMODE_HF 0x1
+
+/*
+ * Different operating modes supported in USIM.
+ * Programming USIM to a different mode from current mode would
+ * endup in state machine state change within the IPs FSM
+ */
+enum usim_mode {
+ USIM_MODE_LEGACY = 0x0,
+ USIM_MODE_FREEZE = 0x1,
+ USIM_MODE_TXRX = 0x2,
+ USIM_MODE_ATR = 0x3,
+ USIM_MODE_ACT = 0x4,
+ USIM_MODE_DEACT = 0x5,
+ USIM_MODE_IDLE = 0x6,
+};
+
+/*
+ * structure to store slot specific information
+ */
+struct usim_slotcontext {
+ char atr[USIM_MAX_ATRLENGTH];
+ char rxbuf[USIM_MAX_APDU_LENGTH];
+ bool emv;
+ enum usim_mode state;
+ int event;
+ int protocol;
+ enum usim_card_voltage supply;
+ int rx_explen;
+ int rx_counter;
+ int atr_length;
+ enum usim_smartcard_clock clock;
+};
+
+struct usim {
+ struct device *dev;
+
+ /* to protect interrput handling */
+ spinlock_t lock;
+ int irq;
+ void __iomem *base;
+ int slot;
+ int max_slots;
+ int phy_present;
+ int txdone;
+ int rxdone;
+ int atrdone;
+ int user_pid;
+ int enable;
+ struct sc_phy *phy;
+ struct usim_slotcontext *slot_ctx;
+
+ struct clk *opt_fclk;
+ struct clk *opt_fclk32;
+ struct clk *usim_dbclk;
+ struct clk *clkdiv32k_ick;
+ struct clk *usim0_fck;
+ struct clk *dpll_core_m4_ck;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_root;
+#endif
+};
+
+/*
+ * Register Definitions: Taken from auto generated file
+ */
+#define USIM_REVISION (0x0U)
+#define USIM_IDENT (0x4U)
+#define USIM_SYSCONFIG (0x10U)
+#define USIM_SYSSTATUS (0x14U)
+#define USIM_IRQSTATUS (0x18U)
+#define USIM_IRQENABLE (0x1cU)
+#define USIM_WAKEUPEN (0x20U)
+#define USIM_CMD (0x24U)
+#define USIM_STAT (0x28U)
+#define USIM_CONF1 (0x2cU)
+#define USIM_CONF2 (0x30U)
+#define USIM_CONF3 (0x34U)
+#define USIM_DRX (0x38U)
+#define USIM_DTX (0x3cU)
+#define USIM_FIFOS (0x40U)
+#define USIM_CGT (0x44U)
+#define USIM_CWT (0x48U)
+#define USIM_BWT (0x4cU)
+#define USIM_DEBUG (0x50U)
+#define USIM_CONF_SAM1_DIV (0x54U)
+#define USIM_CONF4 (0x58U)
+#define USIM_ATR_CLK_PRD_NBS (0x5cU)
+#define USIM_CONF_ETU_DIV (0x60U)
+#define USIM_CONF5 (0x64U)
+#define USIM_TC_GUARD_TIME_ADD (0x68U)
+#define USIM_RXFIFO_LEVEL (0x6cU)
+#define USIM_RXFIFO_BYTECNT (0x70U)
+#define USIM_WWT (0x74U)
+#define USIM_CONF6 (0x78U)
+#define USIM_IO_DIRECT (0x7cU)
+#define USIM_TX_BLOCK (0x84U)
+
+/*
+ * Field Definition Macros
+ */
+#define USIM_REVISION_REV_SHIFT (0U)
+#define USIM_REVISION_REV_MASK (0x000000ffU)
+
+#define USIM_REVISION_RESERVED_24_SHIFT (8U)
+#define USIM_REVISION_RESERVED_24_MASK (0xffffff00U)
+
+#define USIM_IDENT_VC_SHIFT (0U)
+#define USIM_IDENT_VC_MASK (0x0000ffffU)
+
+#define USIM_IDENT_RESERVED_16_31_SHIFT (16U)
+#define USIM_IDENT_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_SYSCONFIG_AUTOIDLE_SHIFT (0U)
+#define USIM_SYSCONFIG_AUTOIDLE_MASK (0x00000001U)
+#define USIM_SYSCONFIG_AUTOIDLE_AUTOIDLE_VALUE_1 (1U)
+#define USIM_SYSCONFIG_AUTOIDLE_AUTOIDLE_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_SOFTRESET_SHIFT (1U)
+#define USIM_SYSCONFIG_SOFTRESET_MASK (0x00000002U)
+#define USIM_SYSCONFIG_SOFTRESET_SOFTRESET_VALUE_1 (1U)
+#define USIM_SYSCONFIG_SOFTRESET_SOFTRESET_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_ENAWAKEUP_SHIFT (2U)
+#define USIM_SYSCONFIG_ENAWAKEUP_MASK (0x00000004U)
+#define USIM_SYSCONFIG_ENAWAKEUP_ENAWAKEUP_VALUE_1 (1U)
+#define USIM_SYSCONFIG_ENAWAKEUP_ENAWAKEUP_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_IDLEMODE_SHIFT (3U)
+#define USIM_SYSCONFIG_IDLEMODE_MASK (0x00000018U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_3 (3U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_2 (2U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_1 (1U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_EMUFREE_SHIFT (5U)
+#define USIM_SYSCONFIG_EMUFREE_MASK (0x00000020U)
+#define USIM_SYSCONFIG_EMUFREE_EMUFREE_VALUE_0 (0U)
+#define USIM_SYSCONFIG_EMUFREE_EMUFREE_VALUE_1 (1U)
+
+#define USIM_SYSCONFIG_RESERVED_6_7_SHIFT (6U)
+#define USIM_SYSCONFIG_RESERVED_6_7_MASK (0x000000c0U)
+
+#define USIM_SYSCONFIG_CLOCKACTIVITY_SHIFT (8U)
+#define USIM_SYSCONFIG_CLOCKACTIVITY_MASK (0x00000300U)
+
+#define USIM_SYSCONFIG_RESERVED_22_SHIFT (10U)
+#define USIM_SYSCONFIG_RESERVED_22_MASK (0xfffffc00U)
+
+#define USIM_SYSSTATUS_RESETDONE_SHIFT (0U)
+#define USIM_SYSSTATUS_RESETDONE_MASK (0x00000001U)
+#define USIM_SYSSTATUS_RESETDONE_RESETDONE_VALUE_1 (1U)
+#define USIM_SYSSTATUS_RESETDONE_RESETDONE_VALUE_0 (0U)
+
+#define USIM_SYSSTATUS_RESERVED_31_SHIFT (1U)
+#define USIM_SYSSTATUS_RESERVED_31_MASK (0xfffffffeU)
+
+#define USIM_IRQSTATUS_USIM_NATR_SHIFT (0U)
+#define USIM_IRQSTATUS_USIM_NATR_MASK (0x00000001U)
+
+#define USIM_IRQSTATUS_USIM_WT_SHIFT (1U)
+#define USIM_IRQSTATUS_USIM_WT_MASK (0x00000002U)
+
+#define USIM_IRQSTATUS_USIM_RXFULL_SHIFT (2U)
+#define USIM_IRQSTATUS_USIM_RXFULL_MASK (0x00000004U)
+
+#define USIM_IRQSTATUS_USIM_TX_SHIFT (3U)
+#define USIM_IRQSTATUS_USIM_TX_MASK (0x00000008U)
+
+#define USIM_IRQSTATUS_USIM_RX_SHIFT (4U)
+#define USIM_IRQSTATUS_USIM_RX_MASK (0x00000010U)
+
+#define USIM_IRQSTATUS_USIM_CD_SHIFT (5U)
+#define USIM_IRQSTATUS_USIM_CD_MASK (0x00000020U)
+
+#define USIM_IRQSTATUS_USIM_EOB_SHIFT (6U)
+#define USIM_IRQSTATUS_USIM_EOB_MASK (0x00000040U)
+
+#define USIM_IRQSTATUS_USIM_TOC_SHIFT (7U)
+#define USIM_IRQSTATUS_USIM_TOC_MASK (0x00000080U)
+
+#define USIM_IRQSTATUS_USIM_TOB_SHIFT (8U)
+#define USIM_IRQSTATUS_USIM_TOB_MASK (0x00000100U)
+
+#define USIM_IRQSTATUS_USIM_RESENT_SHIFT (9U)
+#define USIM_IRQSTATUS_USIM_RESENT_MASK (0x00000200U)
+
+#define USIM_IRQSTATUS_TS_ERROR_SHIFT (10U)
+#define USIM_IRQSTATUS_TS_ERROR_MASK (0x00000400U)
+
+#define USIM_IRQSTATUS_IT_EMV_ATR_LENGTH_TIME_OUT_SHIFT (11U)
+#define USIM_IRQSTATUS_IT_EMV_ATR_LENGTH_TIME_OUT_MASK (0x00000800U)
+
+#define USIM_IRQSTATUS_RESERVED_SHIFT (21U)
+#define USIM_IRQSTATUS_RESERVED_MASK (0xffe00000U)
+
+#define USIM_IRQSTATUS_USIM_STOP_CLK_SHIFT (12U)
+#define USIM_IRQSTATUS_USIM_STOP_CLK_MASK (0x00001000U)
+
+#define USIM_IRQSTATUS_PAR_ERR_LEVEL_REACHED_SHIFT (13U)
+#define USIM_IRQSTATUS_PAR_ERR_LEVEL_REACHED_MASK (0x00002000U)
+
+#define USIM_IRQSTATUS_FRAME_ERR_SHIFT (14U)
+#define USIM_IRQSTATUS_FRAME_ERR_MASK (0x00004000U)
+
+#define USIM_IRQSTATUS_RXDMA_RDY_SHIFT (15U)
+#define USIM_IRQSTATUS_RXDMA_RDY_MASK (0x00008000U)
+
+#define USIM_IRQSTATUS_ATR_START_SHIFT (16U)
+#define USIM_IRQSTATUS_ATR_START_MASK (0x00010000U)
+
+#define USIM_IRQSTATUS_ACT_DONE_SHIFT (17U)
+#define USIM_IRQSTATUS_ACT_DONE_MASK (0x00020000U)
+
+#define USIM_IRQSTATUS_DEACT_DONE_SHIFT (18U)
+#define USIM_IRQSTATUS_DEACT_DONE_MASK (0x00040000U)
+
+#define USIM_IRQSTATUS_TX_BLOCK_DONE_SHIFT (19U)
+#define USIM_IRQSTATUS_TX_BLOCK_DONE_MASK (0x00080000U)
+
+#define USIM_IRQSTATUS_TX_BLOCK_REQ_SHIFT (20U)
+#define USIM_IRQSTATUS_TX_BLOCK_REQ_MASK (0x00100000U)
+
+#define USIM_IRQENABLE_RESERVED_SHIFT (21U)
+#define USIM_IRQENABLE_RESERVED_MASK (0xffe00000U)
+
+#define USIM_IRQENABLE_EMV_ATR_LENGTH_TIME_OUT_EN_SHIFT (11U)
+#define USIM_IRQENABLE_EMV_ATR_LENGTH_TIME_OUT_EN_MASK (0x00000800U)
+
+#define USIM_IRQENABLE_TS_ERR_EN_SHIFT (10U)
+#define USIM_IRQENABLE_TS_ERR_EN_MASK (0x00000400U)
+
+#define USIM_IRQENABLE_RESENT_EN_SHIFT (9U)
+#define USIM_IRQENABLE_RESENT_EN_MASK (0x00000200U)
+
+#define USIM_IRQENABLE_TOB_EN_SHIFT (8U)
+#define USIM_IRQENABLE_TOB_EN_MASK (0x00000100U)
+
+#define USIM_IRQENABLE_TOC_EN_SHIFT (7U)
+#define USIM_IRQENABLE_TOC_EN_MASK (0x00000080U)
+
+#define USIM_IRQENABLE_EOB_EN_SHIFT (6U)
+#define USIM_IRQENABLE_EOB_EN_MASK (0x00000040U)
+
+#define USIM_IRQENABLE_CD_EN_SHIFT (5U)
+#define USIM_IRQENABLE_CD_EN_MASK (0x00000020U)
+
+#define USIM_IRQENABLE_RX_EN_SHIFT (4U)
+#define USIM_IRQENABLE_RX_EN_MASK (0x00000010U)
+
+#define USIM_IRQENABLE_TX_EN_SHIFT (3U)
+#define USIM_IRQENABLE_TX_EN_MASK (0x00000008U)
+
+#define USIM_IRQENABLE_RXFULL_EN_SHIFT (2U)
+#define USIM_IRQENABLE_RXFULL_EN_MASK (0x00000004U)
+
+#define USIM_IRQENABLE_WT_EN_SHIFT (1U)
+#define USIM_IRQENABLE_WT_EN_MASK (0x00000002U)
+
+#define USIM_IRQENABLE_NATR_EN_SHIFT (0U)
+#define USIM_IRQENABLE_NATR_EN_MASK (0x00000001U)
+
+#define USIM_IRQENABLE_STOP_CLK_SHIFT (12U)
+#define USIM_IRQENABLE_STOP_CLK_MASK (0x00001000U)
+
+#define USIM_IRQENABLE_PAR_ERR_LEVEL_REACHED_EN_SHIFT (13U)
+#define USIM_IRQENABLE_PAR_ERR_LEVEL_REACHED_EN_MASK (0x00002000U)
+
+#define USIM_IRQENABLE_FRAME_ERR_EN_SHIFT (14U)
+#define USIM_IRQENABLE_FRAME_ERR_EN_MASK (0x00004000U)
+
+#define USIM_IRQENABLE_RXDMA_RDY_EN_SHIFT (15U)
+#define USIM_IRQENABLE_RXDMA_RDY_EN_MASK (0x00008000U)
+
+#define USIM_IRQENABLE_ATR_START_EN_SHIFT (16U)
+#define USIM_IRQENABLE_ATR_START_EN_MASK (0x00010000U)
+
+#define USIM_IRQENABLE_ACT_DONE_EN_SHIFT (17U)
+#define USIM_IRQENABLE_ACT_DONE_EN_MASK (0x00020000U)
+
+#define USIM_IRQENABLE_DEACT_DONE_EN_SHIFT (18U)
+#define USIM_IRQENABLE_DEACT_DONE_EN_MASK (0x00040000U)
+
+#define USIM_IRQENABLE_TX_BLOCK_DONE_EN_SHIFT (19U)
+#define USIM_IRQENABLE_TX_BLOCK_DONE_EN_MASK (0x00080000U)
+
+#define USIM_IRQENABLE_TX_BLOCK_REQ_EN_SHIFT (20U)
+#define USIM_IRQENABLE_TX_BLOCK_REQ_EN_MASK (0x00100000U)
+
+#define USIM_WAKEUPEN_STOP_CLK_SHIFT (12U)
+#define USIM_WAKEUPEN_STOP_CLK_MASK (0x00001000U)
+
+#define USIM_WAKEUPEN_NATR_EN_SHIFT (0U)
+#define USIM_WAKEUPEN_NATR_EN_MASK (0x00000001U)
+
+#define USIM_WAKEUPEN_WT_EN_SHIFT (1U)
+#define USIM_WAKEUPEN_WT_EN_MASK (0x00000002U)
+
+#define USIM_WAKEUPEN_RXFULL_EN_SHIFT (2U)
+#define USIM_WAKEUPEN_RXFULL_EN_MASK (0x00000004U)
+
+#define USIM_WAKEUPEN_TX_EN_SHIFT (3U)
+#define USIM_WAKEUPEN_TX_EN_MASK (0x00000008U)
+
+#define USIM_WAKEUPEN_RX_EN_SHIFT (4U)
+#define USIM_WAKEUPEN_RX_EN_MASK (0x00000010U)
+
+#define USIM_WAKEUPEN_CD_EN_SHIFT (5U)
+#define USIM_WAKEUPEN_CD_EN_MASK (0x00000020U)
+
+#define USIM_WAKEUPEN_EOB_EN_SHIFT (6U)
+#define USIM_WAKEUPEN_EOB_EN_MASK (0x00000040U)
+
+#define USIM_WAKEUPEN_TOC_EN_SHIFT (7U)
+#define USIM_WAKEUPEN_TOC_EN_MASK (0x00000080U)
+
+#define USIM_WAKEUPEN_TOB_EN_SHIFT (8U)
+#define USIM_WAKEUPEN_TOB_EN_MASK (0x00000100U)
+
+#define USIM_WAKEUPEN_RESENT_EN_SHIFT (9U)
+#define USIM_WAKEUPEN_RESENT_EN_MASK (0x00000200U)
+
+#define USIM_WAKEUPEN_TS_ERR_EN_SHIFT (10U)
+#define USIM_WAKEUPEN_TS_ERR_EN_MASK (0x00000400U)
+
+#define USIM_WAKEUPEN_EMV_ATR_LENGTH_TIME_OUT_EN_SHIFT (11U)
+#define USIM_WAKEUPEN_EMV_ATR_LENGTH_TIME_OUT_EN_MASK (0x00000800U)
+
+#define USIM_WAKEUPEN_RESERVED_SHIFT (21U)
+#define USIM_WAKEUPEN_RESERVED_MASK (0xffe00000U)
+
+#define USIM_WAKEUPEN_PAR_ERR_LEVEL_REACHED_EN_SHIFT (13U)
+#define USIM_WAKEUPEN_PAR_ERR_LEVEL_REACHED_EN_MASK (0x00002000U)
+
+#define USIM_WAKEUPEN_FRAME_ERR_EN_SHIFT (14U)
+#define USIM_WAKEUPEN_FRAME_ERR_EN_MASK (0x00004000U)
+
+#define USIM_WAKEUPEN_RXDMA_RDY_EN_SHIFT (15U)
+#define USIM_WAKEUPEN_RXDMA_RDY_EN_MASK (0x00008000U)
+
+#define USIM_WAKEUPEN_ATR_START_EN_SHIFT (16U)
+#define USIM_WAKEUPEN_ATR_START_EN_MASK (0x00010000U)
+
+#define USIM_WAKEUPEN_ACT_DONE_EN_SHIFT (17U)
+#define USIM_WAKEUPEN_ACT_DONE_EN_MASK (0x00020000U)
+
+#define USIM_WAKEUPEN_DEACT_DONE_EN_SHIFT (18U)
+#define USIM_WAKEUPEN_DEACT_DONE_EN_MASK (0x00040000U)
+
+#define USIM_WAKEUPEN_TX_BLOCK_DONE_EN_SHIFT (19U)
+#define USIM_WAKEUPEN_TX_BLOCK_DONE_EN_MASK (0x00080000U)
+
+#define USIM_WAKEUPEN_TX_BLOCK_REQ_EN_SHIFT (20U)
+#define USIM_WAKEUPEN_TX_BLOCK_REQ_EN_MASK (0x00100000U)
+
+#define USIM_CMD_RESERVED_0_SHIFT (0U)
+#define USIM_CMD_RESERVED_0_MASK (0x00000001U)
+
+#define USIM_CMD_CMDSTOP_SHIFT (1U)
+#define USIM_CMD_CMDSTOP_MASK (0x00000002U)
+#define USIM_CMD_CMDSTOP_CMDSTOP_VALUE_0 (0U)
+#define USIM_CMD_CMDSTOP_CMDSTOP_VALUE_1 (1U)
+
+#define USIM_CMD_CMDSTART_SHIFT (2U)
+#define USIM_CMD_CMDSTART_MASK (0x00000004U)
+#define USIM_CMD_CMDSTART_CMDSTART_VALUE_0 (0U)
+#define USIM_CMD_CMDSTART_CMDSTART_VALUE_1 (1U)
+
+#define USIM_CMD_MODULE_CLK_EN_SHIFT (3U)
+#define USIM_CMD_MODULE_CLK_EN_MASK (0x00000008U)
+#define USIM_CMD_MODULE_CLK_EN_0 (0U)
+#define USIM_CMD_MODULE_CLK_EN_1 (1U)
+
+#define USIM_CMD_CMD_WARM_RST_SHIFT (4U)
+#define USIM_CMD_CMD_WARM_RST_MASK (0x00000010U)
+#define USIM_CMD_CMD_WARM_RST_WARM_RST_VALUE_1 (1U)
+#define USIM_CMD_CMD_WARM_RST_WARM_RST_VALUE_0 (0U)
+
+#define USIM_CMD_CMD_CLOCK_STOP_SHIFT (5U)
+#define USIM_CMD_CMD_CLOCK_STOP_MASK (0x00000020U)
+#define USIM_CMD_CMD_CLOCK_STOP_1 (1U)
+#define USIM_CMD_CMD_CLOCK_STOP_0 (0U)
+
+#define USIM_CMD_RESERVED_7_31_SHIFT (7U)
+#define USIM_CMD_RESERVED_7_31_MASK (0xffffff80U)
+
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_SHIFT (6U)
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_MASK (0x00000040U)
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_1 (1U)
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_0 (0U)
+
+#define USIM_STAT_STATNOCARD_SHIFT (0U)
+#define USIM_STAT_STATNOCARD_MASK (0x00000001U)
+#define USIM_STAT_STATNOCARD_STATNOCARD_VALUE_1 (1U)
+#define USIM_STAT_STATNOCARD_STATNOCARD_VALUE_0 (0U)
+#define USIM_STAT_STATNOCARD_WRITE0 (0U)
+#define USIM_STAT_STATNOCARD_WRITE1 (1U)
+
+#define USIM_STAT_RESERVED_7_31_SHIFT (7U)
+#define USIM_STAT_RESERVED_7_31_MASK (0xffffff80U)
+
+#define USIM_STAT_STATTXPAR_SHIFT (1U)
+#define USIM_STAT_STATTXPAR_MASK (0x00000002U)
+#define USIM_STAT_STATTXPAR_STATTXPAR_VALUE_1 (1U)
+#define USIM_STAT_STATTXPAR_STATTXPAR_VALUE_0 (0U)
+
+#define USIM_STAT_STATLRC_SHIFT (2U)
+#define USIM_STAT_STATLRC_MASK (0x00000004U)
+#define USIM_STAT_STATLRC_STATLRC_VALUE_1 (1U)
+#define USIM_STAT_STATLRC_STATLRC_VALUE_0 (0U)
+
+#define USIM_STAT_CONFCODCONV_SHIFT (3U)
+#define USIM_STAT_CONFCODCONV_MASK (0x00000008U)
+#define USIM_STAT_CONFCODCONV_CONFCODCONV_VALUE_1 (1U)
+#define USIM_STAT_CONFCODCONV_CONFCODCONV_VALUE_0 (0U)
+
+#define USIM_STAT_RESERVED_SHIFT (4U)
+#define USIM_STAT_RESERVED_MASK (0x00000010U)
+
+#define USIM_STAT_FDDEACTSTATE_SHIFT (5U)
+#define USIM_STAT_FDDEACTSTATE_MASK (0x00000020U)
+#define USIM_STAT_FDDEACTSTATE_FDDEACTSTATE_VALUE_0 (0U)
+#define USIM_STAT_FDDEACTSTATE_FDDEACTSTATE_VALUE_1 (1U)
+
+#define USIM_STAT_ATRRX_AFTER_TIMEOUT_SHIFT (6U)
+#define USIM_STAT_ATRRX_AFTER_TIMEOUT_MASK (0x00000040U)
+
+#define USIM_CONF1_CONFSIOLOW_SHIFT (1U)
+#define USIM_CONF1_CONFSIOLOW_MASK (0x00000002U)
+#define USIM_CONF1_CONFSIOLOW_CONFSIOLOW_VALUE_0 (0U)
+#define USIM_CONF1_CONFSIOLOW_CONFSIOLOW_VALUE_1 (1U)
+
+#define USIM_CONF1_RESERVED_8_31_SHIFT (8U)
+#define USIM_CONF1_RESERVED_8_31_MASK (0xffffff00U)
+
+#define USIM_CONF1_SCLKLEV_SHIFT (0U)
+#define USIM_CONF1_SCLKLEV_MASK (0x00000001U)
+#define USIM_CONF1_SCLKLEV_SCLKLEV_VALUE_0 (0U)
+#define USIM_CONF1_SCLKLEV_SCLKLEV_VALUE_1 (1U)
+
+#define USIM_CONF1_CONFBYPASS_SHIFT (2U)
+#define USIM_CONF1_CONFBYPASS_MASK (0x00000004U)
+#define USIM_CONF1_CONFBYPASS_CONFBYPASS_VALUE_0 (0U)
+#define USIM_CONF1_CONFBYPASS_CONFBYPASS_VALUE_1 (1U)
+
+#define USIM_CONF1_SVCCLEV_SHIFT (3U)
+#define USIM_CONF1_SVCCLEV_MASK (0x00000008U)
+#define USIM_CONF1_SVCCLEV_SVCCLEV_VALUE_0 (0U)
+#define USIM_CONF1_SVCCLEV_SVCCLEV_VALUE_1 (1U)
+
+#define USIM_CONF1_SRSTLEV_SHIFT (4U)
+#define USIM_CONF1_SRSTLEV_MASK (0x00000010U)
+#define USIM_CONF1_SRSTLEV_SRSTLEV_VALUE_0 (0U)
+#define USIM_CONF1_SRSTLEV_SRSTLEV_VALUE_1 (1U)
+
+#define USIM_CONF1_CONF_SCLK_EN_SHIFT (5U)
+#define USIM_CONF1_CONF_SCLK_EN_MASK (0x00000020U)
+#define USIM_CONF1_CONF_SCLK_EN_CONF_SCLK_EN_VALUE_0 (0U)
+#define USIM_CONF1_CONF_SCLK_EN_CONF_SCLK_EN_VALUE_1 (1U)
+#define USIM_CONF1_EMV_CONF_SHIFT (6U)
+#define USIM_CONF1_EMV_CONF_MASK (0x00000040U)
+#define USIM_CONF1_EMV_CONF_EMV_CONF_VALUE_0 (0U)
+#define USIM_CONF1_EMV_CONF_EMV_CONF_VALUE_1 (1U)
+#define USIM_CONF1_BYPASS_HW_AUTO_SHIFT (7U)
+#define USIM_CONF1_BYPASS_HW_AUTO_MASK (0x00000080U)
+#define USIM_CONF1_BYPASS_HW_AUTO_BYPASS_HW_AUTO_VALUE_0 (0U)
+#define USIM_CONF1_BYPASS_HW_AUTO_BYPASS_HW_AUTO_VALUE_1 (1U)
+
+#define USIM_CONF2_CONFCHKPAR_SHIFT (0U)
+#define USIM_CONF2_CONFCHKPAR_MASK (0x00000001U)
+#define USIM_CONF2_CONFCHKPAR_CONFCHKPAR_VALUE_0 (0U)
+#define USIM_CONF2_CONFCHKPAR_CONFCHKPAR_VALUE_1 (1U)
+#define USIM_CONF2_RESERVED_22_31_SHIFT (22U)
+#define USIM_CONF2_RESERVED_22_31_MASK (0xffc00000U)
+#define USIM_CONF2_TX_EN_SHIFT (1U)
+#define USIM_CONF2_TX_EN_MASK (0x00000002U)
+#define USIM_CONF2_CONFSCLKDIV_SHIFT (2U)
+#define USIM_CONF2_CONFSCLKDIV_MASK (0x0000000cU)
+#define USIM_CONF2_ATR_ASYN_BYPASS_SHIFT (4U)
+#define USIM_CONF2_ATR_ASYN_BYPASS_MASK (0x00000010U)
+#define USIM_CONF2_ATR_ASYN_BYPASS_ATR_ASYN_BYPASS_VALUE_0 (0U)
+#define USIM_CONF2_ATR_ASYN_BYPASS_ATR_ASYN_BYPASS_VALUE_1 (1U)
+#define USIM_CONF2_CONFPROTOCOL_SHIFT (5U)
+#define USIM_CONF2_CONFPROTOCOL_MASK (0x00000020U)
+#define USIM_CONF2_CONFPROTOCOL_CONFPROTOCOL_VALUE_0 (0U)
+#define USIM_CONF2_CONFPROTOCOL_CONFPROTOCOL_VALUE_1 (1U)
+#define USIM_CONF2_CONFEDC_SHIFT (6U)
+#define USIM_CONF2_CONFEDC_MASK (0x00000040U)
+#define USIM_CONF2_CONFEDC_CONFEDC_VALUE_0 (0U)
+#define USIM_CONF2_CONFEDC_CONFEDC_VALUE_1 (1U)
+#define USIM_CONF2_CONFLRCCHECK_SHIFT (7U)
+#define USIM_CONF2_CONFLRCCHECK_MASK (0x00000080U)
+#define USIM_CONF2_CONFLRCCHECK_CONFLRCCHECK_VALUE_0 (0U)
+#define USIM_CONF2_CONFLRCCHECK_CONFLRCCHECK_VALUE_1 (1U)
+#define USIM_CONF2_CONFRESENT_SHIFT (8U)
+#define USIM_CONF2_CONFRESENT_MASK (0x00000700U)
+
+#define USIM_CONF2_CARD_POLARITY_SHIFT (11U)
+#define USIM_CONF2_CARD_POLARITY_MASK (0x00000800U)
+#define USIM_CONF2_CARD_POLARITY_CARD_POLARITY_VALUE_0 (0U)
+#define USIM_CONF2_CARD_POLARITY_CARD_POLARITY_VALUE_1 (1U)
+
+#define USIM_CONF2_HW_DEACTIV_EN_SHIFT (12U)
+#define USIM_CONF2_HW_DEACTIV_EN_MASK (0x00001000U)
+#define USIM_CONF2_HW_DEACTIV_EN_HW_DEACTIV_EN_VALUE_0 (0U)
+#define USIM_CONF2_HW_DEACTIV_EN_HW_DEACTIV_EN_VALUE_1 (1U)
+
+#define USIM_CONF2_DEBOUNCE_EN_SHIFT (13U)
+#define USIM_CONF2_DEBOUNCE_EN_MASK (0x00002000U)
+#define USIM_CONF2_DEBOUNCE_EN_DEBOUNCE_EN_VALUE0 (0U)
+#define USIM_CONF2_DEBOUNCE_EN_DEBOUNCE_EN_VALUE1 (1U)
+
+#define USIM_CONF2_PUT_ERR_IN_FIFO_SHIFT (14U)
+#define USIM_CONF2_PUT_ERR_IN_FIFO_MASK (0x00004000U)
+
+#define USIM_CONF2_NACKING_EN_SHIFT (15U)
+#define USIM_CONF2_NACKING_EN_MASK (0x00008000U)
+#define USIM_CONF2_NACKING_EN_DISABLED (0U)
+#define USIM_CONF2_NACKING_EN_RXFIFO_FULL_NACK (1U)
+
+#define USIM_CONF2_PAR_ERR_LEVEL_SHIFT (16U)
+#define USIM_CONF2_PAR_ERR_LEVEL_MASK (0x00070000U)
+
+#define USIM_CONF2_CONFSCLKMODE_SHIFT (19U)
+#define USIM_CONF2_CONFSCLKMODE_MASK (0x00080000U)
+
+#define USIM_CONF2_STOP_RESEND_FAILURE_SHIFT (20U)
+#define USIM_CONF2_STOP_RESEND_FAILURE_MASK (0x00100000U)
+
+#define USIM_CONF2_STOP_RX_TIMEOUT_SHIFT (21U)
+#define USIM_CONF2_STOP_RX_TIMEOUT_MASK (0x00200000U)
+
+#define USIM_CONF3_TDUSIM_SHIFT (4U)
+#define USIM_CONF3_TDUSIM_MASK (0x000000f0U)
+
+#define USIM_CONF3_TFUSIM_SHIFT (0U)
+#define USIM_CONF3_TFUSIM_MASK (0x0000000fU)
+
+#define USIM_CONF3_RESERVED_8_31_SHIFT (8U)
+#define USIM_CONF3_RESERVED_8_31_MASK (0xffffff00U)
+
+#define USIM_DRX_USIMDRX_SHIFT (0U)
+#define USIM_DRX_USIMDRX_MASK (0x000000ffU)
+
+#define USIM_DRX_STATRXPAR_SHIFT (8U)
+#define USIM_DRX_STATRXPAR_MASK (0x00000100U)
+#define USIM_DRX_STATRXPAR_STATRXPAR_VALUE_1 (1U)
+#define USIM_DRX_STATRXPAR_STATRXPAR_VALUE_0 (0U)
+
+#define USIM_DRX_RESERVED_9_31_SHIFT (9U)
+#define USIM_DRX_RESERVED_9_31_MASK (0xfffffe00U)
+
+#define USIM_DTX_DTX_SHIFT (0U)
+#define USIM_DTX_DTX_MASK (0x000000ffU)
+
+#define USIM_DTX_RESERVED_8_31_SHIFT (8U)
+#define USIM_DTX_RESERVED_8_31_MASK (0xffffff00U)
+
+#define USIM_FIFOS_DMA_MODE_SHIFT (0U)
+#define USIM_FIFOS_DMA_MODE_MASK (0x00000001U)
+#define USIM_FIFOS_DMA_MODE_DMA_MODE_VALUE_1 (1U)
+#define USIM_FIFOS_DMA_MODE_DMA_MODE_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFO_ENABLE_SHIFT (1U)
+#define USIM_FIFOS_FIFO_ENABLE_MASK (0x00000002U)
+#define USIM_FIFOS_FIFO_ENABLE_FIFO_ENABLE_VALUE_1 (1U)
+#define USIM_FIFOS_FIFO_ENABLE_FIFO_ENABLE_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFO_TX_TRIGGER_SHIFT (23U)
+#define USIM_FIFOS_FIFO_TX_TRIGGER_MASK (0xff800000U)
+
+#define USIM_FIFOS_FIFOTX_RESET_SHIFT (6U)
+#define USIM_FIFOS_FIFOTX_RESET_MASK (0x00000040U)
+#define USIM_FIFOS_FIFOTX_RESET_FIFOTX_RESET_VALUE_1 (1U)
+#define USIM_FIFOS_FIFOTX_RESET_FIFOTX_RESET_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFOTX_EMPTY_SHIFT (7U)
+#define USIM_FIFOS_FIFOTX_EMPTY_MASK (0x00000080U)
+#define USIM_FIFOS_FIFOTX_EMPTY_FIFOTX_EMPTY_VALUE_1 (1U)
+#define USIM_FIFOS_FIFOTX_EMPTY_FIFOTX_EMPTY_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFOTX_FULL_SHIFT (8U)
+#define USIM_FIFOS_FIFOTX_FULL_MASK (0x00000100U)
+#define USIM_FIFOS_FIFOTX_FULL_FIFOTX_FULL_VALUE_1 (1U)
+#define USIM_FIFOS_FIFOTX_FULL_FIFOTX_FULL_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFO_RX_TRIGGER_SHIFT (9U)
+#define USIM_FIFOS_FIFO_RX_TRIGGER_MASK (0x0003fe00U)
+
+#define USIM_FIFOS_FIFORX_RESET_SHIFT (18U)
+#define USIM_FIFOS_FIFORX_RESET_MASK (0x00040000U)
+#define USIM_FIFOS_FIFORX_RESET_FIFORX_RESET_VALUE_1 (1U)
+#define USIM_FIFOS_FIFORX_RESET_FIFORX_RESET_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFORX_EMPTY_SHIFT (19U)
+#define USIM_FIFOS_FIFORX_EMPTY_MASK (0x00080000U)
+#define USIM_FIFOS_FIFORX_EMPTY_FIFORX_EMPTY_VALUE_1 (1U)
+#define USIM_FIFOS_FIFORX_EMPTY_FIFORX_EMPTY_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFORX_FULL_SHIFT (20U)
+#define USIM_FIFOS_FIFORX_FULL_MASK (0x00100000U)
+#define USIM_FIFOS_FIFORX_FULL_FIFORX_FULL_VALUE_1 (1U)
+#define USIM_FIFOS_FIFORX_FULL_FIFORX_FULL_VALUE_0 (0U)
+
+#define USIM_FIFOS_RXDMA_TYPE_SHIFT (21U)
+#define USIM_FIFOS_RXDMA_TYPE_MASK (0x00600000U)
+#define USIM_FIFOS_RXDMA_TYPE_LEGACY (0U)
+#define USIM_FIFOS_RXDMA_TYPE_NEW1 (2U)
+#define USIM_FIFOS_RXDMA_TYPE_NEW2 (3U)
+
+#define USIM_FIFOS_RESERVED_SHIFT (2U)
+#define USIM_FIFOS_RESERVED_MASK (0x0000003cU)
+
+#define USIM_CGT_RESERVED_9_31_SHIFT (9U)
+#define USIM_CGT_RESERVED_9_31_MASK (0xfffffe00U)
+
+#define USIM_CGT_CGT_SHIFT (0U)
+#define USIM_CGT_CGT_MASK (0x000001ffU)
+
+#define USIM_CWT_CWT_SHIFT (0U)
+#define USIM_CWT_CWT_MASK (0xffffffffU)
+
+#define USIM_BWT_BWT_SHIFT (0U)
+#define USIM_BWT_BWT_MASK (0xffffffffU)
+
+#define USIM_DEBUG_RESERVED_25_31_SHIFT (25U)
+#define USIM_DEBUG_RESERVED_25_31_MASK (0xfe000000U)
+
+#define USIM_DEBUG_MAIN_STATE_DEBUG_SHIFT (0U)
+#define USIM_DEBUG_MAIN_STATE_DEBUG_MASK (0x0000000fU)
+
+#define USIM_DEBUG_TX_STATE_MACHINE_SHIFT (4U)
+#define USIM_DEBUG_TX_STATE_MACHINE_MASK (0x00000030U)
+
+#define USIM_DEBUG_RX_STATE_MACHINE_SHIFT (6U)
+#define USIM_DEBUG_RX_STATE_MACHINE_MASK (0x000000c0U)
+
+#define USIM_DEBUG_RXFIFO_PEAK_SHIFT (8U)
+#define USIM_DEBUG_RXFIFO_PEAK_MASK (0x0003ff00U)
+
+#define USIM_DEBUG_RXDMA_SHIFT (18U)
+#define USIM_DEBUG_RXDMA_MASK (0x00040000U)
+
+#define USIM_DEBUG_TXDMA_SHIFT (19U)
+#define USIM_DEBUG_TXDMA_MASK (0x00080000U)
+
+#define USIM_DEBUG_EMV_MAIN_STATE_DEBUG_SHIFT (20U)
+#define USIM_DEBUG_EMV_MAIN_STATE_DEBUG_MASK (0x01f00000U)
+
+#define USIM_CONF_SAM1_DIV_RESERVED_12_31_SHIFT (12U)
+#define USIM_CONF_SAM1_DIV_RESERVED_12_31_MASK (0xfffff000U)
+
+#define USIM_CONF_SAM1_DIV_SAM1_DIV_SHIFT (0U)
+#define USIM_CONF_SAM1_DIV_SAM1_DIV_MASK (0x00000fffU)
+
+#define USIM_CONF4_RESERVED_16_31_SHIFT (16U)
+#define USIM_CONF4_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_CONF4_CONFWAITI_SHIFT (0U)
+#define USIM_CONF4_CONFWAITI_MASK (0x0000ffffU)
+
+#define USIM_ATR_CLK_PRD_NBS_RESERVED_16_31_SHIFT (16U)
+#define USIM_ATR_CLK_PRD_NBS_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_ATR_CLK_PRD_NBS_CLOCK_NUMBER_BEFORE_ATR_SHIFT (0U)
+#define USIM_ATR_CLK_PRD_NBS_CLOCK_NUMBER_BEFORE_ATR_MASK (0x0000ffffU)
+
+#define USIM_CONF_ETU_DIV_RESERVED_16_31_SHIFT (16U)
+#define USIM_CONF_ETU_DIV_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_CONF_ETU_DIV_ETU_DIV_SHIFT (0U)
+#define USIM_CONF_ETU_DIV_ETU_DIV_MASK (0x0000ffffU)
+
+#define USIM_CONF5_RESERVED_12_31_SHIFT (9U)
+#define USIM_CONF5_RESERVED_12_31_MASK (0xfffffe00U)
+
+#define USIM_CONF5_DI_SHIFT (0U)
+#define USIM_CONF5_DI_MASK (0x0000000fU)
+
+#define USIM_CONF5_FI_SHIFT (4U)
+#define USIM_CONF5_FI_MASK (0x000000f0U)
+
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_SHIFT (8U)
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_MASK (0x00000100U)
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_HARDWARE (0U)
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_SOFTWARE (1U)
+
+#define USIM_TC_GUARD_TIME_ADD_RESERVED_14_31_SHIFT (14U)
+#define USIM_TC_GUARD_TIME_ADD_RESERVED_14_31_MASK (0xffffc000U)
+
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_SHIFT (13U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_MASK (0x00002000U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_HW (0U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_SW (1U)
+
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_SHIFT (0U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_MASK (0x00001fffU)
+
+#define USIM_RXFIFO_LEVEL_USIM_RXFIFO_LEVEL_SHIFT (0U)
+#define USIM_RXFIFO_LEVEL_USIM_RXFIFO_LEVEL_MASK (0x000003ffU)
+
+#define USIM_RXFIFO_LEVEL_RESERVED_SHIFT (10U)
+#define USIM_RXFIFO_LEVEL_RESERVED_MASK (0xfffffc00U)
+
+#define USIM_RXFIFO_BYTECNT_USIM_RXFIFO_BYTECNT_SHIFT (0U)
+#define USIM_RXFIFO_BYTECNT_USIM_RXFIFO_BYTECNT_MASK (0x000001ffU)
+
+#define USIM_RXFIFO_BYTECNT_RESERVED_SHIFT (9U)
+#define USIM_RXFIFO_BYTECNT_RESERVED_MASK (0xfffffe00U)
+
+#define USIM_WWT_WWT_SHIFT (0U)
+#define USIM_WWT_WWT_MASK (0xffffffffU)
+
+#define USIM_CONF6_MODE_SHIFT (0U)
+#define USIM_CONF6_MODE_MASK (0x00000007U)
+#define USIM_CONF6_MODE_NO_OVERRIDE (0U)
+#define USIM_CONF6_MODE_FREEZE (1U)
+#define USIM_CONF6_MODE_RX_TX (2U)
+#define USIM_CONF6_MODE_ATR (3U)
+#define USIM_CONF6_MODE_ACTIVATE (4U)
+#define USIM_CONF6_MODE_DEACTIVATE (5U)
+#define USIM_CONF6_MODE_IDLE (6U)
+#define USIM_CONF6_MODE_RESERVED7 (7U)
+
+#define USIM_CONF6_RST_POLARITY_SHIFT (3U)
+#define USIM_CONF6_RST_POLARITY_MASK (0x00000008U)
+#define USIM_CONF6_RST_POLARITY_ACTIVE_LOW (0U)
+#define USIM_CONF6_RST_POLARITY_ACTIVE_HIGH (1U)
+
+#define USIM_CONF6_RESERVED_SHIFT (12U)
+#define USIM_CONF6_RESERVED_MASK (0x0000f000U)
+
+#define USIM_CONF6_ATR_TIMER_BYPASS_SHIFT (4U)
+#define USIM_CONF6_ATR_TIMER_BYPASS_MASK (0x00000010U)
+
+#define USIM_CONF6_IO_BYPASS_SHIFT (5U)
+#define USIM_CONF6_IO_BYPASS_MASK (0x00000060U)
+#define USIM_CONF6_IO_BYPASS_00 (0U)
+#define USIM_CONF6_IO_BYPASS_10 (2U)
+#define USIM_CONF6_IO_BYPASS_01 (1U)
+#define USIM_CONF6_IO_BYPASS_11 (3U)
+
+#define USIM_CONF6_SCLK0_BYPASS_SHIFT (7U)
+#define USIM_CONF6_SCLK0_BYPASS_MASK (0x00000080U)
+
+#define USIM_CONF6_LEN_BYPASS_MASK (0x00000200U)
+
+#define USIM_CONF6_RST_BYPASS_SHIFT (10U)
+#define USIM_CONF6_RST_BYPASS_MASK (0x00000400U)
+
+#define USIM_CONF6_VCC_BYPASS_SHIFT (11U)
+#define USIM_CONF6_VCC_BYPASS_MASK (0x00000800U)
+
+#define USIM_CONF6_ATR_TIMEOUT_SHIFT (16U)
+#define USIM_CONF6_ATR_TIMEOUT_MASK (0xffff0000U)
+
+#define USIM_IO_DIRECT_SCLK0_SHIFT (0U)
+#define USIM_IO_DIRECT_SCLK0_MASK (0x00000001U)
+
+#define USIM_IO_DIRECT_SIORX0_SHIFT (2U)
+#define USIM_IO_DIRECT_SIORX0_MASK (0x00000004U)
+
+#define USIM_IO_DIRECT_SIORX1_SHIFT (3U)
+#define USIM_IO_DIRECT_SIORX1_MASK (0x00000008U)
+
+#define USIM_IO_DIRECT_SIOTX0_SHIFT (4U)
+#define USIM_IO_DIRECT_SIOTX0_MASK (0x00000010U)
+
+#define USIM_IO_DIRECT_SIOEN0_SHIFT (6U)
+#define USIM_IO_DIRECT_SIOEN0_MASK (0x00000040U)
+
+#define USIM_IO_DIRECT_RST_SHIFT (8U)
+#define USIM_IO_DIRECT_RST_MASK (0x00000100U)
+
+#define USIM_IO_DIRECT_SVCC_SHIFT (9U)
+#define USIM_IO_DIRECT_SVCC_MASK (0x00000200U)
+
+#define USIM_IO_DIRECT_SINEX_SHIFT (10U)
+#define USIM_IO_DIRECT_SINEX_MASK (0x00000400U)
+
+#define USIM_IO_DIRECT_LEN_SHIFT (11U)
+#define USIM_IO_DIRECT_LEN_MASK (0x00000800U)
+
+#define USIM_IO_DIRECT_RNW0_SHIFT (12U)
+#define USIM_IO_DIRECT_RNW0_MASK (0x00001000U)
+
+#define USIM_IO_DIRECT_RESERVED_SHIFT (15U)
+#define USIM_IO_DIRECT_RESERVED_MASK (0xffff8000U)
+
+#define USIM_IO_DIRECT_C4_SHIFT (14U)
+#define USIM_IO_DIRECT_C4_MASK (0x00004000U)
+
+#define USIM_TX_BLOCK_BLOCK_LENGTH_SHIFT (0U)
+#define USIM_TX_BLOCK_BLOCK_LENGTH_MASK (0x0000ffffU)
+
+#define USIM_TX_BLOCK_RESERVED_SHIFT (16U)
+#define USIM_TX_BLOCK_RESERVED_MASK (0xffff0000U)
+
+#endif /* __TI_USIM_HW_H__ */
diff --git a/drivers/char/ti-usim.c b/drivers/char/ti-usim.c
new file mode 100644
index 0000000..a2af409
--- /dev/null
+++ b/drivers/char/ti-usim.c
@@ -0,0 +1,1859 @@
+/*
+ * usim.c - USIM driver for Smart Card module
+ *
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/debugfs.h>
+#include <linux/notifier.h>
+#include <linux/clk.h>
+/* for send_sig_info */
+#include <linux/rcupdate.h>
+#include <asm/siginfo.h>
+
+#include "ti-usim-hw.h"
+
+#define USIM_WRITEREG(base, offset, field, value) \
+ usim_writereg(base+offset, offset##_##field##_MASK, \
+ offset##_##field##_SHIFT, value)
+
+#define USIM_READREG(base, offset, field) \
+ usim_readreg(base+offset, offset##_##field##_MASK, \
+ offset##_##field##_SHIFT)
+
+#define USIM_SETFIELD(reg, offset, field, value) \
+ usim_setfield(reg, offset##_##field##_MASK, \
+ offset##_##field##_SHIFT, value)
+
+/*
+ * phy states
+ */
+enum usim_phy_state {
+ USIM_PHY_NOT_PRESENT = 0x0,
+ USIM_PHY_PRESENT,
+ USIM_PHY_NOT_ATTACHED,
+};
+
+static struct miscdevice usim_dev;
+
+static DECLARE_WAIT_QUEUE_HEAD(rx_wait);
+static DECLARE_WAIT_QUEUE_HEAD(tx_wait);
+static DECLARE_WAIT_QUEUE_HEAD(atr_wait);
+
+static int usim_set_smartcardclock(struct usim *usim, u32 clock);
+
+static void usim_writereg(void __iomem *base, u32 mask, u32 shift, u32 value)
+{
+ u32 v = readl(base);
+
+ v &= ~mask;
+ v |= (value << shift) & mask;
+ writel(v, base);
+ v = readl(base);
+ return;
+}
+
+static u32 usim_readreg(void __iomem *base, u32 mask, u32 shift)
+{
+ u32 v = readl(base);
+
+ v &= mask;
+ v = (v >> shift);
+ return v;
+}
+
+static u32 usim_setfield(u32 reg, u32 mask, u32 shift, u32 value)
+{
+ reg &= ~mask;
+ reg |= (value << shift) & mask;
+ return reg;
+}
+
+
+static inline void usim_irq_enable(void __iomem *base, u32 irqs)
+{
+ u32 v = readl(base + USIM_IRQENABLE);
+
+ v |= irqs;
+ writel(v, base + USIM_IRQENABLE);
+}
+
+static inline void usim_irq_disable(void __iomem *base, u32 irqs)
+{
+ u32 v = readl(base + USIM_IRQENABLE);
+
+ v &= ~irqs;
+ writel(v, base + USIM_IRQENABLE);
+}
+
+static inline void usim_irq_get(void __iomem *base, u32 *irqs)
+{
+ *irqs = readl(base + USIM_IRQENABLE);
+}
+
+static inline u32 usim_irqstatus(void __iomem *base)
+{
+ return readl(base + USIM_IRQSTATUS);
+}
+
+static inline void usim_irqstatus_clear(void __iomem *base, u32 irqs)
+{
+ writel(irqs, base + USIM_IRQSTATUS);
+}
+
+static inline struct usim *dev_to_usim(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+static int usim_send_signal(struct usim *usim, int event)
+{
+ struct siginfo info;
+ struct task_struct *tid;
+ int ret = 0;
+ int pid = usim->user_pid;
+
+ if (pid == 0)
+ return -EINVAL;
+ info.si_signo = USIM_SIGID;
+ info.si_code = SI_QUEUE;
+
+ info.si_int = event;
+ rcu_read_lock();
+
+ /* find task structure associated with this pid */
+ tid = pid_task(find_vpid(pid), PIDTYPE_PID);
+ if (tid == NULL) {
+ dev_err(usim->dev, "usim-err:no such pid :%d\n", pid);
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+
+ rcu_read_unlock();
+
+ /* send the signal */
+ ret = send_sig_info(USIM_SIGID, &info, tid);
+ if (ret < 0) {
+ dev_err(usim->dev, "error sending signal:%d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void usim_getrx(struct usim *usim)
+{
+ u32 rxlen = 0;
+ u32 cnt = 0;
+
+ /* Check if FIFO contains some data */
+ rxlen = USIM_READREG(usim->base, USIM_RXFIFO_LEVEL,
+ USIM_RXFIFO_LEVEL);
+
+ usim->slot_ctx[usim->slot].rx_counter += rxlen;
+ if (rxlen > 0) {
+ for (cnt = 0; cnt < rxlen; cnt++) {
+ usim->slot_ctx[usim->slot].rxbuf[cnt] =
+ USIM_READREG(usim->base, USIM_DRX, USIMDRX);
+ }
+ }
+}
+
+static void usim_irq_atrhandler(struct usim *usim, u32 reg)
+{
+ u32 event = 0;
+ u32 val = 0;
+ u32 cnt = 0;
+ u32 rxval = 0;
+ if (usim->atrdone)
+ return;
+ do {
+ /* WWT would be used to identify end of ATR */
+ if (reg & (USIM_IRQ_WT | USIM_IRQ_EMV_ATR_LENGTH_TIME_OUT)) {
+ event |= USIM_EVENT_TIMEOUT;
+ val = USIM_READREG(usim->base, USIM_STAT,
+ ATRRX_AFTER_TIMEOUT);
+ if (val) {
+ /* do not store rx character if it comes after
+ * ATR timeout
+ */
+ dev_dbg(usim->dev, "Error: Rx after ATR Timeout");
+ break;
+ }
+ }
+ if (reg & USIM_IRQ_TS_ERR) {
+ event |= USIM_EVENT_ERR_FRAME;
+ break;
+ }
+
+ /* check the rx fifo and store available bytes in atrbuf */
+ val = USIM_READREG(usim->base, USIM_RXFIFO_LEVEL,
+ USIM_RXFIFO_LEVEL);
+ cnt = usim->slot_ctx[usim->slot].atr_length;
+
+ while (val > 0) {
+ if (cnt < USIM_MAX_ATRLENGTH) {
+ rxval = readl(usim->base + USIM_DRX);
+ usim->slot_ctx[usim->slot].atr[cnt++] = rxval &
+ USIM_DRX_USIMDRX_MASK;
+ /* check of parity */
+ if (!(rxval & USIM_DRX_STATRXPAR_MASK)) {
+ dev_dbg(usim->dev, "Error: incorrect parity:%0x", rxval);
+ event |= USIM_EVENT_ERR_PARITY;
+ }
+ }
+ val--;
+ }
+
+ usim->slot_ctx[usim->slot].atr_length = cnt;
+ } while (0);
+
+ if (event != 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+ usim->slot_ctx[usim->slot].event = event;
+ usim->atrdone = 1;
+ }
+
+ if (usim->atrdone)
+ wake_up(&atr_wait);
+
+ return;
+}
+
+static void usim_irq_txhandler(struct usim *usim, u32 reg)
+{
+ u32 protocol = 0;
+ u32 event = 0;
+
+ if (usim->txdone)
+ return;
+
+ protocol = usim->slot_ctx[usim->slot].protocol;
+ do {
+ if (reg & USIM_IRQ_FRAME_ERR) {
+ event |= USIM_EVENT_ERR_FRAME;
+ break;
+ }
+ if (!protocol && (reg & USIM_IRQ_RESENT)) {
+ event |= USIM_EVENT_ERR_TXRETRY;
+ break;
+ }
+ if (reg & USIM_IRQ_TX_BLOCK_REQ) {
+ /* TODO : As per EMV max tx block will be of 256 bytes
+ * and USIM controller has sufficient place for this.
+ * Need to implement this case when it is practially
+ * required
+ */
+ dev_dbg(usim->dev, "Error: TX_BLOCK_REQ - Not Implemented");
+ }
+ if (reg & USIM_IRQ_TX_BLOCK_DONE) {
+ usim_irq_disable(usim->base, USIM_IRQ_TX_BLOCK_REQ
+ | USIM_IRQ_TX_BLOCK_DONE
+ | USIM_IRQ_TX);
+ usim->txdone = 1;
+ usim_irq_enable(usim->base, USIM_IRQ_RX | USIM_IRQ_EOB
+ | USIM_IRQ_RXDMA_RDY);
+ break;
+ }
+ } while (0);
+
+ if (event != 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+ usim->slot_ctx[usim->slot].event = event;
+ usim->txdone = 1;
+ usim->rxdone = 1;
+ }
+ if (usim->txdone)
+ wake_up(&tx_wait);
+ return;
+}
+
+static void usim_irq_rxhandler(struct usim *usim, u32 reg)
+{
+ u32 event = 0;
+ u32 val = 0;
+
+ u32 protocol = usim->slot_ctx[usim->slot].protocol;
+
+ /* if tx not done then do not check of any rx */
+ if (usim->rxdone || !usim->txdone)
+ return;
+
+ /* For T=0 protocol */
+ if (protocol == 0) {
+ do {
+ /* ignore interrupts if expected bytes recevied */
+ if (usim->slot_ctx[usim->slot].rx_counter >=
+ usim->slot_ctx[usim->slot].rx_explen) {
+ dev_dbg(usim->dev, "All bytes recvd,ignore this timeout\n");
+ usim->rxdone = 1;
+ break;
+ }
+
+ if (reg & USIM_IRQ_WT) {
+ dev_dbg(usim->dev, "Expected bytes not recvd counter = %d\n",
+ usim->slot_ctx[usim->slot].rx_counter);
+ usim_getrx(usim);
+ event |= USIM_EVENT_TIMEOUT;
+ break;
+ }
+
+ if (reg & USIM_IRQ_PAR_ERR_LEVEL_REACHED) {
+ dev_err(usim->dev, "Rx parity level reached:%x\n", reg);
+ usim_getrx(usim);
+ event |= USIM_EVENT_ERR_PARITY;
+ break;
+ }
+
+ if (reg & (USIM_IRQ_RX | USIM_IRQ_RXDMA_RDY)) {
+ /* Read number of bytes present in the FIFO */
+ usim_getrx(usim);
+ usim->rxdone = 1;
+ break;
+ }
+ } while (0);
+ } else {
+ /* T=1 protocol */
+ do {
+ if (reg & (USIM_IRQ_TOB | USIM_IRQ_TOC)) {
+ usim_getrx(usim);
+ event |= USIM_EVENT_TIMEOUT;
+ break;
+ }
+ if (reg & USIM_IRQ_EOB) {
+ usim_getrx(usim);
+ usim->rxdone = 1;
+ val = USIM_READREG(usim->base, USIM_STAT,
+ STATLRC);
+ if (val != 0)
+ event |= USIM_EVENT_ERR_LRC;
+ break;
+ }
+ } while (0);
+ }
+
+ if (event != 0 || usim->rxdone == 1) {
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+ usim->slot_ctx[usim->slot].event = event;
+ usim->rxdone = 1;
+ }
+
+ if (usim->rxdone)
+ wake_up(&rx_wait);
+
+ return;
+}
+
+static irqreturn_t usim_interrupt(int irq, void *_usim)
+{
+ u32 reg = 0;
+ u32 state = 0;
+ struct usim *usim = (struct usim *)_usim;
+
+ state = usim->slot_ctx[usim->slot].state;
+
+ spin_lock(&usim->lock);
+
+ reg = readl(usim->base + USIM_IRQSTATUS);
+
+ if (state == USIM_MODE_ATR)
+ usim_irq_atrhandler(usim, reg);
+
+ if (state == USIM_MODE_TXRX) {
+ usim_irq_txhandler(usim, reg);
+ usim_irq_rxhandler(usim, reg);
+ }
+
+ if (reg & USIM_IRQSTATUS_USIM_NATR_MASK)
+ dev_dbg(usim->dev, "NO ATR\n");
+
+ if (reg & USIM_IRQSTATUS_USIM_CD_MASK)
+ dev_dbg(usim->dev, "CARD Insert/Removed\n");
+
+ if (reg & USIM_IRQSTATUS_USIM_STOP_CLK_MASK)
+ dev_dbg(usim->dev, "SIM CLK STOPPED\n");
+
+ if (reg & USIM_IRQSTATUS_ACT_DONE_MASK)
+ dev_dbg(usim->dev, "Activation Sequence completed\n");
+
+ if (reg & USIM_IRQSTATUS_DEACT_DONE_MASK)
+ dev_dbg(usim->dev, "Deactivation Sequence complteted\n");
+
+ /* Clear the interrupt by writing the corresponding bit
+ * in IRQ_STATUS register
+ */
+ usim_irqstatus_clear(usim->base, reg);
+
+ spin_unlock(&usim->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int usim_configure(struct usim *usim)
+{
+ int reg = 0;
+
+ /* activate phy */
+ if (usim->phy_present)
+ usim->phy->set_config(usim->phy, usim->slot, SC_PHY_MODE,
+ SC_PHY_ACTIVE);
+
+ /* Disable Auto Idle and set NO IDLE config */
+ reg = readl(usim->base + USIM_SYSCONFIG);
+ reg = USIM_SETFIELD(reg, USIM_SYSCONFIG, AUTOIDLE, 0);
+ reg = USIM_SETFIELD(reg, USIM_SYSCONFIG, IDLEMODE, 1);
+ writel(reg, usim->base + USIM_SYSCONFIG);
+
+ if (usim->phy_present) {
+ USIM_WRITEREG(usim->base, USIM_STAT, STATNOCARD, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF1, BYPASS_HW_AUTO, 0);
+ } else {
+ USIM_WRITEREG(usim->base, USIM_STAT, STATNOCARD, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF1, BYPASS_HW_AUTO, 1);
+ }
+
+ /* Set default card type as EMV, Force SIO to low level */
+ reg = readl(usim->base + USIM_CONF1);
+ reg = USIM_SETFIELD(reg, USIM_CONF1, EMV_CONF, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF1, CONFSIOLOW, 1);
+ writel(reg, usim->base + USIM_CONF1);
+
+ /* Set parity level to 1, auto resent to 2 on parity error, */
+ reg = readl(usim->base + USIM_CONF2);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, NACKING_EN, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CARD_POLARITY, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFEDC, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFPROTOCOL, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, ATR_ASYN_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKDIV, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKMODE, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, PAR_ERR_LEVEL, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFRESENT, 2);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, PUT_ERR_IN_FIFO, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFLRCCHECK, 2);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKDIV, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFCHKPAR, 1);
+
+ if (usim->phy_present) {
+ reg = USIM_SETFIELD(reg, USIM_CONF2, HW_DEACTIV_EN, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, DEBOUNCE_EN, 0);
+ } else {
+ reg = USIM_SETFIELD(reg, USIM_CONF2, HW_DEACTIV_EN, 1);
+ }
+
+ writel(reg, usim->base + USIM_CONF2);
+
+ /* Reset Tx FIFO Pointer */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+
+ /* Reset Rx FIFO Pointer */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+
+ /* Configure FIFO settings */
+ /* Set Tx and Rx trigger to 1 byte */
+ reg = readl(usim->base + USIM_FIFOS);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, FIFO_TX_TRIGGER, 0);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, FIFO_RX_TRIGGER, 0);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, RXDMA_TYPE, 0x3);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, DMA_MODE, 0x0);
+ writel(reg, usim->base + USIM_FIFOS);
+
+ /* Enable FIFO access */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_ENABLE, 1);
+
+ /* Use HW mode for ETU calculation and set FI = 372 and DI = 1 */
+ reg = readl(usim->base + USIM_CONF5);
+ reg = USIM_SETFIELD(reg, USIM_CONF5, FI, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF5, DI, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ writel(reg, usim->base + USIM_CONF5);
+
+ /* Configure CONF6 settings */
+ reg = readl(usim->base + USIM_CONF6);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, VCC_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, RST_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, SCLK0_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, RST_POLARITY, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, ATR_TIMER_BYPASS, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, MODE, USIM_CONF6_MODE_FREEZE);
+ writel(reg, usim->base + USIM_CONF6);
+
+ /* Clear all bits in IO_DIRECT register */
+ writel(0, usim->base + USIM_IO_DIRECT);
+
+ /* Disable legacy bypass mode */
+ USIM_WRITEREG(usim->base, USIM_CONF1, CONFBYPASS, 0);
+
+ /* Enable required interrupts */
+ reg = readl(usim->base + USIM_IRQENABLE);
+ writel(reg, usim->base + USIM_IRQENABLE);
+
+ /* Toggling ATR length to ensure 'USIM_STAT_ATRRX_AFTER_TIMEOUT'
+ * gets disable
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT,
+ USIM_ATR_TIMEOUT_EMV);
+
+ /* Set STOP_RX_TIMEOUT */
+ /* Set STOP_RESEND_FAILURE */
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RX_TIMEOUT, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RESEND_FAILURE, 1);
+
+ /* set smartcard clock */
+ usim_set_smartcardclock(usim, usim->slot_ctx[usim->slot].clock);
+
+ return 0;
+}
+
+static int usim_set_voltage(struct usim *usim, u32 voltage)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+ /*
+ * voltage = 0 for 5V,
+ * voltage = 1 for 3V,
+ * voltage = 2 for 1.8V,
+ */
+ if (voltage > 3)
+ return -EINVAL;
+ if (usim->phy_present) {
+ ret = phy->set_config(phy, usim->slot,
+ SC_PHY_CARD_SUPPLY_VOLTAGE, voltage);
+ }
+ usim->slot_ctx[usim->slot].supply = voltage;
+ return ret;
+}
+
+static int usim_set_smartcardclock(struct usim *usim, u32 clock)
+{
+ int clkdiv;
+ int clkmode;
+ struct sc_phy *phy = usim->phy;
+
+ switch (clock) {
+ case USIM_SMARTCART_CLOCK_3_3MHZ:
+ clkmode = USIM_CONFSCLKMODE_HF;
+ clkdiv = 3;
+ break;
+
+ case USIM_SMARTCART_CLOCK_4MHZ:
+ clkmode = USIM_CONFSCLKMODE_HF;
+ clkdiv = 2;
+ break;
+
+ case USIM_SMARTCART_CLOCK_5MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 3;
+ break;
+
+ case USIM_SMARTCART_CLOCK_6_6MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 2;
+ break;
+
+ case USIM_SMARTCART_CLOCK_10MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 1;
+ break;
+
+ case USIM_SMARTCART_CLOCK_20MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 0;
+ break;
+
+ default:
+ dev_err(usim->dev, "Unsupported Clock configuration for smartcard\n");
+ return -EINVAL;
+ break;
+ }
+
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFSCLKMODE, clkmode);
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFSCLKDIV, clkdiv);
+
+ /* setting phy division to zero, as USIM samples smartcard clk line and
+ * put the data in USIM fifo. Phy supply the clock to smartcard wihtout
+ * furhter division
+ */
+ if (usim->phy_present)
+ phy->set_config(phy, usim->slot, SC_PHY_CLKDIV, 0);
+
+ usim->slot_ctx[usim->slot].clock = clock;
+ return 0;
+}
+
+static int usim_set_etu(struct usim *usim, u32 fi, u32 di)
+{
+ USIM_WRITEREG(usim->base, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF5, FI, fi);
+ USIM_WRITEREG(usim->base, USIM_CONF5, DI, di);
+ return 0;
+}
+
+static int usim_set_rxparitycount(struct usim *usim, u32 rxcount)
+{
+ if (rxcount > USIM_MAX_PARITY_RETRIES)
+ return -EINVAL;
+
+ /* Program fields required for RX retry in USIM IP */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PAR_ERR_LEVEL, rxcount);
+
+ /* Enable rx parity check */
+ if (rxcount > 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFCHKPAR, 1);
+ usim_irq_enable(usim->base, USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+ } else {
+ usim_irq_disable(usim->base, USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+ }
+ return 0;
+}
+
+static int usim_set_txretrycount(struct usim *usim, u32 txcount)
+{
+ if (txcount > USIM_MAX_PARITY_RETRIES)
+ return -EINVAL;
+
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFRESENT, txcount);
+ if (txcount > 0)
+ usim_irq_enable(usim->base, USIM_IRQ_RESENT);
+ else
+ usim_irq_disable(usim->base, USIM_IRQ_RESENT);
+
+ return 0;
+}
+
+static int usim_set_c4(struct usim *usim, int state)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+ if (usim->phy_present)
+ ret = phy->set_config(phy, usim->slot, SC_PHY_PIN_C4, state);
+ else
+ USIM_WRITEREG(usim->base, USIM_IO_DIRECT, C4, state);
+ return ret;
+}
+
+static int usim_set_c8(struct usim *usim, int state)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ if (usim->phy_present)
+ ret = phy->set_config(phy, usim->slot, SC_PHY_PIN_C8, state);
+ return ret;
+}
+static int usim_get_version(struct usim *usim)
+{
+ int version = 0x0;
+
+ /* last 16 bytes represents controller version
+ * and first 16 bytes represents phy version (if connected)
+ */
+ version = USIM_READREG(usim->base, USIM_REVISION, REV);
+ if (usim->phy_present)
+ version |= ((usim->phy->get_config(usim->phy, 0,
+ SC_PHY_VERSION)) << 0x10);
+ return version;
+}
+static int usim_init_emvusercard(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ USIM_WRITEREG(usim->base, USIM_CONF1, EMV_CONF, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, DMA_MODE, 0);
+
+ usim_set_etu(usim, 0, 0);
+
+ if (usim_set_txretrycount(usim, 5) != 0)
+ return -EINVAL;
+
+ if (usim_set_rxparitycount(usim, 5) != 0)
+ return -EINVAL;
+
+ usim_set_c4(usim, 0);
+ usim_set_c8(usim, 0);
+
+ if (usim->phy_present) {
+ /* Set early ATR and mute ATR in case of phy */
+ ret = phy->set_config(phy, usim->slot, SC_PHY_ATR_EARLY_TIME,
+ USIM_EMV_ATR_EARLY_TO);
+ if (ret != 0)
+ return ret;
+
+ ret = phy->set_config(phy, usim->slot, SC_PHY_ATR_MUTE_TIME,
+ USIM_EMV_ATR_MUTE_TO);
+ if (ret != 0)
+ return ret;
+
+ /* enable user slot */
+ ret = phy->set_config(phy, usim->slot, SC_PHY_IO, 1);
+ if (ret != 0)
+ return ret;
+ }
+ /* set cwt,wwt,cgt */
+ USIM_WRITEREG(usim->base, USIM_WWT, WWT, USIM_EMV_WWT);
+ USIM_WRITEREG(usim->base, USIM_CWT, CWT, USIM_EMV_WWT - 22);
+ USIM_WRITEREG(usim->base, USIM_CGT, CGT, USIM_EMV_CGT);
+
+ return 0;
+}
+
+static int usim_warmreset(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ usim->slot_ctx[usim->slot].atr_length = 0;
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+
+ /* reset FIFO pointer */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x1);
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x0);
+
+ /* Do store bytes with parity error in Rx FIFO */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PUT_ERR_IN_FIFO, 0x1);
+
+ usim_irq_disable(usim->base, (USIM_IRQ_TX | USIM_IRQ_EOB));
+
+ usim->slot_ctx[usim->slot].state = USIM_MODE_ATR;
+
+ /* warm reset the card */
+ if (usim->phy_present) {
+ ret = phy->warm_reset(phy, usim->slot);
+ if (ret != 0)
+ return ret;
+ } else {
+ /* warm reset using USIM */
+ USIM_WRITEREG(usim->base, USIM_CMD, CMD_WARM_RST, 0x1);
+ }
+
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_ATR);
+
+ return 0;
+}
+
+static int usim_activate_card(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ usim->atrdone = 0;
+ usim->slot_ctx[usim->slot].atr_length = 0;
+
+ if (usim->slot_ctx[usim->slot].emv)
+ usim_init_emvusercard(usim);
+
+ USIM_WRITEREG(usim->base, USIM_CONF1, EMV_CONF, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_RX_TRIGGER, 0x103);
+
+ /* RXDMA_TYPE = 0x1 - USIM_RXFIFO_BYTECNT value is ignored */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, RXDMA_TYPE, 0x1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_ENABLE, 0x1);
+
+ /* Do store bytes with parity error in Rx FIFO */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PUT_ERR_IN_FIFO, 0x1);
+ usim_irq_disable(usim->base, (USIM_IRQ_TX | USIM_IRQ_EOB));
+
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x1);
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x0);
+
+ /*
+ * Toggling ATR length to ensure 'USIM_STAT_ATRRX_AFTER_TIMEOUT'
+ * gets disable. EMVCo Test case ref#1703_21/22
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT, 0x1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT,
+ USIM_ATR_TIMEOUT_EMV);
+ USIM_WRITEREG(usim->base, USIM_CMD, MODULE_CLK_EN, 0x1);
+
+ usim->slot_ctx[usim->slot].state = USIM_MODE_ATR;
+
+ /* set smartcard clock */
+ usim_set_smartcardclock(usim, usim->slot_ctx[usim->slot].clock);
+
+ /* Activate card */
+ if (usim->phy_present) {
+ usim_irq_disable(usim->base, USIM_IRQ_TX|USIM_IRQ_ATR_START);
+ usim_irq_enable(usim->base, 0xFFFFFFF7);
+ usim_irq_disable(usim->base, USIM_IRQ_NATR);
+ usim_irq_enable(usim->base, USIM_IRQ_EMV_ATR_LENGTH_TIME_OUT);
+ usim_irq_disable(usim->base, USIM_IRQ_TX|USIM_IRQ_ATR_START);
+
+ /* do no bypass ATR length timer, also do not
+ * disturb the bypass setting of other param
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMER_BYPASS, 0x1);
+
+ usim_irqstatus_clear(usim->base, usim_irqstatus(usim->base));
+
+ ret = phy->activate_card(phy, usim->slot);
+ if (ret != 0)
+ return ret;
+ } else {
+ /* Activate using USIM */
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTOP, 0x0);
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTOP, 0x1);
+ }
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_ATR);
+ return 0;
+}
+
+static int usim_deactivate_card(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ /* Use USIM IP for deactivation if there is no phy */
+ if (usim->phy_present == USIM_PHY_PRESENT) {
+ ret = phy->deactivate_card(phy, usim->slot);
+ if (ret != 0)
+ return ret;
+ } else {
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTART, 0x0);
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTOP, 1);
+ }
+
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ USIM_WRITEREG(usim->base, USIM_TX_BLOCK, BLOCK_LENGTH, 0);
+
+ /* Toggling ATR length to ensure 'USIM_STAT_ATRRX_AFTER_TIMEOUT'
+ * gets disable TC Ref: 1703_21/22
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT,
+ USIM_ATR_TIMEOUT_EMV);
+
+ /* stop ATR length timeout */
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 1);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_DEACT;
+
+ return 0;
+}
+
+static void usim_set_protocol(struct usim *usim, int protocol)
+{
+ u32 irq;
+
+ /* As per spec, mask all interrupts before switching
+ * from one protocol to other.
+ */
+ usim_irq_get(usim->base, &irq);
+
+ /* disable all interrupts */
+ usim_irq_disable(usim->base, 0xFFFFFFFF);
+
+ /* 0 for T=0 and 1 for T=1 protocol */
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFPROTOCOL, protocol);
+ usim->slot_ctx[usim->slot].protocol = protocol;
+
+ /* read and clear status */
+ usim_irqstatus_clear(usim->base, usim_irqstatus(usim->base));
+
+ /* now renable interrupts */
+ usim_irq_enable(usim->base, irq);
+ return;
+}
+
+static void usim_configure_rx_pio(struct usim *usim)
+{
+ /* Reset RX FIFO pointers */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+
+ /* read and clear any pending interrupt status */
+ usim_irqstatus_clear(usim->base, usim_irqstatus(usim->base));
+
+ /* Enable WWT underflow interupt,
+ * RX FIFO full interrupt,
+ * BWT, CWT and parity error level interrupts.
+ */
+ usim_irq_enable(usim->base, USIM_IRQ_WT | USIM_IRQ_RXFULL |
+ USIM_IRQ_TOB |
+ USIM_IRQ_TOC |
+ USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+
+ /* Lets disable key RX interrupts. We will enable them later
+ * when we want to start RX
+ */
+ usim_irq_disable(usim->base, USIM_IRQ_RX |
+ USIM_IRQ_RXDMA_RDY | USIM_IRQ_EOB);
+
+ /* We will use only RX FIFO threshold in RX */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, RXDMA_TYPE, 0x1);
+
+ if (usim->slot_ctx[usim->slot].protocol == 0) {
+ /* Set Rx FIFO Threshold to expected recv length
+ * Subtract 1 from length as HW adds 1 to the trigger
+ */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_RX_TRIGGER,
+ usim->slot_ctx[usim->slot].rx_explen - 1);
+ } else {
+ /* T=1 protocol */
+ /* for T1 we should not use parity error level interrupt */
+ usim_irq_disable(usim->base, USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+
+ /* set RX FIFO threshold to MAX_RX_FIFO size.
+ * We will rely on End-Of-Block interrupt to
+ * terminate reception in T1
+ */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_RX_TRIGGER,
+ USIM_MAX_RX_FIFO_SIZE - 1);
+ }
+ return;
+}
+
+static void usim_configure_tx_pio(struct usim *usim)
+{
+ /* Make sure TX is stopped first by programming
+ * TX_BLOCK to zero and disabling TX_BLOCK_DONE
+ * and USIM_IRQ_TX_BLOCK_REQ interrupts
+ */
+ USIM_WRITEREG(usim->base, USIM_TX_BLOCK, BLOCK_LENGTH, 0);
+ usim_irq_disable(usim->base, USIM_IRQ_TX_BLOCK_DONE |
+ USIM_IRQ_TX_BLOCK_REQ);
+
+ /* We will use Tx Block length feature so clear TX_EN bit */
+ USIM_WRITEREG(usim->base, USIM_CONF2, TX_EN, 0);
+ /* We will not use USIM_TX interrupt for transmit operation */
+ usim_irq_disable(usim->base, USIM_IRQ_TX);
+ /* Reset TX FIFO pointers */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+
+ /* Ensure PIO mode is programmed */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, DMA_MODE, 0);
+}
+
+static int usim_send_data(struct usim *usim, char *txbuf, int len)
+{
+ u32 val;
+ int i;
+ int ret = 0;
+
+ usim->txdone = 0;
+ usim->rxdone = 0;
+
+ if (len == 0) {
+ dev_dbg(usim->dev, "Error: Invalid Tx length:%d", len);
+ return -EINVAL;
+ }
+
+ usim->slot_ctx[usim->slot].event = 0;
+
+ /* Configure Tx PIO mode patams */
+ usim_configure_tx_pio(usim);
+
+ /* Tx FIFO must be empty after reset */
+ val = USIM_READREG(usim->base, USIM_FIFOS, FIFOTX_EMPTY);
+ if (val == 0) {
+ dev_dbg(usim->dev, "Error: Tx FIFO is not empty");
+ return -EFAULT;
+ }
+
+ /* write data in Tx FIFO */
+ for (i = 0; i < len; i++) {
+ USIM_WRITEREG(usim->base, USIM_DTX, DTX, txbuf[i]);
+ dev_dbg(usim->dev, "txbyte %d = %x\n", i, txbuf[i]);
+ }
+
+ /* Finally re-enable TX_BLOCK_xxx interrupts and clear RX interrupts */
+ usim_irq_enable(usim->base, USIM_IRQ_TX_BLOCK_DONE |
+ USIM_IRQ_TX_BLOCK_REQ);
+
+ /* For T=0, stop re-tranmission after resend failure */
+ if (usim->slot_ctx[usim->slot].protocol == 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RESEND_FAILURE, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RESEND_FAILURE, 1);
+ }
+
+ /* Do not store bytes with parity error in Rx FIFO */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PUT_ERR_IN_FIFO, 0);
+
+ usim_irq_enable(usim->base, USIM_IRQ_TOC);
+
+ if (usim->phy_present)
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_TXRX);
+ else
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_LEGACY);
+
+ usim->slot_ctx[usim->slot].state = USIM_MODE_TXRX;
+
+ /* Configure Rx settings before performing a Tx
+ * As soon as we are done with Tx, card will send
+ * data, which we should be ready to capture
+ */
+ usim_configure_rx_pio(usim);
+ /* Start TX operation - program TX_BLOCK register to length
+ * of the TX buffer to start the TX operation.
+ */
+ USIM_WRITEREG(usim->base, USIM_TX_BLOCK, BLOCK_LENGTH, len);
+
+ /* We need to block the caller here */
+ ret = wait_event_interruptible(tx_wait, (usim->txdone == 1));
+ dev_dbg(usim->dev, "Tx WAIT OVER\n");
+ if (usim->slot_ctx[usim->slot].event == USIM_EVENT_TIMEOUT)
+ usim_send_signal(usim, USIM_EVENT_TIMEOUT);
+
+ return ret;
+}
+
+static int usim_set_config(struct usim *usim, struct usim_config *param)
+{
+ u32 ret = 0;
+ dev_dbg(usim->dev, "param:%d, value:%d\n", param->attr, param->value);
+
+ switch (param->attr) {
+ case USIM_PARAM_CWT:
+ USIM_WRITEREG(usim->base, USIM_CWT, CWT, param->value);
+ break;
+
+ case USIM_PARAM_WWT:
+ USIM_WRITEREG(usim->base, USIM_WWT, WWT, param->value);
+ break;
+
+ case USIM_PARAM_CGT:
+ USIM_WRITEREG(usim->base, USIM_CGT, CGT, param->value);
+ break;
+
+ case USIM_PARAM_BWT:
+ USIM_WRITEREG(usim->base, USIM_BWT, BWT, param->value);
+ break;
+
+ case USIM_PARAM_EDCTYPE:
+ /* 0 = LRC check, 1 = CRC check */
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFEDC, param->value);
+ break;
+
+ case USIM_PARAM_LRCCHECK:
+ /* 0 = No LRC check, 1 = LRC check */
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFLRCCHECK,
+ param->value);
+ break;
+
+ case USIM_PARAM_C4:
+ usim_set_c4(usim, param->value);
+ break;
+
+ case USIM_PARAM_C8:
+ usim_set_c8(usim, param->value);
+ break;
+
+ case USIM_PARAM_PROTOCOL:
+ /* 0 for T=0 and 1 for T=1 */
+ usim_set_protocol(usim, param->value);
+ break;
+
+ case USIM_PARAM_VOLTAGE:
+ ret = usim_set_voltage(usim, param->value);
+ break;
+
+ case USIM_PARAM_EMV:
+ USIM_WRITEREG(usim->base, USIM_CONF1, EMV_CONF, param->value);
+ if (param->value)
+ usim->slot_ctx[usim->slot].emv = true;
+ else
+ usim->slot_ctx[usim->slot].emv = false;
+ break;
+
+ case USIM_PARAM_FI:
+ USIM_WRITEREG(usim->base, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF5, FI, param->value);
+ break;
+
+ case USIM_PARAM_DI:
+ USIM_WRITEREG(usim->base, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF5, DI, param->value);
+ break;
+
+ case USIM_PARAM_CODING_CONV:
+ USIM_WRITEREG(usim->base, USIM_STAT, CONFCODCONV, param->value);
+ break;
+
+ case USIM_PARAM_CLOCK_STOP:
+ USIM_WRITEREG(usim->base, USIM_CMD, CMD_CLOCK_STOP,
+ param->value);
+ break;
+
+ case USIM_PARAM_SMARTCARD_CLOCK:
+ ret = usim_set_smartcardclock(usim, param->value);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int usim_get_config(struct usim *usim, struct usim_config *param)
+{
+ u32 ret = 0;
+ dev_dbg(usim->dev, "param:%d, value:%d\n", param->attr, param->value);
+
+ switch (param->attr) {
+ case USIM_PARAM_CWT:
+ param->value = USIM_READREG(usim->base, USIM_CWT, CWT);
+ break;
+
+ case USIM_PARAM_WWT:
+ param->value = USIM_READREG(usim->base, USIM_WWT, WWT);
+ break;
+
+ case USIM_PARAM_CGT:
+ param->value = USIM_READREG(usim->base, USIM_CGT, CGT);
+ break;
+
+ case USIM_PARAM_BWT:
+ param->value = USIM_READREG(usim->base, USIM_BWT, BWT);
+ break;
+
+ case USIM_PARAM_EDCTYPE:
+ param->value = USIM_READREG(usim->base, USIM_CONF2, CONFEDC);
+ break;
+
+ case USIM_PARAM_LRCCHECK:
+ param->value = USIM_READREG(usim->base, USIM_CONF2,
+ CONFLRCCHECK);
+ break;
+
+ case USIM_PARAM_PROTOCOL:
+ /* 0 for T=0 and 1 for T=1 */
+ param->value = USIM_READREG(usim->base, USIM_CONF2,
+ CONFPROTOCOL);
+ break;
+
+ case USIM_PARAM_VOLTAGE:
+ param->value = usim->slot_ctx[usim->slot].supply;
+ break;
+
+ case USIM_PARAM_EMV:
+ param->value = USIM_READREG(usim->base, USIM_CONF1, EMV_CONF);
+ break;
+
+ case USIM_PARAM_FI:
+ param->value = USIM_READREG(usim->base, USIM_CONF5, FI);
+ break;
+
+ case USIM_PARAM_DI:
+ param->value = USIM_READREG(usim->base, USIM_CONF5, DI);
+ break;
+
+ case USIM_PARAM_CODING_CONV:
+ param->value = USIM_READREG(usim->base, USIM_STAT, CONFCODCONV);
+ break;
+
+ case USIM_PARAM_CLOCK_STOP:
+ param->value = USIM_READREG(usim->base, USIM_CMD,
+ CMD_CLOCK_STOP);
+ break;
+
+ case USIM_PARAM_SMARTCARD_CLOCK:
+ param->value = usim->slot_ctx[usim->slot].clock;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static long usim_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct usim *usim = file->private_data;
+ struct usim_data data;
+ struct usim_config param;
+
+ int ret = 0;
+ int cnt = 0;
+ int version = 0;
+ int u_pid = 0;
+ int present = 0;
+
+ if (usim->phy_present == USIM_PHY_NOT_ATTACHED)
+ return -ENXIO;
+
+ switch (cmd) {
+ case USIM_IOCTL_GET_PROVIDER_VERSION:
+ dev_dbg(usim->dev, "IOCTL: GET PROVIDER VERSION\n");
+ version = usim_get_version(usim);
+ ret = copy_to_user((unsigned int *)arg, &version,
+ sizeof(unsigned int));
+ if (ret != 0)
+ ret = -EFAULT;
+ break;
+
+ case USIM_IOCTL_ACTIVATE_CARD:
+ dev_dbg(usim->dev, "IOCTL: ACTIVATE CARD\n");
+ if (usim->phy_present) {
+ present = usim->phy->get_config(usim->phy, usim->slot,
+ SC_PHY_CARD_PRESENCE);
+ if (present)
+ ret = usim_activate_card(usim);
+ else
+ ret = -EFAULT;
+ }
+ break;
+
+ case USIM_IOCTL_DEACTIVATE_CARD:
+ dev_dbg(usim->dev, "IOCTL: DEACTIVATE CARD\n");
+ ret = usim_deactivate_card(usim);
+ break;
+
+ case USIM_IOCTL_WARM_RESET:
+ dev_dbg(usim->dev, "IOCTL: WARM RESET\n");
+ ret = usim_warmreset(usim);
+ break;
+
+ case USIM_IOCTL_GET_ATR:
+ dev_dbg(usim->dev, "IOCTL: GET ATR\n");
+ wait_event_interruptible(atr_wait, (usim->atrdone == 1));
+ ret = copy_to_user((char __user *)arg,
+ usim->slot_ctx[usim->slot].atr,
+ usim->slot_ctx[usim->slot].atr_length);
+ if (ret != 0)
+ ret = -EFAULT;
+ else
+ ret = usim->slot_ctx[usim->slot].atr_length;
+ break;
+
+ case USIM_IOCTL_SEND_DATA:
+ dev_dbg(usim->dev, "IOCTL: SEND DATA\n");
+ ret = copy_from_user(&data, (struct usim_data *)arg,
+ sizeof(struct usim_data));
+ if (ret != 0)
+ return -EFAULT;
+
+ usim->slot = data.slot;
+ usim->slot_ctx[usim->slot].rx_explen = data.rxexplen;
+ usim->slot_ctx[usim->slot].rx_counter = 0;
+ for (cnt = 0; cnt < data.txlen; cnt++)
+ dev_dbg(usim->dev, "apdu[%d] = %x\n", cnt,
+ data.apdu[cnt]);
+ ret = usim_send_data(usim, &data.apdu[0], data.txlen);
+ break;
+
+ case USIM_IOCTL_SET_CONFIG:
+ dev_dbg(usim->dev, "IOCTL: SET CONFIG\n");
+ ret = copy_from_user(¶m, (struct usim_config *)arg,
+ sizeof(struct usim_config));
+ if (ret != 0)
+ return -EFAULT;
+
+ usim_set_config(usim, ¶m);
+ break;
+
+ case USIM_IOCTL_GET_CONFIG:
+ dev_dbg(usim->dev, "IOCTL: GET CONFIG\n");
+ ret = copy_from_user(¶m, (struct usim_config *)arg,
+ sizeof(struct usim_config));
+ if (ret != 0)
+ return -EFAULT;
+
+ usim_get_config(usim, ¶m);
+ ret = copy_to_user((struct usim_config *)arg, ¶m,
+ sizeof(struct usim_config));
+ if (ret != 0)
+ ret = -EFAULT;
+ break;
+
+ case USIM_IOCTL_GET_CARD_PRESENCE:
+ dev_dbg(usim->dev, "IOCTL: CARD PRESENCE\n");
+ if (usim->phy_present) {
+ present = usim->phy->get_config(usim->phy, usim->slot,
+ SC_PHY_CARD_PRESENCE);
+ ret = copy_to_user((unsigned int *)arg, &present,
+ sizeof(unsigned int));
+ if (ret != 0)
+ ret = -EFAULT;
+ }
+ break;
+
+ case USIM_IOCTL_REGISTER_PID:
+ dev_dbg(usim->dev, "IOCTL: USIM_IOCTL_REGISTER_PID");
+ ret = copy_from_user(&u_pid, (int *)arg, sizeof(int));
+ if (ret != 0)
+ return -EFAULT;
+ usim->user_pid = u_pid;
+ break;
+ }
+ return ret;
+}
+
+static ssize_t usim_read(struct file *file, char *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct usim *usim = file->private_data;
+ if (usim->phy_present == USIM_PHY_NOT_ATTACHED)
+ return -ENXIO;
+
+ wait_event_interruptible(rx_wait, (usim->rxdone == 1));
+ dev_dbg(usim->dev, "RX WAIT over\n");
+
+ /* check for timeout and send signal if any */
+ if (usim->slot_ctx[usim->slot].event == USIM_EVENT_TIMEOUT)
+ usim_send_signal(usim, USIM_EVENT_TIMEOUT);
+
+ if (copy_to_user(user_buf, usim->slot_ctx[usim->slot].rxbuf,
+ usim->slot_ctx[usim->slot].rx_counter)) {
+ dev_err(usim->dev, "Copy failed\n");
+ return -EFAULT;
+ }
+ *ppos = usim->slot_ctx[usim->slot].rx_counter;
+ dev_dbg(usim->dev, "Card response returning %d bytes\n",
+ usim->slot_ctx[usim->slot].rx_counter);
+
+ return usim->slot_ctx[usim->slot].rx_counter;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define DUMP_REG(r) seq_printf(s, "%-25s: %08x\n", #r, readl(usim->base + r));
+
+static int usim_regdump_show(struct seq_file *s, void *unused)
+{
+ struct usim *usim = s->private;
+
+ seq_puts(s, "USIM Register Dump\n");
+
+ DUMP_REG(USIM_REVISION);
+ DUMP_REG(USIM_IDENT);
+ DUMP_REG(USIM_SYSCONFIG);
+ DUMP_REG(USIM_SYSSTATUS);
+ DUMP_REG(USIM_IRQSTATUS);
+ DUMP_REG(USIM_IRQENABLE);
+ DUMP_REG(USIM_WAKEUPEN);
+ DUMP_REG(USIM_CMD);
+ DUMP_REG(USIM_STAT);
+ DUMP_REG(USIM_CONF1);
+ DUMP_REG(USIM_CONF2);
+ DUMP_REG(USIM_CONF3);
+ DUMP_REG(USIM_DRX);
+ DUMP_REG(USIM_DTX);
+ DUMP_REG(USIM_FIFOS);
+ DUMP_REG(USIM_CGT);
+ DUMP_REG(USIM_BWT);
+ DUMP_REG(USIM_DEBUG);
+ DUMP_REG(USIM_CONF_SAM1_DIV);
+ DUMP_REG(USIM_CONF4);
+ DUMP_REG(USIM_ATR_CLK_PRD_NBS);
+ DUMP_REG(USIM_CONF_ETU_DIV);
+ DUMP_REG(USIM_CONF5);
+ DUMP_REG(USIM_TC_GUARD_TIME_ADD);
+ DUMP_REG(USIM_RXFIFO_LEVEL);
+ DUMP_REG(USIM_RXFIFO_BYTECNT);
+ DUMP_REG(USIM_WWT);
+ DUMP_REG(USIM_CONF6);
+ DUMP_REG(USIM_IO_DIRECT);
+ DUMP_REG(USIM_TX_BLOCK);
+
+ return 0;
+}
+
+static int usim_regdump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, usim_regdump_show, inode->i_private);
+}
+
+static const struct file_operations usim_regdump_fops = {
+ .open = usim_regdump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int usim_init_debugfs(struct usim *usim)
+{
+ int ret;
+ struct dentry *root;
+ struct dentry *file;
+
+ root = debugfs_create_dir("usim", NULL);
+ if (!root) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ file = debugfs_create_file("regdump", S_IRUGO, root, usim,
+ &usim_regdump_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ usim->debugfs_root = root;
+
+ return 0;
+err1:
+ debugfs_remove_recursive(root);
+err0:
+ return ret;
+}
+#endif
+
+static int usim_pm_init(struct usim *usim)
+{
+ int ret = 0;
+
+ usim->usim0_fck = clk_get(usim->dev, "usim0_fck");
+ if (IS_ERR(usim->usim0_fck)) {
+ ret = PTR_ERR(usim->usim0_fck);
+ dev_err(usim->dev, "usim0_fck failed error:%d\n", ret);
+ return -1;
+ }
+ usim->dpll_core_m4_ck = clk_get(usim->dev, "dpll_core_m4_ck");
+ if (IS_ERR(usim->dpll_core_m4_ck)) {
+ ret = PTR_ERR(usim->dpll_core_m4_ck);
+ dev_err(usim->dev, "dpll_core_m4_ck failed error:%d\n", ret);
+ return -1;
+ }
+ ret = clk_set_parent(usim->usim0_fck, usim->dpll_core_m4_ck);
+ if (ret != 0)
+ dev_dbg(usim->dev, "clk set parent failed: %d\n", ret);
+
+ usim->usim_dbclk = clk_get(usim->dev, "usim_dbck");
+ if (IS_ERR(usim->usim_dbclk)) {
+ ret = PTR_ERR(usim->usim_dbclk);
+ dev_err(usim->dev, "usim_dbck failed error:%d\n", ret);
+ return -1;
+ }
+
+ usim->clkdiv32k_ick = clk_get(usim->dev, "clkdiv32k_ick");
+ if (IS_ERR(usim->usim_dbclk)) {
+ ret = PTR_ERR(usim->clkdiv32k_ick);
+ dev_err(usim->dev, "clkdiv32k_ick failed error:%d\n", ret);
+ return -1;
+ }
+
+ ret = clk_set_parent(usim->usim_dbclk, usim->clkdiv32k_ick);
+ if (ret != 0)
+ dev_dbg(usim->dev, "usim_dbclk set parent failed: %d\n", ret);
+
+ usim->opt_fclk = devm_clk_get(usim->dev, "opt_fck");
+ if (IS_ERR(usim->opt_fclk))
+ dev_err(usim->dev, "unable to get fck\n");
+ else
+ clk_enable(usim->opt_fclk);
+
+ usim->opt_fclk32 = devm_clk_get(usim->dev, "opt_fck32");
+ if (IS_ERR(usim->opt_fclk32))
+ dev_err(usim->dev, "unable to get dbclk\n");
+ else
+ clk_enable(usim->opt_fclk32);
+
+ return 0;
+}
+static void usim_enable(struct usim *usim)
+{
+ if (usim->enable == 1)
+ return;
+
+ /* enable the clk */
+ pm_runtime_get_sync(usim->dev);
+
+ /* usim init */
+ usim_configure(usim);
+
+ usim->enable = 1;
+ return;
+}
+
+static void usim_disable(struct usim *usim)
+{
+ int cnt = 0;
+
+ if (usim->enable == 0)
+ return;
+
+ /* reset USIM state for deactivation */
+ for (cnt = 0; cnt < usim->max_slots; cnt++) {
+ usim->slot = cnt;
+ usim_deactivate_card(usim);
+ }
+
+ /* reset default slot and clock */
+ usim->slot = 0;
+ usim->slot_ctx[usim->slot].clock = USIM_SMARTCART_CLOCK_5MHZ;
+
+ /* shutdown phy */
+ if (usim->phy_present == USIM_PHY_PRESENT)
+ usim->phy->set_config(usim->phy, usim->slot, SC_PHY_MODE,
+ SC_PHY_SHUTDOWN);
+ /* disable clk */
+ pm_runtime_put_sync_autosuspend(usim->dev);
+ usim->enable = 0;
+ return;
+}
+
+static int usim_open(struct inode *inode, struct file *file)
+{
+ struct usim *usim = (struct usim *)dev_get_drvdata(usim_dev.parent);
+
+ if (usim->phy_present == USIM_PHY_NOT_ATTACHED)
+ return -ENXIO;
+
+ file->private_data = usim;
+ usim_enable(usim);
+ return 0;
+}
+
+static int usim_release(struct inode *inode, struct file *file)
+{
+ struct usim *usim = (struct usim *)dev_get_drvdata(usim_dev.parent);
+
+ usim_disable(usim);
+ usim->user_pid = 0;
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int usim_suspend(struct device *dev)
+{
+ /* struct usim *usim = dev_to_usim(dev); */
+ return 0;
+}
+
+static int usim_resume(struct device *dev)
+{
+ /* struct usim *usim = dev_to_usim(dev); */
+ return 0;
+}
+
+static const struct dev_pm_ops usim_pm_ops = {
+ .suspend = usim_suspend,
+ .resume = usim_resume,
+};
+
+#define USIM_PM_OPS (&usim_pm_ops)
+#else
+#define USIM_PM_OPS NULL
+#endif
+
+static int usim_notify(struct notifier_block *self, unsigned long action, void
+ *data)
+{
+ struct usim *usim = (struct usim *)data;
+ int event = action & SC_PHY_NOTIFICATION_ACTION_MASK;
+ int slot = (action & SC_PHY_NOTIFICATION_SLOT_MASK) >>
+ SC_PHY_NOTIFICATION_SLOT_SHIFT;
+ int t_slot = 0;
+
+ /* if phy is removed using rmmod or by some other mech..
+ * then put phy state in unknown, at this point usim also required to
+ * gets removed from the system, if it is inserted as module and
+ * dependent on phy
+ */
+ if (action == SC_PHY_REMOVED)
+ usim->phy_present = USIM_PHY_NOT_ATTACHED;
+
+ if (event & SC_PHY_CARD_INSERTED)
+ usim_send_signal(usim, USIM_EVENT_CARD_INSERT);
+
+ if (action & SC_PHY_CARD_REMOVED) {
+ usim_send_signal(usim, USIM_EVENT_CARD_REMOVE);
+ dev_dbg(usim->dev, "slot is:%d", slot);
+ /* de-activate USIM & PHY state machine for the slot */
+ t_slot = usim->slot;
+ usim->slot = slot;
+ usim_deactivate_card(usim);
+ usim->slot = t_slot;
+ }
+
+ if (action & SC_PHY_CARD_OVERHEAT)
+ usim_send_signal(usim, USIM_EVENT_CARD_OVERHEAT);
+
+ if (action & SC_PHY_CARD_ATR_TIMEOUT)
+ usim_send_signal(usim, USIM_EVENT_TIMEOUT);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block usim_nb = {
+ .notifier_call = usim_notify,
+};
+
+static const struct file_operations usim_fops = {
+ .owner = THIS_MODULE,
+ .read = usim_read,
+ .open = usim_open,
+ .unlocked_ioctl = usim_ioctl,
+ .release = usim_release,
+};
+
+static int usim_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+
+ struct usim *usim = NULL;
+ struct device_node *phy_node = NULL;
+ struct resource *res = NULL;
+ void __iomem *base = NULL;
+ const __be32 *parp = NULL;
+ struct i2c_client *phy_i2c = NULL;
+
+ int ret = 0;
+ int version = 0;
+ int cnt = 0;
+ u32 prop = 0;
+ int lenp = 0;
+
+ if (!node) {
+ dev_err(dev, "device node not found\n");
+ return -EINVAL;
+ }
+
+ usim = devm_kzalloc(dev, sizeof(*usim), GFP_KERNEL);
+ if (!usim) {
+ dev_err(dev, "not enough memory\n");
+ return -ENOMEM;
+ }
+
+ usim->irq = platform_get_irq(pdev, 0);
+ if (usim->irq < 0) {
+ dev_err(dev, "missing IRQ resource\n");
+ ret = -EINVAL;
+ goto usim_err_ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory base resource\n");
+ ret = -EINVAL;
+ goto usim_err_ret;
+ }
+
+ base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!base) {
+ dev_err(dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto usim_err_ret;
+ }
+
+ usim->dev = &pdev->dev;
+ usim->base = base;
+ usim->max_slots = 1;
+ usim->phy_present = USIM_PHY_NOT_PRESENT;
+ usim->enable = 0;
+
+ /* default slot will be zero : user card */
+ usim->slot = 0;
+
+ ret = devm_request_irq(dev, usim->irq, usim_interrupt, 0,
+ "usim", usim);
+ if (ret) {
+ dev_err(dev, "fail request IRQ #%d --> %d\n", usim->irq, ret);
+ goto usim_err_ret;
+ return ret;
+ }
+
+ /*
+ * Populate all the child nodes here...
+ */
+ ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
+
+ /* get phy details */
+ parp = of_get_property(node, "phy", &lenp);
+ if (parp == NULL || lenp != (sizeof(void *))) {
+ dev_dbg(usim->dev, "parp is null!,no phy");
+ } else {
+ /* get phy node */
+ phy_node = of_find_node_by_phandle(be32_to_cpup(parp));
+ if (phy_node == NULL) {
+ dev_err(usim->dev, "\n phy node is null");
+ ret = -EPROBE_DEFER;
+ goto usim_err_ret;
+ }
+ phy_i2c = of_find_i2c_device_by_node(phy_node);
+ if (phy_i2c == NULL) {
+ dev_err(usim->dev, "\n phy i2c is null");
+ ret = -EPROBE_DEFER;
+ goto usim_err_ret;
+ }
+ /* get phy interface */
+ usim->phy = (struct sc_phy *)i2c_get_clientdata(phy_i2c);
+ if (usim->phy == NULL) {
+ dev_err(usim->dev, "phy data is null");
+ ret = -EPROBE_DEFER;
+ goto usim_err_ret;
+ }
+ usim->phy_present = USIM_PHY_PRESENT;
+
+ ret = of_property_read_u32(node, "phy-slots", &prop);
+ /* if phy-slot is not declared then assume one phy slot */
+ usim->max_slots = prop > 0 ? prop : 1;
+ }
+
+ dev_dbg(usim->dev, "usim max slot:%d", usim->max_slots);
+ /* initialize slot context*/
+ if (usim->max_slots > USIM_MAX_SLOTS) {
+ ret = -EINVAL;
+ goto usim_err_ret;
+ }
+
+ usim->slot_ctx = kmalloc(usim->max_slots *
+ sizeof(struct usim_slotcontext), GFP_KERNEL);
+ if (!usim->slot_ctx)
+ return -ENOMEM;
+
+ for (cnt = 0; cnt < usim->max_slots; cnt++) {
+ /* default protocol */
+ usim->slot_ctx[cnt].protocol = 0;
+ usim->slot_ctx[cnt].emv = true;
+ }
+
+ dev_set_drvdata(dev, usim);
+ usim_pm_init(usim);
+
+ /* enable the clock */
+ pm_runtime_enable(usim->dev);
+ pm_runtime_set_active(usim->dev);
+ spin_lock_init(&usim->lock);
+
+ usim_dev.minor = MISC_DYNAMIC_MINOR;
+ usim_dev.name = "usim";
+ usim_dev.fops = &usim_fops;
+ usim_dev.parent = &(pdev->dev);
+
+ ret = misc_register(&usim_dev);
+ if (ret) {
+ pr_err("unable to register a misc device\n");
+ goto usim_err_reg;
+ }
+#ifdef CONFIG_DEBUG_FS
+ ret = usim_init_debugfs(usim);
+ if (ret) {
+ dev_err(dev, "Debugfs init failed\n");
+ goto usim_err_reg;
+ }
+#endif
+ /* set default ICC clock : 5Mhz */
+ usim->slot_ctx[usim->slot].clock = USIM_SMARTCART_CLOCK_5MHZ;
+
+ /* get the clock & do usim configuration */
+ usim_enable(usim);
+ dev_info(usim->dev, "usim driver initialized\n");
+
+ /* register notifier */
+ if (usim->phy_present)
+ usim->phy->register_notify(usim->phy, &usim_nb, (void *)usim);
+
+ /* get usim version */
+ version = usim_get_version(usim);
+ dev_info(usim->dev, "version is:%0x", version);
+
+ usim_disable(usim);
+ return 0;
+
+usim_err_reg:
+ pm_runtime_set_suspended(usim->dev);
+ pm_runtime_disable(usim->dev);
+ misc_deregister(&usim_dev);
+
+usim_err_ret:
+ if (usim)
+ kfree(usim->slot_ctx);
+ return ret;
+}
+
+static int usim_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usim *usim = dev_to_usim(dev);
+
+ usim_disable(usim);
+ /* unregister notifier, applicable only when phy present and phy state
+ * is not unknown i.e. - phy has not been removed using rmmod */
+ if (usim->phy_present == USIM_PHY_PRESENT)
+ usim->phy->unregister_notify(usim->phy, &usim_nb);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(usim->debugfs_root);
+#endif
+ /* disable pm runtime */
+ pm_runtime_set_suspended(usim->dev);
+ pm_runtime_disable(usim->dev);
+
+ kfree(usim->slot_ctx);
+ misc_deregister(&usim_dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id usim_id_table[] = {
+ { .compatible = "ti,usim" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, usim_id_table);
+#endif
+
+static struct platform_driver usim_driver = {
+ .driver = {
+ .name = "usim",
+ .owner = THIS_MODULE,
+ .pm = USIM_PM_OPS,
+ .of_match_table = of_match_ptr(usim_id_table),
+ },
+ .probe = usim_probe,
+ .remove = usim_remove,
+};
+
+static int __init usim_init(void)
+{
+ return platform_driver_register(&usim_driver);
+}
+
+static void __exit usim_exit(void)
+{
+ platform_driver_unregister(&usim_driver);
+}
+
+late_initcall(usim_init);
+module_exit(usim_exit);
+
+MODULE_AUTHOR("Maulik Mankad <[email protected]>");
+MODULE_DESCRIPTION("USIM Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/ti-usim.h b/include/linux/ti-usim.h
new file mode 100644
index 0000000..f4b3ca8
--- /dev/null
+++ b/include/linux/ti-usim.h
@@ -0,0 +1,98 @@
+/*
+ * ti-usim.h - Header file for USIM SmartCard interface
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __TI_USIM_H__
+#define __TI_USIM_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define USIM_IOCTL 0xFE
+
+enum usim_param {
+ USIM_PARAM_CWT = 0,
+ USIM_PARAM_WWT,
+ USIM_PARAM_CGT,
+ USIM_PARAM_BWT,
+ USIM_PARAM_EDCTYPE,
+ USIM_PARAM_LRCCHECK,
+ USIM_PARAM_C4,
+ USIM_PARAM_C8,
+ USIM_PARAM_PROTOCOL,
+ USIM_PARAM_VOLTAGE,
+ USIM_PARAM_EMV,
+ USIM_PARAM_FI,
+ USIM_PARAM_DI,
+ USIM_PARAM_CODING_CONV,
+ USIM_PARAM_CLOCK_STOP,
+ USIM_PARAM_SMARTCARD_CLOCK,
+};
+
+struct usim_data {
+ int slot;
+ int rxexplen;
+ int txlen;
+ unsigned char apdu[256];
+};
+
+struct usim_config {
+ enum usim_param attr;
+ unsigned int value;
+};
+
+#define USIM_SIGID 51
+
+#define USIM_IOCTL_GET_PROVIDER_VERSION _IOR(USIM_IOCTL, 0, int)
+#define USIM_IOCTL_ACTIVATE_CARD _IO(USIM_IOCTL, 1)
+#define USIM_IOCTL_DEACTIVATE_CARD _IO(USIM_IOCTL, 2)
+#define USIM_IOCTL_WARM_RESET _IO(USIM_IOCTL, 3)
+#define USIM_IOCTL_GET_ATR _IOR(USIM_IOCTL, 4, char *)
+#define USIM_IOCTL_SEND_DATA _IOW(USIM_IOCTL, 5, struct usim_data)
+#define USIM_IOCTL_SET_CONFIG _IOW(USIM_IOCTL, 6, struct usim_config)
+#define USIM_IOCTL_GET_CONFIG _IOW(USIM_IOCTL, 7, struct usim_config)
+#define USIM_IOCTL_GET_CARD_PRESENCE _IOR(USIM_IOCTL, 8, int)
+#define USIM_IOCTL_REGISTER_PID _IOW(USIM_IOCTL, 9, int)
+
+#define USIM_MAX_ATRLENGTH 0xFF
+#define USIM_MAX_APDU_LENGTH 0xFE
+
+enum usim_smartcard_clock {
+ USIM_SMARTCART_CLOCK_3_3MHZ = 0x1,
+ USIM_SMARTCART_CLOCK_4MHZ = 0x2,
+ USIM_SMARTCART_CLOCK_5MHZ = 0x3,
+ USIM_SMARTCART_CLOCK_6_6MHZ = 0x4,
+ USIM_SMARTCART_CLOCK_10MHZ = 0x5,
+ USIM_SMARTCART_CLOCK_20MHZ = 0x6,
+};
+
+enum usim_event {
+ USIM_EVENT_CARD_INSERT = 0x1,
+ USIM_EVENT_CARD_REMOVE = 0x2,
+ USIM_EVENT_TIMEOUT = 0x4,
+ USIM_EVENT_ERR_TXRETRY = 0x8,
+ USIM_EVENT_ERR_LRC = 0x10,
+ USIM_EVENT_ERR_PARITY = 0x20,
+ USIM_EVENT_ERR_FRAME = 0x40,
+ USIM_EVENT_PHYERR = 0x80,
+ USIM_EVENT_CARD_OVERHEAT = 0x100,
+};
+
+enum usim_card_voltage {
+ USIM_CARD_5V = 0,
+ USIM_CARD_3V,
+ USIM_CARD_1_8V
+};
+
+#endif /* __TI_USIM_H__ */
--
1.7.1
SoC specific DT entries added for TI's USIM - smart card controller of AM43xx
platfrom.
Signed-off-by: Satish Patel <[email protected]>
---
arch/arm/boot/dts/am4372.dtsi | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index 974d103..7ed1cd1 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -663,5 +663,15 @@
<&edma 11>;
dma-names = "tx", "rx";
};
+
+ usim0: usim@48034000 {
+ compatible = "ti,usim";
+ reg = <0x48034000 0x1000>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "usim0";
+ clocks = <&usim0_opt_fck>, <&usim0_opt_fck32>;
+ clock-names = "opt_fck", "opt_fck32";
+ status = "disabled";
+ };
};
};
--
1.7.1
SmartCard controller uses this interface to communicate with
SmartCard via PHY
Some SmartCard PHY has multiple slots for cards.
This inerface also enables controller to communicate
with one or more SmartCard connected over phy.
interface structure includes following APIs
- set/get config
- activate/deactivate smart card
- warm reset
- register_notify (for card insert/remove/overheat)
- unregister_notify
Signed-off-by: Satish Patel <[email protected]>
---
Documentation/sc_phy.txt | 171 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/sc_phy.h | 132 +++++++++++++++++++++++++++++++++++
2 files changed, 303 insertions(+), 0 deletions(-)
create mode 100644 Documentation/sc_phy.txt
create mode 100644 include/linux/sc_phy.h
diff --git a/Documentation/sc_phy.txt b/Documentation/sc_phy.txt
new file mode 100644
index 0000000..d610e26
--- /dev/null
+++ b/Documentation/sc_phy.txt
@@ -0,0 +1,171 @@
+ SmartCard PHY Interface
+ Satish Patel <[email protected]>
+
+This document explains the SmartCard interface between SmartCard
+controller and SmartCard phy. Document also describes how-to-use.
+
+1. Why SmartCard PHY Interface?
+
+The use of smartcard is increasing in embedded industry. As smartcard
+not only prevent duplication but also, brings key based authentication
+flow into picture.
+
+SmartCard standards like EMV(EuroPay-Mastercard-Visa) are becoming
+mandatory for payment terminals.
+
+Till date, most of the SmartCard readers are based on USB serial
+interface. Which drives its logic within firmware lies on device.
+Few are based on FPGA solutions. But now SoCs are coming up with
+inbuilt smartcard controller. e.g. TI-AM43x
+
+Role of SmartCard controller and SmartCard phy:
+
+Smartcard Phy:
+Forms electrical interface between controller and SmartCard. Phy
+enables access to more than one smartcard and in addition it provides
+fast deactivation logic when card gets removed from the slot. It can
+also generate the signals like card insert/remove/overheat etc.
+
+Smartcard Controller:
+In built mechanism to meet EMV L1 specification (e.g. T=0, T=1
+protocol timings, ATR timeout etc..) for SmartCard transaction. In
+addition to this, it has FIFO to store tx/rx bytes, internal state
+machine for ATR, Tx/Rx, Synchronous/Asynchronous mode, timing
+counters etc..
+
+Controller can also have direct interface through which SmartCard
+can be connected without phy.
+
+Below is the brief of SmartCard block diagram from user to h/w
+layer.
+
+
+ -----------
+ |PC/SC App|
+ -----------
+ ----------- -----------
+ |PC/SC F/W| | Visa APP|
+ ----------- -----------
+ ----------- ------------ ------------
+ |IFD Hand.| | EMV L1/L2| | Test App |
+ ----------- ------------ ------------
+User Space
+--------------------------------------------------------------------
+
+ -----------------------------------------
+ | SmartCard Controller Driver |
+ -----------------------------------------
+ | |
+ | |
+ ------------- |
+ | Phy Driver | |
+ ------------- |
+ | |
+Kernel Space | |
+--------------------------------------------------------------------
+ | |
+ --------- ----------------
+ | PHY | |Controller IP |
+ --------- ----------------
+ | |
+--------------------------------------------------------------------
+ | |
+ _______________________________________
+ | | |
+ VISA card Master Card Custom Card
+
+
+At present in Linux there is no public interface exist which acts as
+bridge between controller and phy. Mostly vendors uses proprietary
+solution in such cases.
+
+2. Introduction to SmartCard PHY interface
+
+SmartCard PHY interface that exposes phy's capabilities to the smart
+card controller. SmartCard controller uses this interface to
+communicate with SmartCard via phy.
+
+Such capabilities are:
+1) Some SmartCard phy (e.g. TDA8026-NxP) has multiple slots for smart
+cards. This interface enables controller to communicate with specific
+SmartCard inserted to the specific phy's slot.
+
+2) Warm reset to SmartCard inserted to phy slot.
+
+3) Bit banging of SmartCard pins to support vedor specific memory
+cards. Mostly when it comes to sychorous SmartCard
+
+4) Notification of card insert/remove/overheat etc.
+
+
+3. How to use
+
+SmartCard PHY:
+The SmartCard PHY driver, who wants to be interfaced with SmartCard
+controller require to follow below step
+
+- include "sc_phy.h"
+
+- use "sc_phy" structure as driver(client) data. PHY driver can use
+'void *pdata' of "sc_phy" to hold its private data(if any)
+
+- implement following capabilities (whichever is applicable)
+ set_config
+ get_config
+ activate_card
+ deactivate_card
+ warm_reset
+ register_notify
+ unregister_notify
+e.g.
+phyxx_init()
+{
+
+ struct sc_phy *sc_phy;
+ struct phyxx *pdata;
+
+ ...
+ ...
+ sc_phy->pdata = pdata;
+ sc_phy->set_config = phyxx_set_config;
+ sc_phy->get_config = phyxx_get_config;
+ sc_phy->activate_card = phyxx_activate_card;
+}
+
+Device Tree Binding:
+Refer following reference to bind SmartCard PHY with SmartCard
+controller.
+
+ /* SmartCard PHY node */
+ &i2c0 {
+ clock-frequency = <10000000>;
+ phyxx: phyxx@yy {
+ compatible = "xyz,abc";
+ reg = <0xyy>;
+ };
+ };
+
+ /* SmartCard Controller node*/
+ &usim0 {
+ ....
+ phy = <&phyxx>;
+ phy-slots = <1>;
+ };
+
+SmartCard Controller:
+- Access PHY interface using DT.
+Refer below example code
+
+ phy_i2c = of_find_i2c_device_by_node(phy_node);
+ if (phy_i2c == NULL) {
+ ret = -EINVAL;
+ }
+
+ /* get phy interface */
+ sc_phy = (struct sc_phy *)i2c_get_clientdata(phy_i2c);
+
+ /* register notifier */
+ sc_phy->register_notify(sc_phy, ¬ifier_cb, (void *)data);
+
+
+- Controller can use then after all defined SmartCard PHY interface
diff --git a/include/linux/sc_phy.h b/include/linux/sc_phy.h
new file mode 100644
index 0000000..fb3ad78
--- /dev/null
+++ b/include/linux/sc_phy.h
@@ -0,0 +1,132 @@
+/*
+ * sc_phy.h - Header file for Smart Card PHY operations.
+ * This is a smart card phy's interface to smart card controller.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SC_PHY_H__
+#define __SC_PHY_H__
+
+#include <linux/notifier.h>
+
+/* defines used in notify action
+ * action will be composed of two part
+ * | 0x0000 | <slot> - 2 bytes| <action>- 2 bytes |
+ */
+#define SC_PHY_NOTIFICATION_ACTION_MASK (0x000000ffU)
+#define SC_PHY_NOTIFICATION_ACTION_SHIFT (0)
+
+#define SC_PHY_NOTIFICATION_SLOT_MASK (0x0000ff00U)
+#define SC_PHY_NOTIFICATION_SLOT_SHIFT (8)
+/*
+ * smart card supply voltage levels
+ */
+enum sc_supply_voltage {
+ SC_PHY_5V = 0,
+ SC_PHY_3V,
+ SC_PHY_1_8V
+};
+
+/*
+ * smarcard type - async or sync
+ */
+enum sc_phy_card_mode {
+ SC_PHY_ASYNC = 0,
+ SC_PHY_SYNC,
+};
+
+/*
+ * phy operational modes
+ */
+enum sc_phy_mode {
+ SC_PHY_ACTIVE = 0,
+ SC_PHY_CLOCKSTOP,
+ SC_PHY_STANDBY,
+ SC_PHY_SHUTDOWN,
+ SC_PHY_REMOVED,
+};
+
+/*
+ * smartcard actions. more actions will be as they gets introduced in upcoming
+ * smart card phys.
+ */
+enum sc_phy_card_action {
+ SC_PHY_CARD_INSERTED = 0x1,
+ SC_PHY_CARD_REMOVED = 0x2,
+ SC_PHY_CARD_OVERHEAT = 0x4,
+ SC_PHY_CARD_ATR_TIMEOUT = 0x8,
+};
+
+/*
+ * configuration parameters of smart card phy
+ */
+enum sc_phy_config {
+ SC_PHY_VERSION = 0,
+ SC_PHY_CARD_SUPPLY_VOLTAGE,
+ SC_PHY_CARD_PRESENCE,
+ SC_PHY_ATR_MUTE_TIME,
+ SC_PHY_ATR_EARLY_TIME,
+ SC_PHY_CARD_MODE, /* sync or async mode */
+ SC_PHY_IO, /* disable or enable i/o line connected to phy */
+ SC_PHY_CLKDIV, /* clock division for the clock supplied to phy */
+ SC_PHY_MODE,
+ SC_PHY_PIN_CLK,
+ SC_PHY_PIN_RST,
+ SC_PHY_PIN_C4,
+ SC_PHY_PIN_C8
+};
+/**
+ * struct sc_phy - The basic smart card phy structure
+ *
+ * @dev: phy device
+ * @pdata: pointer to phy's private data structure
+ * @set_config: called to set phy's configuration
+ * @get_config: called to get phy's configuration
+ * @activate_card: perform smart card activation
+ * @deactivate_card: perform smart card de-activation
+ * @warm_reset: execute smart card warm reset sequence
+ * @register_card_activity_cb: register call back to phy device.
+ * This call back will be called on card insert or remove event
+ *
+ * smart card controller uses this interface to communicate with
+ * smart card via phy.Some smart card phy has multiple slots for
+ * cards. This inerface also enables controller to communicate with
+ * one or more smart card connected over phy.
+ */
+struct sc_phy {
+ /* phy's device pointer */
+ struct device *dev;
+
+ /* phy's private data */
+ void *pdata;
+
+ /* notify data, passed by interface user as a part of
+ * register_notify API. Data should be passed back when
+ * notification raised to the interface user
+ */
+ void *notify_data;
+
+ int (*set_config)(struct sc_phy *phy, u8 slot,
+ enum sc_phy_config attr, int value);
+ int (*get_config)(struct sc_phy *phy, u8 slot, enum
+ sc_phy_config attr);
+ int (*activate_card)(struct sc_phy *phy, u8 slot);
+ int (*deactivate_card)(struct sc_phy *phy, u8 slot);
+ int (*warm_reset)(struct sc_phy *phy, u8 slot);
+ int (*register_notify)(struct sc_phy *phy,
+ struct notifier_block *nb, void *notify_data);
+ int (*unregister_notify)(struct sc_phy *phy,
+ struct notifier_block *nb);
+};
+
+#endif /* __SC_PHY_H__ */
--
1.7.1
On 1/20/2014 10:03 AM, Satish Patel wrote:
> Changes from v1:
> * RFC(v1) comments are fixed
>
> ** removed "gpio_to_irq" as GPIO controller process cell from DT and
> give it to DT node
> ** comments on documentation
> ** few other comments on null checks are resolved
>
> * BWT timing configuration is added to ti-usim driver
>
> v1 cover letter link#
> https://lkml.org/lkml/2014/1/6/250
>
> Satish Patel (5):
> sc_phy:SmartCard(SC) PHY interface to SC controller
> misc: tda8026: Add NXP TDA8026 PHY driver
> char: ti-usim: Add driver for USIM module on AM43xx
> ARM: dts: AM43xx: DT entries added for ti-usim
> ARM: dts: AM43xx-epos-evm: DT entries for ti-usim and phy
>
> Documentation/devicetree/bindings/misc/tda8026.txt | 19 +
> .../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
> Documentation/sc_phy.txt | 171 ++
> arch/arm/boot/dts/am4372.dtsi | 10 +
> arch/arm/boot/dts/am43x-epos-evm.dts | 43 +
> drivers/char/Kconfig | 7 +
> drivers/char/Makefile | 1 +
> drivers/char/ti-usim-hw.h | 863 +++++++++
> drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
> drivers/misc/Kconfig | 7 +
> drivers/misc/Makefile | 1 +
> drivers/misc/tda8026.c | 1255 +++++++++++++
> include/linux/sc_phy.h | 132 ++
> include/linux/ti-usim.h | 98 +
> 14 files changed, 4497 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
> create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
> create mode 100644 Documentation/sc_phy.txt
> create mode 100644 drivers/char/ti-usim-hw.h
> create mode 100644 drivers/char/ti-usim.c
> create mode 100644 drivers/misc/tda8026.c
> create mode 100644 include/linux/sc_phy.h
> create mode 100644 include/linux/ti-usim.h
Any comments on this patch series ?
If not,
Can you accept these patches for next merge window
Thanks
Satish
>
>
On Thu, Jan 30, 2014 at 11:22:48AM +0530, Satish Patel wrote:
>
> On 1/20/2014 10:03 AM, Satish Patel wrote:
> > Changes from v1:
> > * RFC(v1) comments are fixed
> >
> > ** removed "gpio_to_irq" as GPIO controller process cell from DT and
> > give it to DT node
> > ** comments on documentation
> > ** few other comments on null checks are resolved
> >
> > * BWT timing configuration is added to ti-usim driver
> >
> > v1 cover letter link#
> > https://lkml.org/lkml/2014/1/6/250
> >
> > Satish Patel (5):
> > sc_phy:SmartCard(SC) PHY interface to SC controller
> > misc: tda8026: Add NXP TDA8026 PHY driver
> > char: ti-usim: Add driver for USIM module on AM43xx
> > ARM: dts: AM43xx: DT entries added for ti-usim
> > ARM: dts: AM43xx-epos-evm: DT entries for ti-usim and phy
> >
> > Documentation/devicetree/bindings/misc/tda8026.txt | 19 +
> > .../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
> > Documentation/sc_phy.txt | 171 ++
> > arch/arm/boot/dts/am4372.dtsi | 10 +
> > arch/arm/boot/dts/am43x-epos-evm.dts | 43 +
> > drivers/char/Kconfig | 7 +
> > drivers/char/Makefile | 1 +
> > drivers/char/ti-usim-hw.h | 863 +++++++++
> > drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
> > drivers/misc/Kconfig | 7 +
> > drivers/misc/Makefile | 1 +
> > drivers/misc/tda8026.c | 1255 +++++++++++++
> > include/linux/sc_phy.h | 132 ++
> > include/linux/ti-usim.h | 98 +
> > 14 files changed, 4497 insertions(+), 0 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
> > create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
> > create mode 100644 Documentation/sc_phy.txt
> > create mode 100644 drivers/char/ti-usim-hw.h
> > create mode 100644 drivers/char/ti-usim.c
> > create mode 100644 drivers/misc/tda8026.c
> > create mode 100644 include/linux/sc_phy.h
> > create mode 100644 include/linux/ti-usim.h
> Any comments on this patch series ?
>
> If not,
> Can you accept these patches for next merge window
It's the middle of this merge window, and I can't accept any patches
until after 3.14-rc1 is out, at which point I'll start to work on my
patch backlog.
thanks,
greg k-h
Hi Satish,
On 01/20/2014 06:33 AM, Satish Patel wrote:
> TI-USIM driver is a platform driver that provides a character
> driver interface to user applications.
>
> It allows user applications to call IOCTL's to
> perform smart card operations.
>
> Driver currently supports
> - ATR
> - T=0 & T=1 protocol
> - clock stop mode
> - smart card clock configuration
> - Tx/Rx application data units (APDU) to smart card
> - Interface to PHY using DT & phy interface
>
> Validation is done with ACOS3 smart cards
>
> Signed-off-by: Satish Patel <[email protected]>
> ---
> .../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
> drivers/char/Kconfig | 7 +
> drivers/char/Makefile | 1 +
> drivers/char/ti-usim-hw.h | 863 +++++++++
> drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
ti-usim.c is a very large driver that does everything but looks like limited to TI hardware.
How about splitting it into generic stuff and hw specific glue logic so that most of the generic stuff
could be used by different hardware types.
> include/linux/ti-usim.h | 98 +
> 6 files changed, 2859 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
> create mode 100644 drivers/char/ti-usim-hw.h
> create mode 100644 drivers/char/ti-usim.c
> create mode 100644 include/linux/ti-usim.h
>
cheers,
-roger
On 2/4/2014 6:49 PM, Roger Quadros wrote:
> Hi Satish,
>
> On 01/20/2014 06:33 AM, Satish Patel wrote:
>> TI-USIM driver is a platform driver that provides a character
>> driver interface to user applications.
>>
>> It allows user applications to call IOCTL's to
>> perform smart card operations.
>>
>> Driver currently supports
>> - ATR
>> - T=0 & T=1 protocol
>> - clock stop mode
>> - smart card clock configuration
>> - Tx/Rx application data units (APDU) to smart card
>> - Interface to PHY using DT & phy interface
>>
>> Validation is done with ACOS3 smart cards
>>
>> Signed-off-by: Satish Patel <[email protected]>
>> ---
>> .../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
>> drivers/char/Kconfig | 7 +
>> drivers/char/Makefile | 1 +
>> drivers/char/ti-usim-hw.h | 863 +++++++++
>> drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
>
> ti-usim.c is a very large driver that does everything but looks like limited to TI hardware.
> How about splitting it into generic stuff and hw specific glue logic so that most of the generic stuff
> could be used by different hardware types.
>
Two things over here
- First interface between user application and smartcard controller
driver. There is already an open source f/w in user space called "pcsc"
exists for this.
- Second communication between smartcard controller and smartcard phy. I
have introduce lite interface called sc_phy which covers generic stuff
between controller and phy.
>> include/linux/ti-usim.h | 98 +
>> 6 files changed, 2859 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
>> create mode 100644 drivers/char/ti-usim-hw.h
>> create mode 100644 drivers/char/ti-usim.c
>> create mode 100644 include/linux/ti-usim.h
>>
>
> cheers,
> -roger
>