[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAL_JsqKs=a8GRJRmNE-oxU4imHqnb5szv+Km-wv3KuN0d6Bxkg@mail.gmail.com>
Date: Wed, 28 May 2014 12:56:12 -0500
From: Rob Herring <robherring2@...il.com>
To: Satish Patel <satish.patel@...com>
Cc: "linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
"linux-arm-kernel@...ts.infradead.org"
<linux-arm-kernel@...ts.infradead.org>,
linux-omap <linux-omap@...r.kernel.org>,
"devicetree@...r.kernel.org" <devicetree@...r.kernel.org>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
Rob Landley <rob@...dley.net>,
Tony Lindgren <tony@...mide.com>,
Grant Likely <grant.likely@...aro.org>,
Rob Herring <robh+dt@...nel.org>
Subject: Re: [PATCH v3 3/5] char: ti-usim: Add driver for USIM module on AM43xx
On Wed, May 28, 2014 at 3:57 AM, Satish Patel <satish.patel@...com> 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@...com>
> ---
> .../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@...34000 {
> + 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@...com>");
> +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 linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists