On Wed, May 28, 2014 at 3:57 AM, Satish Patel <satish.patel@xxxxxx> 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. What's the usecase? For cellular, isn't the SC typically attached to the modem? > > Driver currently supports > - Cold & Warm Reset > - 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 <satish.patel@xxxxxx> > --- > .../devicetree/bindings/ti-usim/ti-usim.txt | 32 + > drivers/char/Kconfig | 7 + > drivers/char/Makefile | 1 + > drivers/char/ti-usim-hw.h | 864 ++++++++ > drivers/char/ti-usim.c | 2213 ++++++++++++++++++++ Perhaps drivers/char/smartcard or drivers/smartcard would be a better location. This should be designed assuming we get more than 1 SmartCard controller. Perhaps there already is one in the kernel. > include/linux/ti-usim.h | 111 + > 6 files changed, 3228 insertions(+) > 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..4e599e2 > --- /dev/null > +++ b/Documentation/devicetree/bindings/ti-usim/ti-usim.txt > @@ -0,0 +1,32 @@ > +ti-usim: USIM - Smart Card Controller > + > +Required Properties: > +- compatible: Should be "ti,usim" This should be more specific like "ti,am43xx-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 How many clocks and what is their use and order? > +- 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>, > + <&dpll_per_m2_div4_ck>; > + clock-names = "opt_fck", "opt_fck32", "fck"; > + status = "disabled"; > + }; > diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig > index 6e9f74a..c7c5fae 100644 > --- a/drivers/char/Kconfig > +++ b/drivers/char/Kconfig > @@ -600,5 +600,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 || COMPILE_TEST > + 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 a324f93..f7ee777 100644 > --- a/drivers/char/Makefile > +++ b/drivers/char/Makefile > @@ -61,3 +61,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..1d3dd6e > --- /dev/null > +++ b/drivers/char/ti-usim-hw.h > @@ -0,0 +1,864 @@ > +/* > + * ti-usim-hw.h - Header file for USIM smart card interface This can go into the .c file. > + * > + * 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; > + enum usim_card_mode card_mode; > +}; > + > +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) Parenthesis and U are generally not needed throughout. > +#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) Can't you have common defines for IRQSTATUS, IRQENABLE and WAKEUPEN? They seem to all be the same bit positions. And you already have IRQ bit defines above. [lots more register defines...] > + > +#define USIM_TX_BLOCK_RESERVED_SHIFT (16U) > +#define USIM_TX_BLOCK_RESERVED_MASK (0xffff0000U) In general, trim these defines down to what you actually use. Single bit fields rarely need both a shift and mask. > + > +#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..ffabf87 > --- /dev/null > +++ b/drivers/char/ti-usim.c > @@ -0,0 +1,2213 @@ > +/* > + * 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> > +#include <linux/delay.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) Get rid of this. > + > +/* calculation of max ATR waiting time > + * 372 is default FI value, so etu for 1Mhz SC clock cycle would be > + * etu = FI/f sec = 372/1Mhz = 372 micro second > + * Max ATR waiting is - USIM_ATR_TIMEOUT_EMV etu > + */ > +#define MAX_ATR_WAITTIME_US (372 * USIM_ATR_TIMEOUT_EMV) > + > +/* > + * 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 int usim_deactivate_card(struct usim *usim); > + > +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; > +} Use readl/writel directly. If we wanted drivers written with accessors like these, then there would be common ones to use. > + > + > +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); I assume these are called with appropriate locking? > +} > + > +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); > +} Use readl/writel directly. > + > +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) > +{ Using signals is not a typical driver interface. > + 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); More register accessors? > + > + 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); It would be nicer to see these written w/o all these while (0) loops. > + > + 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; This does not need to be initialized. > + 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; How about using "else if" instead of all these breaks. Can't you have multiple interrupt bits set? > + } > + > + 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; > + int count = 3; > + > + /* perform softreset of IP */ > + USIM_WRITEREG(usim->base, USIM_SYSCONFIG, SOFTRESET, 1); > + > + /* wait until reset get completed */ > + while (count > 0) { > + reg = USIM_READREG(usim->base, USIM_SYSCONFIG, SOFTRESET); > + if (reg == 0x0) > + break; > + mdelay(10); Does this really need to be a delay loop rather than a sleep? If not, use msleep and time_after or time_before. > + count--; > + } > + if (reg != 0x0) > + return -EIO; > + > + /* 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; > + int reg = 0; > + 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; > + } > + > + /* Set default card type as EMV, Force SIO to low level */ > + reg = readl(usim->base + USIM_CONF2); > + reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKMODE, clkmode); > + reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKDIV, clkdiv); > + writel(reg, usim->base + USIM_CONF2); > + > + /* 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; > + > + /* reset ATR wait flag */ > + usim->atrdone = 0; > + > + 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_set_cardmode(struct usim *usim, int slot, int card_mode) > +{ > + int val = 0; > + struct sc_phy *phy = usim->phy; > + > + if (card_mode != usim->slot_ctx[slot].card_mode) { > + /* deactivate current card before changing the > + * mode of smart card > + */ > + usim_deactivate_card(usim); > + } else { > + dev_dbg(usim->dev, "mode is same as previous, no action!!"); > + return 0; > + } > + > + /* set card mode */ > + switch (card_mode) { > + case USIM_CARD_MODE_ASYNC: > + val = SC_PHY_ASYNC; > + break; > + > + case USIM_CARD_MODE_SYNC_TYPE1: > + val = SC_PHY_SYNC_TYPE1; > + break; > + > + case USIM_CARD_MODE_SYNC_TYPE2: > + val = SC_PHY_SYNC_TYPE2; > + break; > + > + case USIM_CARD_MODE_SYNC_OTHER: > + val = SC_PHY_SYNC; > + break; > + > + default: > + dev_err(usim->dev, "Invalid card mode"); > + return -EINVAL; > + break; > + } > + if (usim->phy_present == USIM_PHY_PRESENT) > + phy->set_config(usim->phy, slot, SC_PHY_CARD_MODE, val); > + usim->slot_ctx[slot].card_mode = card_mode; > + return 0; > +} > + > +static int usim_activate_synccard(struct usim *usim) > +{ > + int ret = 0; > + int reg = 0; These don't need to be initialized and can be 1 line. > + struct sc_phy *phy = usim->phy; > + if (usim->phy_present != USIM_PHY_PRESENT) { > + dev_err(usim->dev, "Sync card w/o phy is not supported"); > + return -EPERM; > + } > + > + /* Enable legacy bypass mode */ > + USIM_WRITEREG(usim->base, USIM_CONF1, CONFBYPASS, 1); > + > + /* set all lines to state L */ > + writel(0x0, usim->base + USIM_IO_DIRECT); > + > + /* configure h/w control mode for select slot */ > + reg = readl(usim->base + USIM_CONF6); > + reg = USIM_SETFIELD(reg, USIM_CONF6, SCLK0_BYPASS, 1); > + reg = USIM_SETFIELD(reg, USIM_CONF6, ATR_TIMER_BYPASS, 1); > + /* lets put i/o line in SW ctrl */ > + reg = USIM_SETFIELD(reg, USIM_CONF6, IO_BYPASS, 0x3); > + writel(reg, usim->base + USIM_CONF6); > + > + /* activate the card */ > + ret = phy->activate_card(phy, usim->slot); > + return ret; > +} > + > +static int usim_activate_card(struct usim *usim) > +{ > + int ret = 0; > + int reg = 0; > + struct sc_phy *phy = usim->phy; > + int mode = usim->slot_ctx[usim->slot].card_mode; > + > + usim->atrdone = 0; > + usim->slot_ctx[usim->slot].atr_length = 0; > + > + /* set card mode */ > + usim_set_cardmode(usim, usim->slot, mode); > + > + if (usim->slot_ctx[usim->slot].card_mode != USIM_CARD_MODE_ASYNC) { > + /* synchronous card activation */ > + ret = usim_activate_synccard(usim); > + return ret; > + } > + > + if (usim->slot_ctx[usim->slot].emv) > + usim_init_emvusercard(usim); > + > + /* disable legacy bypass mode */ > + USIM_WRITEREG(usim->base, USIM_CONF1, CONFBYPASS, 0); > + > + /* configure h/w control mode for select slot */ > + reg = readl(usim->base + USIM_CONF6); > + reg = USIM_SETFIELD(reg, USIM_CONF6, SCLK0_BYPASS, 0); > + reg = USIM_SETFIELD(reg, USIM_CONF6, ATR_TIMER_BYPASS, 0); > + /* lets put i/o line in h/w ctrl */ > + reg = USIM_SETFIELD(reg, USIM_CONF6, IO_BYPASS, 0x2); > + writel(reg, usim->base + USIM_CONF6); > + > + 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; > + int cnt = 0; > + struct sc_phy *phy = usim->phy; > + > + /* clear atr buffer */ > + for (cnt = 0; cnt < USIM_MAX_ATRLENGTH; cnt++) > + usim->slot_ctx[usim->slot].atr[cnt] = 0x0; > + usim->slot_ctx[usim->slot].atr_length = 0x0; > + > + /* 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; > + usim->atrdone = 0; > + > + 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 int usim_get_cardpinlevel(struct usim *usim, int pin, int *level) > +{ > + struct sc_phy *phy = usim->phy; > + int param = 0; > + > + if (usim->slot_ctx[usim->slot].card_mode == USIM_CARD_MODE_ASYNC) { > + dev_err(usim->dev, "Operation not permitted for async mode"); > + return -EPERM; > + } > + if (pin == USIM_PARAM_CARD_PIN_IO) { > + /* For Rx, RNW:1, OEN:1 */ > + USIM_WRITEREG(usim->base, USIM_IO_DIRECT, RNW0, 1); > + USIM_WRITEREG(usim->base, USIM_IO_DIRECT, SIOEN0, 1); > + *level = USIM_READREG(usim->base, USIM_IO_DIRECT, SIORX0); > + return 0; > + } > + > + if (usim->phy_present == USIM_PHY_PRESENT) { > + switch (pin) { > + case USIM_PARAM_CARD_PIN_VCC: > + break; > + > + case USIM_PARAM_CARD_PIN_RST: > + param = SC_PHY_PIN_RST; > + break; > + > + case USIM_PARAM_CARD_PIN_CLK: > + param = SC_PHY_PIN_CLK; > + break; > + > + default: > + dev_err(usim->dev, "Invalid pin"); > + return -EINVAL; > + } > + *level = phy->get_config(phy, usim->slot, param); > + return 0; > + } else { > + switch (pin) { > + case USIM_PARAM_CARD_PIN_VCC: > + *level = USIM_READREG(usim->base, > + USIM_IO_DIRECT, SVCC); > + break; > + > + case USIM_PARAM_CARD_PIN_RST: > + *level = USIM_READREG(usim->base, > + USIM_IO_DIRECT, RST); > + break; > + > + case USIM_PARAM_CARD_PIN_CLK: > + *level = USIM_READREG(usim->base, > + USIM_IO_DIRECT, SCLK0); > + break; > + > + default: > + dev_err(usim->dev, "Invalid pin"); > + return -EINVAL; > + } > + } > + return 0; > +} > + > +static int usim_set_cardpinlevel(struct usim *usim, int pin, int level) > +{ > + int ret = 0; > + int param = 0; > + int value = level > 0 ? 1 : 0; > + struct sc_phy *phy = usim->phy; > + > + if (usim->slot_ctx[usim->slot].card_mode == USIM_CARD_MODE_ASYNC) { > + dev_err(usim->dev, "Operation not permitted for async mode"); > + return -EPERM; > + } > + if (pin == USIM_PARAM_CARD_PIN_IO) { > + /* For Tx: RNW=0; OEN=Tx */ > + /* Tx line will be followed by OEN line, so setting OEN bit*/ > + USIM_WRITEREG(usim->base, USIM_IO_DIRECT, RNW0, 0); > + USIM_WRITEREG(usim->base, USIM_IO_DIRECT, SIOEN0, value); > + return 0; > + } > + > + if (usim->phy_present == USIM_PHY_PRESENT) { > + switch (pin) { > + case USIM_PARAM_CARD_PIN_VCC: > + /* will be set by activation or deactivation */ > + break; > + > + case USIM_PARAM_CARD_PIN_RST: > + param = SC_PHY_PIN_RST; > + break; > + > + case USIM_PARAM_CARD_PIN_CLK: > + param = SC_PHY_PIN_CLK; > + break; > + > + default: > + dev_err(usim->dev, "Invalid pin"); > + return -EINVAL; > + } Make this switch a helper function. Same code exists above. > + ret = phy->set_config(phy, usim->slot, param, value); > + return ret; > + } else { > + switch (pin) { > + case USIM_PARAM_CARD_PIN_VCC: > + USIM_WRITEREG(usim->base, > + USIM_IO_DIRECT, SVCC, value); > + break; > + > + case USIM_PARAM_CARD_PIN_RST: > + USIM_WRITEREG(usim->base, > + USIM_IO_DIRECT, RST, value); > + break; > + > + case USIM_PARAM_CARD_PIN_CLK: > + USIM_WRITEREG(usim->base, > + USIM_IO_DIRECT, SCLK0, value); > + break; > + > + default: > + dev_err(usim->dev, "Invalid pin"); > + return -EINVAL; > + } Probably something similar can be done here. Perhaps you should have default phy ops which is built-in. Then you can always just call phy->set_config. > + } > + return 0; > +} > +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; > + > + case USIM_PARAM_SMARTCARD_MODE: > + ret = usim_set_cardmode(usim, usim->slot, param->value); > + break; > + > + case USIM_PARAM_CARD_PIN_VCC: > + case USIM_PARAM_CARD_PIN_RST: > + case USIM_PARAM_CARD_PIN_CLK: > + case USIM_PARAM_CARD_PIN_IO: > + ret = usim_set_cardpinlevel(usim, param->attr, param->value); > + break; > + > + default: > + ret = -EINVAL; > + break; > + } > + return ret; > +} > +static void usim_get_syncatr(struct usim *usim) > +{ > + int ret = 0; > + struct sc_phy *phy = usim->phy; > + > + /* Max ATR bytes for sync card is 4 */ > + usim->slot_ctx[usim->slot].atr_length = 0x4; > + if (usim->phy_present != USIM_PHY_PRESENT) { > + dev_err(usim->dev, "Sync card w/o phy is not supported"); > + return; > + } > + /* get sync ATR */ > + if (phy->get_syncatr == NULL) > + return; > + ret = phy->get_syncatr(phy, > + usim->slot, > + usim->slot_ctx[usim->slot].atr_length, > + usim->slot_ctx[usim->slot].atr); > + return; > +} > + > +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; > + > + case USIM_PARAM_SMARTCARD_MODE: > + param->value = usim->slot_ctx[usim->slot].card_mode; > + break; > + > + case USIM_PARAM_CARD_PIN_VCC: > + case USIM_PARAM_CARD_PIN_RST: > + case USIM_PARAM_CARD_PIN_CLK: > + case USIM_PARAM_CARD_PIN_IO: > + ret = usim_get_cardpinlevel(usim, param->attr, ¶m->value); > + 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; > + unsigned long atr_timeout = 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"); > + /* waiting for max ATR response timeout */ > + atr_timeout = usecs_to_jiffies(MAX_ATR_WAITTIME_US); > + dev_dbg(usim->dev, > + "GET_ATR:atr timeout, us:%d, jiffies:%ld", > + MAX_ATR_WAITTIME_US, atr_timeout); > + wait_event_timeout(atr_wait, (usim->atrdone == 1), atr_timeout); > + if (usim->slot_ctx[usim->slot].card_mode != > + USIM_CARD_MODE_ASYNC) { > + usim_get_syncatr(usim); > + } > + 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) > +{ devmem2 will not work for this purpose? > + 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)) { > + ret = PTR_ERR(usim->opt_fclk); > + dev_err(usim->dev, "unable to get fck\n"); > + return ret; > + } > + > + usim->opt_fclk32 = devm_clk_get(usim->dev, "opt_fck32"); > + if (IS_ERR(usim->opt_fclk32)) { > + ret = PTR_ERR(usim->opt_fclk32); > + dev_err(usim->dev, "unable to get dbclk\n"); > + return ret; > + } > + > + return 0; > +} > +static int usim_enable(struct usim *usim) > +{ > + int ret = 0; > + if (usim->enable == 1) > + return 0; > + > + /* enable the clk */ > + pm_runtime_get_sync(usim->dev); > + clk_enable(usim->opt_fclk32); > + clk_enable(usim->opt_fclk); > + > + /* usim init */ > + ret = usim_configure(usim); > + if (ret) > + return -EIO; > + > + usim->enable = 1; > + return 0; > +} > + > +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 */ > + clk_disable(usim->opt_fclk32); > + clk_disable(usim->opt_fclk); > + pm_runtime_put_sync_autosuspend(usim->dev); > + usim->enable = 0; > + return; > +} > + > +static int usim_open(struct inode *inode, struct file *file) > +{ > + int ret = 0; > + 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; > + ret = usim_enable(usim); > + if (ret) > + return -ENXIO; > + > + 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); */ > + struct usim *usim = dev_to_usim(dev); > + if (usim->atrdone == 1) { > + dev_dbg(usim->dev, "card is active state, aborting suspend"); > + return -EBUSY; > + } > + usim_disable(usim); > + return 0; > +} > + > +static int usim_resume(struct device *dev) > +{ > + /* struct usim *usim = dev_to_usim(dev); */ > + struct usim *usim = dev_to_usim(dev); > + usim_enable(usim); > + 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; > + > + dev_dbg(usim->dev, "%s:action:%ld", __func__, action); > + /* 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 > + */ Why would the phy be removed? A better mechanism than signals and notifiers is needed here. > + 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); > + > + if (action & SC_PHY_CARD_SYNC_ACT_COMPLETE) { > + usim->atrdone = 1; > + wake_up(&atr_wait); > + } > + 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; Remove unnecessary initialization. > + > + 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; ENODEV is probably more appropriate. > + goto usim_err_ret; > + } > + > + base = devm_ioremap_nocache(dev, res->start, resource_size(res)); devm_ioremap_resource > + 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; > + usim_set_cardmode(usim, cnt, USIM_CARD_MODE_ASYNC); > + } > + > + dev_set_drvdata(dev, usim); > + ret = usim_pm_init(usim); > + if (ret) > + goto usim_err_ret; > + > + /* 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); > + if (ret) > + goto usim_err_reg; > + 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 > + if (!IS_ERR(usim->usim_dbclk)) > + clk_put(usim->usim_dbclk); > + if (!IS_ERR(usim->clkdiv32k_ick)) > + clk_put(usim->clkdiv32k_ick); > + if (!IS_ERR(usim->usim0_fck)) > + clk_put(usim->usim0_fck); > + if (!IS_ERR(usim->dpll_core_m4_ck)) > + clk_put(usim->dpll_core_m4_ck); > + > + if (!IS_ERR(usim->opt_fclk)) > + devm_clk_put(usim->dev, usim->opt_fclk); > + if (!IS_ERR(usim->opt_fclk32)) > + devm_clk_put(usim->dev, usim->opt_fclk32); > + /* 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 <maulik@xxxxxx>"); > +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..e9794df > --- /dev/null > +++ b/include/linux/ti-usim.h Isn't this a uapi header? > @@ -0,0 +1,111 @@ > +/* > + * 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, > + USIM_PARAM_SMARTCARD_MODE, > + USIM_PARAM_CARD_PIN_VCC, > + USIM_PARAM_CARD_PIN_RST, > + USIM_PARAM_CARD_PIN_CLK, > + USIM_PARAM_CARD_PIN_IO, > + USIM_PARAM_CARD_PIN_C4, > + USIM_PARAM_CARD_PIN_C8, > +}; > + > +enum usim_card_mode { > + USIM_CARD_MODE_ASYNC = 0, /* asynchronous mode */ > + USIM_CARD_MODE_SYNC_TYPE1, /* synchronous mode: Type 1 */ > + USIM_CARD_MODE_SYNC_TYPE2, /* synchronous mode: Type 2 */ > + USIM_CARD_MODE_SYNC_OTHER, /* Any other synchronous type */ > +}; > +struct usim_data { > + int slot; > + int rxexplen; > + int txlen; > + unsigned char apdu[256]; > +}; > + > +struct usim_config { > + enum usim_param attr; Probably not a good idea to have an undefined size element in a user ABI struct... > + 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.9.5 > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html