* [PATCHv10 2/2] dma: Add Freescale eDMA engine driver support
From: Vinod Koul @ 2014-01-20 7:40 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389938684-29467-3-git-send-email-b35083@freescale.com>
On Fri, Jan 17, 2014 at 02:04:44PM +0800, Jingchang Lu wrote:
> Add Freescale enhanced direct memory(eDMA) controller support.
> This module can be found on Vybrid and LS-1 SoCs.
>
> Signed-off-by: Alison Wang <b18965@freescale.com>
> Signed-off-by: Jingchang Lu <b35083@freescale.com>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> ---
> +struct fsl_edma_sw_tcd {
> + dma_addr_t ptcd;
> + struct fsl_edma_hw_tcd *vtcd;
> +};
> +
> +struct fsl_edma_slave_config {
> + enum dma_transfer_direction dir;
> + enum dma_slave_buswidth addr_width;
> + u32 dev_addr;
u32 for device address doesnt look right, we should be using dma_addr_t?
> + u32 burst;
> + u32 attr;
Looking at this, all fields expect attr are in dma_slave_config! So why do we
need these here and what does the attr mean?
> +static int fsl_edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> + unsigned long arg)
> +{
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + struct dma_slave_config *cfg = (void *)arg;
> + unsigned long flags;
> + LIST_HEAD(head);
> +
> + switch (cmd) {
> + case DMA_TERMINATE_ALL:
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + fsl_edma_disable_request(fsl_chan);
> + fsl_chan->edesc = NULL;
> + vchan_get_all_descriptors(&fsl_chan->vchan, &head);
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> + vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
> + return 0;
well what happens to the current ongoing transactions, i don't see those getting
terminated?
> +
> + case DMA_SLAVE_CONFIG:
> + fsl_chan->fsc.dir = cfg->direction;
> + if (cfg->direction == DMA_DEV_TO_MEM) {
> + fsl_chan->fsc.dev_addr = cfg->src_addr;
> + fsl_chan->fsc.addr_width = cfg->src_addr_width;
> + fsl_chan->fsc.burst = cfg->src_maxburst;
> + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width);
> + } else if (cfg->direction == DMA_MEM_TO_DEV) {
> + fsl_chan->fsc.dev_addr = cfg->dst_addr;
> + fsl_chan->fsc.addr_width = cfg->dst_addr_width;
> + fsl_chan->fsc.burst = cfg->dst_maxburst;
> + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width);
okay atrr is address width, why not save this standard struct instead?
> + } else {
> + return -EINVAL;
> + }
> + return 0;
> +
> + case DMA_PAUSE:
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + if (fsl_chan->edesc) {
> + fsl_edma_disable_request(fsl_chan);
> + fsl_chan->status = DMA_PAUSED;
> + }
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> + return 0;
> +
> + case DMA_RESUME:
> + spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
> + if (fsl_chan->edesc) {
> + fsl_edma_enable_request(fsl_chan);
> + fsl_chan->status = DMA_IN_PROGRESS;
> + }
> + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
> + return 0;
> +
> + default:
> + return -ENXIO;
> + }
> +}
> +
> +static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan,
> + int sg_len)
> +{
> + struct fsl_edma_desc *fsl_desc;
> + int i;
> +
> + fsl_desc = kzalloc(sizeof(*fsl_desc) + sizeof(struct fsl_edma_sw_tcd) * sg_len,
> + GFP_NOWAIT);
> + if (!fsl_desc)
> + return NULL;
> +
> + fsl_desc->echan = fsl_chan;
> + fsl_desc->n_tcds = sg_len;
> + for (i = 0; i < sg_len; i++) {
> + fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool,
> + GFP_NOWAIT, &fsl_desc->tcd[i].ptcd);
> + if (!fsl_desc->tcd[i].vtcd)
> + goto err;
> + }
> + return fsl_desc;
> +
> +err:
> + while (--i >= 0)
> + dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd,
> + fsl_desc->tcd[i].ptcd);
> + kfree(fsl_desc);
> + return NULL;
> +}
> +
> +static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic(
> + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len,
> + size_t period_len, enum dma_transfer_direction direction,
> + unsigned long flags, void *context)
> +{
you may want to implement the capablities api subsequently for audio usage.
> + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
> + struct fsl_edma_desc *fsl_desc;
> + dma_addr_t dma_buf_next;
> + int sg_len, i;
> + u32 src_addr, dst_addr, last_sg, nbytes;
> + u16 soff, doff, iter;
> +
> + if (!is_slave_direction(fsl_chan->fsc.dir))
> + return NULL;
> +
> + sg_len = buf_len / period_len;
> + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len);
> + if (!fsl_desc)
> + return NULL;
> + fsl_desc->iscyclic = true;
> +
> + dma_buf_next = dma_addr;
> + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst;
> + iter = period_len / nbytes;
empty line here pls
> + for (i = 0; i < sg_len; i++) {
> + if (dma_buf_next >= dma_addr + buf_len)
> + dma_buf_next = dma_addr;
> +
> + /* get next sg's physical address */
> + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd;
> +
> + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) {
> + src_addr = dma_buf_next;
> + dst_addr = fsl_chan->fsc.dev_addr;
> + soff = fsl_chan->fsc.addr_width;
> + doff = 0;
> + } else {
> + src_addr = fsl_chan->fsc.dev_addr;
> + dst_addr = dma_buf_next;
> + soff = 0;
> + doff = fsl_chan->fsc.addr_width;
> + }
> +
> + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr,
> + dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0,
> + iter, iter, doff, last_sg, true, false, true);
> + dma_buf_next += period_len;
> + }
> +
> + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags);
> +}
--
~Vinod
^ permalink raw reply
* [PATCH v2 4/7] ARM: dts: Exynos: add cpu nodes, opp and cpu clock frequency table
From: Lukasz Majewski @ 2014-01-20 7:32 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390047057-2239-5-git-send-email-thomas.ab@samsung.com>
Hi Thomas,
> From: Thomas Abraham <thomas.ab@samsung.com>
>
> For all Exynos based platforms, add CPU nodes, operating points and
> cpu clock frequency table for migrating from Exynos specific cpufreq
> driver to using generic cpufreq-cpu0 driver.
>
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> ---
> arch/arm/boot/dts/exynos4210-origen.dts | 6 +++
> arch/arm/boot/dts/exynos4210-trats.dts | 6 +++
> arch/arm/boot/dts/exynos4210-universal_c210.dts | 6 +++
> arch/arm/boot/dts/exynos4210.dtsi | 35
> ++++++++++++++++++ arch/arm/boot/dts/exynos4212.dtsi
> | 17 +++++++++ arch/arm/boot/dts/exynos4412-odroidx.dts |
> 6 +++ arch/arm/boot/dts/exynos4412-origen.dts | 6 +++
> arch/arm/boot/dts/exynos4412-trats2.dts | 6 +++
> arch/arm/boot/dts/exynos4412.dtsi | 30
> ++++++++++++++++ arch/arm/boot/dts/exynos4x12.dtsi |
> 35 ++++++++++++++++++ arch/arm/boot/dts/exynos5250-arndale.dts
> | 6 +++ arch/arm/boot/dts/exynos5250-cros-common.dtsi | 6 +++
> arch/arm/boot/dts/exynos5250-smdk5250.dts | 6 +++
> arch/arm/boot/dts/exynos5250.dtsi | 43
> ++++++++++++++++++++++- 14 files changed, 213 insertions(+), 1
> deletions(-)
>
> diff --git a/arch/arm/boot/dts/exynos4210-origen.dts
> b/arch/arm/boot/dts/exynos4210-origen.dts index 2aa13cb..dd17e93
> 100644 --- a/arch/arm/boot/dts/exynos4210-origen.dts
> +++ b/arch/arm/boot/dts/exynos4210-origen.dts
> @@ -32,6 +32,12 @@
> bootargs ="root=/dev/ram0 rw ramdisk=8192
> initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc"; };
>
> + cpus {
> + cpu at 0 {
> + cpu0-supply = <&buck1_reg>;
> + };
> + };
> +
> regulators {
> compatible = "simple-bus";
> #address-cells = <1>;
> diff --git a/arch/arm/boot/dts/exynos4210-trats.dts
> b/arch/arm/boot/dts/exynos4210-trats.dts index 63cc571..25487d7 100644
> --- a/arch/arm/boot/dts/exynos4210-trats.dts
> +++ b/arch/arm/boot/dts/exynos4210-trats.dts
> @@ -30,6 +30,12 @@
> bootargs = "console=ttySAC2,115200N8
> root=/dev/mmcblk0p5 rootwait earlyprintk panic=5"; };
>
> + cpus {
> + cpu: cpu at 0 {
> + cpu0-supply = <&varm_breg>;
> + };
> + };
> +
> regulators {
> compatible = "simple-bus";
>
> diff --git a/arch/arm/boot/dts/exynos4210-universal_c210.dts
> b/arch/arm/boot/dts/exynos4210-universal_c210.dts index
> d2e3f5f..74d5a70 100644 ---
> a/arch/arm/boot/dts/exynos4210-universal_c210.dts +++
> b/arch/arm/boot/dts/exynos4210-universal_c210.dts @@ -28,6 +28,12 @@
> bootargs = "console=ttySAC2,115200N8
> root=/dev/mmcblk0p5 rw rootwait earlyprintk panic=5 maxcpus=1"; };
>
> + cpus {
> + cpu: cpu at 0 {
> + cpu0-supply = <&vdd_arm_reg>;
> + };
> + };
> +
> mct at 10050000 {
> compatible = "none";
> };
> diff --git a/arch/arm/boot/dts/exynos4210.dtsi
> b/arch/arm/boot/dts/exynos4210.dtsi index 48ecd7a..40cd663 100644
> --- a/arch/arm/boot/dts/exynos4210.dtsi
> +++ b/arch/arm/boot/dts/exynos4210.dtsi
> @@ -36,6 +36,34 @@
> reg = <0x10023CA0 0x20>;
> };
>
> + cpus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + cpu at 0 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a9";
> + reg = <0>;
> + clocks = <&clock 12>;
> + clock-names = "cpu";
> +
> + operating-points = <
> + 200000 950000
> + 400000 975000
> + 500000 975000
> + 800000 1075000
> + 1000000 1150000
> + 1200000 1250000
Please be consistent with "operating-points" definition. Here you use
increasing order, when below you use decreasing one.
> + >;
> + safe-opp = <800000 1075000>;
> + };
> +
> + cpu at 1 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a9";
> + reg = <1>;
> + };
> + };
> +
> gic: interrupt-controller at 10490000 {
> cpu-offset = <0x8000>;
> };
> @@ -73,6 +101,13 @@
> compatible = "samsung,exynos4210-clock";
> reg = <0x10030000 0x20000>;
> #clock-cells = <1>;
> +
> + arm-frequency-table = <1200000 1200000 0 3 7 3 4 1 7
> 0 5 0 0>,
> + <1000000 1000000 0 3 7 3 4 1 7
> 0 4 0 0>,
> + < 800000 800000 0 3 7 3 3 1 7
> 0 3 0 0>,
> + < 500000 500000 0 3 7 3 3 1 7
> 0 3 0 0>,
> + < 400000 400000 0 3 7 3 3 1 7
> 0 3 0 0>,
> + < 200000 200000 0 1 3 1 1 1 0
> 0 3 0 0>; };
>
> pmu {
> diff --git a/arch/arm/boot/dts/exynos4212.dtsi
> b/arch/arm/boot/dts/exynos4212.dtsi index 94a43f9..2ea0f83 100644
> --- a/arch/arm/boot/dts/exynos4212.dtsi
> +++ b/arch/arm/boot/dts/exynos4212.dtsi
> @@ -22,6 +22,23 @@
> / {
> compatible = "samsung,exynos4212";
>
> + clock: clock-controller at 10030000 {
> + arm-frequency-table = <1500000 1500000 0 3 7 0 6 1 2
> 0 6 2 0>,
> + <1400000 1400000 0 3 7 0 6 1 2
> 0 6 2 0>,
> + <1300000 1300000 0 3 7 0 5 1 2
> 0 5 2 0>,
> + <1200000 1200000 0 3 7 0 5 1 2
> 0 5 2 0>,
> + <1100000 1100000 0 3 6 0 4 1 2
> 0 4 2 0>,
> + <1000000 1000000 0 2 5 0 4 1 1
> 0 4 2 0>,
> + < 900000 900000 0 2 5 0 3 1 1
> 0 3 2 0>,
> + < 800000 800000 0 2 5 0 3 1 1
> 0 3 2 0>,
> + < 700000 700000 0 2 4 0 3 1 1
> 0 3 2 0>,
> + < 600000 600000 0 2 4 0 3 1 1
> 0 3 2 0>,
> + < 500000 500000 0 2 4 0 3 1 1
> 0 3 2 0>,
> + < 400000 400000 0 2 4 0 3 1 1
> 0 3 2 0>,
> + < 300000 300000 0 2 4 0 2 1 1
> 0 3 2 0>,
> + < 200000 200000 0 1 3 0 1 1 1
> 0 3 2 0>;
> + };
> +
> gic: interrupt-controller at 10490000 {
> cpu-offset = <0x8000>;
> };
> diff --git a/arch/arm/boot/dts/exynos4412-odroidx.dts
> b/arch/arm/boot/dts/exynos4412-odroidx.dts index 12459b0..1c751f9
> 100644 --- a/arch/arm/boot/dts/exynos4412-odroidx.dts
> +++ b/arch/arm/boot/dts/exynos4412-odroidx.dts
> @@ -22,6 +22,12 @@
> reg = <0x40000000 0x40000000>;
> };
>
> + cpus {
> + cpu at 0 {
> + cpu0-supply = <&buck2_reg>;
> + };
> + };
> +
> leds {
> compatible = "gpio-leds";
> led1 {
> diff --git a/arch/arm/boot/dts/exynos4412-origen.dts
> b/arch/arm/boot/dts/exynos4412-origen.dts index 388f035..36080e5
> 100644 --- a/arch/arm/boot/dts/exynos4412-origen.dts
> +++ b/arch/arm/boot/dts/exynos4412-origen.dts
> @@ -27,6 +27,12 @@
> bootargs ="console=ttySAC2,115200";
> };
>
> + cpus {
> + cpu at 0 {
> + cpu0-supply = <&buck2_reg>;
> + };
> + };
> +
> firmware at 0203F000 {
> compatible = "samsung,secure-firmware";
> reg = <0x0203F000 0x1000>;
> diff --git a/arch/arm/boot/dts/exynos4412-trats2.dts
> b/arch/arm/boot/dts/exynos4412-trats2.dts index 4f851cc..4a4d446
> 100644 --- a/arch/arm/boot/dts/exynos4412-trats2.dts
> +++ b/arch/arm/boot/dts/exynos4412-trats2.dts
> @@ -31,6 +31,12 @@
> bootargs = "console=ttySAC2,115200N8
> root=/dev/mmcblk0p5 rootwait earlyprintk panic=5"; };
>
> + cpus {
> + cpu at 0 {
> + cpu0-supply = <&buck2_reg>;
> + };
> + };
> +
> firmware at 0204F000 {
> compatible = "samsung,secure-firmware";
> reg = <0x0204F000 0x1000>;
> diff --git a/arch/arm/boot/dts/exynos4412.dtsi
> b/arch/arm/boot/dts/exynos4412.dtsi index 87b339c..7e9eca7 100644
> --- a/arch/arm/boot/dts/exynos4412.dtsi
> +++ b/arch/arm/boot/dts/exynos4412.dtsi
> @@ -22,6 +22,36 @@
> / {
> compatible = "samsung,exynos4412";
>
> + cpus {
> + cpu at 2 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a9";
> + reg = <2>;
> + };
> + cpu at 3 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a9";
> + reg = <3>;
> + };
> + };
> +
> + clock: clock-controller at 10030000 {
> + arm-frequency-table = <1500000 1500000 0 3 7 0 6 1 2
> 0 6 0 7>,
> + <1400000 1400000 0 3 7 0 6 1 2
> 0 6 0 6>,
> + <1300000 1300000 0 3 7 0 5 1 2
> 0 5 0 6>,
> + <1200000 1200000 0 3 7 0 5 1 2
> 0 5 0 5>,
> + <1100000 1100000 0 3 6 0 4 1 2
> 0 4 0 5>,
> + <1000000 1000000 0 2 5 0 4 1 1
> 0 4 0 4>,
> + < 900000 900000 0 2 5 0 3 1 1
> 0 3 0 4>,
> + < 800000 800000 0 2 5 0 3 1 1
> 0 3 0 3>,
> + < 700000 700000 0 2 4 0 3 1 1
> 0 3 0 3>,
> + < 600000 600000 0 2 4 0 3 1 1
> 0 3 0 2>,
> + < 500000 500000 0 2 4 0 3 1 1
> 0 3 0 2>,
> + < 400000 400000 0 2 4 0 3 1 1
> 0 3 0 1>,
> + < 300000 300000 0 2 4 0 2 1 1
> 0 3 0 1>,
> + < 200000 200000 0 1 3 0 1 1 1
> 0 3 0 0>;
> + };
> +
> gic: interrupt-controller at 10490000 {
> cpu-offset = <0x4000>;
> };
> diff --git a/arch/arm/boot/dts/exynos4x12.dtsi
> b/arch/arm/boot/dts/exynos4x12.dtsi index 5c412aa..47e2195 100644
> --- a/arch/arm/boot/dts/exynos4x12.dtsi
> +++ b/arch/arm/boot/dts/exynos4x12.dtsi
> @@ -31,6 +31,41 @@
> mshc0 = &mshc_0;
> };
>
> + cpus {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + cpu at 0 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a9";
> + reg = <0>;
> + clocks = <&clock 12>;
> + clock-names = "cpu";
> +
> + operating-points = <
> + 1400000 1350000
> + 1300000 1287500
> + 1200000 1250000
> + 1100000 1187500
> + 1000000 1137500
> + 900000 1087500
> + 800000 1037500
> + 700000 1000000
> + 600000 987500
> + 500000 950000
> + 400000 925000
> + 300000 900000
> + 200000 900000
> + >;
> + clock-latency = <200000>;
> + safe-opp = <800000 1037500>;
> + };
> + cpu at 1 {
> + device_type = "cpu";
> + compatible = "arm,cortex-a9";
> + reg = <1>;
> + };
> + };
> +
> pd_isp: isp-power-domain at 10023CA0 {
> compatible = "samsung,exynos4210-pd";
> reg = <0x10023CA0 0x20>;
> diff --git a/arch/arm/boot/dts/exynos5250-arndale.dts
> b/arch/arm/boot/dts/exynos5250-arndale.dts index b42e658..4716eef
> 100644 --- a/arch/arm/boot/dts/exynos5250-arndale.dts
> +++ b/arch/arm/boot/dts/exynos5250-arndale.dts
> @@ -25,6 +25,12 @@
> bootargs = "console=ttySAC2,115200";
> };
>
> + cpus {
> + cpu at 0 {
> + cpu0-supply = <&buck2_reg>;
> + };
> + };
> +
> codec at 11000000 {
> samsung,mfc-r = <0x43000000 0x800000>;
> samsung,mfc-l = <0x51000000 0x800000>;
> diff --git a/arch/arm/boot/dts/exynos5250-cros-common.dtsi
> b/arch/arm/boot/dts/exynos5250-cros-common.dtsi index
> 2c1560d..4bde756 100644 ---
> a/arch/arm/boot/dts/exynos5250-cros-common.dtsi +++
> b/arch/arm/boot/dts/exynos5250-cros-common.dtsi @@ -19,6 +19,12 @@
> chosen {
> };
>
> + cpus {
> + cpu at 0 {
> + cpu0-supply = <&buck2_reg>;
> + };
> + };
> +
> pinctrl at 11400000 {
> /*
> * Disabled pullups since external part has its own
> pullups and diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts
> b/arch/arm/boot/dts/exynos5250-smdk5250.dts index 5c1b7d9..7c228e2
> 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
> +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
> @@ -27,6 +27,12 @@
> bootargs = "root=/dev/ram0 rw ramdisk=8192
> initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc"; };
>
> + cpus {
> + cpu at 0 {
> + cpu0-supply = <&buck2_reg>;
> + };
> + };
> +
> i2c at 12C60000 {
> samsung,i2c-sda-delay = <100>;
> samsung,i2c-max-bus-freq = <20000>;
> diff --git a/arch/arm/boot/dts/exynos5250.dtsi
> b/arch/arm/boot/dts/exynos5250.dtsi index b7dec41..d2f98dc 100644
> --- a/arch/arm/boot/dts/exynos5250.dtsi
> +++ b/arch/arm/boot/dts/exynos5250.dtsi
> @@ -61,6 +61,30 @@
> compatible = "arm,cortex-a15";
> reg = <0>;
> clock-frequency = <1700000000>;
> +
> + clocks = <&clock 12>;
> + clock-names = "cpu";
> +
> + operating-points = <
> + 1700000 1300000
> + 1600000 1250000
> + 1500000 1225000
> + 1400000 1200000
> + 1300000 1150000
> + 1200000 1125000
> + 1100000 1100000
> + 1000000 1075000
> + 900000 1050000
> + 800000 1025000
> + 700000 1012500
> + 600000 1000000
> + 500000 975000
> + 400000 950000
> + 300000 937500
> + 200000 925000
> + >;
> + clock-latency = <200000>;
> + safe-opp = <800000 1025000>;
> };
> cpu at 1 {
> device_type = "cpu";
> @@ -84,7 +108,24 @@
> compatible = "samsung,exynos5250-clock";
> reg = <0x10010000 0x30000>;
> #clock-cells = <1>;
> - };
> +
> + arm-frequency-table = <1700000 1700000 0 3 7 7 7 3 5
> 0 0 2 0>,
> + <1600000 1600000 0 3 7 7 7 1 4
> 0 0 2 0>,
> + <1500000 1500000 0 2 7 7 7 1 4
> 0 0 2 0>,
> + <1400000 1400000 0 2 7 7 6 1 4
> 0 0 2 0>,
> + <1300000 1300000 0 2 7 7 6 1 3
> 0 0 2 0>,
> + <1200000 1200000 0 2 7 7 5 1 3
> 0 0 2 0>,
> + <1100000 1100000 0 3 7 7 5 1 3
> 0 0 2 0>,
> + <1000000 1000000 0 1 7 7 4 1 2
> 0 0 2 0>,
> + < 900000 900000 0 1 7 7 4 1 2
> 0 0 2 0>,
> + < 800000 800000 0 1 7 7 4 1 2
> 0 0 2 0>,
> + < 700000 700000 0 1 7 7 3 1 1
> 0 0 2 0>,
> + < 600000 600000 0 1 7 7 3 1 1
> 0 0 2 0>,
> + < 500000 500000 0 1 7 7 2 1 1
> 0 0 2 0>,
> + < 400000 400000 0 1 7 7 2 1 1
> 0 0 2 0>,
> + < 300000 300000 0 1 7 7 1 1 1
> 0 0 2 0>,
> + < 200000 200000 0 1 7 7 1 1 1
> 0 0 2 0>;
> + };
>
> clock_audss: audss-clock-controller at 3810000 {
> compatible = "samsung,exynos5250-audss-clock";
--
Best regards,
Lukasz Majewski
Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
^ permalink raw reply
* [PATCH] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Srikanth Thokala @ 2014-01-20 7:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52D94006.2090104@balister.org>
Hi Philip,
On Fri, Jan 17, 2014 at 8:06 PM, Philip Balister <philip@balister.org> wrote:
> On 01/16/2014 12:53 PM, Srikanth Thokala wrote:
>>
>> This is the driver for the AXI Video Direct Memory Access (AXI
>> VDMA) core, which is a soft Xilinx IP core that provides high-
>> bandwidth direct memory access between memory and AXI4-Stream
>> type video target peripherals. The core provides efficient two
>> dimensional DMA operations with independent asynchronous read
>> and write channel operation.
>>
>
> [snip]
>
>
>> +/**
>> + * xilinx_vdma_start - Start VDMA channel
>> + * @chan: Driver specific VDMA channel
>> + */
>> +static void xilinx_vdma_start(struct xilinx_vdma_chan *chan)
>> +{
>> + int loop = XILINX_VDMA_LOOP_COUNT + 1;
>> +
>> + vdma_ctrl_set(chan, XILINX_VDMA_REG_DMACR,
>> XILINX_VDMA_DMACR_RUNSTOP);
>> +
>> + /* Wait for the hardware to start */
>> + while (loop)
>
>
>
> loop-- ?
I will fix it in v2.
Thanks
Srikanth
^ permalink raw reply
* [PATCH] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Srikanth Thokala @ 2014-01-20 7:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52D82480.5030901@linux.com>
Hi Levente,
On Thu, Jan 16, 2014 at 11:57 PM, Levente Kurusa <levex@linux.com> wrote:
> Hello,
>
> On 01/16/2014 06:53 PM, Srikanth Thokala wrote:
>> This is the driver for the AXI Video Direct Memory Access (AXI
>> VDMA) core, which is a soft Xilinx IP core that provides high-
>> bandwidth direct memory access between memory and AXI4-Stream
>> type video target peripherals. The core provides efficient two
>> dimensional DMA operations with independent asynchronous read
>> and write channel operation.
>>
>> This module works on Zynq (ARM Based SoC) and Microblaze platforms.
>>
>> Signed-off-by: Srikanth Thokala <sthokal@xilinx.com>
>> ---
>
> Just a few suggestions and fixes.
>
>> NOTE:
>> 1. Created a separate directory 'dma/xilinx' as Xilinx has two more
>> DMA IPs and we are also planning to upstream these drivers soon.
>> 2. Rebased on v3.13.0-rc8
>> ---
>> .../devicetree/bindings/dma/xilinx/xilinx_vdma.txt | 71 +
>> .../bindings/dma/xilinx/xilinx_vdma_test.txt | 39 +
>> drivers/dma/Kconfig | 23 +
>> drivers/dma/Makefile | 1 +
>> drivers/dma/xilinx/Makefile | 2 +
>> drivers/dma/xilinx/xilinx_vdma.c | 1497 ++++++++++++++++++++
>> drivers/dma/xilinx/xilinx_vdma_test.c | 629 ++++++++
>> 7 files changed, 2262 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
>> create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
>> create mode 100644 drivers/dma/xilinx/Makefile
>> create mode 100644 drivers/dma/xilinx/xilinx_vdma.c
>> create mode 100644 drivers/dma/xilinx/xilinx_vdma_test.c
>>
>> diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
>> new file mode 100644
>> index 0000000..3f5c428
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
>> @@ -0,0 +1,71 @@
>> +Xilinx AXI VDMA engine, it does transfers between memory and video devices.
>> +It can be configured to have one channel or two channels. If configured
>> +as two channels, one is to transmit to the video device and another is
>> +to receive from the video device.
>> +
>> +Required properties:
>> +- compatible: Should be "xlnx,axi-vdma-1.00.a"
>> +- #dma-cells: Should be <1>, see "dmas" property below
>> +- reg: Should contain VDMA registers location and length.
>> +- interrupts: Should contain per channel VDMA interrupts.
>> +- compatible (child node): It should be either "xlnx,axi-vdma-mm2s-channel" or
>> + "xlnx,axi-vdma-s2mm-channel". It depends on the hardware design and it
>> + can also have both channels.
>> +- xlnx,device-id: Should contain device number in each channel. It should be
>> + {0,1,2...so on} to the number of VDMA devices configured in hardware.
>> +- xlnx,num-fstores: Should be the number of framebuffers as configured in h/w.
>> +- xlnx,data-width: Should contain the stream data width, takes {32,64...so on}.
>> +- xlnx,flush-fsync: (Optional) Tells whether which channel to Flush on Fsync.
>> + It takes following values:
>> + {1}, flush both channels
>> + {2}, flush mm2s channel
>> + {3}, flush s2mm channel
>> +- xlnx,include-sg: (Optional) Tells whether configured for Scatter-mode in
>> + the hardware.
>> +- xlnx,include-dre: (Optional) Tells whether hardware is configured for Data
>> + Realignment Engine.
>> +- xlnx,genlock-mode: (Optional) Tells whether Genlock synchornisation is
>
> s/synchornisation/synchronization
Sure, will take care.
>
>> + enabled/disabled in hardware.
>> +
>> +Example:
>> +++++++++
>> +
>> +axi_vdma_0: axivdma at 40030000 {
>> + compatible = "xlnx,axi-vdma-1.00.a";
>> + #dma_cells = <1>;
>> + reg = < 0x40030000 0x10000 >;
>> + xlnx,flush-fsync = <0x1>;
>> + dma-channel at 40030000 {
>> + compatible = "xlnx,axi-vdma-mm2s-channel";
>> + interrupts = < 0 54 4 >;
>> + xlnx,num-fstores = <0x8>;
>> + xlnx,device-id = <0x0>;
>> + xlnx,datawidth = <0x40>;
>> + } ;
>> + dma-channel at 40030030 {
>> + compatible = "xlnx,axi-vdma-s2mm-channel";
>> + interrupts = < 0 53 4 >;
>> + xlnx,num-fstores = <0x8>;
>> + xlnx,device-id = <0x0>;
>> + xlnx,datawidth = <0x40>;
>> + } ;
>> +} ;
>> +
>> +
>> +* Xilinx Video DMA client
>> +
>> +Required properties:
>> +- dmas: a list of <[Video DMA device phandle] [Channel ID]> pairs,
>> + where Channel ID is '0' for write/tx and '1' for read/rx
>> + channel.
>> +- dma-names: a list of DMA channel names, one per "dmas" entry
>> +
>> +VDMA Test Client Example:
>> ++++++++++++++++++++++++++
>> +
>> +vdmatest_0: vdmatest at 0 {
>> + compatible ="xlnx,axi-vdma-test-1.00.a";
>> + dmas = <&axi_vdma_0 0
>> + &axi_vdma_0 1>;
>> + dma-names = "vdma0", "vdma1";
>> +} ;
>> diff --git a/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
>> new file mode 100644
>> index 0000000..5821fdc
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma_test.txt
>> @@ -0,0 +1,39 @@
>> +* Xilinx Video DMA Test client
>> +
>> +Required properties:
>> +- compatible: Should be "xlnx,axi-vdma-test-1.00.a"
>> +- dmas: a list of <[Video DMA device phandle] [Channel ID]> pairs,
>> + where Channel ID is '0' for write/tx and '1' for read/rx
>> + channel.
>> +- dma-names: a list of DMA channel names, one per "dmas" entry
>> +- xlnx,num-fstores: Should be the number of framebuffers as configured in
>> + VDMA device node.
>> +
>> +Example:
>> +++++++++
>> +
>> +vdmatest_0: vdmatest at 0 {
>> + compatible ="xlnx,axi-vdma-test-1.00.a";
>> + dmas = <&axi_vdma_0 0
>> + &axi_vdma_0 1>;
>> + dma-names = "vdma0", "vdma1";
>> + xlnx,num-fstores = <0x8>;
>> +} ;
>> +
>> +
>> +Xilinx Video DMA Device Node Example
>> +++++++++++++++++++++++++++++++++++++
>> +axi_vdma_0: axivdma at 44A40000 {
>> + compatible = "xlnx,axi-vdma-1.00.a";
>> + ...
>> + dma-channel at 44A40000 {
>> + ...
>> + xlnx,num-fstores = <0x8>;
>> + ...
>> + } ;
>> + dma-channel at 44A40030 {
>> + ...
>> + xlnx,num-fstores = <0x8>;
>> + ...
>> + } ;
>> +} ;
>> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
>> index c823daa..675719f 100644
>> --- a/drivers/dma/Kconfig
>> +++ b/drivers/dma/Kconfig
>> @@ -334,6 +334,20 @@ config K3_DMA
>> Support the DMA engine for Hisilicon K3 platform
>> devices.
>>
>> +config XILINX_VDMA
>> + tristate "Xilinx AXI VDMA Engine"
>> + depends on (ARCH_ZYNQ || MICROBLAZE)
>> + select DMA_ENGINE
>> + help
>> + Enable support for Xilinx AXI VDMA Soft IP.
>> +
>> + This engine provides high-bandwidth direct memory access
>> + between memory and AXI4-Stream video type target
>> + peripherals including peripherals which support AXI4-
>> + Stream Video Protocol. It has two stream interfaces/
>> + channels, Memory Mapped to Stream (MM2S) and Stream to
>> + Memory Mapped (S2MM) for the data transfers.
>> +
>> config DMA_ENGINE
>> bool
>>
>> @@ -384,4 +398,13 @@ config DMATEST
>> config DMA_ENGINE_RAID
>> bool
>>
>> +config XILINX_VDMA_TEST
>> + tristate "DMA Test client for Xilinx AXI VDMA"
>> + depends on XILINX_VDMA
>> + help
>> + Simple VDMA driver test client. This test assumes both
>> + the stream interfaces of VDMA engine, MM2S and S2MM are
>> + connected back-to-back in the hardware design.
>> +
>> + Say N unless you're debugging a DMA Device driver.
>> endif
>> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
>> index 0ce2da9..d84130b 100644
>> --- a/drivers/dma/Makefile
>> +++ b/drivers/dma/Makefile
>> @@ -42,3 +42,4 @@ obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
>> obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
>> obj-$(CONFIG_TI_CPPI41) += cppi41.o
>> obj-$(CONFIG_K3_DMA) += k3dma.o
>> +obj-y += xilinx/
>> diff --git a/drivers/dma/xilinx/Makefile b/drivers/dma/xilinx/Makefile
>> new file mode 100644
>> index 0000000..cef1e88
>> --- /dev/null
>> +++ b/drivers/dma/xilinx/Makefile
>> @@ -0,0 +1,2 @@
>> +obj-$(CONFIG_XILINX_VDMA) += xilinx_vdma.o
>> +obj-$(CONFIG_XILINX_VDMA_TEST) += xilinx_vdma_test.o
>> diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c
>> new file mode 100644
>> index 0000000..66a12de
>> --- /dev/null
>> +++ b/drivers/dma/xilinx/xilinx_vdma.c
>> @@ -0,0 +1,1497 @@
>> +/*
>> + * DMA driver for Xilinx Video DMA Engine
>> + *
>> + * Copyright (C) 2010-2013 Xilinx, Inc. All rights reserved.
>
> Happy new year! ;-)
Ok :-)
>
>
>> + *
>> + * Based on the Freescale DMA driver.
>> + *
>> + * Description:
>> + * The AXI Video Direct Memory Access (AXI VDMA) core is a soft Xilinx IP
>> + * core that provides high-bandwidth direct memory access between memory
>> + * and AXI4-Stream type video target peripherals. The core provides efficient
>> + * two dimensional DMA operations with independent asynchronous read (S2MM)
>> + * and write (MM2S) channel operation. It can be configured to have either
>> + * one channel or two channels. If configured as two channels, one is to
>> + * transmit to the video device (MM2S) and another is to receive from the
>> + * video device (S2MM). Initialization, status, interrupt and management
>> + * registers are accessed through an AXI4-Lite slave interface.
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation, either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/amba/xilinx_dma.h>
>> +#include <linux/bitops.h>
>> +#include <linux/dmapool.h>
>> +#include <linux/init.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_dma.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/slab.h>
>> +
>> +/* Register/Descriptor Offsets */
>> +#define XILINX_VDMA_MM2S_CTRL_OFFSET 0x0000
>> +#define XILINX_VDMA_S2MM_CTRL_OFFSET 0x0030
>> +#define XILINX_VDMA_MM2S_DESC_OFFSET 0x0050
>> +#define XILINX_VDMA_S2MM_DESC_OFFSET 0x00a0
>> +
>> +/* Control Registers */
>> +#define XILINX_VDMA_REG_DMACR 0x0000
>> +#define XILINX_VDMA_DMACR_DELAY_MAX 0xff
>> +#define XILINX_VDMA_DMACR_DELAY_SHIFT 24
>> +#define XILINX_VDMA_DMACR_FRAME_COUNT_MAX 0xff
>> +#define XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT 16
>> +#define XILINX_VDMA_DMACR_ERR_IRQ (1 << 14)
>> +#define XILINX_VDMA_DMACR_DLY_CNT_IRQ (1 << 13)
>> +#define XILINX_VDMA_DMACR_FRM_CNT_IRQ (1 << 12)
>> +#define XILINX_VDMA_DMACR_MASTER_SHIFT 8
>> +#define XILINX_VDMA_DMACR_FSYNCSRC_SHIFT 5
>> +#define XILINX_VDMA_DMACR_FRAMECNT_EN (1 << 4)
>> +#define XILINX_VDMA_DMACR_GENLOCK_EN (1 << 3)
>> +#define XILINX_VDMA_DMACR_RESET (1 << 2)
>> +#define XILINX_VDMA_DMACR_CIRC_EN (1 << 1)
>> +#define XILINX_VDMA_DMACR_RUNSTOP (1 << 0)
>
> You could use the BIT(n) field from <linux/bitops.h>
>
>> +#define XILINX_VDMA_DMACR_DELAY_MASK \
>> + (XILINX_VDMA_DMACR_DELAY_MAX << \
>> + XILINX_VDMA_DMACR_DELAY_SHIFT)
>> +#define XILINX_VDMA_DMACR_FRAME_COUNT_MASK \
>> + (XILINX_VDMA_DMACR_FRAME_COUNT_MAX << \
>> + XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT)
>> +#define XILINX_VDMA_DMACR_MASTER_MASK \
>> + (0xf << XILINX_VDMA_DMACR_MASTER_SHIFT)
>> +#define XILINX_VDMA_DMACR_FSYNCSRC_MASK \
>> + (3 << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT)
>> +
>> +#define XILINX_VDMA_REG_DMASR 0x0004
>> +#define XILINX_VDMA_DMASR_DELAY_SHIFT 24
>> +#define XILINX_VDMA_DMASR_FRAME_COUNT_SHIFT 16
>> +#define XILINX_VDMA_DMASR_EOL_LATE_ERR (1 << 15)
>> +#define XILINX_VDMA_DMASR_ERR_IRQ (1 << 14)
>> +#define XILINX_VDMA_DMASR_DLY_CNT_IRQ (1 << 13)
>> +#define XILINX_VDMA_DMASR_FRM_CNT_IRQ (1 << 12)
>> +#define XILINX_VDMA_DMASR_SOF_LATE_ERR (1 << 11)
>> +#define XILINX_VDMA_DMASR_SG_DEC_ERR (1 << 10)
>> +#define XILINX_VDMA_DMASR_SG_SLV_ERR (1 << 9)
>> +#define XILINX_VDMA_DMASR_EOF_EARLY_ERR (1 << 8)
>> +#define XILINX_VDMA_DMASR_SOF_EARLY_ERR (1 << 7)
>> +#define XILINX_VDMA_DMASR_DMA_DEC_ERR (1 << 6)
>> +#define XILINX_VDMA_DMASR_DMA_SLAVE_ERR (1 << 5)
>> +#define XILINX_VDMA_DMASR_DMA_INT_ERR (1 << 4)
>> +#define XILINX_VDMA_DMASR_IDLE (1 << 1)
>> +#define XILINX_VDMA_DMASR_HALTED (1 << 0)
>
> Here as well.
Sure.
>> +#define XILINX_VDMA_DMASR_DELAY_MASK \
>> + (0xff << XILINX_VDMA_DMASR_DELAY_SHIFT)
>> +#define XILINX_VDMA_DMASR_FRAME_COUNT_MASK \
>> + (0xff << XILINX_VDMA_DMASR_FRAME_COUNT_SHIFT)
>> +
>> +#define XILINX_VDMA_REG_CURDESC 0x0008
>> +#define XILINX_VDMA_REG_TAILDESC 0x0010
>> +#define XILINX_VDMA_REG_REG_INDEX 0x0014
>> +#define XILINX_VDMA_REG_FRMSTORE 0x0018
>> +#define XILINX_VDMA_REG_THRESHOLD 0x001c
>> +#define XILINX_VDMA_REG_FRMPTR_STS 0x0024
>> +#define XILINX_VDMA_REG_PARK_PTR 0x0028
>> +#define XILINX_VDMA_PARK_PTR_WR_REF_SHIFT 8
>> +#define XILINX_VDMA_PARK_PTR_RD_REF_SHIFT 0
>> +#define XILINX_VDMA_REG_VDMA_VERSION 0x002c
>> +
>> [...]
>> +
>> +/**
>> + * xilinx_vdma_slave_config - Configure VDMA channel
>> + * Run-time configuration for Axi VDMA, supports:
>> + * . halt the channel
>> + * . configure interrupt coalescing and inter-packet delay threshold
>> + * . start/stop parking
>> + * . enable genlock
>> + * . set transfer information using config struct
>> + *
>> + * @chan: Driver specific VDMA Channel pointer
>> + * @cfg: Channel configuration pointer
>> + *
>> + * Return: '0' on success and failure value on error
>> + */
>> +static int xilinx_vdma_slave_config(struct xilinx_vdma_chan *chan,
>> + struct xilinx_vdma_config *cfg)
>> +{
>> + u32 dmacr;
>> +
>> + if (cfg->reset)
>> + return xilinx_vdma_chan_reset(chan);
>> +
>> + dmacr = vdma_ctrl_read(chan, XILINX_VDMA_REG_DMACR);
>> +
>> + /* If vsize is -1, it is park-related operations */
>> + if (cfg->vsize == -1) {
>> + if (cfg->park)
>> + dmacr &= ~XILINX_VDMA_DMACR_CIRC_EN;
>> + else
>> + dmacr |= XILINX_VDMA_DMACR_CIRC_EN;
>> +
>> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
>> + return 0;
>> + }
>> +
>> + /* If hsize is -1, it is interrupt threshold settings */
>> + if (cfg->hsize == -1) {
>> + if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
>> + dmacr &= ~XILINX_VDMA_DMACR_FRAME_COUNT_MASK;
>> + dmacr |= cfg->coalesc <<
>> + XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
>> + chan->config.coalesc = cfg->coalesc;
>> + }
>> +
>> + if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
>> + dmacr &= ~XILINX_VDMA_DMACR_DELAY_MASK;
>> + dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
>> + chan->config.delay = cfg->delay;
>> + }
>> +
>> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
>> + return 0;
>> + }
>> +
>> + /* Transfer information */
>> + chan->config.vsize = cfg->vsize;
>> + chan->config.hsize = cfg->hsize;
>> + chan->config.stride = cfg->stride;
>> + chan->config.frm_dly = cfg->frm_dly;
>> + chan->config.park = cfg->park;
>
> Can't this be done with a memcpy? Not sure though.
>> +
>> + /* genlock settings */
>> + chan->config.gen_lock = cfg->gen_lock;
>> + chan->config.master = cfg->master;
>
> This as well maybe.
Some of the parameters have condition checks before assigning (please
see below),
so I felt this would be a better way.
>> +
>> + if (cfg->gen_lock && chan->genlock) {
>> + dmacr |= XILINX_VDMA_DMACR_GENLOCK_EN;
>> + dmacr |= cfg->master << XILINX_VDMA_DMACR_MASTER_SHIFT;
>> + }
>> +
>> + chan->config.frm_cnt_en = cfg->frm_cnt_en;
>> + if (cfg->park)
>> + chan->config.park_frm = cfg->park_frm;
>> + else
>> + chan->config.park_frm = -1;
>> +
>> + chan->config.coalesc = cfg->coalesc;
>> + chan->config.delay = cfg->delay;
>> + if (cfg->coalesc <= XILINX_VDMA_DMACR_FRAME_COUNT_MAX) {
>> + dmacr |= cfg->coalesc << XILINX_VDMA_DMACR_FRAME_COUNT_SHIFT;
>> + chan->config.coalesc = cfg->coalesc;
>> + }
>> +
>> + if (cfg->delay <= XILINX_VDMA_DMACR_DELAY_MAX) {
>> + dmacr |= cfg->delay << XILINX_VDMA_DMACR_DELAY_SHIFT;
>> + chan->config.delay = cfg->delay;
>> + }
>> +
>> + /* FSync Source selection */
>> + dmacr &= ~XILINX_VDMA_DMACR_FSYNCSRC_MASK;
>> + dmacr |= cfg->ext_fsync << XILINX_VDMA_DMACR_FSYNCSRC_SHIFT;
>> +
>> + vdma_ctrl_write(chan, XILINX_VDMA_REG_DMACR, dmacr);
>> + return 0;
>> +}
>> +
>> [...]
>> +
>> +/**
>> + * xilinx_vdma_probe - Driver probe function
>> + * @pdev: Pointer to the platform_device structure
>> + *
>> + * Return: '0' on success and failure value on error
>> + */
>> +static int xilinx_vdma_probe(struct platform_device *pdev)
>> +{
>> + struct device_node *node = pdev->dev.of_node;
>> + struct xilinx_vdma_device *xdev;
>> + struct device_node *child;
>> + struct resource *io;
>> + u32 num_frames;
>> + int i, err;
>> +
>> + dev_info(&pdev->dev, "Probing xilinx axi vdma engine\n");
>> +
>> + /* Allocate and initialize the DMA engine structure */
>> + xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
>> + if (!xdev)
>> + return -ENOMEM;
>> +
>> + xdev->dev = &pdev->dev;
>> +
>> + /* Request and map I/O memory */
>> + io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + xdev->regs = devm_ioremap_resource(&pdev->dev, io);
>> + if (IS_ERR(xdev->regs))
>> + return PTR_ERR(xdev->regs);
>> +
>> + /* Retrieve the DMA engine properties from the device tree */
>> + xdev->has_sg = of_property_read_bool(node, "xlnx,include-sg");
>> +
>> + err = of_property_read_u32(node, "xlnx,num-fstores", &num_frames);
>> + if (err < 0) {
>> + dev_err(xdev->dev, "missing xlnx,num-fstores property\n");
>> + return err;
>> + }
>> +
>> + of_property_read_u32(node, "xlnx,flush-fsync", &xdev->flush_on_fsync);
>> +
>> + /* Initialize the DMA engine */
>> + xdev->common.dev = &pdev->dev;
>> +
>> + INIT_LIST_HEAD(&xdev->common.channels);
>> + dma_cap_set(DMA_SLAVE, xdev->common.cap_mask);
>> + dma_cap_set(DMA_PRIVATE, xdev->common.cap_mask);
>> +
>> + xdev->common.device_alloc_chan_resources =
>> + xilinx_vdma_alloc_chan_resources;
>> + xdev->common.device_free_chan_resources =
>> + xilinx_vdma_free_chan_resources;
>> + xdev->common.device_prep_slave_sg = xilinx_vdma_prep_slave_sg;
>> + xdev->common.device_control = xilinx_vdma_device_control;
>> + xdev->common.device_tx_status = xilinx_vdma_tx_status;
>> + xdev->common.device_issue_pending = xilinx_vdma_issue_pending;
>> +
>> + platform_set_drvdata(pdev, xdev);
>> +
>> + /* Initialize the channels */
>> + for_each_child_of_node(node, child) {
>> + err = xilinx_vdma_chan_probe(xdev, child);
>> + if (err < 0)
>> + goto error;
>> + }
>> +
>> + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
>> + if (xdev->chan[i])
>> + xdev->chan[i]->num_frms = num_frames;
>> + }
>> +
>> + /* Register the DMA engine with the core */
>> + dma_async_device_register(&xdev->common);
>> +
>> + err = of_dma_controller_register(node, of_dma_xilinx_xlate,
>> + &xdev->common);
>> + if (err < 0)
>> + dev_err(&pdev->dev, "Unable to register DMA to DT\n");
>
> Shouldn't this return 'err'? Like when you can't register the DMA to the node,
> you might want to bail out, don't you?
DMA Client devices, that do not have dt nodes, can still get the channel
through dma_request_channel() even if this call failed to register. So, doing
this way.
>
>> +
>> + return 0;
>> +
>> +error:
>> + for (i = 0; i < XILINX_VDMA_MAX_CHANS_PER_DEVICE; i++) {
>> + if (xdev->chan[i])
>> + xilinx_vdma_chan_remove(xdev->chan[i]);
>> + }
>> +
>> + return err;
>> +}
>> [...]
>> +static int xilinx_vdmatest_add_slave_channels(struct dma_chan *tx_chan,
>> + struct dma_chan *rx_chan)
>> +{
>> + struct xilinx_vdmatest_chan *tx_dtc, *rx_dtc;
>> + unsigned int thread_count = 0;
>> +
>> + tx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
>> + if (!tx_dtc)
>> + return -ENOMEM;
>> +
>> + rx_dtc = kmalloc(sizeof(struct xilinx_vdmatest_chan), GFP_KERNEL);
>> + if (!rx_dtc)
>> + return -ENOMEM;
>
> That's a memory leak. You gotta kfree tx_dtc.
Will take care in the v2 patch.
Thanks
Srikanth
>> [...]
>>
>
>
> --
> Regards,
> Levente Kurusa
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* PWM...
From: Sascha Hauer @ 2014-01-20 7:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140120001446.GD9752@verge.net.au>
On Mon, Jan 20, 2014 at 09:14:46AM +0900, Simon Horman wrote:
> On Sun, Jan 19, 2014 at 09:26:40PM +0100, Arnd Bergmann wrote:
> > On Sunday 19 January 2014, Russell King - ARM Linux wrote:
> > > On Sun, Jan 19, 2014 at 11:11:41AM -0800, Olof Johansson wrote:
> > > > On Sun, Jan 19, 2014 at 11:08 AM, Arnd Bergmann <arnd@arndb.de> wrote:
> >
> > > > Ah, yes, if you add a cell that can be done. There'll still be the
> > > > "dead" first cell that will always be 0, but that's alright.
> > >
> > > Does it not mean that PWM specifications of:
> > >
> > > <&pwm1 0 n> <&pwm2 0 n>
> > >
> > > would need to be converted to:
> > >
> > > <&pwm1 0 n 0> <&pwm2 0 n 0>
> > >
> > > in every DT file referring to these PWMs - because isn't this just
> > > treated in DT as one single array of values? (If DT knew how many
> > > were in each specification, we wouldn't need the #foo-cells...)
> >
> > Right: if you change an existing dts file from #pwm-cells=<2> to
> > #pwm-cells=<3>, that requires changing all references to the pwm
> > controller at the same time. If both the per-soc .dtsi files
> > and the per-board .dts files contain references to the same pwm
> > controller, that can end up in significant work. I have not checked
> > if this is the case for i.MX though.
>
> Would this change imply that old dtbs would no longer work with new kernels?
Not necessarily. With Lothars patch the driver works with pwm-cells=2
and pwm-cells=3. The only problem is that if you compile an old board
dts with a new SoC dtsi it will silently fail since the pwm-cells
setting doesn't match the atcual cells in the dts.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* PWM...
From: Sascha Hauer @ 2014-01-20 7:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119190324.GC28056@quad.lixom.net>
On Sun, Jan 19, 2014 at 11:03:24AM -0800, Olof Johansson wrote:
> [Adding devicetree list since we're talking bindings]
>
> On Sun, Jan 19, 2014 at 04:49:57PM +0000, Russell King - ARM Linux wrote:
> > So, having looked at what else I can add support for on the cubox-i, I
> > decided it would be nice and simple to add support for the front panel
> > LED. What could possibly go wrong with this.
> >
> > Well, the hardware is wired such that the LED is connected between the
> > PWM output and +3.3v. So, a constant low turns the LED on full, whereas
> > a constant high turns the LED off.
> >
> > So, the polarity of the LED is inverted - but this _can't_ be specified
> > in the totally and utterly fucked misdesigned crap that DT is:
> >
> > pwmleds {
> > compatible = "pwm-leds";
> > pinctrl-names = "default";
> > pinctrl-0 = <&pinctrl_cubox_i_pwm1>;
> >
> > front {
> > label = "imx6:red:front";
> > pwms = <&pwm1 0 50000>;
> > max-brightness = <248>;
> > };
> > };
> >
> > pwm1: pwm at 02080000 {
> > #pwm-cells = <2>;
> > compatible = "fsl,imx6q-pwm", "fsl,imx27-pwm";
> > reg = <0x02080000 0x4000>;
> > interrupts = <0 83 0x04>;
> > clocks = <&clks 62>, <&clks 145>;
> > clock-names = "ipg", "per";
> > };
> >
> > Yes, because iMX6 specifies #pwm-cells as 2, there's no flags able to
> > be specified in the pwms declaration in pwmleds. So that doesn't work.
> > There's no property to tell pwmleds that it should use inverted sense
> > either.
>
> Adding a property for active-low to the pwm-leds binding would be
> easy, and backwards compatible. I'm surprised the original binding
> didn't specify it. The leds-pwm driver already seems to support it for
> C-configured instances.
>
> I'm also surprised that the imx pwm driver even has a #pwm-cells of
> two, since the driver only supports one output. It'd be nice if they
> had allocated the extra cell for flags, but it's hard to change now,
> unless you do a new binding/compatible value and deprecate the old one.
>
> > Moreover, there's no way to specify a default brightness for the LED
> > in the absence of any trigger, so this results in the LED being fully
> > on.
>
> That seems to be missing from the led-pwm driver altogether, not just the DT
> bindings? Shouldn't be too bad to add a default-brightness property and plumb
> that up through the driver in this case.
>
> > So, something which _should_ be nice and simple is turned into a major
> > fuckup because of the total and utter crappiness that DT is and the
> > total misdesign that this shite is.
>
> It's not a major fuckup. None of the above is unfixable.
Indeed, and we already have a patch for this:
http://comments.gmane.org/gmane.linux.ports.arm.kernel/294823
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* [PATCH v16] dmaengine: Add MOXA ART DMA engine driver
From: Vinod Koul @ 2014-01-20 7:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389948365-13999-1-git-send-email-jonas.jensen@gmail.com>
On Fri, Jan 17, 2014 at 09:46:05AM +0100, Jonas Jensen wrote:
> The MOXA ART SoC has a DMA controller capable of offloading expensive
> memory operations, such as large copies. This patch adds support for
> the controller including four channels. Two of these are used to
> handle MMC copy on the UC-7112-LX hardware. The remaining two can be
> used in a future audio driver or client application.
>
> Signed-off-by: Jonas Jensen <jonas.jensen@gmail.com>
Applied thanks.
Though I wasnt able to find the ARCH_MOXART in my next, it resolved after using
the linux-next as test tree and was able to compile test
--
~Vinod
> ---
>
> Notes:
> Changes since v15:
> 1. rebase drivers/dma/Kconfig to next-20140117
>
> Applies to next-20140117
>
> .../devicetree/bindings/dma/moxa,moxart-dma.txt | 45 ++
> drivers/dma/Kconfig | 8 +
> drivers/dma/Makefile | 1 +
> drivers/dma/moxart-dma.c | 699 +++++++++++++++++++++
> 4 files changed, 753 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt
> create mode 100644 drivers/dma/moxart-dma.c
>
> diff --git a/Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt b/Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt
> new file mode 100644
> index 0000000..8a9f355
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/moxa,moxart-dma.txt
> @@ -0,0 +1,45 @@
> +MOXA ART DMA Controller
> +
> +See dma.txt first
> +
> +Required properties:
> +
> +- compatible : Must be "moxa,moxart-dma"
> +- reg : Should contain registers location and length
> +- interrupts : Should contain an interrupt-specifier for the sole
> + interrupt generated by the device
> +- #dma-cells : Should be 1, a single cell holding a line request number
> +
> +Example:
> +
> + dma: dma at 90500000 {
> + compatible = "moxa,moxart-dma";
> + reg = <0x90500080 0x40>;
> + interrupts = <24 0>;
> + #dma-cells = <1>;
> + };
> +
> +
> +Clients:
> +
> +DMA clients connected to the MOXA ART DMA controller must use the format
> +described in the dma.txt file, using a two-cell specifier for each channel:
> +a phandle plus one integer cells.
> +The two cells in order are:
> +
> +1. A phandle pointing to the DMA controller.
> +2. Peripheral identifier for the hardware handshaking interface.
> +
> +Example:
> +Use specific request line passing from dma
> +For example, MMC request line is 5
> +
> + sdhci: sdhci at 98e00000 {
> + compatible = "moxa,moxart-sdhci";
> + reg = <0x98e00000 0x5C>;
> + interrupts = <5 0>;
> + clocks = <&clk_apb>;
> + dmas = <&dma 5>,
> + <&dma 5>;
> + dma-names = "tx", "rx";
> + };
> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> index 9ae6f54..9bed1a2 100644
> --- a/drivers/dma/Kconfig
> +++ b/drivers/dma/Kconfig
> @@ -342,6 +342,14 @@ config K3_DMA
> Support the DMA engine for Hisilicon K3 platform
> devices.
>
> +config MOXART_DMA
> + tristate "MOXART DMA support"
> + depends on ARCH_MOXART
> + select DMA_ENGINE
> + select DMA_VIRTUAL_CHANNELS
> + help
> + Enable support for the MOXA ART SoC DMA controller.
> +
> config DMA_ENGINE
> bool
>
> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> index 0a6f08e..a029d0f4 100644
> --- a/drivers/dma/Makefile
> +++ b/drivers/dma/Makefile
> @@ -43,3 +43,4 @@ obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o
> obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o
> obj-$(CONFIG_TI_CPPI41) += cppi41.o
> obj-$(CONFIG_K3_DMA) += k3dma.o
> +obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
> diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c
> new file mode 100644
> index 0000000..3258e48
> --- /dev/null
> +++ b/drivers/dma/moxart-dma.c
> @@ -0,0 +1,699 @@
> +/*
> + * MOXA ART SoCs DMA Engine support.
> + *
> + * Copyright (C) 2013 Jonas Jensen
> + *
> + * Jonas Jensen <jonas.jensen@gmail.com>
> + *
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_dma.h>
> +#include <linux/bitops.h>
> +
> +#include <asm/cacheflush.h>
> +
> +#include "dmaengine.h"
> +#include "virt-dma.h"
> +
> +#define APB_DMA_MAX_CHANNEL 4
> +
> +#define REG_OFF_ADDRESS_SOURCE 0
> +#define REG_OFF_ADDRESS_DEST 4
> +#define REG_OFF_CYCLES 8
> +#define REG_OFF_CTRL 12
> +#define REG_OFF_CHAN_SIZE 16
> +
> +#define APB_DMA_ENABLE BIT(0)
> +#define APB_DMA_FIN_INT_STS BIT(1)
> +#define APB_DMA_FIN_INT_EN BIT(2)
> +#define APB_DMA_BURST_MODE BIT(3)
> +#define APB_DMA_ERR_INT_STS BIT(4)
> +#define APB_DMA_ERR_INT_EN BIT(5)
> +
> +/*
> + * Unset: APB
> + * Set: AHB
> + */
> +#define APB_DMA_SOURCE_SELECT 0x40
> +#define APB_DMA_DEST_SELECT 0x80
> +
> +#define APB_DMA_SOURCE 0x100
> +#define APB_DMA_DEST 0x1000
> +
> +#define APB_DMA_SOURCE_MASK 0x700
> +#define APB_DMA_DEST_MASK 0x7000
> +
> +/*
> + * 000: No increment
> + * 001: +1 (Burst=0), +4 (Burst=1)
> + * 010: +2 (Burst=0), +8 (Burst=1)
> + * 011: +4 (Burst=0), +16 (Burst=1)
> + * 101: -1 (Burst=0), -4 (Burst=1)
> + * 110: -2 (Burst=0), -8 (Burst=1)
> + * 111: -4 (Burst=0), -16 (Burst=1)
> + */
> +#define APB_DMA_SOURCE_INC_0 0
> +#define APB_DMA_SOURCE_INC_1_4 0x100
> +#define APB_DMA_SOURCE_INC_2_8 0x200
> +#define APB_DMA_SOURCE_INC_4_16 0x300
> +#define APB_DMA_SOURCE_DEC_1_4 0x500
> +#define APB_DMA_SOURCE_DEC_2_8 0x600
> +#define APB_DMA_SOURCE_DEC_4_16 0x700
> +#define APB_DMA_DEST_INC_0 0
> +#define APB_DMA_DEST_INC_1_4 0x1000
> +#define APB_DMA_DEST_INC_2_8 0x2000
> +#define APB_DMA_DEST_INC_4_16 0x3000
> +#define APB_DMA_DEST_DEC_1_4 0x5000
> +#define APB_DMA_DEST_DEC_2_8 0x6000
> +#define APB_DMA_DEST_DEC_4_16 0x7000
> +
> +/*
> + * Request signal select source/destination address for DMA hardware handshake.
> + *
> + * The request line number is a property of the DMA controller itself,
> + * e.g. MMC must always request channels where dma_slave_config->slave_id is 5.
> + *
> + * 0: No request / Grant signal
> + * 1-15: Request / Grant signal
> + */
> +#define APB_DMA_SOURCE_REQ_NO 0x1000000
> +#define APB_DMA_SOURCE_REQ_NO_MASK 0xf000000
> +#define APB_DMA_DEST_REQ_NO 0x10000
> +#define APB_DMA_DEST_REQ_NO_MASK 0xf0000
> +
> +#define APB_DMA_DATA_WIDTH 0x100000
> +#define APB_DMA_DATA_WIDTH_MASK 0x300000
> +/*
> + * Data width of transfer:
> + *
> + * 00: Word
> + * 01: Half
> + * 10: Byte
> + */
> +#define APB_DMA_DATA_WIDTH_4 0
> +#define APB_DMA_DATA_WIDTH_2 0x100000
> +#define APB_DMA_DATA_WIDTH_1 0x200000
> +
> +#define APB_DMA_CYCLES_MASK 0x00ffffff
> +
> +#define MOXART_DMA_DATA_TYPE_S8 0x00
> +#define MOXART_DMA_DATA_TYPE_S16 0x01
> +#define MOXART_DMA_DATA_TYPE_S32 0x02
> +
> +struct moxart_sg {
> + dma_addr_t addr;
> + uint32_t len;
> +};
> +
> +struct moxart_desc {
> + enum dma_transfer_direction dma_dir;
> + dma_addr_t dev_addr;
> + unsigned int sglen;
> + unsigned int dma_cycles;
> + struct virt_dma_desc vd;
> + uint8_t es;
> + struct moxart_sg sg[0];
> +};
> +
> +struct moxart_chan {
> + struct virt_dma_chan vc;
> +
> + void __iomem *base;
> + struct moxart_desc *desc;
> +
> + struct dma_slave_config cfg;
> +
> + bool allocated;
> + bool error;
> + int ch_num;
> + unsigned int line_reqno;
> + unsigned int sgidx;
> +};
> +
> +struct moxart_dmadev {
> + struct dma_device dma_slave;
> + struct moxart_chan slave_chans[APB_DMA_MAX_CHANNEL];
> +};
> +
> +struct moxart_filter_data {
> + struct moxart_dmadev *mdc;
> + struct of_phandle_args *dma_spec;
> +};
> +
> +static const unsigned int es_bytes[] = {
> + [MOXART_DMA_DATA_TYPE_S8] = 1,
> + [MOXART_DMA_DATA_TYPE_S16] = 2,
> + [MOXART_DMA_DATA_TYPE_S32] = 4,
> +};
> +
> +static struct device *chan2dev(struct dma_chan *chan)
> +{
> + return &chan->dev->device;
> +}
> +
> +static inline struct moxart_chan *to_moxart_dma_chan(struct dma_chan *c)
> +{
> + return container_of(c, struct moxart_chan, vc.chan);
> +}
> +
> +static inline struct moxart_desc *to_moxart_dma_desc(
> + struct dma_async_tx_descriptor *t)
> +{
> + return container_of(t, struct moxart_desc, vd.tx);
> +}
> +
> +static void moxart_dma_desc_free(struct virt_dma_desc *vd)
> +{
> + kfree(container_of(vd, struct moxart_desc, vd));
> +}
> +
> +static int moxart_terminate_all(struct dma_chan *chan)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> + unsigned long flags;
> + LIST_HEAD(head);
> + u32 ctrl;
> +
> + dev_dbg(chan2dev(chan), "%s: ch=%p\n", __func__, ch);
> +
> + spin_lock_irqsave(&ch->vc.lock, flags);
> +
> + if (ch->desc)
> + ch->desc = NULL;
> +
> + ctrl = readl(ch->base + REG_OFF_CTRL);
> + ctrl &= ~(APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
> + writel(ctrl, ch->base + REG_OFF_CTRL);
> +
> + vchan_get_all_descriptors(&ch->vc, &head);
> + spin_unlock_irqrestore(&ch->vc.lock, flags);
> + vchan_dma_desc_free_list(&ch->vc, &head);
> +
> + return 0;
> +}
> +
> +static int moxart_slave_config(struct dma_chan *chan,
> + struct dma_slave_config *cfg)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> + u32 ctrl;
> +
> + ch->cfg = *cfg;
> +
> + ctrl = readl(ch->base + REG_OFF_CTRL);
> + ctrl |= APB_DMA_BURST_MODE;
> + ctrl &= ~(APB_DMA_DEST_MASK | APB_DMA_SOURCE_MASK);
> + ctrl &= ~(APB_DMA_DEST_REQ_NO_MASK | APB_DMA_SOURCE_REQ_NO_MASK);
> +
> + switch (ch->cfg.src_addr_width) {
> + case DMA_SLAVE_BUSWIDTH_1_BYTE:
> + ctrl |= APB_DMA_DATA_WIDTH_1;
> + if (ch->cfg.direction != DMA_MEM_TO_DEV)
> + ctrl |= APB_DMA_DEST_INC_1_4;
> + else
> + ctrl |= APB_DMA_SOURCE_INC_1_4;
> + break;
> + case DMA_SLAVE_BUSWIDTH_2_BYTES:
> + ctrl |= APB_DMA_DATA_WIDTH_2;
> + if (ch->cfg.direction != DMA_MEM_TO_DEV)
> + ctrl |= APB_DMA_DEST_INC_2_8;
> + else
> + ctrl |= APB_DMA_SOURCE_INC_2_8;
> + break;
> + case DMA_SLAVE_BUSWIDTH_4_BYTES:
> + ctrl &= ~APB_DMA_DATA_WIDTH;
> + if (ch->cfg.direction != DMA_MEM_TO_DEV)
> + ctrl |= APB_DMA_DEST_INC_4_16;
> + else
> + ctrl |= APB_DMA_SOURCE_INC_4_16;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (ch->cfg.direction == DMA_MEM_TO_DEV) {
> + ctrl &= ~APB_DMA_DEST_SELECT;
> + ctrl |= APB_DMA_SOURCE_SELECT;
> + ctrl |= (ch->line_reqno << 16 &
> + APB_DMA_DEST_REQ_NO_MASK);
> + } else {
> + ctrl |= APB_DMA_DEST_SELECT;
> + ctrl &= ~APB_DMA_SOURCE_SELECT;
> + ctrl |= (ch->line_reqno << 24 &
> + APB_DMA_SOURCE_REQ_NO_MASK);
> + }
> +
> + writel(ctrl, ch->base + REG_OFF_CTRL);
> +
> + return 0;
> +}
> +
> +static int moxart_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> + unsigned long arg)
> +{
> + int ret = 0;
> +
> + switch (cmd) {
> + case DMA_PAUSE:
> + case DMA_RESUME:
> + return -EINVAL;
> + case DMA_TERMINATE_ALL:
> + moxart_terminate_all(chan);
> + break;
> + case DMA_SLAVE_CONFIG:
> + ret = moxart_slave_config(chan, (struct dma_slave_config *)arg);
> + break;
> + default:
> + ret = -ENOSYS;
> + }
> +
> + return ret;
> +}
> +
> +static struct dma_async_tx_descriptor *moxart_prep_slave_sg(
> + struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction dir,
> + unsigned long tx_flags, void *context)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> + struct moxart_desc *d;
> + enum dma_slave_buswidth dev_width;
> + dma_addr_t dev_addr;
> + struct scatterlist *sgent;
> + unsigned int es;
> + unsigned int i;
> +
> + if (!is_slave_direction(dir)) {
> + dev_err(chan2dev(chan), "%s: invalid DMA direction\n",
> + __func__);
> + return NULL;
> + }
> +
> + if (dir == DMA_DEV_TO_MEM) {
> + dev_addr = ch->cfg.src_addr;
> + dev_width = ch->cfg.src_addr_width;
> + } else {
> + dev_addr = ch->cfg.dst_addr;
> + dev_width = ch->cfg.dst_addr_width;
> + }
> +
> + switch (dev_width) {
> + case DMA_SLAVE_BUSWIDTH_1_BYTE:
> + es = MOXART_DMA_DATA_TYPE_S8;
> + break;
> + case DMA_SLAVE_BUSWIDTH_2_BYTES:
> + es = MOXART_DMA_DATA_TYPE_S16;
> + break;
> + case DMA_SLAVE_BUSWIDTH_4_BYTES:
> + es = MOXART_DMA_DATA_TYPE_S32;
> + break;
> + default:
> + dev_err(chan2dev(chan), "%s: unsupported data width (%u)\n",
> + __func__, dev_width);
> + return NULL;
> + }
> +
> + d = kzalloc(sizeof(*d) + sg_len * sizeof(d->sg[0]), GFP_ATOMIC);
> + if (!d)
> + return NULL;
> +
> + d->dma_dir = dir;
> + d->dev_addr = dev_addr;
> + d->es = es;
> +
> + for_each_sg(sgl, sgent, sg_len, i) {
> + d->sg[i].addr = sg_dma_address(sgent);
> + d->sg[i].len = sg_dma_len(sgent);
> + }
> +
> + d->sglen = sg_len;
> +
> + ch->error = 0;
> +
> + return vchan_tx_prep(&ch->vc, &d->vd, tx_flags);
> +}
> +
> +static struct dma_chan *moxart_of_xlate(struct of_phandle_args *dma_spec,
> + struct of_dma *ofdma)
> +{
> + struct moxart_dmadev *mdc = ofdma->of_dma_data;
> + struct dma_chan *chan;
> + struct moxart_chan *ch;
> +
> + chan = dma_get_any_slave_channel(&mdc->dma_slave);
> + if (!chan)
> + return NULL;
> +
> + ch = to_moxart_dma_chan(chan);
> + ch->line_reqno = dma_spec->args[0];
> +
> + return chan;
> +}
> +
> +static int moxart_alloc_chan_resources(struct dma_chan *chan)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> +
> + dev_dbg(chan2dev(chan), "%s: allocating channel #%u\n",
> + __func__, ch->ch_num);
> + ch->allocated = 1;
> +
> + return 0;
> +}
> +
> +static void moxart_free_chan_resources(struct dma_chan *chan)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> +
> + vchan_free_chan_resources(&ch->vc);
> +
> + dev_dbg(chan2dev(chan), "%s: freeing channel #%u\n",
> + __func__, ch->ch_num);
> + ch->allocated = 0;
> +}
> +
> +static void moxart_dma_set_params(struct moxart_chan *ch, dma_addr_t src_addr,
> + dma_addr_t dst_addr)
> +{
> + writel(src_addr, ch->base + REG_OFF_ADDRESS_SOURCE);
> + writel(dst_addr, ch->base + REG_OFF_ADDRESS_DEST);
> +}
> +
> +static void moxart_set_transfer_params(struct moxart_chan *ch, unsigned int len)
> +{
> + struct moxart_desc *d = ch->desc;
> + unsigned int sglen_div = es_bytes[d->es];
> +
> + d->dma_cycles = len >> sglen_div;
> +
> + /*
> + * There are 4 cycles on 64 bytes copied, i.e. one cycle copies 16
> + * bytes ( when width is APB_DMAB_DATA_WIDTH_4 ).
> + */
> + writel(d->dma_cycles, ch->base + REG_OFF_CYCLES);
> +
> + dev_dbg(chan2dev(&ch->vc.chan), "%s: set %u DMA cycles (len=%u)\n",
> + __func__, d->dma_cycles, len);
> +}
> +
> +static void moxart_start_dma(struct moxart_chan *ch)
> +{
> + u32 ctrl;
> +
> + ctrl = readl(ch->base + REG_OFF_CTRL);
> + ctrl |= (APB_DMA_ENABLE | APB_DMA_FIN_INT_EN | APB_DMA_ERR_INT_EN);
> + writel(ctrl, ch->base + REG_OFF_CTRL);
> +}
> +
> +static void moxart_dma_start_sg(struct moxart_chan *ch, unsigned int idx)
> +{
> + struct moxart_desc *d = ch->desc;
> + struct moxart_sg *sg = ch->desc->sg + idx;
> +
> + if (ch->desc->dma_dir == DMA_MEM_TO_DEV)
> + moxart_dma_set_params(ch, sg->addr, d->dev_addr);
> + else if (ch->desc->dma_dir == DMA_DEV_TO_MEM)
> + moxart_dma_set_params(ch, d->dev_addr, sg->addr);
> +
> + moxart_set_transfer_params(ch, sg->len);
> +
> + moxart_start_dma(ch);
> +}
> +
> +static void moxart_dma_start_desc(struct dma_chan *chan)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> + struct virt_dma_desc *vd;
> +
> + vd = vchan_next_desc(&ch->vc);
> +
> + if (!vd) {
> + ch->desc = NULL;
> + return;
> + }
> +
> + list_del(&vd->node);
> +
> + ch->desc = to_moxart_dma_desc(&vd->tx);
> + ch->sgidx = 0;
> +
> + moxart_dma_start_sg(ch, 0);
> +}
> +
> +static void moxart_issue_pending(struct dma_chan *chan)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&ch->vc.lock, flags);
> + if (vchan_issue_pending(&ch->vc) && !ch->desc)
> + moxart_dma_start_desc(chan);
> + spin_unlock_irqrestore(&ch->vc.lock, flags);
> +}
> +
> +static size_t moxart_dma_desc_size(struct moxart_desc *d,
> + unsigned int completed_sgs)
> +{
> + unsigned int i;
> + size_t size;
> +
> + for (size = i = completed_sgs; i < d->sglen; i++)
> + size += d->sg[i].len;
> +
> + return size;
> +}
> +
> +static size_t moxart_dma_desc_size_in_flight(struct moxart_chan *ch)
> +{
> + size_t size;
> + unsigned int completed_cycles, cycles;
> +
> + size = moxart_dma_desc_size(ch->desc, ch->sgidx);
> + cycles = readl(ch->base + REG_OFF_CYCLES);
> + completed_cycles = (ch->desc->dma_cycles - cycles);
> + size -= completed_cycles << es_bytes[ch->desc->es];
> +
> + dev_dbg(chan2dev(&ch->vc.chan), "%s: size=%zu\n", __func__, size);
> +
> + return size;
> +}
> +
> +static enum dma_status moxart_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie,
> + struct dma_tx_state *txstate)
> +{
> + struct moxart_chan *ch = to_moxart_dma_chan(chan);
> + struct virt_dma_desc *vd;
> + struct moxart_desc *d;
> + enum dma_status ret;
> + unsigned long flags;
> +
> + /*
> + * dma_cookie_status() assigns initial residue value.
> + */
> + ret = dma_cookie_status(chan, cookie, txstate);
> +
> + spin_lock_irqsave(&ch->vc.lock, flags);
> + vd = vchan_find_desc(&ch->vc, cookie);
> + if (vd) {
> + d = to_moxart_dma_desc(&vd->tx);
> + txstate->residue = moxart_dma_desc_size(d, 0);
> + } else if (ch->desc && ch->desc->vd.tx.cookie == cookie) {
> + txstate->residue = moxart_dma_desc_size_in_flight(ch);
> + }
> + spin_unlock_irqrestore(&ch->vc.lock, flags);
> +
> + if (ch->error)
> + return DMA_ERROR;
> +
> + return ret;
> +}
> +
> +static void moxart_dma_init(struct dma_device *dma, struct device *dev)
> +{
> + dma->device_prep_slave_sg = moxart_prep_slave_sg;
> + dma->device_alloc_chan_resources = moxart_alloc_chan_resources;
> + dma->device_free_chan_resources = moxart_free_chan_resources;
> + dma->device_issue_pending = moxart_issue_pending;
> + dma->device_tx_status = moxart_tx_status;
> + dma->device_control = moxart_control;
> + dma->dev = dev;
> +
> + INIT_LIST_HEAD(&dma->channels);
> +}
> +
> +static irqreturn_t moxart_dma_interrupt(int irq, void *devid)
> +{
> + struct moxart_dmadev *mc = devid;
> + struct moxart_chan *ch = &mc->slave_chans[0];
> + unsigned int i;
> + unsigned long flags;
> + u32 ctrl;
> +
> + dev_dbg(chan2dev(&ch->vc.chan), "%s\n", __func__);
> +
> + for (i = 0; i < APB_DMA_MAX_CHANNEL; i++, ch++) {
> + if (!ch->allocated)
> + continue;
> +
> + ctrl = readl(ch->base + REG_OFF_CTRL);
> +
> + dev_dbg(chan2dev(&ch->vc.chan), "%s: ch=%p ch->base=%p ctrl=%x\n",
> + __func__, ch, ch->base, ctrl);
> +
> + if (ctrl & APB_DMA_FIN_INT_STS) {
> + ctrl &= ~APB_DMA_FIN_INT_STS;
> + if (ch->desc) {
> + spin_lock_irqsave(&ch->vc.lock, flags);
> + if (++ch->sgidx < ch->desc->sglen) {
> + moxart_dma_start_sg(ch, ch->sgidx);
> + } else {
> + vchan_cookie_complete(&ch->desc->vd);
> + moxart_dma_start_desc(&ch->vc.chan);
> + }
> + spin_unlock_irqrestore(&ch->vc.lock, flags);
> + }
> + }
> +
> + if (ctrl & APB_DMA_ERR_INT_STS) {
> + ctrl &= ~APB_DMA_ERR_INT_STS;
> + ch->error = 1;
> + }
> +
> + writel(ctrl, ch->base + REG_OFF_CTRL);
> + }
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int moxart_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *node = dev->of_node;
> + struct resource *res;
> + static void __iomem *dma_base_addr;
> + int ret, i;
> + unsigned int irq;
> + struct moxart_chan *ch;
> + struct moxart_dmadev *mdc;
> +
> + mdc = devm_kzalloc(dev, sizeof(*mdc), GFP_KERNEL);
> + if (!mdc) {
> + dev_err(dev, "can't allocate DMA container\n");
> + return -ENOMEM;
> + }
> +
> + irq = irq_of_parse_and_map(node, 0);
> + if (irq == NO_IRQ) {
> + dev_err(dev, "no IRQ resource\n");
> + return -EINVAL;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + dma_base_addr = devm_ioremap_resource(dev, res);
> + if (IS_ERR(dma_base_addr))
> + return PTR_ERR(dma_base_addr);
> +
> + dma_cap_zero(mdc->dma_slave.cap_mask);
> + dma_cap_set(DMA_SLAVE, mdc->dma_slave.cap_mask);
> + dma_cap_set(DMA_PRIVATE, mdc->dma_slave.cap_mask);
> +
> + moxart_dma_init(&mdc->dma_slave, dev);
> +
> + ch = &mdc->slave_chans[0];
> + for (i = 0; i < APB_DMA_MAX_CHANNEL; i++, ch++) {
> + ch->ch_num = i;
> + ch->base = dma_base_addr + i * REG_OFF_CHAN_SIZE;
> + ch->allocated = 0;
> +
> + ch->vc.desc_free = moxart_dma_desc_free;
> + vchan_init(&ch->vc, &mdc->dma_slave);
> +
> + dev_dbg(dev, "%s: chs[%d]: ch->ch_num=%u ch->base=%p\n",
> + __func__, i, ch->ch_num, ch->base);
> + }
> +
> + platform_set_drvdata(pdev, mdc);
> +
> + ret = devm_request_irq(dev, irq, moxart_dma_interrupt, 0,
> + "moxart-dma-engine", mdc);
> + if (ret) {
> + dev_err(dev, "devm_request_irq failed\n");
> + return ret;
> + }
> +
> + ret = dma_async_device_register(&mdc->dma_slave);
> + if (ret) {
> + dev_err(dev, "dma_async_device_register failed\n");
> + return ret;
> + }
> +
> + ret = of_dma_controller_register(node, moxart_of_xlate, mdc);
> + if (ret) {
> + dev_err(dev, "of_dma_controller_register failed\n");
> + dma_async_device_unregister(&mdc->dma_slave);
> + return ret;
> + }
> +
> + dev_dbg(dev, "%s: IRQ=%u\n", __func__, irq);
> +
> + return 0;
> +}
> +
> +static int moxart_remove(struct platform_device *pdev)
> +{
> + struct moxart_dmadev *m = platform_get_drvdata(pdev);
> +
> + dma_async_device_unregister(&m->dma_slave);
> +
> + if (pdev->dev.of_node)
> + of_dma_controller_free(pdev->dev.of_node);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id moxart_dma_match[] = {
> + { .compatible = "moxa,moxart-dma" },
> + { }
> +};
> +
> +static struct platform_driver moxart_driver = {
> + .probe = moxart_probe,
> + .remove = moxart_remove,
> + .driver = {
> + .name = "moxart-dma-engine",
> + .owner = THIS_MODULE,
> + .of_match_table = moxart_dma_match,
> + },
> +};
> +
> +static int moxart_init(void)
> +{
> + return platform_driver_register(&moxart_driver);
> +}
> +subsys_initcall(moxart_init);
> +
> +static void __exit moxart_exit(void)
> +{
> + platform_driver_unregister(&moxart_driver);
> +}
> +module_exit(moxart_exit);
> +
> +MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
> +MODULE_DESCRIPTION("MOXART DMA engine driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.8.2.1
>
--
^ permalink raw reply
* [PATCH v9 00/12] Add power management support for mxs phy
From: Peter Chen @ 2014-01-20 6:48 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1388111921-17326-1-git-send-email-peter.chen@freescale.com>
>
> Hi Felipe & Shawn,
>
> The serial adds power management support for MXS PHY, it includes:
>
> - Add one PHY API .set_wakeup, and related API implementation at mxs phy
> driver
> - misc changes and bug fixes for mxs phy to support low power mode and
> wakeup.
>
> It is based on the lastest Greg's usb-next, 3.13-rc5.
>
Hi Felipe,
I would like to confirm if this patchset is ok, and will be in
your 3.15 tree?
Peter
> Changes for v9:
> - Fix the build warning if CONFIG_SUSPEND is not set
>
> Changes for v8:
> - Shawn has already applied two dts patches, so delete them at
> v8 version, http://marc.info/?l=linux-arm-kernel&m=138785188404939&w=2
> - Using of_get_property to avoid imx6 fail at probe when using old DTB
> [5/12]
> - Do not consider failure if alias id is not existed, it can avoid fail
> at probe when using old DTB [8/12]
> - We don't need more changes for [8/12] change due to we use
> "mxs_phy->port_id ==" as condition
>
> Changes for v7:
> - Fixed indentation problem at binding doc [1/14]
> - s/ganranteed/guaranteed, and add explanation for "auto setting" [3/14]
> - add static before SIMPLE_DEV_PM_OPS [13/14]
> - add one patch to change usb device description [7/14]
> - Delete the .nofity_suspend and .notify_resume due to Felipe
> does not agree to add them as PHY API, will use other ways to implement
> them in future.
>
> Changes for v6:
> - Add description for IC bug fixes logic. [9/15]
> - Move is_imx6q_phy and is_imx6sl_phy from [9/15] to [14/15]
> - %s/mxs_phy_clock_switch/mxs_phy_clock_switch_delay to reflect
> the function meaning more precise [15/15]
>
> Changes for v5:
> Add Marc and Michael Grzeschik's commnets
> - typo error at [2/15]
> - sqhash patches which introducing mxs_phy_disconnect_line and
> fixed but at this function. [13/15]
> - Introducing flag MXS_PHY_NEED_IP_FIX who stands for the SoCs
> who have IC fixes. [2/15, 8/15]
> - Delete one patch for low speed connection problem at every rare
> situations due to the root cause has still not found.
>
> Peter Chen (12):
> usb: doc: phy-mxs: Add more compatible strings
> usb: phy-mxs: Add platform judgement code
> usb: phy-mxs: Add auto clock and power setting
> usb: doc: phy-mxs: update binding for adding anatop phandle
> usb: phy-mxs: Add anatop regmap
> usb: phy-mxs: change description of usb device speed
> usb: phy-mxs: Enable IC fixes for related SoCs
> usb: phy-mxs: add controller id
> usb: phy: Add set_wakeup API
> usb: phy-mxs: Add implementation of set_wakeup
> usb: phy-mxs: Add system suspend/resume API
> usb: phy-mxs: Add sync time after controller clear phcd
>
> Documentation/devicetree/bindings/usb/mxs-phy.txt | 5 +-
> drivers/usb/phy/phy-mxs-usb.c | 310
> ++++++++++++++++++++-
> include/linux/usb/phy.h | 16 +
> 3 files changed, 316 insertions(+), 15 deletions(-)
>
> --
> 1.7.8
>
^ permalink raw reply
* [PATCH v2 4/6] arm64: audit: Add 32-bit (compat) syscall support
From: AKASHI Takahiro @ 2014-01-20 5:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140117164610.GQ16003@mudshark.cambridge.arm.com>
On 01/18/2014 01:46 AM, Will Deacon wrote:
> Hi Akashi,
>
> On Fri, Jan 17, 2014 at 08:13:17AM +0000, AKASHI Takahiro wrote:
>> Generic audit code also support compat system calls now.
>> This patch adds a small piece of architecture dependent code.
>
> [...]
>
>> static inline int syscall_get_nr(struct task_struct *task,
>> @@ -109,6 +110,15 @@ static inline void syscall_set_arguments(struct task_struct *task,
>> static inline int syscall_get_arch(struct task_struct *task,
>> struct pt_regs *regs)
>> {
>> +#ifdef CONFIG_COMPAT
>> + if (is_compat_thread(task_thread_info(task)))
>
> You can call is_compat_thread even when !CONFIG_COMPAT, so you don't need
> that #ifdef.
Right. I will remove it.
>> +#ifdef __AARCH64EB__
>> + return AUDIT_ARCH_ARMEB; /* only BE on BE */
>
> Well, actually, we only support userspace to be the same endianness as the
> kernel, so you that comment is slightly misleading. You could probably avoid
> these repeated ifdefs by defining things like ARM64_AUDIT_ARCH and
> ARM64_COMPAT_AUDIT_ARCH once depending on endianness.
As in the discussions about "audit(userspace)", if we don't have to care
about endianness, I will remove this #ifdef instead.
Thanks,
-Takahiro AKASHI
> Will
>
^ permalink raw reply
* [PATCH v2] arm64: mm: use ubfm for dcache_line_size
From: Jingoo Han @ 2014-01-20 5:00 UTC (permalink / raw)
To: linux-arm-kernel
Use 'ubfm' for the bitfield move instruction; thus, single
instruction can be used instead of two instructions, when
getting the minimum D-cache line size from CTR_EL0 register.
Signed-off-by: Jingoo Han <jg1.han@samsung.com>
---
Change since v1
- Remove unnecessary prefix '0x' from immediates, per Will Deacon
- Add '#' to immediates for consistency, per Ard Biesheuvel
arch/arm64/mm/proc-macros.S | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S
index 8957b82..005d29e 100644
--- a/arch/arm64/mm/proc-macros.S
+++ b/arch/arm64/mm/proc-macros.S
@@ -38,8 +38,7 @@
*/
.macro dcache_line_size, reg, tmp
mrs \tmp, ctr_el0 // read CTR
- lsr \tmp, \tmp, #16
- and \tmp, \tmp, #0xf // cache line size encoding
+ ubfm \tmp, \tmp, #16, #19 // cache line size encoding
mov \reg, #4 // bytes per word
lsl \reg, \reg, \tmp // actual cache line size
.endm
--
1.7.10.4
^ permalink raw reply related
* [PATCH 2/3] mmc: dw_mmc: call mmc_of_parse to fill in common options
From: Jaehoon Chung @ 2014-01-20 4:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390190215-22700-3-git-send-email-olof@lixom.net>
Hi, Olof.
It looks good to me. When use the mmc_of_parse, we can also remove the duplicated property.
I have also worked the patch for using mmc_of_parse().
1. caps2-mmc-hs200-1_8v/1_2v capability can be moved into mmc_of_parse.
then we can remove parsing of dt into dw_mmc.c
2. keep-power-in-suspend/enable-sdio-wakeup can be removed.
If i missed other patch, let me know, plz.
how about?
if you're ok, i will send the patch-set on this week.
Best Regards,
Jaehoon Chung
On 01/20/2014 12:56 PM, Olof Johansson wrote:
> The shared of parse function fills in common options for capabilities,
> etc, but it needs to be called from each driver that wants to make use
> of it. dw_mmc was missing the call.
>
> Signed-off-by: Olof Johansson <olof@lixom.net>
> ---
> drivers/mmc/host/dw_mmc.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index a776f24..7119f63 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -2137,6 +2137,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
> if (!mmc)
> return -ENOMEM;
>
> + mmc_of_parse(mmc);
> +
> slot = mmc_priv(mmc);
> slot->id = id;
> slot->mmc = mmc;
>
^ permalink raw reply
* [PATCH v2 0/3] ARM: clps711x: IRQCHIP driver
From: Alexander Shiyan @ 2014-01-20 4:49 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20131229084340.10a4abdfc5815a709d7a6cc6@mail.ru>
On Sun, 29 Dec 2013 08:43:40 +0400
Alexander Shiyan <shc_work@mail.ru> wrote:
> On Thu, 19 Dec 2013 15:56:54 +0400
> Alexander Shiyan <shc_work@mail.ru> wrote:
>
> > This series is devoted to new CLPS711X irqchip driver.
> > The driver designed for booting from ATAGS and devicetree.
> >
> > Alexander Shiyan (3):
> > ARM: clps711x: Add CLPS711X irqchip driver
> > ARM: dts: clps711x: Add bindings documentation for CLPS711X irqchip
> > driver
> > ARM: clps711x: Migrate CLPS711X subarch to the new irqchip driver
> >
> > .../interrupt-controller/cirrus,clps711x-intc.txt | 41 ++++
> > arch/arm/Kconfig | 2 -
> > arch/arm/mach-clps711x/board-autcpu12.c | 2 -
> > arch/arm/mach-clps711x/board-cdb89712.c | 2 -
> > arch/arm/mach-clps711x/board-clep7312.c | 2 -
> > arch/arm/mach-clps711x/board-edb7211.c | 2 -
> > arch/arm/mach-clps711x/board-p720t.c | 2 -
> > arch/arm/mach-clps711x/common.c | 201 +----------------
> > arch/arm/mach-clps711x/common.h | 5 +-
> > arch/arm/mach-clps711x/include/mach/clps711x.h | 16 --
> > drivers/irqchip/Kconfig | 8 +
> > drivers/irqchip/Makefile | 1 +
> > drivers/irqchip/irq-clps711x.c | 243 +++++++++++++++++++++
> > 13 files changed, 299 insertions(+), 228 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/interrupt-controller/cirrus,clps711x-intc.txt
> > create mode 100644 drivers/irqchip/irq-clps711x.c
> >
> > --
>
> Ping.
Silence more than two weeks from the last post on this series.
Arnd, Olof, I beg to apply these patches to arm-soc.
Thanks.
--
Alexander Shiyan <shc_work@mail.ru>
^ permalink raw reply
* [PATCH v2 5/5] ARM: dts: AM43xx-epos-evm: DT entries for ti-usim and phy
From: Satish Patel @ 2014-01-20 4:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390192434-19386-1-git-send-email-satish.patel@ti.com>
- Board specific DT entries for TI's USIM - smart card controller of AM43xx
platfrom.These entries are used by USIM driver for various configurations.
- Shutdown line of NXP phy is maped to GPIO5. So enabling same to have support
for NXP phy.
- i2c2 pinmux configuration - NxP tda8026 phy is connected to i2c2 lines
Signed-off-by: Satish Patel <satish.patel@ti.com>
---
arch/arm/boot/dts/am43x-epos-evm.dts | 43 ++++++++++++++++++++++++++++++++++
1 files changed, 43 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index fbf9c4c..38ef2b8 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -79,6 +79,24 @@
0x18c (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE0) /* i2c0_scl.i2c0_scl */
>;
};
+
+ i2c2_pins: pinmux_i2c2_pins {
+ pinctrl-single,pins = <
+ 0x1c0 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE8) /* i2c2_sda.i2c2_sda */
+ 0x1c4 (PIN_INPUT_PULLUP | SLEWCTRL_FAST | MUX_MODE8) /* i2c2_scl.i2c2_scl */
+ >;
+ };
+
+ usim0_default: usim0_default {
+ pinctrl-single,pins = <
+ /* USIM 0 */
+ 0x1B4 (SLEWCTRL_FAST | PULL_DISABLE | MUX_MODE8) /* CLK0 */
+ 0x1B0 (SLEWCTRL_FAST | PULL_DISABLE | MUX_MODE8) /* CLK1 */
+ 0x1B8 (SLEWCTRL_FAST | INPUT_EN | PULL_DISABLE | MUX_MODE8) /* DATA0 */
+ 0x1BC (SLEWCTRL_FAST | INPUT_EN | PULL_DISABLE | MUX_MODE8) /* DATA1 */
+ 0x1C8 (SLEWCTRL_FAST | INPUT_EN | PULL_UP | MUX_MODE8) /* IRQn */
+ >;
+ };
};
matrix_keypad: matrix_keypad at 0 {
@@ -169,6 +187,27 @@
};
};
+&usim0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&usim0_default>;
+ phy = <&tda8026>;
+ phy-slots = <1>;
+ status = "okay";
+};
+
+&i2c2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2c2_pins>;
+ status = "okay";
+
+ tda8026: tda8026 at 48 {
+ compatible = "nxp,tda8026";
+ reg = <0x48>;
+ shutdown-gpio = <&gpio5 19 GPIO_ACTIVE_HIGH>; /* Bank5, pin19 */
+ interrupts = <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+ };
+};
+
&gpio0 {
status = "okay";
};
@@ -184,3 +223,7 @@
&gpio3 {
status = "okay";
};
+
+&gpio5 {
+ status = "okay";
+};
--
1.7.1
^ permalink raw reply related
* [PATCH v2 4/5] ARM: dts: AM43xx: DT entries added for ti-usim
From: Satish Patel @ 2014-01-20 4:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390192434-19386-1-git-send-email-satish.patel@ti.com>
SoC specific DT entries added for TI's USIM - smart card controller of AM43xx
platfrom.
Signed-off-by: Satish Patel <satish.patel@ti.com>
---
arch/arm/boot/dts/am4372.dtsi | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index 974d103..7ed1cd1 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -663,5 +663,15 @@
<&edma 11>;
dma-names = "tx", "rx";
};
+
+ usim0: usim at 48034000 {
+ compatible = "ti,usim";
+ reg = <0x48034000 0x1000>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "usim0";
+ clocks = <&usim0_opt_fck>, <&usim0_opt_fck32>;
+ clock-names = "opt_fck", "opt_fck32";
+ status = "disabled";
+ };
};
};
--
1.7.1
^ permalink raw reply related
* [PATCH v2 3/5] char: ti-usim: Add driver for USIM module on AM43xx
From: Satish Patel @ 2014-01-20 4:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390192434-19386-1-git-send-email-satish.patel@ti.com>
TI-USIM driver is a platform driver that provides a character
driver interface to user applications.
It allows user applications to call IOCTL's to
perform smart card operations.
Driver currently supports
- ATR
- T=0 & T=1 protocol
- clock stop mode
- smart card clock configuration
- Tx/Rx application data units (APDU) to smart card
- Interface to PHY using DT & phy interface
Validation is done with ACOS3 smart cards
Signed-off-by: Satish Patel <satish.patel@ti.com>
---
.../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
drivers/char/Kconfig | 7 +
drivers/char/Makefile | 1 +
drivers/char/ti-usim-hw.h | 863 +++++++++
drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
include/linux/ti-usim.h | 98 +
6 files changed, 2859 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
create mode 100644 drivers/char/ti-usim-hw.h
create mode 100644 drivers/char/ti-usim.c
create mode 100644 include/linux/ti-usim.h
diff --git a/Documentation/devicetree/bindings/ti-usim/ti-usim.txt b/Documentation/devicetree/bindings/ti-usim/ti-usim.txt
new file mode 100644
index 0000000..6dc5d9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/ti-usim/ti-usim.txt
@@ -0,0 +1,31 @@
+ti-usim: USIM - Smart Card Controller
+
+Required Properties:
+- compatible: Should be "ti,usim"
+- reg: Specifies base physical address and size of the USIM registers
+- interrupts: Interrupt number for the USIM controller
+- ti,hwmods: Name of the hwmod associated to the USIM controller
+
+- clocks : list of clock specifiers, corresponding to entries in the
+ clock-names property
+- clock-names : should contain "opt_fck" and "opt_fck32" entries, matching
+ entries in the clocks property.
+
+Optional properties:
+- pinctrl-0: Should specify pin control group used for this controller.
+- pinctrl-names: Should contain only one value - "default", for more details
+ please refer to pinctrl-bindings.txt
+
+- phy : Should specify <smart card phy> reference connected to controller
+- phy-slots : No of slots to which controller will communicate
+
+Example:
+
+usim0: usim at 48034000 {
+ compatible = "ti,usim";
+ reg = <0x48034000 0x1000>;
+ interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+ ti,hwmods = "usim0";
+ clocks = <&usim0_opt_fck>, <&usim0_opt_fck32>;
+ clock-names = "opt_fck", "opt_fck32";
+ };
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index fa3243d..dee0209 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -599,5 +599,12 @@ config TILE_SROM
device appear much like a simple EEPROM, and knows
how to partition a single ROM for multiple purposes.
+config TI_USIM
+ tristate "Character device access to TI's USIM module on AM43X"
+ depends on SOC_AM43XX
+ help
+ This device creates a character device interface that enables
+ user applications to exchange data with TI's USIM module.
+
endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 7ff1d0d..763fd3d 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_JS_RTC) += js-rtc.o
js-rtc-y = rtc.o
obj-$(CONFIG_TILE_SROM) += tile-srom.o
+obj-$(CONFIG_TI_USIM) += ti-usim.o
diff --git a/drivers/char/ti-usim-hw.h b/drivers/char/ti-usim-hw.h
new file mode 100644
index 0000000..b6d69ff
--- /dev/null
+++ b/drivers/char/ti-usim-hw.h
@@ -0,0 +1,863 @@
+/*
+ * ti-usim-hw.h - Header file for USIM smart card interface
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __TI_USIM_HW_H__
+#define __TI_USIM_HW_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/sc_phy.h>
+#include <linux/ti-usim.h>
+
+
+#define USIM_MAX_SLOTS 0x2
+
+/* WWT Work Wait Time */
+#define USIM_EMV_WI (10)
+#define USIM_EMV_WWT ((960 * USIM_EMV_WI) + (480))
+/* CGT Character Guard Time */
+#define USIM_EMV_CGT (12)
+
+#define USIM_ATR_TIMEOUT_EMV (20160)
+#define USIM_EMV_ATR_EARLY_TO (370)
+#define USIM_EMV_ATR_MUTE_TO (42000)
+
+#define USIM_MAX_RX_FIFO_SIZE (260)
+#define USIM_MAX_TX_FIFO_SIZE (260)
+#define USIM_MAX_PARITY_RETRIES (7)
+
+#define USIM_IRQ_NATR (0x00000001)
+#define USIM_IRQ_WT (0x00000002)
+#define USIM_IRQ_RXFULL (0x00000004)
+#define USIM_IRQ_TX (0x00000008)
+#define USIM_IRQ_RX (0x00000010)
+#define USIM_IRQ_CD (0x00000020)
+#define USIM_IRQ_EOB (0x00000040)
+#define USIM_IRQ_TOC (0x00000080)
+#define USIM_IRQ_TOB (0x00000100)
+#define USIM_IRQ_RESENT (0x00000200)
+#define USIM_IRQ_TS_ERR (0x00000400)
+#define USIM_IRQ_EMV_ATR_LENGTH_TIME_OUT (0x00000800)
+#define USIM_IRQ_STOP (0x00001000)
+#define USIM_IRQ_PAR_ERR_LEVEL_REACHED (0x00002000)
+#define USIM_IRQ_FRAME_ERR (0x00004000)
+#define USIM_IRQ_RXDMA_RDY (0x00008000)
+#define USIM_IRQ_ATR_START (0x00010000)
+#define USIM_IRQ_ACT_DONE (0x00020000)
+#define USIM_IRQ_DEACT_DONE (0x00040000)
+#define USIM_IRQ_TX_BLOCK_DONE (0x00080000)
+#define USIM_IRQ_TX_BLOCK_REQ (0x00100000)
+
+#define USIM_CONFSCLKMODE_LEGACY 0x0
+#define USIM_CONFSCLKMODE_HF 0x1
+
+/*
+ * Different operating modes supported in USIM.
+ * Programming USIM to a different mode from current mode would
+ * endup in state machine state change within the IPs FSM
+ */
+enum usim_mode {
+ USIM_MODE_LEGACY = 0x0,
+ USIM_MODE_FREEZE = 0x1,
+ USIM_MODE_TXRX = 0x2,
+ USIM_MODE_ATR = 0x3,
+ USIM_MODE_ACT = 0x4,
+ USIM_MODE_DEACT = 0x5,
+ USIM_MODE_IDLE = 0x6,
+};
+
+/*
+ * structure to store slot specific information
+ */
+struct usim_slotcontext {
+ char atr[USIM_MAX_ATRLENGTH];
+ char rxbuf[USIM_MAX_APDU_LENGTH];
+ bool emv;
+ enum usim_mode state;
+ int event;
+ int protocol;
+ enum usim_card_voltage supply;
+ int rx_explen;
+ int rx_counter;
+ int atr_length;
+ enum usim_smartcard_clock clock;
+};
+
+struct usim {
+ struct device *dev;
+
+ /* to protect interrput handling */
+ spinlock_t lock;
+ int irq;
+ void __iomem *base;
+ int slot;
+ int max_slots;
+ int phy_present;
+ int txdone;
+ int rxdone;
+ int atrdone;
+ int user_pid;
+ int enable;
+ struct sc_phy *phy;
+ struct usim_slotcontext *slot_ctx;
+
+ struct clk *opt_fclk;
+ struct clk *opt_fclk32;
+ struct clk *usim_dbclk;
+ struct clk *clkdiv32k_ick;
+ struct clk *usim0_fck;
+ struct clk *dpll_core_m4_ck;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_root;
+#endif
+};
+
+/*
+ * Register Definitions: Taken from auto generated file
+ */
+#define USIM_REVISION (0x0U)
+#define USIM_IDENT (0x4U)
+#define USIM_SYSCONFIG (0x10U)
+#define USIM_SYSSTATUS (0x14U)
+#define USIM_IRQSTATUS (0x18U)
+#define USIM_IRQENABLE (0x1cU)
+#define USIM_WAKEUPEN (0x20U)
+#define USIM_CMD (0x24U)
+#define USIM_STAT (0x28U)
+#define USIM_CONF1 (0x2cU)
+#define USIM_CONF2 (0x30U)
+#define USIM_CONF3 (0x34U)
+#define USIM_DRX (0x38U)
+#define USIM_DTX (0x3cU)
+#define USIM_FIFOS (0x40U)
+#define USIM_CGT (0x44U)
+#define USIM_CWT (0x48U)
+#define USIM_BWT (0x4cU)
+#define USIM_DEBUG (0x50U)
+#define USIM_CONF_SAM1_DIV (0x54U)
+#define USIM_CONF4 (0x58U)
+#define USIM_ATR_CLK_PRD_NBS (0x5cU)
+#define USIM_CONF_ETU_DIV (0x60U)
+#define USIM_CONF5 (0x64U)
+#define USIM_TC_GUARD_TIME_ADD (0x68U)
+#define USIM_RXFIFO_LEVEL (0x6cU)
+#define USIM_RXFIFO_BYTECNT (0x70U)
+#define USIM_WWT (0x74U)
+#define USIM_CONF6 (0x78U)
+#define USIM_IO_DIRECT (0x7cU)
+#define USIM_TX_BLOCK (0x84U)
+
+/*
+ * Field Definition Macros
+ */
+#define USIM_REVISION_REV_SHIFT (0U)
+#define USIM_REVISION_REV_MASK (0x000000ffU)
+
+#define USIM_REVISION_RESERVED_24_SHIFT (8U)
+#define USIM_REVISION_RESERVED_24_MASK (0xffffff00U)
+
+#define USIM_IDENT_VC_SHIFT (0U)
+#define USIM_IDENT_VC_MASK (0x0000ffffU)
+
+#define USIM_IDENT_RESERVED_16_31_SHIFT (16U)
+#define USIM_IDENT_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_SYSCONFIG_AUTOIDLE_SHIFT (0U)
+#define USIM_SYSCONFIG_AUTOIDLE_MASK (0x00000001U)
+#define USIM_SYSCONFIG_AUTOIDLE_AUTOIDLE_VALUE_1 (1U)
+#define USIM_SYSCONFIG_AUTOIDLE_AUTOIDLE_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_SOFTRESET_SHIFT (1U)
+#define USIM_SYSCONFIG_SOFTRESET_MASK (0x00000002U)
+#define USIM_SYSCONFIG_SOFTRESET_SOFTRESET_VALUE_1 (1U)
+#define USIM_SYSCONFIG_SOFTRESET_SOFTRESET_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_ENAWAKEUP_SHIFT (2U)
+#define USIM_SYSCONFIG_ENAWAKEUP_MASK (0x00000004U)
+#define USIM_SYSCONFIG_ENAWAKEUP_ENAWAKEUP_VALUE_1 (1U)
+#define USIM_SYSCONFIG_ENAWAKEUP_ENAWAKEUP_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_IDLEMODE_SHIFT (3U)
+#define USIM_SYSCONFIG_IDLEMODE_MASK (0x00000018U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_3 (3U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_2 (2U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_1 (1U)
+#define USIM_SYSCONFIG_IDLEMODE_IDLEMODE_VALUE_0 (0U)
+
+#define USIM_SYSCONFIG_EMUFREE_SHIFT (5U)
+#define USIM_SYSCONFIG_EMUFREE_MASK (0x00000020U)
+#define USIM_SYSCONFIG_EMUFREE_EMUFREE_VALUE_0 (0U)
+#define USIM_SYSCONFIG_EMUFREE_EMUFREE_VALUE_1 (1U)
+
+#define USIM_SYSCONFIG_RESERVED_6_7_SHIFT (6U)
+#define USIM_SYSCONFIG_RESERVED_6_7_MASK (0x000000c0U)
+
+#define USIM_SYSCONFIG_CLOCKACTIVITY_SHIFT (8U)
+#define USIM_SYSCONFIG_CLOCKACTIVITY_MASK (0x00000300U)
+
+#define USIM_SYSCONFIG_RESERVED_22_SHIFT (10U)
+#define USIM_SYSCONFIG_RESERVED_22_MASK (0xfffffc00U)
+
+#define USIM_SYSSTATUS_RESETDONE_SHIFT (0U)
+#define USIM_SYSSTATUS_RESETDONE_MASK (0x00000001U)
+#define USIM_SYSSTATUS_RESETDONE_RESETDONE_VALUE_1 (1U)
+#define USIM_SYSSTATUS_RESETDONE_RESETDONE_VALUE_0 (0U)
+
+#define USIM_SYSSTATUS_RESERVED_31_SHIFT (1U)
+#define USIM_SYSSTATUS_RESERVED_31_MASK (0xfffffffeU)
+
+#define USIM_IRQSTATUS_USIM_NATR_SHIFT (0U)
+#define USIM_IRQSTATUS_USIM_NATR_MASK (0x00000001U)
+
+#define USIM_IRQSTATUS_USIM_WT_SHIFT (1U)
+#define USIM_IRQSTATUS_USIM_WT_MASK (0x00000002U)
+
+#define USIM_IRQSTATUS_USIM_RXFULL_SHIFT (2U)
+#define USIM_IRQSTATUS_USIM_RXFULL_MASK (0x00000004U)
+
+#define USIM_IRQSTATUS_USIM_TX_SHIFT (3U)
+#define USIM_IRQSTATUS_USIM_TX_MASK (0x00000008U)
+
+#define USIM_IRQSTATUS_USIM_RX_SHIFT (4U)
+#define USIM_IRQSTATUS_USIM_RX_MASK (0x00000010U)
+
+#define USIM_IRQSTATUS_USIM_CD_SHIFT (5U)
+#define USIM_IRQSTATUS_USIM_CD_MASK (0x00000020U)
+
+#define USIM_IRQSTATUS_USIM_EOB_SHIFT (6U)
+#define USIM_IRQSTATUS_USIM_EOB_MASK (0x00000040U)
+
+#define USIM_IRQSTATUS_USIM_TOC_SHIFT (7U)
+#define USIM_IRQSTATUS_USIM_TOC_MASK (0x00000080U)
+
+#define USIM_IRQSTATUS_USIM_TOB_SHIFT (8U)
+#define USIM_IRQSTATUS_USIM_TOB_MASK (0x00000100U)
+
+#define USIM_IRQSTATUS_USIM_RESENT_SHIFT (9U)
+#define USIM_IRQSTATUS_USIM_RESENT_MASK (0x00000200U)
+
+#define USIM_IRQSTATUS_TS_ERROR_SHIFT (10U)
+#define USIM_IRQSTATUS_TS_ERROR_MASK (0x00000400U)
+
+#define USIM_IRQSTATUS_IT_EMV_ATR_LENGTH_TIME_OUT_SHIFT (11U)
+#define USIM_IRQSTATUS_IT_EMV_ATR_LENGTH_TIME_OUT_MASK (0x00000800U)
+
+#define USIM_IRQSTATUS_RESERVED_SHIFT (21U)
+#define USIM_IRQSTATUS_RESERVED_MASK (0xffe00000U)
+
+#define USIM_IRQSTATUS_USIM_STOP_CLK_SHIFT (12U)
+#define USIM_IRQSTATUS_USIM_STOP_CLK_MASK (0x00001000U)
+
+#define USIM_IRQSTATUS_PAR_ERR_LEVEL_REACHED_SHIFT (13U)
+#define USIM_IRQSTATUS_PAR_ERR_LEVEL_REACHED_MASK (0x00002000U)
+
+#define USIM_IRQSTATUS_FRAME_ERR_SHIFT (14U)
+#define USIM_IRQSTATUS_FRAME_ERR_MASK (0x00004000U)
+
+#define USIM_IRQSTATUS_RXDMA_RDY_SHIFT (15U)
+#define USIM_IRQSTATUS_RXDMA_RDY_MASK (0x00008000U)
+
+#define USIM_IRQSTATUS_ATR_START_SHIFT (16U)
+#define USIM_IRQSTATUS_ATR_START_MASK (0x00010000U)
+
+#define USIM_IRQSTATUS_ACT_DONE_SHIFT (17U)
+#define USIM_IRQSTATUS_ACT_DONE_MASK (0x00020000U)
+
+#define USIM_IRQSTATUS_DEACT_DONE_SHIFT (18U)
+#define USIM_IRQSTATUS_DEACT_DONE_MASK (0x00040000U)
+
+#define USIM_IRQSTATUS_TX_BLOCK_DONE_SHIFT (19U)
+#define USIM_IRQSTATUS_TX_BLOCK_DONE_MASK (0x00080000U)
+
+#define USIM_IRQSTATUS_TX_BLOCK_REQ_SHIFT (20U)
+#define USIM_IRQSTATUS_TX_BLOCK_REQ_MASK (0x00100000U)
+
+#define USIM_IRQENABLE_RESERVED_SHIFT (21U)
+#define USIM_IRQENABLE_RESERVED_MASK (0xffe00000U)
+
+#define USIM_IRQENABLE_EMV_ATR_LENGTH_TIME_OUT_EN_SHIFT (11U)
+#define USIM_IRQENABLE_EMV_ATR_LENGTH_TIME_OUT_EN_MASK (0x00000800U)
+
+#define USIM_IRQENABLE_TS_ERR_EN_SHIFT (10U)
+#define USIM_IRQENABLE_TS_ERR_EN_MASK (0x00000400U)
+
+#define USIM_IRQENABLE_RESENT_EN_SHIFT (9U)
+#define USIM_IRQENABLE_RESENT_EN_MASK (0x00000200U)
+
+#define USIM_IRQENABLE_TOB_EN_SHIFT (8U)
+#define USIM_IRQENABLE_TOB_EN_MASK (0x00000100U)
+
+#define USIM_IRQENABLE_TOC_EN_SHIFT (7U)
+#define USIM_IRQENABLE_TOC_EN_MASK (0x00000080U)
+
+#define USIM_IRQENABLE_EOB_EN_SHIFT (6U)
+#define USIM_IRQENABLE_EOB_EN_MASK (0x00000040U)
+
+#define USIM_IRQENABLE_CD_EN_SHIFT (5U)
+#define USIM_IRQENABLE_CD_EN_MASK (0x00000020U)
+
+#define USIM_IRQENABLE_RX_EN_SHIFT (4U)
+#define USIM_IRQENABLE_RX_EN_MASK (0x00000010U)
+
+#define USIM_IRQENABLE_TX_EN_SHIFT (3U)
+#define USIM_IRQENABLE_TX_EN_MASK (0x00000008U)
+
+#define USIM_IRQENABLE_RXFULL_EN_SHIFT (2U)
+#define USIM_IRQENABLE_RXFULL_EN_MASK (0x00000004U)
+
+#define USIM_IRQENABLE_WT_EN_SHIFT (1U)
+#define USIM_IRQENABLE_WT_EN_MASK (0x00000002U)
+
+#define USIM_IRQENABLE_NATR_EN_SHIFT (0U)
+#define USIM_IRQENABLE_NATR_EN_MASK (0x00000001U)
+
+#define USIM_IRQENABLE_STOP_CLK_SHIFT (12U)
+#define USIM_IRQENABLE_STOP_CLK_MASK (0x00001000U)
+
+#define USIM_IRQENABLE_PAR_ERR_LEVEL_REACHED_EN_SHIFT (13U)
+#define USIM_IRQENABLE_PAR_ERR_LEVEL_REACHED_EN_MASK (0x00002000U)
+
+#define USIM_IRQENABLE_FRAME_ERR_EN_SHIFT (14U)
+#define USIM_IRQENABLE_FRAME_ERR_EN_MASK (0x00004000U)
+
+#define USIM_IRQENABLE_RXDMA_RDY_EN_SHIFT (15U)
+#define USIM_IRQENABLE_RXDMA_RDY_EN_MASK (0x00008000U)
+
+#define USIM_IRQENABLE_ATR_START_EN_SHIFT (16U)
+#define USIM_IRQENABLE_ATR_START_EN_MASK (0x00010000U)
+
+#define USIM_IRQENABLE_ACT_DONE_EN_SHIFT (17U)
+#define USIM_IRQENABLE_ACT_DONE_EN_MASK (0x00020000U)
+
+#define USIM_IRQENABLE_DEACT_DONE_EN_SHIFT (18U)
+#define USIM_IRQENABLE_DEACT_DONE_EN_MASK (0x00040000U)
+
+#define USIM_IRQENABLE_TX_BLOCK_DONE_EN_SHIFT (19U)
+#define USIM_IRQENABLE_TX_BLOCK_DONE_EN_MASK (0x00080000U)
+
+#define USIM_IRQENABLE_TX_BLOCK_REQ_EN_SHIFT (20U)
+#define USIM_IRQENABLE_TX_BLOCK_REQ_EN_MASK (0x00100000U)
+
+#define USIM_WAKEUPEN_STOP_CLK_SHIFT (12U)
+#define USIM_WAKEUPEN_STOP_CLK_MASK (0x00001000U)
+
+#define USIM_WAKEUPEN_NATR_EN_SHIFT (0U)
+#define USIM_WAKEUPEN_NATR_EN_MASK (0x00000001U)
+
+#define USIM_WAKEUPEN_WT_EN_SHIFT (1U)
+#define USIM_WAKEUPEN_WT_EN_MASK (0x00000002U)
+
+#define USIM_WAKEUPEN_RXFULL_EN_SHIFT (2U)
+#define USIM_WAKEUPEN_RXFULL_EN_MASK (0x00000004U)
+
+#define USIM_WAKEUPEN_TX_EN_SHIFT (3U)
+#define USIM_WAKEUPEN_TX_EN_MASK (0x00000008U)
+
+#define USIM_WAKEUPEN_RX_EN_SHIFT (4U)
+#define USIM_WAKEUPEN_RX_EN_MASK (0x00000010U)
+
+#define USIM_WAKEUPEN_CD_EN_SHIFT (5U)
+#define USIM_WAKEUPEN_CD_EN_MASK (0x00000020U)
+
+#define USIM_WAKEUPEN_EOB_EN_SHIFT (6U)
+#define USIM_WAKEUPEN_EOB_EN_MASK (0x00000040U)
+
+#define USIM_WAKEUPEN_TOC_EN_SHIFT (7U)
+#define USIM_WAKEUPEN_TOC_EN_MASK (0x00000080U)
+
+#define USIM_WAKEUPEN_TOB_EN_SHIFT (8U)
+#define USIM_WAKEUPEN_TOB_EN_MASK (0x00000100U)
+
+#define USIM_WAKEUPEN_RESENT_EN_SHIFT (9U)
+#define USIM_WAKEUPEN_RESENT_EN_MASK (0x00000200U)
+
+#define USIM_WAKEUPEN_TS_ERR_EN_SHIFT (10U)
+#define USIM_WAKEUPEN_TS_ERR_EN_MASK (0x00000400U)
+
+#define USIM_WAKEUPEN_EMV_ATR_LENGTH_TIME_OUT_EN_SHIFT (11U)
+#define USIM_WAKEUPEN_EMV_ATR_LENGTH_TIME_OUT_EN_MASK (0x00000800U)
+
+#define USIM_WAKEUPEN_RESERVED_SHIFT (21U)
+#define USIM_WAKEUPEN_RESERVED_MASK (0xffe00000U)
+
+#define USIM_WAKEUPEN_PAR_ERR_LEVEL_REACHED_EN_SHIFT (13U)
+#define USIM_WAKEUPEN_PAR_ERR_LEVEL_REACHED_EN_MASK (0x00002000U)
+
+#define USIM_WAKEUPEN_FRAME_ERR_EN_SHIFT (14U)
+#define USIM_WAKEUPEN_FRAME_ERR_EN_MASK (0x00004000U)
+
+#define USIM_WAKEUPEN_RXDMA_RDY_EN_SHIFT (15U)
+#define USIM_WAKEUPEN_RXDMA_RDY_EN_MASK (0x00008000U)
+
+#define USIM_WAKEUPEN_ATR_START_EN_SHIFT (16U)
+#define USIM_WAKEUPEN_ATR_START_EN_MASK (0x00010000U)
+
+#define USIM_WAKEUPEN_ACT_DONE_EN_SHIFT (17U)
+#define USIM_WAKEUPEN_ACT_DONE_EN_MASK (0x00020000U)
+
+#define USIM_WAKEUPEN_DEACT_DONE_EN_SHIFT (18U)
+#define USIM_WAKEUPEN_DEACT_DONE_EN_MASK (0x00040000U)
+
+#define USIM_WAKEUPEN_TX_BLOCK_DONE_EN_SHIFT (19U)
+#define USIM_WAKEUPEN_TX_BLOCK_DONE_EN_MASK (0x00080000U)
+
+#define USIM_WAKEUPEN_TX_BLOCK_REQ_EN_SHIFT (20U)
+#define USIM_WAKEUPEN_TX_BLOCK_REQ_EN_MASK (0x00100000U)
+
+#define USIM_CMD_RESERVED_0_SHIFT (0U)
+#define USIM_CMD_RESERVED_0_MASK (0x00000001U)
+
+#define USIM_CMD_CMDSTOP_SHIFT (1U)
+#define USIM_CMD_CMDSTOP_MASK (0x00000002U)
+#define USIM_CMD_CMDSTOP_CMDSTOP_VALUE_0 (0U)
+#define USIM_CMD_CMDSTOP_CMDSTOP_VALUE_1 (1U)
+
+#define USIM_CMD_CMDSTART_SHIFT (2U)
+#define USIM_CMD_CMDSTART_MASK (0x00000004U)
+#define USIM_CMD_CMDSTART_CMDSTART_VALUE_0 (0U)
+#define USIM_CMD_CMDSTART_CMDSTART_VALUE_1 (1U)
+
+#define USIM_CMD_MODULE_CLK_EN_SHIFT (3U)
+#define USIM_CMD_MODULE_CLK_EN_MASK (0x00000008U)
+#define USIM_CMD_MODULE_CLK_EN_0 (0U)
+#define USIM_CMD_MODULE_CLK_EN_1 (1U)
+
+#define USIM_CMD_CMD_WARM_RST_SHIFT (4U)
+#define USIM_CMD_CMD_WARM_RST_MASK (0x00000010U)
+#define USIM_CMD_CMD_WARM_RST_WARM_RST_VALUE_1 (1U)
+#define USIM_CMD_CMD_WARM_RST_WARM_RST_VALUE_0 (0U)
+
+#define USIM_CMD_CMD_CLOCK_STOP_SHIFT (5U)
+#define USIM_CMD_CMD_CLOCK_STOP_MASK (0x00000020U)
+#define USIM_CMD_CMD_CLOCK_STOP_1 (1U)
+#define USIM_CMD_CMD_CLOCK_STOP_0 (0U)
+
+#define USIM_CMD_RESERVED_7_31_SHIFT (7U)
+#define USIM_CMD_RESERVED_7_31_MASK (0xffffff80U)
+
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_SHIFT (6U)
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_MASK (0x00000040U)
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_1 (1U)
+#define USIM_CMD_STOP_EMV_ATR_LENGTH_TIMER_0 (0U)
+
+#define USIM_STAT_STATNOCARD_SHIFT (0U)
+#define USIM_STAT_STATNOCARD_MASK (0x00000001U)
+#define USIM_STAT_STATNOCARD_STATNOCARD_VALUE_1 (1U)
+#define USIM_STAT_STATNOCARD_STATNOCARD_VALUE_0 (0U)
+#define USIM_STAT_STATNOCARD_WRITE0 (0U)
+#define USIM_STAT_STATNOCARD_WRITE1 (1U)
+
+#define USIM_STAT_RESERVED_7_31_SHIFT (7U)
+#define USIM_STAT_RESERVED_7_31_MASK (0xffffff80U)
+
+#define USIM_STAT_STATTXPAR_SHIFT (1U)
+#define USIM_STAT_STATTXPAR_MASK (0x00000002U)
+#define USIM_STAT_STATTXPAR_STATTXPAR_VALUE_1 (1U)
+#define USIM_STAT_STATTXPAR_STATTXPAR_VALUE_0 (0U)
+
+#define USIM_STAT_STATLRC_SHIFT (2U)
+#define USIM_STAT_STATLRC_MASK (0x00000004U)
+#define USIM_STAT_STATLRC_STATLRC_VALUE_1 (1U)
+#define USIM_STAT_STATLRC_STATLRC_VALUE_0 (0U)
+
+#define USIM_STAT_CONFCODCONV_SHIFT (3U)
+#define USIM_STAT_CONFCODCONV_MASK (0x00000008U)
+#define USIM_STAT_CONFCODCONV_CONFCODCONV_VALUE_1 (1U)
+#define USIM_STAT_CONFCODCONV_CONFCODCONV_VALUE_0 (0U)
+
+#define USIM_STAT_RESERVED_SHIFT (4U)
+#define USIM_STAT_RESERVED_MASK (0x00000010U)
+
+#define USIM_STAT_FDDEACTSTATE_SHIFT (5U)
+#define USIM_STAT_FDDEACTSTATE_MASK (0x00000020U)
+#define USIM_STAT_FDDEACTSTATE_FDDEACTSTATE_VALUE_0 (0U)
+#define USIM_STAT_FDDEACTSTATE_FDDEACTSTATE_VALUE_1 (1U)
+
+#define USIM_STAT_ATRRX_AFTER_TIMEOUT_SHIFT (6U)
+#define USIM_STAT_ATRRX_AFTER_TIMEOUT_MASK (0x00000040U)
+
+#define USIM_CONF1_CONFSIOLOW_SHIFT (1U)
+#define USIM_CONF1_CONFSIOLOW_MASK (0x00000002U)
+#define USIM_CONF1_CONFSIOLOW_CONFSIOLOW_VALUE_0 (0U)
+#define USIM_CONF1_CONFSIOLOW_CONFSIOLOW_VALUE_1 (1U)
+
+#define USIM_CONF1_RESERVED_8_31_SHIFT (8U)
+#define USIM_CONF1_RESERVED_8_31_MASK (0xffffff00U)
+
+#define USIM_CONF1_SCLKLEV_SHIFT (0U)
+#define USIM_CONF1_SCLKLEV_MASK (0x00000001U)
+#define USIM_CONF1_SCLKLEV_SCLKLEV_VALUE_0 (0U)
+#define USIM_CONF1_SCLKLEV_SCLKLEV_VALUE_1 (1U)
+
+#define USIM_CONF1_CONFBYPASS_SHIFT (2U)
+#define USIM_CONF1_CONFBYPASS_MASK (0x00000004U)
+#define USIM_CONF1_CONFBYPASS_CONFBYPASS_VALUE_0 (0U)
+#define USIM_CONF1_CONFBYPASS_CONFBYPASS_VALUE_1 (1U)
+
+#define USIM_CONF1_SVCCLEV_SHIFT (3U)
+#define USIM_CONF1_SVCCLEV_MASK (0x00000008U)
+#define USIM_CONF1_SVCCLEV_SVCCLEV_VALUE_0 (0U)
+#define USIM_CONF1_SVCCLEV_SVCCLEV_VALUE_1 (1U)
+
+#define USIM_CONF1_SRSTLEV_SHIFT (4U)
+#define USIM_CONF1_SRSTLEV_MASK (0x00000010U)
+#define USIM_CONF1_SRSTLEV_SRSTLEV_VALUE_0 (0U)
+#define USIM_CONF1_SRSTLEV_SRSTLEV_VALUE_1 (1U)
+
+#define USIM_CONF1_CONF_SCLK_EN_SHIFT (5U)
+#define USIM_CONF1_CONF_SCLK_EN_MASK (0x00000020U)
+#define USIM_CONF1_CONF_SCLK_EN_CONF_SCLK_EN_VALUE_0 (0U)
+#define USIM_CONF1_CONF_SCLK_EN_CONF_SCLK_EN_VALUE_1 (1U)
+#define USIM_CONF1_EMV_CONF_SHIFT (6U)
+#define USIM_CONF1_EMV_CONF_MASK (0x00000040U)
+#define USIM_CONF1_EMV_CONF_EMV_CONF_VALUE_0 (0U)
+#define USIM_CONF1_EMV_CONF_EMV_CONF_VALUE_1 (1U)
+#define USIM_CONF1_BYPASS_HW_AUTO_SHIFT (7U)
+#define USIM_CONF1_BYPASS_HW_AUTO_MASK (0x00000080U)
+#define USIM_CONF1_BYPASS_HW_AUTO_BYPASS_HW_AUTO_VALUE_0 (0U)
+#define USIM_CONF1_BYPASS_HW_AUTO_BYPASS_HW_AUTO_VALUE_1 (1U)
+
+#define USIM_CONF2_CONFCHKPAR_SHIFT (0U)
+#define USIM_CONF2_CONFCHKPAR_MASK (0x00000001U)
+#define USIM_CONF2_CONFCHKPAR_CONFCHKPAR_VALUE_0 (0U)
+#define USIM_CONF2_CONFCHKPAR_CONFCHKPAR_VALUE_1 (1U)
+#define USIM_CONF2_RESERVED_22_31_SHIFT (22U)
+#define USIM_CONF2_RESERVED_22_31_MASK (0xffc00000U)
+#define USIM_CONF2_TX_EN_SHIFT (1U)
+#define USIM_CONF2_TX_EN_MASK (0x00000002U)
+#define USIM_CONF2_CONFSCLKDIV_SHIFT (2U)
+#define USIM_CONF2_CONFSCLKDIV_MASK (0x0000000cU)
+#define USIM_CONF2_ATR_ASYN_BYPASS_SHIFT (4U)
+#define USIM_CONF2_ATR_ASYN_BYPASS_MASK (0x00000010U)
+#define USIM_CONF2_ATR_ASYN_BYPASS_ATR_ASYN_BYPASS_VALUE_0 (0U)
+#define USIM_CONF2_ATR_ASYN_BYPASS_ATR_ASYN_BYPASS_VALUE_1 (1U)
+#define USIM_CONF2_CONFPROTOCOL_SHIFT (5U)
+#define USIM_CONF2_CONFPROTOCOL_MASK (0x00000020U)
+#define USIM_CONF2_CONFPROTOCOL_CONFPROTOCOL_VALUE_0 (0U)
+#define USIM_CONF2_CONFPROTOCOL_CONFPROTOCOL_VALUE_1 (1U)
+#define USIM_CONF2_CONFEDC_SHIFT (6U)
+#define USIM_CONF2_CONFEDC_MASK (0x00000040U)
+#define USIM_CONF2_CONFEDC_CONFEDC_VALUE_0 (0U)
+#define USIM_CONF2_CONFEDC_CONFEDC_VALUE_1 (1U)
+#define USIM_CONF2_CONFLRCCHECK_SHIFT (7U)
+#define USIM_CONF2_CONFLRCCHECK_MASK (0x00000080U)
+#define USIM_CONF2_CONFLRCCHECK_CONFLRCCHECK_VALUE_0 (0U)
+#define USIM_CONF2_CONFLRCCHECK_CONFLRCCHECK_VALUE_1 (1U)
+#define USIM_CONF2_CONFRESENT_SHIFT (8U)
+#define USIM_CONF2_CONFRESENT_MASK (0x00000700U)
+
+#define USIM_CONF2_CARD_POLARITY_SHIFT (11U)
+#define USIM_CONF2_CARD_POLARITY_MASK (0x00000800U)
+#define USIM_CONF2_CARD_POLARITY_CARD_POLARITY_VALUE_0 (0U)
+#define USIM_CONF2_CARD_POLARITY_CARD_POLARITY_VALUE_1 (1U)
+
+#define USIM_CONF2_HW_DEACTIV_EN_SHIFT (12U)
+#define USIM_CONF2_HW_DEACTIV_EN_MASK (0x00001000U)
+#define USIM_CONF2_HW_DEACTIV_EN_HW_DEACTIV_EN_VALUE_0 (0U)
+#define USIM_CONF2_HW_DEACTIV_EN_HW_DEACTIV_EN_VALUE_1 (1U)
+
+#define USIM_CONF2_DEBOUNCE_EN_SHIFT (13U)
+#define USIM_CONF2_DEBOUNCE_EN_MASK (0x00002000U)
+#define USIM_CONF2_DEBOUNCE_EN_DEBOUNCE_EN_VALUE0 (0U)
+#define USIM_CONF2_DEBOUNCE_EN_DEBOUNCE_EN_VALUE1 (1U)
+
+#define USIM_CONF2_PUT_ERR_IN_FIFO_SHIFT (14U)
+#define USIM_CONF2_PUT_ERR_IN_FIFO_MASK (0x00004000U)
+
+#define USIM_CONF2_NACKING_EN_SHIFT (15U)
+#define USIM_CONF2_NACKING_EN_MASK (0x00008000U)
+#define USIM_CONF2_NACKING_EN_DISABLED (0U)
+#define USIM_CONF2_NACKING_EN_RXFIFO_FULL_NACK (1U)
+
+#define USIM_CONF2_PAR_ERR_LEVEL_SHIFT (16U)
+#define USIM_CONF2_PAR_ERR_LEVEL_MASK (0x00070000U)
+
+#define USIM_CONF2_CONFSCLKMODE_SHIFT (19U)
+#define USIM_CONF2_CONFSCLKMODE_MASK (0x00080000U)
+
+#define USIM_CONF2_STOP_RESEND_FAILURE_SHIFT (20U)
+#define USIM_CONF2_STOP_RESEND_FAILURE_MASK (0x00100000U)
+
+#define USIM_CONF2_STOP_RX_TIMEOUT_SHIFT (21U)
+#define USIM_CONF2_STOP_RX_TIMEOUT_MASK (0x00200000U)
+
+#define USIM_CONF3_TDUSIM_SHIFT (4U)
+#define USIM_CONF3_TDUSIM_MASK (0x000000f0U)
+
+#define USIM_CONF3_TFUSIM_SHIFT (0U)
+#define USIM_CONF3_TFUSIM_MASK (0x0000000fU)
+
+#define USIM_CONF3_RESERVED_8_31_SHIFT (8U)
+#define USIM_CONF3_RESERVED_8_31_MASK (0xffffff00U)
+
+#define USIM_DRX_USIMDRX_SHIFT (0U)
+#define USIM_DRX_USIMDRX_MASK (0x000000ffU)
+
+#define USIM_DRX_STATRXPAR_SHIFT (8U)
+#define USIM_DRX_STATRXPAR_MASK (0x00000100U)
+#define USIM_DRX_STATRXPAR_STATRXPAR_VALUE_1 (1U)
+#define USIM_DRX_STATRXPAR_STATRXPAR_VALUE_0 (0U)
+
+#define USIM_DRX_RESERVED_9_31_SHIFT (9U)
+#define USIM_DRX_RESERVED_9_31_MASK (0xfffffe00U)
+
+#define USIM_DTX_DTX_SHIFT (0U)
+#define USIM_DTX_DTX_MASK (0x000000ffU)
+
+#define USIM_DTX_RESERVED_8_31_SHIFT (8U)
+#define USIM_DTX_RESERVED_8_31_MASK (0xffffff00U)
+
+#define USIM_FIFOS_DMA_MODE_SHIFT (0U)
+#define USIM_FIFOS_DMA_MODE_MASK (0x00000001U)
+#define USIM_FIFOS_DMA_MODE_DMA_MODE_VALUE_1 (1U)
+#define USIM_FIFOS_DMA_MODE_DMA_MODE_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFO_ENABLE_SHIFT (1U)
+#define USIM_FIFOS_FIFO_ENABLE_MASK (0x00000002U)
+#define USIM_FIFOS_FIFO_ENABLE_FIFO_ENABLE_VALUE_1 (1U)
+#define USIM_FIFOS_FIFO_ENABLE_FIFO_ENABLE_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFO_TX_TRIGGER_SHIFT (23U)
+#define USIM_FIFOS_FIFO_TX_TRIGGER_MASK (0xff800000U)
+
+#define USIM_FIFOS_FIFOTX_RESET_SHIFT (6U)
+#define USIM_FIFOS_FIFOTX_RESET_MASK (0x00000040U)
+#define USIM_FIFOS_FIFOTX_RESET_FIFOTX_RESET_VALUE_1 (1U)
+#define USIM_FIFOS_FIFOTX_RESET_FIFOTX_RESET_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFOTX_EMPTY_SHIFT (7U)
+#define USIM_FIFOS_FIFOTX_EMPTY_MASK (0x00000080U)
+#define USIM_FIFOS_FIFOTX_EMPTY_FIFOTX_EMPTY_VALUE_1 (1U)
+#define USIM_FIFOS_FIFOTX_EMPTY_FIFOTX_EMPTY_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFOTX_FULL_SHIFT (8U)
+#define USIM_FIFOS_FIFOTX_FULL_MASK (0x00000100U)
+#define USIM_FIFOS_FIFOTX_FULL_FIFOTX_FULL_VALUE_1 (1U)
+#define USIM_FIFOS_FIFOTX_FULL_FIFOTX_FULL_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFO_RX_TRIGGER_SHIFT (9U)
+#define USIM_FIFOS_FIFO_RX_TRIGGER_MASK (0x0003fe00U)
+
+#define USIM_FIFOS_FIFORX_RESET_SHIFT (18U)
+#define USIM_FIFOS_FIFORX_RESET_MASK (0x00040000U)
+#define USIM_FIFOS_FIFORX_RESET_FIFORX_RESET_VALUE_1 (1U)
+#define USIM_FIFOS_FIFORX_RESET_FIFORX_RESET_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFORX_EMPTY_SHIFT (19U)
+#define USIM_FIFOS_FIFORX_EMPTY_MASK (0x00080000U)
+#define USIM_FIFOS_FIFORX_EMPTY_FIFORX_EMPTY_VALUE_1 (1U)
+#define USIM_FIFOS_FIFORX_EMPTY_FIFORX_EMPTY_VALUE_0 (0U)
+
+#define USIM_FIFOS_FIFORX_FULL_SHIFT (20U)
+#define USIM_FIFOS_FIFORX_FULL_MASK (0x00100000U)
+#define USIM_FIFOS_FIFORX_FULL_FIFORX_FULL_VALUE_1 (1U)
+#define USIM_FIFOS_FIFORX_FULL_FIFORX_FULL_VALUE_0 (0U)
+
+#define USIM_FIFOS_RXDMA_TYPE_SHIFT (21U)
+#define USIM_FIFOS_RXDMA_TYPE_MASK (0x00600000U)
+#define USIM_FIFOS_RXDMA_TYPE_LEGACY (0U)
+#define USIM_FIFOS_RXDMA_TYPE_NEW1 (2U)
+#define USIM_FIFOS_RXDMA_TYPE_NEW2 (3U)
+
+#define USIM_FIFOS_RESERVED_SHIFT (2U)
+#define USIM_FIFOS_RESERVED_MASK (0x0000003cU)
+
+#define USIM_CGT_RESERVED_9_31_SHIFT (9U)
+#define USIM_CGT_RESERVED_9_31_MASK (0xfffffe00U)
+
+#define USIM_CGT_CGT_SHIFT (0U)
+#define USIM_CGT_CGT_MASK (0x000001ffU)
+
+#define USIM_CWT_CWT_SHIFT (0U)
+#define USIM_CWT_CWT_MASK (0xffffffffU)
+
+#define USIM_BWT_BWT_SHIFT (0U)
+#define USIM_BWT_BWT_MASK (0xffffffffU)
+
+#define USIM_DEBUG_RESERVED_25_31_SHIFT (25U)
+#define USIM_DEBUG_RESERVED_25_31_MASK (0xfe000000U)
+
+#define USIM_DEBUG_MAIN_STATE_DEBUG_SHIFT (0U)
+#define USIM_DEBUG_MAIN_STATE_DEBUG_MASK (0x0000000fU)
+
+#define USIM_DEBUG_TX_STATE_MACHINE_SHIFT (4U)
+#define USIM_DEBUG_TX_STATE_MACHINE_MASK (0x00000030U)
+
+#define USIM_DEBUG_RX_STATE_MACHINE_SHIFT (6U)
+#define USIM_DEBUG_RX_STATE_MACHINE_MASK (0x000000c0U)
+
+#define USIM_DEBUG_RXFIFO_PEAK_SHIFT (8U)
+#define USIM_DEBUG_RXFIFO_PEAK_MASK (0x0003ff00U)
+
+#define USIM_DEBUG_RXDMA_SHIFT (18U)
+#define USIM_DEBUG_RXDMA_MASK (0x00040000U)
+
+#define USIM_DEBUG_TXDMA_SHIFT (19U)
+#define USIM_DEBUG_TXDMA_MASK (0x00080000U)
+
+#define USIM_DEBUG_EMV_MAIN_STATE_DEBUG_SHIFT (20U)
+#define USIM_DEBUG_EMV_MAIN_STATE_DEBUG_MASK (0x01f00000U)
+
+#define USIM_CONF_SAM1_DIV_RESERVED_12_31_SHIFT (12U)
+#define USIM_CONF_SAM1_DIV_RESERVED_12_31_MASK (0xfffff000U)
+
+#define USIM_CONF_SAM1_DIV_SAM1_DIV_SHIFT (0U)
+#define USIM_CONF_SAM1_DIV_SAM1_DIV_MASK (0x00000fffU)
+
+#define USIM_CONF4_RESERVED_16_31_SHIFT (16U)
+#define USIM_CONF4_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_CONF4_CONFWAITI_SHIFT (0U)
+#define USIM_CONF4_CONFWAITI_MASK (0x0000ffffU)
+
+#define USIM_ATR_CLK_PRD_NBS_RESERVED_16_31_SHIFT (16U)
+#define USIM_ATR_CLK_PRD_NBS_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_ATR_CLK_PRD_NBS_CLOCK_NUMBER_BEFORE_ATR_SHIFT (0U)
+#define USIM_ATR_CLK_PRD_NBS_CLOCK_NUMBER_BEFORE_ATR_MASK (0x0000ffffU)
+
+#define USIM_CONF_ETU_DIV_RESERVED_16_31_SHIFT (16U)
+#define USIM_CONF_ETU_DIV_RESERVED_16_31_MASK (0xffff0000U)
+
+#define USIM_CONF_ETU_DIV_ETU_DIV_SHIFT (0U)
+#define USIM_CONF_ETU_DIV_ETU_DIV_MASK (0x0000ffffU)
+
+#define USIM_CONF5_RESERVED_12_31_SHIFT (9U)
+#define USIM_CONF5_RESERVED_12_31_MASK (0xfffffe00U)
+
+#define USIM_CONF5_DI_SHIFT (0U)
+#define USIM_CONF5_DI_MASK (0x0000000fU)
+
+#define USIM_CONF5_FI_SHIFT (4U)
+#define USIM_CONF5_FI_MASK (0x000000f0U)
+
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_SHIFT (8U)
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_MASK (0x00000100U)
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_HARDWARE (0U)
+#define USIM_CONF5_SOFT_NHARD_FIDI_PROG_SOFTWARE (1U)
+
+#define USIM_TC_GUARD_TIME_ADD_RESERVED_14_31_SHIFT (14U)
+#define USIM_TC_GUARD_TIME_ADD_RESERVED_14_31_MASK (0xffffc000U)
+
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_SHIFT (13U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_MASK (0x00002000U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_HW (0U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_EN_SW (1U)
+
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_SHIFT (0U)
+#define USIM_TC_GUARD_TIME_ADD_SOFT_TC_GUARD_TIME_ADD_MASK (0x00001fffU)
+
+#define USIM_RXFIFO_LEVEL_USIM_RXFIFO_LEVEL_SHIFT (0U)
+#define USIM_RXFIFO_LEVEL_USIM_RXFIFO_LEVEL_MASK (0x000003ffU)
+
+#define USIM_RXFIFO_LEVEL_RESERVED_SHIFT (10U)
+#define USIM_RXFIFO_LEVEL_RESERVED_MASK (0xfffffc00U)
+
+#define USIM_RXFIFO_BYTECNT_USIM_RXFIFO_BYTECNT_SHIFT (0U)
+#define USIM_RXFIFO_BYTECNT_USIM_RXFIFO_BYTECNT_MASK (0x000001ffU)
+
+#define USIM_RXFIFO_BYTECNT_RESERVED_SHIFT (9U)
+#define USIM_RXFIFO_BYTECNT_RESERVED_MASK (0xfffffe00U)
+
+#define USIM_WWT_WWT_SHIFT (0U)
+#define USIM_WWT_WWT_MASK (0xffffffffU)
+
+#define USIM_CONF6_MODE_SHIFT (0U)
+#define USIM_CONF6_MODE_MASK (0x00000007U)
+#define USIM_CONF6_MODE_NO_OVERRIDE (0U)
+#define USIM_CONF6_MODE_FREEZE (1U)
+#define USIM_CONF6_MODE_RX_TX (2U)
+#define USIM_CONF6_MODE_ATR (3U)
+#define USIM_CONF6_MODE_ACTIVATE (4U)
+#define USIM_CONF6_MODE_DEACTIVATE (5U)
+#define USIM_CONF6_MODE_IDLE (6U)
+#define USIM_CONF6_MODE_RESERVED7 (7U)
+
+#define USIM_CONF6_RST_POLARITY_SHIFT (3U)
+#define USIM_CONF6_RST_POLARITY_MASK (0x00000008U)
+#define USIM_CONF6_RST_POLARITY_ACTIVE_LOW (0U)
+#define USIM_CONF6_RST_POLARITY_ACTIVE_HIGH (1U)
+
+#define USIM_CONF6_RESERVED_SHIFT (12U)
+#define USIM_CONF6_RESERVED_MASK (0x0000f000U)
+
+#define USIM_CONF6_ATR_TIMER_BYPASS_SHIFT (4U)
+#define USIM_CONF6_ATR_TIMER_BYPASS_MASK (0x00000010U)
+
+#define USIM_CONF6_IO_BYPASS_SHIFT (5U)
+#define USIM_CONF6_IO_BYPASS_MASK (0x00000060U)
+#define USIM_CONF6_IO_BYPASS_00 (0U)
+#define USIM_CONF6_IO_BYPASS_10 (2U)
+#define USIM_CONF6_IO_BYPASS_01 (1U)
+#define USIM_CONF6_IO_BYPASS_11 (3U)
+
+#define USIM_CONF6_SCLK0_BYPASS_SHIFT (7U)
+#define USIM_CONF6_SCLK0_BYPASS_MASK (0x00000080U)
+
+#define USIM_CONF6_LEN_BYPASS_MASK (0x00000200U)
+
+#define USIM_CONF6_RST_BYPASS_SHIFT (10U)
+#define USIM_CONF6_RST_BYPASS_MASK (0x00000400U)
+
+#define USIM_CONF6_VCC_BYPASS_SHIFT (11U)
+#define USIM_CONF6_VCC_BYPASS_MASK (0x00000800U)
+
+#define USIM_CONF6_ATR_TIMEOUT_SHIFT (16U)
+#define USIM_CONF6_ATR_TIMEOUT_MASK (0xffff0000U)
+
+#define USIM_IO_DIRECT_SCLK0_SHIFT (0U)
+#define USIM_IO_DIRECT_SCLK0_MASK (0x00000001U)
+
+#define USIM_IO_DIRECT_SIORX0_SHIFT (2U)
+#define USIM_IO_DIRECT_SIORX0_MASK (0x00000004U)
+
+#define USIM_IO_DIRECT_SIORX1_SHIFT (3U)
+#define USIM_IO_DIRECT_SIORX1_MASK (0x00000008U)
+
+#define USIM_IO_DIRECT_SIOTX0_SHIFT (4U)
+#define USIM_IO_DIRECT_SIOTX0_MASK (0x00000010U)
+
+#define USIM_IO_DIRECT_SIOEN0_SHIFT (6U)
+#define USIM_IO_DIRECT_SIOEN0_MASK (0x00000040U)
+
+#define USIM_IO_DIRECT_RST_SHIFT (8U)
+#define USIM_IO_DIRECT_RST_MASK (0x00000100U)
+
+#define USIM_IO_DIRECT_SVCC_SHIFT (9U)
+#define USIM_IO_DIRECT_SVCC_MASK (0x00000200U)
+
+#define USIM_IO_DIRECT_SINEX_SHIFT (10U)
+#define USIM_IO_DIRECT_SINEX_MASK (0x00000400U)
+
+#define USIM_IO_DIRECT_LEN_SHIFT (11U)
+#define USIM_IO_DIRECT_LEN_MASK (0x00000800U)
+
+#define USIM_IO_DIRECT_RNW0_SHIFT (12U)
+#define USIM_IO_DIRECT_RNW0_MASK (0x00001000U)
+
+#define USIM_IO_DIRECT_RESERVED_SHIFT (15U)
+#define USIM_IO_DIRECT_RESERVED_MASK (0xffff8000U)
+
+#define USIM_IO_DIRECT_C4_SHIFT (14U)
+#define USIM_IO_DIRECT_C4_MASK (0x00004000U)
+
+#define USIM_TX_BLOCK_BLOCK_LENGTH_SHIFT (0U)
+#define USIM_TX_BLOCK_BLOCK_LENGTH_MASK (0x0000ffffU)
+
+#define USIM_TX_BLOCK_RESERVED_SHIFT (16U)
+#define USIM_TX_BLOCK_RESERVED_MASK (0xffff0000U)
+
+#endif /* __TI_USIM_HW_H__ */
diff --git a/drivers/char/ti-usim.c b/drivers/char/ti-usim.c
new file mode 100644
index 0000000..a2af409
--- /dev/null
+++ b/drivers/char/ti-usim.c
@@ -0,0 +1,1859 @@
+/*
+ * usim.c - USIM driver for Smart Card module
+ *
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/debugfs.h>
+#include <linux/notifier.h>
+#include <linux/clk.h>
+/* for send_sig_info */
+#include <linux/rcupdate.h>
+#include <asm/siginfo.h>
+
+#include "ti-usim-hw.h"
+
+#define USIM_WRITEREG(base, offset, field, value) \
+ usim_writereg(base+offset, offset##_##field##_MASK, \
+ offset##_##field##_SHIFT, value)
+
+#define USIM_READREG(base, offset, field) \
+ usim_readreg(base+offset, offset##_##field##_MASK, \
+ offset##_##field##_SHIFT)
+
+#define USIM_SETFIELD(reg, offset, field, value) \
+ usim_setfield(reg, offset##_##field##_MASK, \
+ offset##_##field##_SHIFT, value)
+
+/*
+ * phy states
+ */
+enum usim_phy_state {
+ USIM_PHY_NOT_PRESENT = 0x0,
+ USIM_PHY_PRESENT,
+ USIM_PHY_NOT_ATTACHED,
+};
+
+static struct miscdevice usim_dev;
+
+static DECLARE_WAIT_QUEUE_HEAD(rx_wait);
+static DECLARE_WAIT_QUEUE_HEAD(tx_wait);
+static DECLARE_WAIT_QUEUE_HEAD(atr_wait);
+
+static int usim_set_smartcardclock(struct usim *usim, u32 clock);
+
+static void usim_writereg(void __iomem *base, u32 mask, u32 shift, u32 value)
+{
+ u32 v = readl(base);
+
+ v &= ~mask;
+ v |= (value << shift) & mask;
+ writel(v, base);
+ v = readl(base);
+ return;
+}
+
+static u32 usim_readreg(void __iomem *base, u32 mask, u32 shift)
+{
+ u32 v = readl(base);
+
+ v &= mask;
+ v = (v >> shift);
+ return v;
+}
+
+static u32 usim_setfield(u32 reg, u32 mask, u32 shift, u32 value)
+{
+ reg &= ~mask;
+ reg |= (value << shift) & mask;
+ return reg;
+}
+
+
+static inline void usim_irq_enable(void __iomem *base, u32 irqs)
+{
+ u32 v = readl(base + USIM_IRQENABLE);
+
+ v |= irqs;
+ writel(v, base + USIM_IRQENABLE);
+}
+
+static inline void usim_irq_disable(void __iomem *base, u32 irqs)
+{
+ u32 v = readl(base + USIM_IRQENABLE);
+
+ v &= ~irqs;
+ writel(v, base + USIM_IRQENABLE);
+}
+
+static inline void usim_irq_get(void __iomem *base, u32 *irqs)
+{
+ *irqs = readl(base + USIM_IRQENABLE);
+}
+
+static inline u32 usim_irqstatus(void __iomem *base)
+{
+ return readl(base + USIM_IRQSTATUS);
+}
+
+static inline void usim_irqstatus_clear(void __iomem *base, u32 irqs)
+{
+ writel(irqs, base + USIM_IRQSTATUS);
+}
+
+static inline struct usim *dev_to_usim(struct device *dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+static int usim_send_signal(struct usim *usim, int event)
+{
+ struct siginfo info;
+ struct task_struct *tid;
+ int ret = 0;
+ int pid = usim->user_pid;
+
+ if (pid == 0)
+ return -EINVAL;
+ info.si_signo = USIM_SIGID;
+ info.si_code = SI_QUEUE;
+
+ info.si_int = event;
+ rcu_read_lock();
+
+ /* find task structure associated with this pid */
+ tid = pid_task(find_vpid(pid), PIDTYPE_PID);
+ if (tid == NULL) {
+ dev_err(usim->dev, "usim-err:no such pid :%d\n", pid);
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+
+ rcu_read_unlock();
+
+ /* send the signal */
+ ret = send_sig_info(USIM_SIGID, &info, tid);
+ if (ret < 0) {
+ dev_err(usim->dev, "error sending signal:%d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void usim_getrx(struct usim *usim)
+{
+ u32 rxlen = 0;
+ u32 cnt = 0;
+
+ /* Check if FIFO contains some data */
+ rxlen = USIM_READREG(usim->base, USIM_RXFIFO_LEVEL,
+ USIM_RXFIFO_LEVEL);
+
+ usim->slot_ctx[usim->slot].rx_counter += rxlen;
+ if (rxlen > 0) {
+ for (cnt = 0; cnt < rxlen; cnt++) {
+ usim->slot_ctx[usim->slot].rxbuf[cnt] =
+ USIM_READREG(usim->base, USIM_DRX, USIMDRX);
+ }
+ }
+}
+
+static void usim_irq_atrhandler(struct usim *usim, u32 reg)
+{
+ u32 event = 0;
+ u32 val = 0;
+ u32 cnt = 0;
+ u32 rxval = 0;
+ if (usim->atrdone)
+ return;
+ do {
+ /* WWT would be used to identify end of ATR */
+ if (reg & (USIM_IRQ_WT | USIM_IRQ_EMV_ATR_LENGTH_TIME_OUT)) {
+ event |= USIM_EVENT_TIMEOUT;
+ val = USIM_READREG(usim->base, USIM_STAT,
+ ATRRX_AFTER_TIMEOUT);
+ if (val) {
+ /* do not store rx character if it comes after
+ * ATR timeout
+ */
+ dev_dbg(usim->dev, "Error: Rx after ATR Timeout");
+ break;
+ }
+ }
+ if (reg & USIM_IRQ_TS_ERR) {
+ event |= USIM_EVENT_ERR_FRAME;
+ break;
+ }
+
+ /* check the rx fifo and store available bytes in atrbuf */
+ val = USIM_READREG(usim->base, USIM_RXFIFO_LEVEL,
+ USIM_RXFIFO_LEVEL);
+ cnt = usim->slot_ctx[usim->slot].atr_length;
+
+ while (val > 0) {
+ if (cnt < USIM_MAX_ATRLENGTH) {
+ rxval = readl(usim->base + USIM_DRX);
+ usim->slot_ctx[usim->slot].atr[cnt++] = rxval &
+ USIM_DRX_USIMDRX_MASK;
+ /* check of parity */
+ if (!(rxval & USIM_DRX_STATRXPAR_MASK)) {
+ dev_dbg(usim->dev, "Error: incorrect parity:%0x", rxval);
+ event |= USIM_EVENT_ERR_PARITY;
+ }
+ }
+ val--;
+ }
+
+ usim->slot_ctx[usim->slot].atr_length = cnt;
+ } while (0);
+
+ if (event != 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+ usim->slot_ctx[usim->slot].event = event;
+ usim->atrdone = 1;
+ }
+
+ if (usim->atrdone)
+ wake_up(&atr_wait);
+
+ return;
+}
+
+static void usim_irq_txhandler(struct usim *usim, u32 reg)
+{
+ u32 protocol = 0;
+ u32 event = 0;
+
+ if (usim->txdone)
+ return;
+
+ protocol = usim->slot_ctx[usim->slot].protocol;
+ do {
+ if (reg & USIM_IRQ_FRAME_ERR) {
+ event |= USIM_EVENT_ERR_FRAME;
+ break;
+ }
+ if (!protocol && (reg & USIM_IRQ_RESENT)) {
+ event |= USIM_EVENT_ERR_TXRETRY;
+ break;
+ }
+ if (reg & USIM_IRQ_TX_BLOCK_REQ) {
+ /* TODO : As per EMV max tx block will be of 256 bytes
+ * and USIM controller has sufficient place for this.
+ * Need to implement this case when it is practially
+ * required
+ */
+ dev_dbg(usim->dev, "Error: TX_BLOCK_REQ - Not Implemented");
+ }
+ if (reg & USIM_IRQ_TX_BLOCK_DONE) {
+ usim_irq_disable(usim->base, USIM_IRQ_TX_BLOCK_REQ
+ | USIM_IRQ_TX_BLOCK_DONE
+ | USIM_IRQ_TX);
+ usim->txdone = 1;
+ usim_irq_enable(usim->base, USIM_IRQ_RX | USIM_IRQ_EOB
+ | USIM_IRQ_RXDMA_RDY);
+ break;
+ }
+ } while (0);
+
+ if (event != 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+ usim->slot_ctx[usim->slot].event = event;
+ usim->txdone = 1;
+ usim->rxdone = 1;
+ }
+ if (usim->txdone)
+ wake_up(&tx_wait);
+ return;
+}
+
+static void usim_irq_rxhandler(struct usim *usim, u32 reg)
+{
+ u32 event = 0;
+ u32 val = 0;
+
+ u32 protocol = usim->slot_ctx[usim->slot].protocol;
+
+ /* if tx not done then do not check of any rx */
+ if (usim->rxdone || !usim->txdone)
+ return;
+
+ /* For T=0 protocol */
+ if (protocol == 0) {
+ do {
+ /* ignore interrupts if expected bytes recevied */
+ if (usim->slot_ctx[usim->slot].rx_counter >=
+ usim->slot_ctx[usim->slot].rx_explen) {
+ dev_dbg(usim->dev, "All bytes recvd,ignore this timeout\n");
+ usim->rxdone = 1;
+ break;
+ }
+
+ if (reg & USIM_IRQ_WT) {
+ dev_dbg(usim->dev, "Expected bytes not recvd counter = %d\n",
+ usim->slot_ctx[usim->slot].rx_counter);
+ usim_getrx(usim);
+ event |= USIM_EVENT_TIMEOUT;
+ break;
+ }
+
+ if (reg & USIM_IRQ_PAR_ERR_LEVEL_REACHED) {
+ dev_err(usim->dev, "Rx parity level reached:%x\n", reg);
+ usim_getrx(usim);
+ event |= USIM_EVENT_ERR_PARITY;
+ break;
+ }
+
+ if (reg & (USIM_IRQ_RX | USIM_IRQ_RXDMA_RDY)) {
+ /* Read number of bytes present in the FIFO */
+ usim_getrx(usim);
+ usim->rxdone = 1;
+ break;
+ }
+ } while (0);
+ } else {
+ /* T=1 protocol */
+ do {
+ if (reg & (USIM_IRQ_TOB | USIM_IRQ_TOC)) {
+ usim_getrx(usim);
+ event |= USIM_EVENT_TIMEOUT;
+ break;
+ }
+ if (reg & USIM_IRQ_EOB) {
+ usim_getrx(usim);
+ usim->rxdone = 1;
+ val = USIM_READREG(usim->base, USIM_STAT,
+ STATLRC);
+ if (val != 0)
+ event |= USIM_EVENT_ERR_LRC;
+ break;
+ }
+ } while (0);
+ }
+
+ if (event != 0 || usim->rxdone == 1) {
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+ usim->slot_ctx[usim->slot].event = event;
+ usim->rxdone = 1;
+ }
+
+ if (usim->rxdone)
+ wake_up(&rx_wait);
+
+ return;
+}
+
+static irqreturn_t usim_interrupt(int irq, void *_usim)
+{
+ u32 reg = 0;
+ u32 state = 0;
+ struct usim *usim = (struct usim *)_usim;
+
+ state = usim->slot_ctx[usim->slot].state;
+
+ spin_lock(&usim->lock);
+
+ reg = readl(usim->base + USIM_IRQSTATUS);
+
+ if (state == USIM_MODE_ATR)
+ usim_irq_atrhandler(usim, reg);
+
+ if (state == USIM_MODE_TXRX) {
+ usim_irq_txhandler(usim, reg);
+ usim_irq_rxhandler(usim, reg);
+ }
+
+ if (reg & USIM_IRQSTATUS_USIM_NATR_MASK)
+ dev_dbg(usim->dev, "NO ATR\n");
+
+ if (reg & USIM_IRQSTATUS_USIM_CD_MASK)
+ dev_dbg(usim->dev, "CARD Insert/Removed\n");
+
+ if (reg & USIM_IRQSTATUS_USIM_STOP_CLK_MASK)
+ dev_dbg(usim->dev, "SIM CLK STOPPED\n");
+
+ if (reg & USIM_IRQSTATUS_ACT_DONE_MASK)
+ dev_dbg(usim->dev, "Activation Sequence completed\n");
+
+ if (reg & USIM_IRQSTATUS_DEACT_DONE_MASK)
+ dev_dbg(usim->dev, "Deactivation Sequence complteted\n");
+
+ /* Clear the interrupt by writing the corresponding bit
+ * in IRQ_STATUS register
+ */
+ usim_irqstatus_clear(usim->base, reg);
+
+ spin_unlock(&usim->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int usim_configure(struct usim *usim)
+{
+ int reg = 0;
+
+ /* activate phy */
+ if (usim->phy_present)
+ usim->phy->set_config(usim->phy, usim->slot, SC_PHY_MODE,
+ SC_PHY_ACTIVE);
+
+ /* Disable Auto Idle and set NO IDLE config */
+ reg = readl(usim->base + USIM_SYSCONFIG);
+ reg = USIM_SETFIELD(reg, USIM_SYSCONFIG, AUTOIDLE, 0);
+ reg = USIM_SETFIELD(reg, USIM_SYSCONFIG, IDLEMODE, 1);
+ writel(reg, usim->base + USIM_SYSCONFIG);
+
+ if (usim->phy_present) {
+ USIM_WRITEREG(usim->base, USIM_STAT, STATNOCARD, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF1, BYPASS_HW_AUTO, 0);
+ } else {
+ USIM_WRITEREG(usim->base, USIM_STAT, STATNOCARD, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF1, BYPASS_HW_AUTO, 1);
+ }
+
+ /* Set default card type as EMV, Force SIO to low level */
+ reg = readl(usim->base + USIM_CONF1);
+ reg = USIM_SETFIELD(reg, USIM_CONF1, EMV_CONF, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF1, CONFSIOLOW, 1);
+ writel(reg, usim->base + USIM_CONF1);
+
+ /* Set parity level to 1, auto resent to 2 on parity error, */
+ reg = readl(usim->base + USIM_CONF2);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, NACKING_EN, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CARD_POLARITY, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFEDC, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFPROTOCOL, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, ATR_ASYN_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKDIV, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKMODE, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, PAR_ERR_LEVEL, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFRESENT, 2);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, PUT_ERR_IN_FIFO, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFLRCCHECK, 2);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFSCLKDIV, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, CONFCHKPAR, 1);
+
+ if (usim->phy_present) {
+ reg = USIM_SETFIELD(reg, USIM_CONF2, HW_DEACTIV_EN, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF2, DEBOUNCE_EN, 0);
+ } else {
+ reg = USIM_SETFIELD(reg, USIM_CONF2, HW_DEACTIV_EN, 1);
+ }
+
+ writel(reg, usim->base + USIM_CONF2);
+
+ /* Reset Tx FIFO Pointer */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+
+ /* Reset Rx FIFO Pointer */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+
+ /* Configure FIFO settings */
+ /* Set Tx and Rx trigger to 1 byte */
+ reg = readl(usim->base + USIM_FIFOS);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, FIFO_TX_TRIGGER, 0);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, FIFO_RX_TRIGGER, 0);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, RXDMA_TYPE, 0x3);
+ reg = USIM_SETFIELD(reg, USIM_FIFOS, DMA_MODE, 0x0);
+ writel(reg, usim->base + USIM_FIFOS);
+
+ /* Enable FIFO access */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_ENABLE, 1);
+
+ /* Use HW mode for ETU calculation and set FI = 372 and DI = 1 */
+ reg = readl(usim->base + USIM_CONF5);
+ reg = USIM_SETFIELD(reg, USIM_CONF5, FI, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF5, DI, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ writel(reg, usim->base + USIM_CONF5);
+
+ /* Configure CONF6 settings */
+ reg = readl(usim->base + USIM_CONF6);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, VCC_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, RST_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, SCLK0_BYPASS, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, RST_POLARITY, 0);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, ATR_TIMER_BYPASS, 1);
+ reg = USIM_SETFIELD(reg, USIM_CONF6, MODE, USIM_CONF6_MODE_FREEZE);
+ writel(reg, usim->base + USIM_CONF6);
+
+ /* Clear all bits in IO_DIRECT register */
+ writel(0, usim->base + USIM_IO_DIRECT);
+
+ /* Disable legacy bypass mode */
+ USIM_WRITEREG(usim->base, USIM_CONF1, CONFBYPASS, 0);
+
+ /* Enable required interrupts */
+ reg = readl(usim->base + USIM_IRQENABLE);
+ writel(reg, usim->base + USIM_IRQENABLE);
+
+ /* Toggling ATR length to ensure 'USIM_STAT_ATRRX_AFTER_TIMEOUT'
+ * gets disable
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT,
+ USIM_ATR_TIMEOUT_EMV);
+
+ /* Set STOP_RX_TIMEOUT */
+ /* Set STOP_RESEND_FAILURE */
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RX_TIMEOUT, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RESEND_FAILURE, 1);
+
+ /* set smartcard clock */
+ usim_set_smartcardclock(usim, usim->slot_ctx[usim->slot].clock);
+
+ return 0;
+}
+
+static int usim_set_voltage(struct usim *usim, u32 voltage)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+ /*
+ * voltage = 0 for 5V,
+ * voltage = 1 for 3V,
+ * voltage = 2 for 1.8V,
+ */
+ if (voltage > 3)
+ return -EINVAL;
+ if (usim->phy_present) {
+ ret = phy->set_config(phy, usim->slot,
+ SC_PHY_CARD_SUPPLY_VOLTAGE, voltage);
+ }
+ usim->slot_ctx[usim->slot].supply = voltage;
+ return ret;
+}
+
+static int usim_set_smartcardclock(struct usim *usim, u32 clock)
+{
+ int clkdiv;
+ int clkmode;
+ struct sc_phy *phy = usim->phy;
+
+ switch (clock) {
+ case USIM_SMARTCART_CLOCK_3_3MHZ:
+ clkmode = USIM_CONFSCLKMODE_HF;
+ clkdiv = 3;
+ break;
+
+ case USIM_SMARTCART_CLOCK_4MHZ:
+ clkmode = USIM_CONFSCLKMODE_HF;
+ clkdiv = 2;
+ break;
+
+ case USIM_SMARTCART_CLOCK_5MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 3;
+ break;
+
+ case USIM_SMARTCART_CLOCK_6_6MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 2;
+ break;
+
+ case USIM_SMARTCART_CLOCK_10MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 1;
+ break;
+
+ case USIM_SMARTCART_CLOCK_20MHZ:
+ clkmode = USIM_CONFSCLKMODE_LEGACY;
+ clkdiv = 0;
+ break;
+
+ default:
+ dev_err(usim->dev, "Unsupported Clock configuration for smartcard\n");
+ return -EINVAL;
+ break;
+ }
+
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFSCLKMODE, clkmode);
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFSCLKDIV, clkdiv);
+
+ /* setting phy division to zero, as USIM samples smartcard clk line and
+ * put the data in USIM fifo. Phy supply the clock to smartcard wihtout
+ * furhter division
+ */
+ if (usim->phy_present)
+ phy->set_config(phy, usim->slot, SC_PHY_CLKDIV, 0);
+
+ usim->slot_ctx[usim->slot].clock = clock;
+ return 0;
+}
+
+static int usim_set_etu(struct usim *usim, u32 fi, u32 di)
+{
+ USIM_WRITEREG(usim->base, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF5, FI, fi);
+ USIM_WRITEREG(usim->base, USIM_CONF5, DI, di);
+ return 0;
+}
+
+static int usim_set_rxparitycount(struct usim *usim, u32 rxcount)
+{
+ if (rxcount > USIM_MAX_PARITY_RETRIES)
+ return -EINVAL;
+
+ /* Program fields required for RX retry in USIM IP */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PAR_ERR_LEVEL, rxcount);
+
+ /* Enable rx parity check */
+ if (rxcount > 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFCHKPAR, 1);
+ usim_irq_enable(usim->base, USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+ } else {
+ usim_irq_disable(usim->base, USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+ }
+ return 0;
+}
+
+static int usim_set_txretrycount(struct usim *usim, u32 txcount)
+{
+ if (txcount > USIM_MAX_PARITY_RETRIES)
+ return -EINVAL;
+
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFRESENT, txcount);
+ if (txcount > 0)
+ usim_irq_enable(usim->base, USIM_IRQ_RESENT);
+ else
+ usim_irq_disable(usim->base, USIM_IRQ_RESENT);
+
+ return 0;
+}
+
+static int usim_set_c4(struct usim *usim, int state)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+ if (usim->phy_present)
+ ret = phy->set_config(phy, usim->slot, SC_PHY_PIN_C4, state);
+ else
+ USIM_WRITEREG(usim->base, USIM_IO_DIRECT, C4, state);
+ return ret;
+}
+
+static int usim_set_c8(struct usim *usim, int state)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ if (usim->phy_present)
+ ret = phy->set_config(phy, usim->slot, SC_PHY_PIN_C8, state);
+ return ret;
+}
+static int usim_get_version(struct usim *usim)
+{
+ int version = 0x0;
+
+ /* last 16 bytes represents controller version
+ * and first 16 bytes represents phy version (if connected)
+ */
+ version = USIM_READREG(usim->base, USIM_REVISION, REV);
+ if (usim->phy_present)
+ version |= ((usim->phy->get_config(usim->phy, 0,
+ SC_PHY_VERSION)) << 0x10);
+ return version;
+}
+static int usim_init_emvusercard(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ USIM_WRITEREG(usim->base, USIM_CONF1, EMV_CONF, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, DMA_MODE, 0);
+
+ usim_set_etu(usim, 0, 0);
+
+ if (usim_set_txretrycount(usim, 5) != 0)
+ return -EINVAL;
+
+ if (usim_set_rxparitycount(usim, 5) != 0)
+ return -EINVAL;
+
+ usim_set_c4(usim, 0);
+ usim_set_c8(usim, 0);
+
+ if (usim->phy_present) {
+ /* Set early ATR and mute ATR in case of phy */
+ ret = phy->set_config(phy, usim->slot, SC_PHY_ATR_EARLY_TIME,
+ USIM_EMV_ATR_EARLY_TO);
+ if (ret != 0)
+ return ret;
+
+ ret = phy->set_config(phy, usim->slot, SC_PHY_ATR_MUTE_TIME,
+ USIM_EMV_ATR_MUTE_TO);
+ if (ret != 0)
+ return ret;
+
+ /* enable user slot */
+ ret = phy->set_config(phy, usim->slot, SC_PHY_IO, 1);
+ if (ret != 0)
+ return ret;
+ }
+ /* set cwt,wwt,cgt */
+ USIM_WRITEREG(usim->base, USIM_WWT, WWT, USIM_EMV_WWT);
+ USIM_WRITEREG(usim->base, USIM_CWT, CWT, USIM_EMV_WWT - 22);
+ USIM_WRITEREG(usim->base, USIM_CGT, CGT, USIM_EMV_CGT);
+
+ return 0;
+}
+
+static int usim_warmreset(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ usim->slot_ctx[usim->slot].atr_length = 0;
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_IDLE;
+
+ /* reset FIFO pointer */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x1);
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x0);
+
+ /* Do store bytes with parity error in Rx FIFO */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PUT_ERR_IN_FIFO, 0x1);
+
+ usim_irq_disable(usim->base, (USIM_IRQ_TX | USIM_IRQ_EOB));
+
+ usim->slot_ctx[usim->slot].state = USIM_MODE_ATR;
+
+ /* warm reset the card */
+ if (usim->phy_present) {
+ ret = phy->warm_reset(phy, usim->slot);
+ if (ret != 0)
+ return ret;
+ } else {
+ /* warm reset using USIM */
+ USIM_WRITEREG(usim->base, USIM_CMD, CMD_WARM_RST, 0x1);
+ }
+
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_ATR);
+
+ return 0;
+}
+
+static int usim_activate_card(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ usim->atrdone = 0;
+ usim->slot_ctx[usim->slot].atr_length = 0;
+
+ if (usim->slot_ctx[usim->slot].emv)
+ usim_init_emvusercard(usim);
+
+ USIM_WRITEREG(usim->base, USIM_CONF1, EMV_CONF, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_RX_TRIGGER, 0x103);
+
+ /* RXDMA_TYPE = 0x1 - USIM_RXFIFO_BYTECNT value is ignored */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, RXDMA_TYPE, 0x1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_ENABLE, 0x1);
+
+ /* Do store bytes with parity error in Rx FIFO */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PUT_ERR_IN_FIFO, 0x1);
+ usim_irq_disable(usim->base, (USIM_IRQ_TX | USIM_IRQ_EOB));
+
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x1);
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 0x0);
+
+ /*
+ * Toggling ATR length to ensure 'USIM_STAT_ATRRX_AFTER_TIMEOUT'
+ * gets disable. EMVCo Test case ref#1703_21/22
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT, 0x1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT,
+ USIM_ATR_TIMEOUT_EMV);
+ USIM_WRITEREG(usim->base, USIM_CMD, MODULE_CLK_EN, 0x1);
+
+ usim->slot_ctx[usim->slot].state = USIM_MODE_ATR;
+
+ /* set smartcard clock */
+ usim_set_smartcardclock(usim, usim->slot_ctx[usim->slot].clock);
+
+ /* Activate card */
+ if (usim->phy_present) {
+ usim_irq_disable(usim->base, USIM_IRQ_TX|USIM_IRQ_ATR_START);
+ usim_irq_enable(usim->base, 0xFFFFFFF7);
+ usim_irq_disable(usim->base, USIM_IRQ_NATR);
+ usim_irq_enable(usim->base, USIM_IRQ_EMV_ATR_LENGTH_TIME_OUT);
+ usim_irq_disable(usim->base, USIM_IRQ_TX|USIM_IRQ_ATR_START);
+
+ /* do no bypass ATR length timer, also do not
+ * disturb the bypass setting of other param
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMER_BYPASS, 0x1);
+
+ usim_irqstatus_clear(usim->base, usim_irqstatus(usim->base));
+
+ ret = phy->activate_card(phy, usim->slot);
+ if (ret != 0)
+ return ret;
+ } else {
+ /* Activate using USIM */
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTOP, 0x0);
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTOP, 0x1);
+ }
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_ATR);
+ return 0;
+}
+
+static int usim_deactivate_card(struct usim *usim)
+{
+ int ret = 0;
+ struct sc_phy *phy = usim->phy;
+
+ /* Use USIM IP for deactivation if there is no phy */
+ if (usim->phy_present == USIM_PHY_PRESENT) {
+ ret = phy->deactivate_card(phy, usim->slot);
+ if (ret != 0)
+ return ret;
+ } else {
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTART, 0x0);
+ USIM_WRITEREG(usim->base, USIM_CMD, CMDSTOP, 1);
+ }
+
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_IDLE);
+ USIM_WRITEREG(usim->base, USIM_TX_BLOCK, BLOCK_LENGTH, 0);
+
+ /* Toggling ATR length to ensure 'USIM_STAT_ATRRX_AFTER_TIMEOUT'
+ * gets disable TC Ref: 1703_21/22
+ */
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT, 1);
+ USIM_WRITEREG(usim->base, USIM_CONF6, ATR_TIMEOUT,
+ USIM_ATR_TIMEOUT_EMV);
+
+ /* stop ATR length timeout */
+ USIM_WRITEREG(usim->base, USIM_CMD, STOP_EMV_ATR_LENGTH_TIMER, 1);
+ usim->slot_ctx[usim->slot].state = USIM_MODE_DEACT;
+
+ return 0;
+}
+
+static void usim_set_protocol(struct usim *usim, int protocol)
+{
+ u32 irq;
+
+ /* As per spec, mask all interrupts before switching
+ * from one protocol to other.
+ */
+ usim_irq_get(usim->base, &irq);
+
+ /* disable all interrupts */
+ usim_irq_disable(usim->base, 0xFFFFFFFF);
+
+ /* 0 for T=0 and 1 for T=1 protocol */
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFPROTOCOL, protocol);
+ usim->slot_ctx[usim->slot].protocol = protocol;
+
+ /* read and clear status */
+ usim_irqstatus_clear(usim->base, usim_irqstatus(usim->base));
+
+ /* now renable interrupts */
+ usim_irq_enable(usim->base, irq);
+ return;
+}
+
+static void usim_configure_rx_pio(struct usim *usim)
+{
+ /* Reset RX FIFO pointers */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFORX_RESET, 0);
+
+ /* read and clear any pending interrupt status */
+ usim_irqstatus_clear(usim->base, usim_irqstatus(usim->base));
+
+ /* Enable WWT underflow interupt,
+ * RX FIFO full interrupt,
+ * BWT, CWT and parity error level interrupts.
+ */
+ usim_irq_enable(usim->base, USIM_IRQ_WT | USIM_IRQ_RXFULL |
+ USIM_IRQ_TOB |
+ USIM_IRQ_TOC |
+ USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+
+ /* Lets disable key RX interrupts. We will enable them later
+ * when we want to start RX
+ */
+ usim_irq_disable(usim->base, USIM_IRQ_RX |
+ USIM_IRQ_RXDMA_RDY | USIM_IRQ_EOB);
+
+ /* We will use only RX FIFO threshold in RX */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, RXDMA_TYPE, 0x1);
+
+ if (usim->slot_ctx[usim->slot].protocol == 0) {
+ /* Set Rx FIFO Threshold to expected recv length
+ * Subtract 1 from length as HW adds 1 to the trigger
+ */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_RX_TRIGGER,
+ usim->slot_ctx[usim->slot].rx_explen - 1);
+ } else {
+ /* T=1 protocol */
+ /* for T1 we should not use parity error level interrupt */
+ usim_irq_disable(usim->base, USIM_IRQ_PAR_ERR_LEVEL_REACHED);
+
+ /* set RX FIFO threshold to MAX_RX_FIFO size.
+ * We will rely on End-Of-Block interrupt to
+ * terminate reception in T1
+ */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFO_RX_TRIGGER,
+ USIM_MAX_RX_FIFO_SIZE - 1);
+ }
+ return;
+}
+
+static void usim_configure_tx_pio(struct usim *usim)
+{
+ /* Make sure TX is stopped first by programming
+ * TX_BLOCK to zero and disabling TX_BLOCK_DONE
+ * and USIM_IRQ_TX_BLOCK_REQ interrupts
+ */
+ USIM_WRITEREG(usim->base, USIM_TX_BLOCK, BLOCK_LENGTH, 0);
+ usim_irq_disable(usim->base, USIM_IRQ_TX_BLOCK_DONE |
+ USIM_IRQ_TX_BLOCK_REQ);
+
+ /* We will use Tx Block length feature so clear TX_EN bit */
+ USIM_WRITEREG(usim->base, USIM_CONF2, TX_EN, 0);
+ /* We will not use USIM_TX interrupt for transmit operation */
+ usim_irq_disable(usim->base, USIM_IRQ_TX);
+ /* Reset TX FIFO pointers */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 1);
+ USIM_WRITEREG(usim->base, USIM_FIFOS, FIFOTX_RESET, 0);
+
+ /* Ensure PIO mode is programmed */
+ USIM_WRITEREG(usim->base, USIM_FIFOS, DMA_MODE, 0);
+}
+
+static int usim_send_data(struct usim *usim, char *txbuf, int len)
+{
+ u32 val;
+ int i;
+ int ret = 0;
+
+ usim->txdone = 0;
+ usim->rxdone = 0;
+
+ if (len == 0) {
+ dev_dbg(usim->dev, "Error: Invalid Tx length:%d", len);
+ return -EINVAL;
+ }
+
+ usim->slot_ctx[usim->slot].event = 0;
+
+ /* Configure Tx PIO mode patams */
+ usim_configure_tx_pio(usim);
+
+ /* Tx FIFO must be empty after reset */
+ val = USIM_READREG(usim->base, USIM_FIFOS, FIFOTX_EMPTY);
+ if (val == 0) {
+ dev_dbg(usim->dev, "Error: Tx FIFO is not empty");
+ return -EFAULT;
+ }
+
+ /* write data in Tx FIFO */
+ for (i = 0; i < len; i++) {
+ USIM_WRITEREG(usim->base, USIM_DTX, DTX, txbuf[i]);
+ dev_dbg(usim->dev, "txbyte %d = %x\n", i, txbuf[i]);
+ }
+
+ /* Finally re-enable TX_BLOCK_xxx interrupts and clear RX interrupts */
+ usim_irq_enable(usim->base, USIM_IRQ_TX_BLOCK_DONE |
+ USIM_IRQ_TX_BLOCK_REQ);
+
+ /* For T=0, stop re-tranmission after resend failure */
+ if (usim->slot_ctx[usim->slot].protocol == 0) {
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RESEND_FAILURE, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF2, STOP_RESEND_FAILURE, 1);
+ }
+
+ /* Do not store bytes with parity error in Rx FIFO */
+ USIM_WRITEREG(usim->base, USIM_CONF2, PUT_ERR_IN_FIFO, 0);
+
+ usim_irq_enable(usim->base, USIM_IRQ_TOC);
+
+ if (usim->phy_present)
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_TXRX);
+ else
+ USIM_WRITEREG(usim->base, USIM_CONF6, MODE, USIM_MODE_LEGACY);
+
+ usim->slot_ctx[usim->slot].state = USIM_MODE_TXRX;
+
+ /* Configure Rx settings before performing a Tx
+ * As soon as we are done with Tx, card will send
+ * data, which we should be ready to capture
+ */
+ usim_configure_rx_pio(usim);
+ /* Start TX operation - program TX_BLOCK register to length
+ * of the TX buffer to start the TX operation.
+ */
+ USIM_WRITEREG(usim->base, USIM_TX_BLOCK, BLOCK_LENGTH, len);
+
+ /* We need to block the caller here */
+ ret = wait_event_interruptible(tx_wait, (usim->txdone == 1));
+ dev_dbg(usim->dev, "Tx WAIT OVER\n");
+ if (usim->slot_ctx[usim->slot].event == USIM_EVENT_TIMEOUT)
+ usim_send_signal(usim, USIM_EVENT_TIMEOUT);
+
+ return ret;
+}
+
+static int usim_set_config(struct usim *usim, struct usim_config *param)
+{
+ u32 ret = 0;
+ dev_dbg(usim->dev, "param:%d, value:%d\n", param->attr, param->value);
+
+ switch (param->attr) {
+ case USIM_PARAM_CWT:
+ USIM_WRITEREG(usim->base, USIM_CWT, CWT, param->value);
+ break;
+
+ case USIM_PARAM_WWT:
+ USIM_WRITEREG(usim->base, USIM_WWT, WWT, param->value);
+ break;
+
+ case USIM_PARAM_CGT:
+ USIM_WRITEREG(usim->base, USIM_CGT, CGT, param->value);
+ break;
+
+ case USIM_PARAM_BWT:
+ USIM_WRITEREG(usim->base, USIM_BWT, BWT, param->value);
+ break;
+
+ case USIM_PARAM_EDCTYPE:
+ /* 0 = LRC check, 1 = CRC check */
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFEDC, param->value);
+ break;
+
+ case USIM_PARAM_LRCCHECK:
+ /* 0 = No LRC check, 1 = LRC check */
+ USIM_WRITEREG(usim->base, USIM_CONF2, CONFLRCCHECK,
+ param->value);
+ break;
+
+ case USIM_PARAM_C4:
+ usim_set_c4(usim, param->value);
+ break;
+
+ case USIM_PARAM_C8:
+ usim_set_c8(usim, param->value);
+ break;
+
+ case USIM_PARAM_PROTOCOL:
+ /* 0 for T=0 and 1 for T=1 */
+ usim_set_protocol(usim, param->value);
+ break;
+
+ case USIM_PARAM_VOLTAGE:
+ ret = usim_set_voltage(usim, param->value);
+ break;
+
+ case USIM_PARAM_EMV:
+ USIM_WRITEREG(usim->base, USIM_CONF1, EMV_CONF, param->value);
+ if (param->value)
+ usim->slot_ctx[usim->slot].emv = true;
+ else
+ usim->slot_ctx[usim->slot].emv = false;
+ break;
+
+ case USIM_PARAM_FI:
+ USIM_WRITEREG(usim->base, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF5, FI, param->value);
+ break;
+
+ case USIM_PARAM_DI:
+ USIM_WRITEREG(usim->base, USIM_CONF5, SOFT_NHARD_FIDI_PROG, 0);
+ USIM_WRITEREG(usim->base, USIM_CONF5, DI, param->value);
+ break;
+
+ case USIM_PARAM_CODING_CONV:
+ USIM_WRITEREG(usim->base, USIM_STAT, CONFCODCONV, param->value);
+ break;
+
+ case USIM_PARAM_CLOCK_STOP:
+ USIM_WRITEREG(usim->base, USIM_CMD, CMD_CLOCK_STOP,
+ param->value);
+ break;
+
+ case USIM_PARAM_SMARTCARD_CLOCK:
+ ret = usim_set_smartcardclock(usim, param->value);
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int usim_get_config(struct usim *usim, struct usim_config *param)
+{
+ u32 ret = 0;
+ dev_dbg(usim->dev, "param:%d, value:%d\n", param->attr, param->value);
+
+ switch (param->attr) {
+ case USIM_PARAM_CWT:
+ param->value = USIM_READREG(usim->base, USIM_CWT, CWT);
+ break;
+
+ case USIM_PARAM_WWT:
+ param->value = USIM_READREG(usim->base, USIM_WWT, WWT);
+ break;
+
+ case USIM_PARAM_CGT:
+ param->value = USIM_READREG(usim->base, USIM_CGT, CGT);
+ break;
+
+ case USIM_PARAM_BWT:
+ param->value = USIM_READREG(usim->base, USIM_BWT, BWT);
+ break;
+
+ case USIM_PARAM_EDCTYPE:
+ param->value = USIM_READREG(usim->base, USIM_CONF2, CONFEDC);
+ break;
+
+ case USIM_PARAM_LRCCHECK:
+ param->value = USIM_READREG(usim->base, USIM_CONF2,
+ CONFLRCCHECK);
+ break;
+
+ case USIM_PARAM_PROTOCOL:
+ /* 0 for T=0 and 1 for T=1 */
+ param->value = USIM_READREG(usim->base, USIM_CONF2,
+ CONFPROTOCOL);
+ break;
+
+ case USIM_PARAM_VOLTAGE:
+ param->value = usim->slot_ctx[usim->slot].supply;
+ break;
+
+ case USIM_PARAM_EMV:
+ param->value = USIM_READREG(usim->base, USIM_CONF1, EMV_CONF);
+ break;
+
+ case USIM_PARAM_FI:
+ param->value = USIM_READREG(usim->base, USIM_CONF5, FI);
+ break;
+
+ case USIM_PARAM_DI:
+ param->value = USIM_READREG(usim->base, USIM_CONF5, DI);
+ break;
+
+ case USIM_PARAM_CODING_CONV:
+ param->value = USIM_READREG(usim->base, USIM_STAT, CONFCODCONV);
+ break;
+
+ case USIM_PARAM_CLOCK_STOP:
+ param->value = USIM_READREG(usim->base, USIM_CMD,
+ CMD_CLOCK_STOP);
+ break;
+
+ case USIM_PARAM_SMARTCARD_CLOCK:
+ param->value = usim->slot_ctx[usim->slot].clock;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static long usim_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct usim *usim = file->private_data;
+ struct usim_data data;
+ struct usim_config param;
+
+ int ret = 0;
+ int cnt = 0;
+ int version = 0;
+ int u_pid = 0;
+ int present = 0;
+
+ if (usim->phy_present == USIM_PHY_NOT_ATTACHED)
+ return -ENXIO;
+
+ switch (cmd) {
+ case USIM_IOCTL_GET_PROVIDER_VERSION:
+ dev_dbg(usim->dev, "IOCTL: GET PROVIDER VERSION\n");
+ version = usim_get_version(usim);
+ ret = copy_to_user((unsigned int *)arg, &version,
+ sizeof(unsigned int));
+ if (ret != 0)
+ ret = -EFAULT;
+ break;
+
+ case USIM_IOCTL_ACTIVATE_CARD:
+ dev_dbg(usim->dev, "IOCTL: ACTIVATE CARD\n");
+ if (usim->phy_present) {
+ present = usim->phy->get_config(usim->phy, usim->slot,
+ SC_PHY_CARD_PRESENCE);
+ if (present)
+ ret = usim_activate_card(usim);
+ else
+ ret = -EFAULT;
+ }
+ break;
+
+ case USIM_IOCTL_DEACTIVATE_CARD:
+ dev_dbg(usim->dev, "IOCTL: DEACTIVATE CARD\n");
+ ret = usim_deactivate_card(usim);
+ break;
+
+ case USIM_IOCTL_WARM_RESET:
+ dev_dbg(usim->dev, "IOCTL: WARM RESET\n");
+ ret = usim_warmreset(usim);
+ break;
+
+ case USIM_IOCTL_GET_ATR:
+ dev_dbg(usim->dev, "IOCTL: GET ATR\n");
+ wait_event_interruptible(atr_wait, (usim->atrdone == 1));
+ ret = copy_to_user((char __user *)arg,
+ usim->slot_ctx[usim->slot].atr,
+ usim->slot_ctx[usim->slot].atr_length);
+ if (ret != 0)
+ ret = -EFAULT;
+ else
+ ret = usim->slot_ctx[usim->slot].atr_length;
+ break;
+
+ case USIM_IOCTL_SEND_DATA:
+ dev_dbg(usim->dev, "IOCTL: SEND DATA\n");
+ ret = copy_from_user(&data, (struct usim_data *)arg,
+ sizeof(struct usim_data));
+ if (ret != 0)
+ return -EFAULT;
+
+ usim->slot = data.slot;
+ usim->slot_ctx[usim->slot].rx_explen = data.rxexplen;
+ usim->slot_ctx[usim->slot].rx_counter = 0;
+ for (cnt = 0; cnt < data.txlen; cnt++)
+ dev_dbg(usim->dev, "apdu[%d] = %x\n", cnt,
+ data.apdu[cnt]);
+ ret = usim_send_data(usim, &data.apdu[0], data.txlen);
+ break;
+
+ case USIM_IOCTL_SET_CONFIG:
+ dev_dbg(usim->dev, "IOCTL: SET CONFIG\n");
+ ret = copy_from_user(¶m, (struct usim_config *)arg,
+ sizeof(struct usim_config));
+ if (ret != 0)
+ return -EFAULT;
+
+ usim_set_config(usim, ¶m);
+ break;
+
+ case USIM_IOCTL_GET_CONFIG:
+ dev_dbg(usim->dev, "IOCTL: GET CONFIG\n");
+ ret = copy_from_user(¶m, (struct usim_config *)arg,
+ sizeof(struct usim_config));
+ if (ret != 0)
+ return -EFAULT;
+
+ usim_get_config(usim, ¶m);
+ ret = copy_to_user((struct usim_config *)arg, ¶m,
+ sizeof(struct usim_config));
+ if (ret != 0)
+ ret = -EFAULT;
+ break;
+
+ case USIM_IOCTL_GET_CARD_PRESENCE:
+ dev_dbg(usim->dev, "IOCTL: CARD PRESENCE\n");
+ if (usim->phy_present) {
+ present = usim->phy->get_config(usim->phy, usim->slot,
+ SC_PHY_CARD_PRESENCE);
+ ret = copy_to_user((unsigned int *)arg, &present,
+ sizeof(unsigned int));
+ if (ret != 0)
+ ret = -EFAULT;
+ }
+ break;
+
+ case USIM_IOCTL_REGISTER_PID:
+ dev_dbg(usim->dev, "IOCTL: USIM_IOCTL_REGISTER_PID");
+ ret = copy_from_user(&u_pid, (int *)arg, sizeof(int));
+ if (ret != 0)
+ return -EFAULT;
+ usim->user_pid = u_pid;
+ break;
+ }
+ return ret;
+}
+
+static ssize_t usim_read(struct file *file, char *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct usim *usim = file->private_data;
+ if (usim->phy_present == USIM_PHY_NOT_ATTACHED)
+ return -ENXIO;
+
+ wait_event_interruptible(rx_wait, (usim->rxdone == 1));
+ dev_dbg(usim->dev, "RX WAIT over\n");
+
+ /* check for timeout and send signal if any */
+ if (usim->slot_ctx[usim->slot].event == USIM_EVENT_TIMEOUT)
+ usim_send_signal(usim, USIM_EVENT_TIMEOUT);
+
+ if (copy_to_user(user_buf, usim->slot_ctx[usim->slot].rxbuf,
+ usim->slot_ctx[usim->slot].rx_counter)) {
+ dev_err(usim->dev, "Copy failed\n");
+ return -EFAULT;
+ }
+ *ppos = usim->slot_ctx[usim->slot].rx_counter;
+ dev_dbg(usim->dev, "Card response returning %d bytes\n",
+ usim->slot_ctx[usim->slot].rx_counter);
+
+ return usim->slot_ctx[usim->slot].rx_counter;
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define DUMP_REG(r) seq_printf(s, "%-25s: %08x\n", #r, readl(usim->base + r));
+
+static int usim_regdump_show(struct seq_file *s, void *unused)
+{
+ struct usim *usim = s->private;
+
+ seq_puts(s, "USIM Register Dump\n");
+
+ DUMP_REG(USIM_REVISION);
+ DUMP_REG(USIM_IDENT);
+ DUMP_REG(USIM_SYSCONFIG);
+ DUMP_REG(USIM_SYSSTATUS);
+ DUMP_REG(USIM_IRQSTATUS);
+ DUMP_REG(USIM_IRQENABLE);
+ DUMP_REG(USIM_WAKEUPEN);
+ DUMP_REG(USIM_CMD);
+ DUMP_REG(USIM_STAT);
+ DUMP_REG(USIM_CONF1);
+ DUMP_REG(USIM_CONF2);
+ DUMP_REG(USIM_CONF3);
+ DUMP_REG(USIM_DRX);
+ DUMP_REG(USIM_DTX);
+ DUMP_REG(USIM_FIFOS);
+ DUMP_REG(USIM_CGT);
+ DUMP_REG(USIM_BWT);
+ DUMP_REG(USIM_DEBUG);
+ DUMP_REG(USIM_CONF_SAM1_DIV);
+ DUMP_REG(USIM_CONF4);
+ DUMP_REG(USIM_ATR_CLK_PRD_NBS);
+ DUMP_REG(USIM_CONF_ETU_DIV);
+ DUMP_REG(USIM_CONF5);
+ DUMP_REG(USIM_TC_GUARD_TIME_ADD);
+ DUMP_REG(USIM_RXFIFO_LEVEL);
+ DUMP_REG(USIM_RXFIFO_BYTECNT);
+ DUMP_REG(USIM_WWT);
+ DUMP_REG(USIM_CONF6);
+ DUMP_REG(USIM_IO_DIRECT);
+ DUMP_REG(USIM_TX_BLOCK);
+
+ return 0;
+}
+
+static int usim_regdump_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, usim_regdump_show, inode->i_private);
+}
+
+static const struct file_operations usim_regdump_fops = {
+ .open = usim_regdump_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int usim_init_debugfs(struct usim *usim)
+{
+ int ret;
+ struct dentry *root;
+ struct dentry *file;
+
+ root = debugfs_create_dir("usim", NULL);
+ if (!root) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ file = debugfs_create_file("regdump", S_IRUGO, root, usim,
+ &usim_regdump_fops);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ usim->debugfs_root = root;
+
+ return 0;
+err1:
+ debugfs_remove_recursive(root);
+err0:
+ return ret;
+}
+#endif
+
+static int usim_pm_init(struct usim *usim)
+{
+ int ret = 0;
+
+ usim->usim0_fck = clk_get(usim->dev, "usim0_fck");
+ if (IS_ERR(usim->usim0_fck)) {
+ ret = PTR_ERR(usim->usim0_fck);
+ dev_err(usim->dev, "usim0_fck failed error:%d\n", ret);
+ return -1;
+ }
+ usim->dpll_core_m4_ck = clk_get(usim->dev, "dpll_core_m4_ck");
+ if (IS_ERR(usim->dpll_core_m4_ck)) {
+ ret = PTR_ERR(usim->dpll_core_m4_ck);
+ dev_err(usim->dev, "dpll_core_m4_ck failed error:%d\n", ret);
+ return -1;
+ }
+ ret = clk_set_parent(usim->usim0_fck, usim->dpll_core_m4_ck);
+ if (ret != 0)
+ dev_dbg(usim->dev, "clk set parent failed: %d\n", ret);
+
+ usim->usim_dbclk = clk_get(usim->dev, "usim_dbck");
+ if (IS_ERR(usim->usim_dbclk)) {
+ ret = PTR_ERR(usim->usim_dbclk);
+ dev_err(usim->dev, "usim_dbck failed error:%d\n", ret);
+ return -1;
+ }
+
+ usim->clkdiv32k_ick = clk_get(usim->dev, "clkdiv32k_ick");
+ if (IS_ERR(usim->usim_dbclk)) {
+ ret = PTR_ERR(usim->clkdiv32k_ick);
+ dev_err(usim->dev, "clkdiv32k_ick failed error:%d\n", ret);
+ return -1;
+ }
+
+ ret = clk_set_parent(usim->usim_dbclk, usim->clkdiv32k_ick);
+ if (ret != 0)
+ dev_dbg(usim->dev, "usim_dbclk set parent failed: %d\n", ret);
+
+ usim->opt_fclk = devm_clk_get(usim->dev, "opt_fck");
+ if (IS_ERR(usim->opt_fclk))
+ dev_err(usim->dev, "unable to get fck\n");
+ else
+ clk_enable(usim->opt_fclk);
+
+ usim->opt_fclk32 = devm_clk_get(usim->dev, "opt_fck32");
+ if (IS_ERR(usim->opt_fclk32))
+ dev_err(usim->dev, "unable to get dbclk\n");
+ else
+ clk_enable(usim->opt_fclk32);
+
+ return 0;
+}
+static void usim_enable(struct usim *usim)
+{
+ if (usim->enable == 1)
+ return;
+
+ /* enable the clk */
+ pm_runtime_get_sync(usim->dev);
+
+ /* usim init */
+ usim_configure(usim);
+
+ usim->enable = 1;
+ return;
+}
+
+static void usim_disable(struct usim *usim)
+{
+ int cnt = 0;
+
+ if (usim->enable == 0)
+ return;
+
+ /* reset USIM state for deactivation */
+ for (cnt = 0; cnt < usim->max_slots; cnt++) {
+ usim->slot = cnt;
+ usim_deactivate_card(usim);
+ }
+
+ /* reset default slot and clock */
+ usim->slot = 0;
+ usim->slot_ctx[usim->slot].clock = USIM_SMARTCART_CLOCK_5MHZ;
+
+ /* shutdown phy */
+ if (usim->phy_present == USIM_PHY_PRESENT)
+ usim->phy->set_config(usim->phy, usim->slot, SC_PHY_MODE,
+ SC_PHY_SHUTDOWN);
+ /* disable clk */
+ pm_runtime_put_sync_autosuspend(usim->dev);
+ usim->enable = 0;
+ return;
+}
+
+static int usim_open(struct inode *inode, struct file *file)
+{
+ struct usim *usim = (struct usim *)dev_get_drvdata(usim_dev.parent);
+
+ if (usim->phy_present == USIM_PHY_NOT_ATTACHED)
+ return -ENXIO;
+
+ file->private_data = usim;
+ usim_enable(usim);
+ return 0;
+}
+
+static int usim_release(struct inode *inode, struct file *file)
+{
+ struct usim *usim = (struct usim *)dev_get_drvdata(usim_dev.parent);
+
+ usim_disable(usim);
+ usim->user_pid = 0;
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int usim_suspend(struct device *dev)
+{
+ /* struct usim *usim = dev_to_usim(dev); */
+ return 0;
+}
+
+static int usim_resume(struct device *dev)
+{
+ /* struct usim *usim = dev_to_usim(dev); */
+ return 0;
+}
+
+static const struct dev_pm_ops usim_pm_ops = {
+ .suspend = usim_suspend,
+ .resume = usim_resume,
+};
+
+#define USIM_PM_OPS (&usim_pm_ops)
+#else
+#define USIM_PM_OPS NULL
+#endif
+
+static int usim_notify(struct notifier_block *self, unsigned long action, void
+ *data)
+{
+ struct usim *usim = (struct usim *)data;
+ int event = action & SC_PHY_NOTIFICATION_ACTION_MASK;
+ int slot = (action & SC_PHY_NOTIFICATION_SLOT_MASK) >>
+ SC_PHY_NOTIFICATION_SLOT_SHIFT;
+ int t_slot = 0;
+
+ /* if phy is removed using rmmod or by some other mech..
+ * then put phy state in unknown, at this point usim also required to
+ * gets removed from the system, if it is inserted as module and
+ * dependent on phy
+ */
+ if (action == SC_PHY_REMOVED)
+ usim->phy_present = USIM_PHY_NOT_ATTACHED;
+
+ if (event & SC_PHY_CARD_INSERTED)
+ usim_send_signal(usim, USIM_EVENT_CARD_INSERT);
+
+ if (action & SC_PHY_CARD_REMOVED) {
+ usim_send_signal(usim, USIM_EVENT_CARD_REMOVE);
+ dev_dbg(usim->dev, "slot is:%d", slot);
+ /* de-activate USIM & PHY state machine for the slot */
+ t_slot = usim->slot;
+ usim->slot = slot;
+ usim_deactivate_card(usim);
+ usim->slot = t_slot;
+ }
+
+ if (action & SC_PHY_CARD_OVERHEAT)
+ usim_send_signal(usim, USIM_EVENT_CARD_OVERHEAT);
+
+ if (action & SC_PHY_CARD_ATR_TIMEOUT)
+ usim_send_signal(usim, USIM_EVENT_TIMEOUT);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block usim_nb = {
+ .notifier_call = usim_notify,
+};
+
+static const struct file_operations usim_fops = {
+ .owner = THIS_MODULE,
+ .read = usim_read,
+ .open = usim_open,
+ .unlocked_ioctl = usim_ioctl,
+ .release = usim_release,
+};
+
+static int usim_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+
+ struct usim *usim = NULL;
+ struct device_node *phy_node = NULL;
+ struct resource *res = NULL;
+ void __iomem *base = NULL;
+ const __be32 *parp = NULL;
+ struct i2c_client *phy_i2c = NULL;
+
+ int ret = 0;
+ int version = 0;
+ int cnt = 0;
+ u32 prop = 0;
+ int lenp = 0;
+
+ if (!node) {
+ dev_err(dev, "device node not found\n");
+ return -EINVAL;
+ }
+
+ usim = devm_kzalloc(dev, sizeof(*usim), GFP_KERNEL);
+ if (!usim) {
+ dev_err(dev, "not enough memory\n");
+ return -ENOMEM;
+ }
+
+ usim->irq = platform_get_irq(pdev, 0);
+ if (usim->irq < 0) {
+ dev_err(dev, "missing IRQ resource\n");
+ ret = -EINVAL;
+ goto usim_err_ret;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory base resource\n");
+ ret = -EINVAL;
+ goto usim_err_ret;
+ }
+
+ base = devm_ioremap_nocache(dev, res->start, resource_size(res));
+ if (!base) {
+ dev_err(dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto usim_err_ret;
+ }
+
+ usim->dev = &pdev->dev;
+ usim->base = base;
+ usim->max_slots = 1;
+ usim->phy_present = USIM_PHY_NOT_PRESENT;
+ usim->enable = 0;
+
+ /* default slot will be zero : user card */
+ usim->slot = 0;
+
+ ret = devm_request_irq(dev, usim->irq, usim_interrupt, 0,
+ "usim", usim);
+ if (ret) {
+ dev_err(dev, "fail request IRQ #%d --> %d\n", usim->irq, ret);
+ goto usim_err_ret;
+ return ret;
+ }
+
+ /*
+ * Populate all the child nodes here...
+ */
+ ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
+
+ /* get phy details */
+ parp = of_get_property(node, "phy", &lenp);
+ if (parp == NULL || lenp != (sizeof(void *))) {
+ dev_dbg(usim->dev, "parp is null!,no phy");
+ } else {
+ /* get phy node */
+ phy_node = of_find_node_by_phandle(be32_to_cpup(parp));
+ if (phy_node == NULL) {
+ dev_err(usim->dev, "\n phy node is null");
+ ret = -EPROBE_DEFER;
+ goto usim_err_ret;
+ }
+ phy_i2c = of_find_i2c_device_by_node(phy_node);
+ if (phy_i2c == NULL) {
+ dev_err(usim->dev, "\n phy i2c is null");
+ ret = -EPROBE_DEFER;
+ goto usim_err_ret;
+ }
+ /* get phy interface */
+ usim->phy = (struct sc_phy *)i2c_get_clientdata(phy_i2c);
+ if (usim->phy == NULL) {
+ dev_err(usim->dev, "phy data is null");
+ ret = -EPROBE_DEFER;
+ goto usim_err_ret;
+ }
+ usim->phy_present = USIM_PHY_PRESENT;
+
+ ret = of_property_read_u32(node, "phy-slots", &prop);
+ /* if phy-slot is not declared then assume one phy slot */
+ usim->max_slots = prop > 0 ? prop : 1;
+ }
+
+ dev_dbg(usim->dev, "usim max slot:%d", usim->max_slots);
+ /* initialize slot context*/
+ if (usim->max_slots > USIM_MAX_SLOTS) {
+ ret = -EINVAL;
+ goto usim_err_ret;
+ }
+
+ usim->slot_ctx = kmalloc(usim->max_slots *
+ sizeof(struct usim_slotcontext), GFP_KERNEL);
+ if (!usim->slot_ctx)
+ return -ENOMEM;
+
+ for (cnt = 0; cnt < usim->max_slots; cnt++) {
+ /* default protocol */
+ usim->slot_ctx[cnt].protocol = 0;
+ usim->slot_ctx[cnt].emv = true;
+ }
+
+ dev_set_drvdata(dev, usim);
+ usim_pm_init(usim);
+
+ /* enable the clock */
+ pm_runtime_enable(usim->dev);
+ pm_runtime_set_active(usim->dev);
+ spin_lock_init(&usim->lock);
+
+ usim_dev.minor = MISC_DYNAMIC_MINOR;
+ usim_dev.name = "usim";
+ usim_dev.fops = &usim_fops;
+ usim_dev.parent = &(pdev->dev);
+
+ ret = misc_register(&usim_dev);
+ if (ret) {
+ pr_err("unable to register a misc device\n");
+ goto usim_err_reg;
+ }
+#ifdef CONFIG_DEBUG_FS
+ ret = usim_init_debugfs(usim);
+ if (ret) {
+ dev_err(dev, "Debugfs init failed\n");
+ goto usim_err_reg;
+ }
+#endif
+ /* set default ICC clock : 5Mhz */
+ usim->slot_ctx[usim->slot].clock = USIM_SMARTCART_CLOCK_5MHZ;
+
+ /* get the clock & do usim configuration */
+ usim_enable(usim);
+ dev_info(usim->dev, "usim driver initialized\n");
+
+ /* register notifier */
+ if (usim->phy_present)
+ usim->phy->register_notify(usim->phy, &usim_nb, (void *)usim);
+
+ /* get usim version */
+ version = usim_get_version(usim);
+ dev_info(usim->dev, "version is:%0x", version);
+
+ usim_disable(usim);
+ return 0;
+
+usim_err_reg:
+ pm_runtime_set_suspended(usim->dev);
+ pm_runtime_disable(usim->dev);
+ misc_deregister(&usim_dev);
+
+usim_err_ret:
+ if (usim)
+ kfree(usim->slot_ctx);
+ return ret;
+}
+
+static int usim_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct usim *usim = dev_to_usim(dev);
+
+ usim_disable(usim);
+ /* unregister notifier, applicable only when phy present and phy state
+ * is not unknown i.e. - phy has not been removed using rmmod */
+ if (usim->phy_present == USIM_PHY_PRESENT)
+ usim->phy->unregister_notify(usim->phy, &usim_nb);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove_recursive(usim->debugfs_root);
+#endif
+ /* disable pm runtime */
+ pm_runtime_set_suspended(usim->dev);
+ pm_runtime_disable(usim->dev);
+
+ kfree(usim->slot_ctx);
+ misc_deregister(&usim_dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id usim_id_table[] = {
+ { .compatible = "ti,usim" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, usim_id_table);
+#endif
+
+static struct platform_driver usim_driver = {
+ .driver = {
+ .name = "usim",
+ .owner = THIS_MODULE,
+ .pm = USIM_PM_OPS,
+ .of_match_table = of_match_ptr(usim_id_table),
+ },
+ .probe = usim_probe,
+ .remove = usim_remove,
+};
+
+static int __init usim_init(void)
+{
+ return platform_driver_register(&usim_driver);
+}
+
+static void __exit usim_exit(void)
+{
+ platform_driver_unregister(&usim_driver);
+}
+
+late_initcall(usim_init);
+module_exit(usim_exit);
+
+MODULE_AUTHOR("Maulik Mankad <maulik@ti.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..f4b3ca8
--- /dev/null
+++ b/include/linux/ti-usim.h
@@ -0,0 +1,98 @@
+/*
+ * ti-usim.h - Header file for USIM SmartCard interface
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __TI_USIM_H__
+#define __TI_USIM_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define USIM_IOCTL 0xFE
+
+enum usim_param {
+ USIM_PARAM_CWT = 0,
+ USIM_PARAM_WWT,
+ USIM_PARAM_CGT,
+ USIM_PARAM_BWT,
+ USIM_PARAM_EDCTYPE,
+ USIM_PARAM_LRCCHECK,
+ USIM_PARAM_C4,
+ USIM_PARAM_C8,
+ USIM_PARAM_PROTOCOL,
+ USIM_PARAM_VOLTAGE,
+ USIM_PARAM_EMV,
+ USIM_PARAM_FI,
+ USIM_PARAM_DI,
+ USIM_PARAM_CODING_CONV,
+ USIM_PARAM_CLOCK_STOP,
+ USIM_PARAM_SMARTCARD_CLOCK,
+};
+
+struct usim_data {
+ int slot;
+ int rxexplen;
+ int txlen;
+ unsigned char apdu[256];
+};
+
+struct usim_config {
+ enum usim_param attr;
+ unsigned int value;
+};
+
+#define USIM_SIGID 51
+
+#define USIM_IOCTL_GET_PROVIDER_VERSION _IOR(USIM_IOCTL, 0, int)
+#define USIM_IOCTL_ACTIVATE_CARD _IO(USIM_IOCTL, 1)
+#define USIM_IOCTL_DEACTIVATE_CARD _IO(USIM_IOCTL, 2)
+#define USIM_IOCTL_WARM_RESET _IO(USIM_IOCTL, 3)
+#define USIM_IOCTL_GET_ATR _IOR(USIM_IOCTL, 4, char *)
+#define USIM_IOCTL_SEND_DATA _IOW(USIM_IOCTL, 5, struct usim_data)
+#define USIM_IOCTL_SET_CONFIG _IOW(USIM_IOCTL, 6, struct usim_config)
+#define USIM_IOCTL_GET_CONFIG _IOW(USIM_IOCTL, 7, struct usim_config)
+#define USIM_IOCTL_GET_CARD_PRESENCE _IOR(USIM_IOCTL, 8, int)
+#define USIM_IOCTL_REGISTER_PID _IOW(USIM_IOCTL, 9, int)
+
+#define USIM_MAX_ATRLENGTH 0xFF
+#define USIM_MAX_APDU_LENGTH 0xFE
+
+enum usim_smartcard_clock {
+ USIM_SMARTCART_CLOCK_3_3MHZ = 0x1,
+ USIM_SMARTCART_CLOCK_4MHZ = 0x2,
+ USIM_SMARTCART_CLOCK_5MHZ = 0x3,
+ USIM_SMARTCART_CLOCK_6_6MHZ = 0x4,
+ USIM_SMARTCART_CLOCK_10MHZ = 0x5,
+ USIM_SMARTCART_CLOCK_20MHZ = 0x6,
+};
+
+enum usim_event {
+ USIM_EVENT_CARD_INSERT = 0x1,
+ USIM_EVENT_CARD_REMOVE = 0x2,
+ USIM_EVENT_TIMEOUT = 0x4,
+ USIM_EVENT_ERR_TXRETRY = 0x8,
+ USIM_EVENT_ERR_LRC = 0x10,
+ USIM_EVENT_ERR_PARITY = 0x20,
+ USIM_EVENT_ERR_FRAME = 0x40,
+ USIM_EVENT_PHYERR = 0x80,
+ USIM_EVENT_CARD_OVERHEAT = 0x100,
+};
+
+enum usim_card_voltage {
+ USIM_CARD_5V = 0,
+ USIM_CARD_3V,
+ USIM_CARD_1_8V
+};
+
+#endif /* __TI_USIM_H__ */
--
1.7.1
^ permalink raw reply related
* [PATCH v2 2/5] misc: tda8026: Add NXP TDA8026 PHY driver
From: Satish Patel @ 2014-01-20 4:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390192434-19386-1-git-send-email-satish.patel@ti.com>
TDA8026 is a SmartCard PHY from NXP.
The PHY interfaces with the main processor over the
I2C interface and acts as a slave device.
The driver also exposes the phy interface
(defined at include/linux/sc_phy.h) for SmartCard controller.
Controller uses this interface to communicate with smart card
inserted to the phy's slot.
Note: gpio irq is not validated as I do not have device with that.
I have validated interrupt with dedicated interrupt line on my device.
Signed-off-by: Satish Patel <satish.patel@ti.com>
---
Documentation/devicetree/bindings/misc/tda8026.txt | 19 +
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/tda8026.c | 1255 ++++++++++++++++++++
4 files changed, 1282 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
create mode 100644 drivers/misc/tda8026.c
diff --git a/Documentation/devicetree/bindings/misc/tda8026.txt b/Documentation/devicetree/bindings/misc/tda8026.txt
new file mode 100644
index 0000000..f115c9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/tda8026.txt
@@ -0,0 +1,19 @@
+TDA8026 smart card slot interface
+
+This is an i2c based smart card interface device forming the electrical
+interface between a microcontroller and smart cards. This device supports
+asynchronous cards (micro controller-based IC cards) as well as synchronous
+cards (mainly memory cards)
+
+Required properties:
+- compatible: "nxp,tda8026"
+- shutdown-gpio = GPIO pin mapping for SDWNN pin
+- reg = i2c interface address
+
+
+Example:
+tda8026: tda8026 at 48 {
+ compatible = "nxp,tda8026";
+ reg = <0x48>;
+ shutdown-gpio = <&gpio5 19 GPIO_ACTIVE_HIGH>;/* Bank5, pin19 */
+ };
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index a3e291d..bb94a6f 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,13 @@ config SRAM
the genalloc API. It is supposed to be used for small on-chip SRAM
areas found on many SoCs.
+config NXP_TDA8026_PHY
+ tristate "NXP PHY Driver for Smart Card PHY"
+ depends on I2C=y
+ help
+ If you say yes here you get support for the TDA8026 Smart card PHY
+ with I2C interface.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f45473e..24374dd 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
+obj-$(CONFIG_NXP_TDA8026_PHY) += tda8026.o
diff --git a/drivers/misc/tda8026.c b/drivers/misc/tda8026.c
new file mode 100644
index 0000000..6a77686
--- /dev/null
+++ b/drivers/misc/tda8026.c
@@ -0,0 +1,1255 @@
+/*
+ * tda8026.c - TDA8026 PHY driver for NXP Smart card PHY
+ *
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/notifier.h>
+#include <linux/sc_phy.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/delay.h>
+
+#define TDA8026_MAX_SLOTS (5)
+#define TDA8026_NUM_SAM_SLOTS (4)
+#define TDA8026_USERCARD_SLOT (1)
+
+#define TDA8026_CSB_ADDR (0x24)
+#define TDA8026_REG0_ADDR (0x20)
+#define TDA8026_REG1_ADDR (0x21)
+#define TDA8026_SLEWRATE_ADDR (0x20)
+#define TDA8026_PRODVER_ADDR (0x20)
+#define TDA8026_INTSTAT_ADDR (0x21)
+
+#define TDA8026_PHY_PRODUCT_VERSION (0xC2)
+
+/* CSB register values */
+#define TDA8026_CSB_PV_INTSTAT_VAL (0x0)
+#define TDA8026_CSB_SLEWRATE_VAL (0x6)
+
+/* Slot REG0 read mode bit fields */
+#define TDA8026_REG0_ACTIVE_MASK (0x80)
+#define TDA8026_REG0_EARLY_MASK (0x40)
+#define TDA8026_REG0_MUTE_MASK (0x20)
+#define TDA8026_REG0_PROT_MASK (0x10)
+#define TDA8026_REG0_SUPL_MASK (0x08)
+#define TDA8026_REG0_CLKSW_MASK (0x04)
+#define TDA8026_REG0_PREL_MASK (0x02)
+#define TDA8026_REG0_PRES_MASK (0x01)
+
+/* Slot REG0 write mode bit fields */
+#define TDA8026_REG0_VCC1V8_MASK (0x80)
+#define TDA8026_REG0_IOEN_MASK (0x40)
+
+#define TDA8026_REG0_REG10_MASK (0x30)
+#define TDA8026_REG0_REG10_SHIFT (4)
+#define TDA8026_REG0_REG10_CFG_VAL (0x0)
+#define TDA8026_REG0_REG10_D_VAL (0x1)
+#define TDA8026_REG0_REG10_CMSB_VAL (0x2)
+#define TDA8026_REG0_REG10_CLSB_VAL (0x3)
+
+#define TDA8026_REG0_PDWN_MASK (0x08)
+#define TDA8026_REG0_5V3VN_MASK (0x04)
+#define TDA8026_REG0_WARM_RESET_MASK (0x02)
+#define TDA8026_REG0_START_MASK (0x01)
+
+/* Slot REG1 CFG bit fields REG[1:0] = 00b */
+#define TDA8026_REG1CFG_CFGP2_MASK (0x80)
+#define TDA8026_REG1CFG_RSTIN_MASK (0x40)
+#define TDA8026_REG1CFG_C8_MASK (0x20)
+#define TDA8026_REG1CFG_C4_MASK (0x10)
+#define TDA8026_REG1CFG_CLKPD_MASK (0x0C)
+#define TDA8026_REG1CFG_CLKPD_SHIFT (2)
+#define TDA8026_REG1CFG_CLKDIV_MASK (0x03)
+#define TDA8026_REG1CFG_CLKDIV_SHIFT (0)
+
+/* Slew rate register bit fields */
+#define TDA8026_SR_CLKSR_SLOT0_MASK 0x0C
+#define TDA8026_SR_CLKSR_SLOT0_SHIFT 0x2
+#define TDA8026_SR_IOSR_SLOT0_MASK 0x3
+#define TDA8026_SR_IOSR_SLOT0_SHIFT 0x0
+
+#define TDA8026_SR_CLKSR_SLOT2TO5_MASK 0xC0
+#define TDA8026_SR_CLKSR_SLOT2TO5_SHIFT 0x6
+#define TDA8026_SR_IOSR_SLOT2TO5_MASK 0x30
+#define TDA8026_SR_IOSR_SLOT2TO5_SHIFT 0x4
+
+#define TDA8026_MIN_EARLY_CYCLE (200)
+
+struct tda8026 {
+ /* device pointer */
+ struct device *dev;
+
+ /* For device IO interfaces: I2C or SPI */
+ void *control_data;
+ /* Store a shadow of Slot Register 0 as it cannot be read */
+ u8 reg0[TDA8026_MAX_SLOTS];
+ int irq;
+ int notify;
+ int shutdown_gpio;
+ int enable;
+};
+
+static BLOCKING_NOTIFIER_HEAD(tda8026_notifier_list);
+
+static int tda8026_i2c_read(struct tda8026 *tda8026, u8 reg,
+ int bytes, u8 *dest)
+{
+ struct i2c_client *i2c = tda8026->control_data;
+ struct i2c_msg xfer;
+ int ret;
+
+ /* We need to read from the slave address itself, as there
+ * is no separate register to be accessed in TDA8026
+ */
+
+ xfer.addr = reg;
+ xfer.flags = I2C_M_RD;
+ xfer.len = bytes;
+ xfer.buf = dest;
+
+ ret = i2c_transfer(i2c->adapter, &xfer, 1);
+ if (ret < 0)
+ dev_err(tda8026->dev, "Read [0x%x] Error %d\n", reg, ret);
+
+ return ret;
+}
+
+static int tda8026_i2c_write(struct tda8026 *tda8026, u8 reg,
+ int bytes, u8 *src)
+{
+ struct i2c_client *i2c = tda8026->control_data;
+ struct i2c_msg xfer;
+ int ret;
+
+ /* We have to write to the slave address itself, as
+ * there is no separate register to be accessed in TDA8026
+ */
+ xfer.addr = reg;
+ xfer.flags = 0;
+ xfer.len = bytes;
+ xfer.buf = src;
+
+ ret = i2c_transfer(i2c->adapter, &xfer, 1);
+ if (ret < 0)
+ dev_err(tda8026->dev, "Write [0x%x] Error %d\n", reg, ret);
+
+ return ret;
+}
+
+/* put the phy in shutdown mode, which in turn deactivate
+ * all the cards in the slot and card pins are forced to 0V
+ */
+static inline void tda8026_disable(struct tda8026 *tda8026)
+{
+ dev_dbg(tda8026->dev, "tda8026_disable!!!!");
+ tda8026->enable = 0;
+ if (gpio_is_valid(tda8026->shutdown_gpio))
+ gpio_set_value(tda8026->shutdown_gpio, 0);
+}
+
+/* exit from shutdown mode */
+static inline void tda8026_enable(struct tda8026 *tda8026)
+{
+ dev_dbg(tda8026->dev, "tda8026_enable!!!!");
+ tda8026->enable = 1;
+ if (gpio_is_valid(tda8026->shutdown_gpio))
+ gpio_set_value(tda8026->shutdown_gpio, 1);
+ /* Added dealy to stabilized phy state after pulling shutdown line */
+ mdelay(100);
+}
+
+/*
+ * Select the card slot to communicate with the card
+ * Note that card slots are numbered from 0 in software.
+ * However TDA8026 PHY numbers the slot starting 1.
+ */
+static inline int tda8026_select_slot(struct tda8026 *tda8026, u8 slot)
+{
+ int ret = 0;
+
+ /* In SW slot starts from 0, in TDA8026 it starts from 1 */
+ slot = slot + 1;
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &slot);
+
+ return ret;
+}
+
+static int tda8026_clear_interrupts(struct tda8026 *tda8026)
+{
+ u8 val;
+ u8 status;
+ int ret = 0;
+
+ /* Select the Interrupt register bank */
+ val = TDA8026_CSB_PV_INTSTAT_VAL;
+ tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+
+ /* Read the interrupt status which will tell us the slot */
+ ret = tda8026_i2c_read(tda8026, TDA8026_INTSTAT_ADDR, 1, &status);
+ if (ret < 0)
+ return ret;
+
+ for (val = 1; val > TDA8026_MAX_SLOTS; val++) {
+ /* Program the slot number to the CSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG0_ADDR, 1, &status);
+ if (ret < 0)
+ return ret;
+ }
+ return ret;
+}
+/*
+ * TDA8026 PHY IRQ handler
+ */
+static irqreturn_t tda8026_irq(int irq, void *irq_data)
+{
+ struct sc_phy *phy_tda8026 = (struct sc_phy *)irq_data;
+ struct tda8026 *tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ u8 slot;
+ u8 val;
+ u8 status;
+ int ret = 0;
+ int action = 0;
+
+ dev_dbg(tda8026->dev, "tda8026_irq!!");
+
+ if (tda8026->enable == 0) {
+ dev_dbg(tda8026->dev, "phy is disable not serving interrputs!!");
+ /* when, phy is in shutdown mode, it can detect the card insert
+ * event. But if phy is not enable (i.e.) there is no consumer
+ * of phy then just enable phy, clear the interrupt and disable
+ * again
+ */
+ tda8026_enable(tda8026);
+ tda8026_clear_interrupts(tda8026);
+ tda8026_disable(tda8026);
+ return IRQ_HANDLED;
+ }
+
+ /* Select the Interrupt register bank */
+ val = TDA8026_CSB_PV_INTSTAT_VAL;
+ tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+
+ /* Read the interrupt status which will tell us the slot */
+ ret = tda8026_i2c_read(tda8026, TDA8026_INTSTAT_ADDR, 1, &status);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+
+ /* find out for which slot interrupt has occur */
+ slot = 0;
+ while (val == 0 && slot < TDA8026_MAX_SLOTS) {
+ val = status & (1 << slot);
+ slot++;
+ }
+
+ if (slot > TDA8026_MAX_SLOTS) {
+ dev_err(tda8026->dev, "invalid slot interrput");
+ return IRQ_HANDLED;
+ }
+
+ /* Program the slot number to the CSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &slot);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ /* Now read the slot reg0 to find out the cause of interrupt
+ * Note that IRQ is raised only when one of the SUPL, PROT,
+ * MUTE and EARLY bits are set to logic 1.
+ */
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG0_ADDR, 1, &status);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (slot < 3) {
+ /* slot 1 and slot 2 can be used for user card, it can raise
+ * interrupt for card insert and remove.Other slot are for SAM
+ * modules. They are either always present or alwyas absent
+ */
+ if (status & TDA8026_REG0_PREL_MASK) {
+ if (status & TDA8026_REG0_PRES_MASK) {
+ dev_dbg(tda8026->dev, "card is inserted");
+ action |= SC_PHY_CARD_INSERTED;
+ } else {
+ dev_dbg(tda8026->dev, "card is removed");
+ action |= SC_PHY_CARD_REMOVED;
+ }
+ }
+ }
+
+ if (status & (TDA8026_REG0_EARLY_MASK | TDA8026_REG0_MUTE_MASK)) {
+ dev_dbg(tda8026->dev, "CARD EARLY INTERRUPT\n");
+ action |= SC_PHY_CARD_ATR_TIMEOUT;
+ }
+
+ if (status & TDA8026_REG0_PROT_MASK) {
+ dev_dbg(tda8026->dev, "CARD OVERHEAT/OVERLOAD INTERRUPT\n");
+ action |= SC_PHY_CARD_OVERHEAT;
+ }
+
+ if (action != 0x0 && tda8026->notify) {
+ /* add slot information. Pass slot-1 as for controller slot
+ * starts from 0,1,2.. */
+ action |= ((slot-1) << SC_PHY_NOTIFICATION_SLOT_SHIFT);
+
+ /* notify action */
+ blocking_notifier_call_chain(&tda8026_notifier_list, action,
+ phy_tda8026->notify_data);
+ }
+ return IRQ_HANDLED;
+}
+
+/*
+ * PDWN bit is set/cleared to apply CLKPD[1:0] bit clock settings to
+ * the clock pin for the selected card slot.
+ */
+static int tda8026_pwdn(struct tda8026 *tda8026, u8 slot, int state)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+
+ if (state)
+ reg0 |= TDA8026_REG0_PDWN_MASK;
+ else
+ reg0 &= ~(TDA8026_REG0_PDWN_MASK);
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Set the card supply voltage.
+ * TDA PHY supports supply voltage of 1.8V, 3V and 5V.
+ */
+static int tda8026_set_voltage(struct tda8026 *tda8026, u8 slot, int voltage)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+
+ reg0 &= ~(TDA8026_REG0_VCC1V8_MASK | TDA8026_REG0_5V3VN_MASK);
+
+ switch (voltage) {
+ case SC_PHY_1_8V:
+ reg0 |= TDA8026_REG0_VCC1V8_MASK;
+ break;
+
+ case SC_PHY_5V:
+ reg0 |= TDA8026_REG0_5V3VN_MASK;
+ break;
+
+ case SC_PHY_3V:
+ default:
+ break;
+ }
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Enable the I/O line by setting I/OEN bit of slot's main address register.
+ * The I/O line should be enabled prior to card activation.
+ */
+static int tda8026_io_enable(struct tda8026 *tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_IOEN_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Disable the I/O line by clearing I/OEN bit of slot's main address register.
+ * The I/O line can be disabled post card activation.
+ */
+static int tda8026_io_disable(struct tda8026 *tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~(TDA8026_REG0_IOEN_MASK);
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Sets the mute counter in C[15:8] and C[7:0] register
+ * Write Reg[1:0] = 11 to select C[7:0] register
+ * Write Reg[1:0] = 10 to select C[15:8] register
+ */
+static int tda8026_set_atr_mute_time(struct tda8026 *tda8026, u8 slot,
+ int mute_counter)
+{
+ u8 reg0;
+ u8 mute_counter_high = (mute_counter & 0xFF00) >> 8;
+ u8 mute_counter_low = mute_counter & 0xFF;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_REG10_CLSB_VAL << TDA8026_REG0_REG10_SHIFT;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Write the mute counter value in C[7:0] LSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1,
+ &mute_counter_low);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_REG10_CMSB_VAL << TDA8026_REG0_REG10_SHIFT;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Write the mute counter value in C[15:8] MSB register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1,
+ &mute_counter_high);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Sets the ATR early time counter.
+ * Write Reg[1:0] = 01 to select D register
+ */
+static int tda8026_set_atr_early_time(struct tda8026 *tda8026, u8 slot,
+ int early_counter)
+{
+ u8 reg0;
+ int ret = 0;
+ u8 counter = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_REG10_D_VAL << TDA8026_REG0_REG10_SHIFT;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Write the early atr counter value in D register */
+ counter = (early_counter - TDA8026_MIN_EARLY_CYCLE) & 0xFF;
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, &counter);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static int tda8026_set_rstpin(struct tda8026 *tda8026, u8 slot, int state)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ /* Write Reg[1:0] = 00 to select CONFIG register */
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ /* Read the Reg1 value */
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+
+ if (state)
+ reg1 |= TDA8026_REG1CFG_RSTIN_MASK;
+ else
+ reg1 &= ~TDA8026_REG1CFG_RSTIN_MASK;
+
+ /* Write the Reset value to the register */
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Activate the card by setting START bit of slot's main register.
+ * Voltage selction and enabling of I/O lines should be done prior
+ * to activating the card.
+ */
+static int tda8026_activate_card(struct sc_phy *phy_tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ /* if PHY is used then RSTIN should be 1 for async cards,
+ * currently this is applicable only for TDA8026
+ */
+ tda8026_set_rstpin(tda8026, slot, 1);
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_START_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Deactivate the card by clearing the START bit of slot's main register
+ * We implement normal de-activation here.
+ * On clearing the START bit with normal deactivation, automatic
+ * deactivation is initiated and performaed by TDA8026.
+ */
+static int tda8026_deactivate_card(struct sc_phy *phy_tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~(TDA8026_REG0_START_MASK);
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Warm reset is initiated by setting the WARM bit of slot's main register
+ */
+static int tda8026_warm_reset(struct sc_phy *phy_tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ int ret = 0;
+
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ /* See section 6.5 in TDA app note */
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 |= TDA8026_REG0_WARM_RESET_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ return 0;
+}
+
+/*
+ * Read and return the TDA8026 PHY product version
+ */
+static int tda8026_get_provider_version(struct tda8026 *tda8026)
+{
+ u8 val;
+ u8 version = 0;
+ int ret = 0;
+
+ /* Select Product version bank i.e Write CSB = 00 */
+ val = TDA8026_CSB_PV_INTSTAT_VAL;
+ ret = tda8026_i2c_write(tda8026, TDA8026_CSB_ADDR, 1, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_PRODVER_ADDR, 1, &version);
+ if (ret < 0)
+ return ret;
+ else
+ dev_info(tda8026->dev, "Product Version = %x\n", version);
+
+ return version;
+}
+
+
+/*
+ * Set/reset the C4/C8 Pin
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_set_c4c8(struct tda8026 *tda8026, u8 slot, int state, int pin)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+ int mask = 0;
+
+ if (slot != 0) {
+ /* C4/C8 pin value valid only for slot 1 */
+ return -EINVAL;
+ } else {
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL <<
+ TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+ if (pin == SC_PHY_PIN_C8)
+ mask = TDA8026_REG1CFG_C8_MASK;
+ else if (pin == SC_PHY_PIN_C4)
+ mask = TDA8026_REG1CFG_C4_MASK;
+ else
+ return -EINVAL;
+
+ if (state)
+ reg1 |= mask;
+ else
+ reg1 &= ~mask;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/*
+ * Get the state of C4/C8 pin (high/low)
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_get_c4c8(struct tda8026 *tda8026, u8 slot, int pin)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+ int mask = 0;
+
+ if (slot != 0) {
+ /* C4/C8 pin value valid only for slot 1 */
+ return -EINVAL;
+ } else {
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL <<
+ TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1,
+ ®1);
+ if (ret < 0)
+ return ret;
+
+ if (pin == SC_PHY_PIN_C8)
+ mask = TDA8026_REG1CFG_C8_MASK;
+ else if (pin == SC_PHY_PIN_C4)
+ mask = TDA8026_REG1CFG_C4_MASK;
+ else
+ return -EINVAL;
+
+ return (reg1 &= mask) ? 1 : 0;
+ }
+ return 0;
+}
+
+/*
+ * Set card clock
+ * Applicable only for synchronous mode
+ */
+static int tda8026_set_cardclk(struct tda8026 *tda8026, u8 slot,
+ int config)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+
+ reg1 &= ~(TDA8026_REG1CFG_CLKPD_MASK);
+ reg1 |= (config << TDA8026_REG1CFG_CLKPD_SHIFT) &
+ TDA8026_REG1CFG_CLKPD_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Set the CLKDIV[1:0] bits.
+ * CLKDIV[1:0] bits define the card clock frequency
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_set_clkdiv(struct tda8026 *tda8026, u8 slot, int div)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+
+ reg1 &= ~(TDA8026_REG1CFG_CLKDIV_MASK);
+ reg1 |= (div << TDA8026_REG1CFG_CLKDIV_SHIFT) &
+ TDA8026_REG1CFG_CLKDIV_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+/*
+ * Get the CLKDIV[1:0] bits.
+ * CLKDIV[1:0] bits define the card clock frequency
+ * Write Reg[1:0] = 00 to select CONFIG register
+ */
+static int tda8026_get_clkdiv(struct tda8026 *tda8026, u8 slot)
+{
+ u8 reg0 = 0;
+ u8 reg1 = 0;
+ int ret = 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ reg0 = tda8026->reg0[slot];
+ reg0 &= ~TDA8026_REG0_REG10_MASK;
+ reg0 |= (TDA8026_REG0_REG10_CFG_VAL << TDA8026_REG0_REG10_SHIFT) &
+ TDA8026_REG0_REG10_MASK;
+
+ ret = tda8026_i2c_write(tda8026, TDA8026_REG0_ADDR, 1, ®0);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1)
+ tda8026->reg0[slot] = reg0;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG1_ADDR, 1, ®1);
+ if (ret < 0)
+ return ret;
+
+ ret = reg1 & TDA8026_REG1CFG_CLKDIV_MASK;
+
+ return ret;
+}
+
+/*
+ * Check if card is present in the slot.
+ */
+static int tda8026_card_present(struct tda8026 *tda8026, u8 slot)
+{
+ int present = 0;
+ int ret = 0;
+ u8 status = 0;
+
+ if (tda8026->enable == 0)
+ return 0;
+
+ ret = tda8026_select_slot(tda8026, slot);
+ if (ret < 0)
+ return ret;
+
+ ret = tda8026_i2c_read(tda8026, TDA8026_REG0_ADDR, 1, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status & TDA8026_REG0_PRES_MASK)
+ present = 1;
+
+ return present;
+}
+
+/**
+ * tda8026_register_notify - register a notifier callback whenever a card
+ * event happens
+ * @nb: pointer to the notifier block for the callback events.
+ */
+static int tda8026_register_notify(struct sc_phy *phy_tda8026,
+ struct notifier_block *nb, void *data)
+{
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ phy_tda8026->notify_data = data;
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ blocking_notifier_chain_register(&tda8026_notifier_list, nb);
+ tda8026->notify = 1;
+ return 0;
+}
+
+/**
+ * tda8026_unregister_notify - unregister a notifier callback
+ * event happens
+ * @nb: pointer to the notifier block for the callback events.
+ */
+static int tda8026_unregister_notify(struct sc_phy *phy_tda8026,
+ struct notifier_block *nb)
+{
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ blocking_notifier_chain_unregister(&tda8026_notifier_list, nb);
+ tda8026->notify = 0;
+ return 0;
+}
+
+static int tda8026_set_config(struct sc_phy *phy_tda8026, u8 slot, enum
+ sc_phy_config attr, int value)
+{
+ int ret = 0;
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ switch (attr) {
+ case SC_PHY_CARD_SUPPLY_VOLTAGE:
+ ret = tda8026_set_voltage(tda8026, slot, value);
+ break;
+
+ case SC_PHY_ATR_MUTE_TIME:
+ ret = tda8026_set_atr_mute_time(tda8026, slot, value);
+ break;
+
+ case SC_PHY_ATR_EARLY_TIME:
+ ret = tda8026_set_atr_early_time(tda8026, slot, value);
+ break;
+
+ case SC_PHY_CARD_MODE:
+ if (value == SC_PHY_SYNC) {
+ /* set clkdiv to zero, rst pin to low and pwdn bit to
+ * logic 0
+ */
+ tda8026_set_clkdiv(tda8026, slot, 0);
+ tda8026_set_rstpin(tda8026, slot, 0);
+ tda8026_pwdn(tda8026, slot, 0);
+ } else {
+ /* Nothing to do, default mode is async */
+ }
+ break;
+
+ case SC_PHY_IO:
+ if (value)
+ ret = tda8026_io_enable(tda8026, slot);
+ else
+ ret = tda8026_io_disable(tda8026, slot);
+ break;
+
+ case SC_PHY_PIN_RST:
+ ret = tda8026_set_rstpin(tda8026, slot, value);
+ break;
+
+ case SC_PHY_CLKDIV:
+ ret = tda8026_set_clkdiv(tda8026, slot, value);
+ break;
+
+ case SC_PHY_MODE:
+ if (value == SC_PHY_ACTIVE)
+ tda8026_enable(tda8026);
+ else if (value == SC_PHY_SHUTDOWN)
+ tda8026_disable(tda8026);
+ else
+ ret = -EINVAL;
+ break;
+
+ case SC_PHY_PIN_C4:
+ ret = tda8026_set_c4c8(tda8026, slot, value, SC_PHY_PIN_C4);
+ break;
+
+ case SC_PHY_PIN_C8:
+ ret = tda8026_set_c4c8(tda8026, slot, value, SC_PHY_PIN_C8);
+ break;
+
+ case SC_PHY_PIN_CLK:
+ ret = tda8026_set_cardclk(tda8026, slot, value);
+ break;
+
+ default:
+ ret = -EINVAL;
+ dev_err(phy_tda8026->dev, "operation not supported:%d", attr);
+ break;
+ }
+ return ret;
+}
+
+static int tda8026_get_config(struct sc_phy *phy_tda8026, u8 slot, enum
+ sc_phy_config attr)
+{
+ int ret = -1;
+ struct tda8026 *tda8026;
+
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+ switch (attr) {
+ case SC_PHY_CARD_PRESENCE:
+ ret = tda8026_card_present(tda8026, slot);
+ break;
+
+ case SC_PHY_VERSION:
+ ret = tda8026_get_provider_version(tda8026);
+ break;
+
+ case SC_PHY_CLKDIV:
+ ret = tda8026_get_clkdiv(tda8026, slot);
+ break;
+
+ case SC_PHY_PIN_C4:
+ ret = tda8026_get_c4c8(tda8026, slot, SC_PHY_PIN_C4);
+ break;
+
+ case SC_PHY_PIN_C8:
+ ret = tda8026_get_c4c8(tda8026, slot, SC_PHY_PIN_C8);
+ break;
+
+ default:
+ ret = -EINVAL;
+ dev_err(phy_tda8026->dev, "operation not supported:%d", attr);
+ break;
+ }
+ return ret;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tda8026_id_table[];
+#endif
+
+static int tda8026_parse_dt(struct device *dev, struct tda8026 *pdata)
+{
+ struct device_node *np = dev->of_node;
+ int ret = 0;
+
+ pdata->shutdown_gpio = of_get_named_gpio(np, "shutdown-gpio", 0);
+ if (!gpio_is_valid(pdata->shutdown_gpio)) {
+ dev_err(dev, "Failed to get shutdown gpio\n");
+ return -EINVAL;
+ }
+
+ ret = devm_gpio_request_one(dev, pdata->shutdown_gpio,
+ GPIOF_DIR_OUT, "shutdown_gpio");
+ if (ret) {
+ dev_err(dev, "Failed to request shutdown_gpio\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int tda8026_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ struct sc_phy *phy_tda8026;
+ struct tda8026 *pdata;
+
+ struct device *dev = &i2c->dev;
+ struct device_node *np = dev->of_node;
+
+ if (np == NULL)
+ return -EINVAL;
+
+ pdata = devm_kzalloc(dev, sizeof(struct tda8026), GFP_KERNEL);
+ if (pdata == NULL)
+ return -ENOMEM;
+
+ phy_tda8026 = devm_kzalloc(dev, sizeof(struct sc_phy), GFP_KERNEL);
+ if (phy_tda8026 == NULL)
+ return -ENOMEM;
+
+ ret = tda8026_parse_dt(dev, pdata);
+ if (ret != 0)
+ return ret;
+
+ i2c_set_clientdata(i2c, phy_tda8026);
+ phy_tda8026->dev = &i2c->dev;
+
+ phy_tda8026->pdata = (void *)pdata;
+ pdata->control_data = i2c;
+ pdata->irq = i2c->irq;
+ pdata->dev = phy_tda8026->dev;
+ pdata->notify = 0;
+
+ if (pdata->irq == 0) {
+ return -EINVAL;
+ }
+ ret = devm_request_threaded_irq(dev, pdata->irq, NULL, tda8026_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "tda8026", phy_tda8026);
+ if (ret < 0) {
+ dev_err(phy_tda8026->dev, "can't get irq %d err %d\n",
+ pdata->irq, ret);
+ return ret;
+ }
+
+ /* enable phy */
+ tda8026_enable(pdata);
+
+ tda8026_clear_interrupts(pdata);
+ phy_tda8026->set_config = tda8026_set_config;
+ phy_tda8026->get_config = tda8026_get_config;
+ phy_tda8026->activate_card = tda8026_activate_card;
+ phy_tda8026->deactivate_card = tda8026_deactivate_card;
+ phy_tda8026->warm_reset = tda8026_warm_reset;
+ phy_tda8026->register_notify = tda8026_register_notify;
+ phy_tda8026->unregister_notify = tda8026_unregister_notify;
+
+ /* disable phy */
+ tda8026_disable(pdata);
+
+ return 0;
+}
+
+static int tda8026_i2c_remove(struct i2c_client *i2c)
+{
+ struct sc_phy *phy_tda8026;
+ struct tda8026 *tda8026;
+ int action = 0;
+
+ phy_tda8026 = i2c_get_clientdata(i2c);
+ if (phy_tda8026 == NULL)
+ return -EINVAL;
+
+ tda8026 = (struct tda8026 *)phy_tda8026->pdata;
+
+ /* notify action */
+ action = SC_PHY_REMOVED;
+ blocking_notifier_call_chain(&tda8026_notifier_list, action,
+ phy_tda8026->notify_data);
+ tda8026->notify = 0;
+
+ /* enable shutdown mode */
+ tda8026_disable(tda8026);
+
+ return 0;
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tda8026_id_table[] = {
+ { .compatible = "nxp,tda8026" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tda8026_id_table);
+#endif
+
+static const struct i2c_device_id tda8026_i2c_id[] = {
+ {"tda8026", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tda8026_i2c_id);
+
+static struct i2c_driver tda8026_i2c_driver = {
+ .driver = {
+ .name = "tda8026",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(tda8026_id_table),
+ },
+ .probe = tda8026_i2c_probe,
+ .remove = tda8026_i2c_remove,
+ .id_table = tda8026_i2c_id,
+};
+static int __init tda8026_i2c_init(void)
+{
+ int ret;
+ ret = i2c_add_driver(&tda8026_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register TDA8026 I2C driver: %d\n", ret);
+ return ret;
+}
+/* init early so consumer devices can complete system boot */
+subsys_initcall(tda8026_i2c_init);
+
+static void __exit tda8026_i2c_exit(void)
+{
+ i2c_del_driver(&tda8026_i2c_driver);
+}
+module_exit(tda8026_i2c_exit);
+
+MODULE_AUTHOR("Maulik Mankad <maulik@ti.com>");
+MODULE_DESCRIPTION("TDA8026 Smart Card NXP PHY driver");
+MODULE_LICENSE("GPL");
--
1.7.1
^ permalink raw reply related
* [PATCH v2 1/5] sc_phy:SmartCard(SC) PHY interface to SC controller
From: Satish Patel @ 2014-01-20 4:33 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390192434-19386-1-git-send-email-satish.patel@ti.com>
SmartCard controller uses this interface to communicate with
SmartCard via PHY
Some SmartCard PHY has multiple slots for cards.
This inerface also enables controller to communicate
with one or more SmartCard connected over phy.
interface structure includes following APIs
- set/get config
- activate/deactivate smart card
- warm reset
- register_notify (for card insert/remove/overheat)
- unregister_notify
Signed-off-by: Satish Patel <satish.patel@ti.com>
---
Documentation/sc_phy.txt | 171 ++++++++++++++++++++++++++++++++++++++++++++++
include/linux/sc_phy.h | 132 +++++++++++++++++++++++++++++++++++
2 files changed, 303 insertions(+), 0 deletions(-)
create mode 100644 Documentation/sc_phy.txt
create mode 100644 include/linux/sc_phy.h
diff --git a/Documentation/sc_phy.txt b/Documentation/sc_phy.txt
new file mode 100644
index 0000000..d610e26
--- /dev/null
+++ b/Documentation/sc_phy.txt
@@ -0,0 +1,171 @@
+ SmartCard PHY Interface
+ Satish Patel <satish.patel@ti.com>
+
+This document explains the SmartCard interface between SmartCard
+controller and SmartCard phy. Document also describes how-to-use.
+
+1. Why SmartCard PHY Interface?
+
+The use of smartcard is increasing in embedded industry. As smartcard
+not only prevent duplication but also, brings key based authentication
+flow into picture.
+
+SmartCard standards like EMV(EuroPay-Mastercard-Visa) are becoming
+mandatory for payment terminals.
+
+Till date, most of the SmartCard readers are based on USB serial
+interface. Which drives its logic within firmware lies on device.
+Few are based on FPGA solutions. But now SoCs are coming up with
+inbuilt smartcard controller. e.g. TI-AM43x
+
+Role of SmartCard controller and SmartCard phy:
+
+Smartcard Phy:
+Forms electrical interface between controller and SmartCard. Phy
+enables access to more than one smartcard and in addition it provides
+fast deactivation logic when card gets removed from the slot. It can
+also generate the signals like card insert/remove/overheat etc.
+
+Smartcard Controller:
+In built mechanism to meet EMV L1 specification (e.g. T=0, T=1
+protocol timings, ATR timeout etc..) for SmartCard transaction. In
+addition to this, it has FIFO to store tx/rx bytes, internal state
+machine for ATR, Tx/Rx, Synchronous/Asynchronous mode, timing
+counters etc..
+
+Controller can also have direct interface through which SmartCard
+can be connected without phy.
+
+Below is the brief of SmartCard block diagram from user to h/w
+layer.
+
+
+ -----------
+ |PC/SC App|
+ -----------
+ ----------- -----------
+ |PC/SC F/W| | Visa APP|
+ ----------- -----------
+ ----------- ------------ ------------
+ |IFD Hand.| | EMV L1/L2| | Test App |
+ ----------- ------------ ------------
+User Space
+--------------------------------------------------------------------
+
+ -----------------------------------------
+ | SmartCard Controller Driver |
+ -----------------------------------------
+ | |
+ | |
+ ------------- |
+ | Phy Driver | |
+ ------------- |
+ | |
+Kernel Space | |
+--------------------------------------------------------------------
+ | |
+ --------- ----------------
+ | PHY | |Controller IP |
+ --------- ----------------
+ | |
+--------------------------------------------------------------------
+ | |
+ _______________________________________
+ | | |
+ VISA card Master Card Custom Card
+
+
+At present in Linux there is no public interface exist which acts as
+bridge between controller and phy. Mostly vendors uses proprietary
+solution in such cases.
+
+2. Introduction to SmartCard PHY interface
+
+SmartCard PHY interface that exposes phy's capabilities to the smart
+card controller. SmartCard controller uses this interface to
+communicate with SmartCard via phy.
+
+Such capabilities are:
+1) Some SmartCard phy (e.g. TDA8026-NxP) has multiple slots for smart
+cards. This interface enables controller to communicate with specific
+SmartCard inserted to the specific phy's slot.
+
+2) Warm reset to SmartCard inserted to phy slot.
+
+3) Bit banging of SmartCard pins to support vedor specific memory
+cards. Mostly when it comes to sychorous SmartCard
+
+4) Notification of card insert/remove/overheat etc.
+
+
+3. How to use
+
+SmartCard PHY:
+The SmartCard PHY driver, who wants to be interfaced with SmartCard
+controller require to follow below step
+
+- include "sc_phy.h"
+
+- use "sc_phy" structure as driver(client) data. PHY driver can use
+'void *pdata' of "sc_phy" to hold its private data(if any)
+
+- implement following capabilities (whichever is applicable)
+ set_config
+ get_config
+ activate_card
+ deactivate_card
+ warm_reset
+ register_notify
+ unregister_notify
+e.g.
+phyxx_init()
+{
+
+ struct sc_phy *sc_phy;
+ struct phyxx *pdata;
+
+ ...
+ ...
+ sc_phy->pdata = pdata;
+ sc_phy->set_config = phyxx_set_config;
+ sc_phy->get_config = phyxx_get_config;
+ sc_phy->activate_card = phyxx_activate_card;
+}
+
+Device Tree Binding:
+Refer following reference to bind SmartCard PHY with SmartCard
+controller.
+
+ /* SmartCard PHY node */
+ &i2c0 {
+ clock-frequency = <10000000>;
+ phyxx: phyxx at yy {
+ compatible = "xyz,abc";
+ reg = <0xyy>;
+ };
+ };
+
+ /* SmartCard Controller node*/
+ &usim0 {
+ ....
+ phy = <&phyxx>;
+ phy-slots = <1>;
+ };
+
+SmartCard Controller:
+- Access PHY interface using DT.
+Refer below example code
+
+ phy_i2c = of_find_i2c_device_by_node(phy_node);
+ if (phy_i2c == NULL) {
+ ret = -EINVAL;
+ }
+
+ /* get phy interface */
+ sc_phy = (struct sc_phy *)i2c_get_clientdata(phy_i2c);
+
+ /* register notifier */
+ sc_phy->register_notify(sc_phy, ¬ifier_cb, (void *)data);
+
+
+- Controller can use then after all defined SmartCard PHY interface
diff --git a/include/linux/sc_phy.h b/include/linux/sc_phy.h
new file mode 100644
index 0000000..fb3ad78
--- /dev/null
+++ b/include/linux/sc_phy.h
@@ -0,0 +1,132 @@
+/*
+ * sc_phy.h - Header file for Smart Card PHY operations.
+ * This is a smart card phy's interface to smart card controller.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SC_PHY_H__
+#define __SC_PHY_H__
+
+#include <linux/notifier.h>
+
+/* defines used in notify action
+ * action will be composed of two part
+ * | 0x0000 | <slot> - 2 bytes| <action>- 2 bytes |
+ */
+#define SC_PHY_NOTIFICATION_ACTION_MASK (0x000000ffU)
+#define SC_PHY_NOTIFICATION_ACTION_SHIFT (0)
+
+#define SC_PHY_NOTIFICATION_SLOT_MASK (0x0000ff00U)
+#define SC_PHY_NOTIFICATION_SLOT_SHIFT (8)
+/*
+ * smart card supply voltage levels
+ */
+enum sc_supply_voltage {
+ SC_PHY_5V = 0,
+ SC_PHY_3V,
+ SC_PHY_1_8V
+};
+
+/*
+ * smarcard type - async or sync
+ */
+enum sc_phy_card_mode {
+ SC_PHY_ASYNC = 0,
+ SC_PHY_SYNC,
+};
+
+/*
+ * phy operational modes
+ */
+enum sc_phy_mode {
+ SC_PHY_ACTIVE = 0,
+ SC_PHY_CLOCKSTOP,
+ SC_PHY_STANDBY,
+ SC_PHY_SHUTDOWN,
+ SC_PHY_REMOVED,
+};
+
+/*
+ * smartcard actions. more actions will be as they gets introduced in upcoming
+ * smart card phys.
+ */
+enum sc_phy_card_action {
+ SC_PHY_CARD_INSERTED = 0x1,
+ SC_PHY_CARD_REMOVED = 0x2,
+ SC_PHY_CARD_OVERHEAT = 0x4,
+ SC_PHY_CARD_ATR_TIMEOUT = 0x8,
+};
+
+/*
+ * configuration parameters of smart card phy
+ */
+enum sc_phy_config {
+ SC_PHY_VERSION = 0,
+ SC_PHY_CARD_SUPPLY_VOLTAGE,
+ SC_PHY_CARD_PRESENCE,
+ SC_PHY_ATR_MUTE_TIME,
+ SC_PHY_ATR_EARLY_TIME,
+ SC_PHY_CARD_MODE, /* sync or async mode */
+ SC_PHY_IO, /* disable or enable i/o line connected to phy */
+ SC_PHY_CLKDIV, /* clock division for the clock supplied to phy */
+ SC_PHY_MODE,
+ SC_PHY_PIN_CLK,
+ SC_PHY_PIN_RST,
+ SC_PHY_PIN_C4,
+ SC_PHY_PIN_C8
+};
+/**
+ * struct sc_phy - The basic smart card phy structure
+ *
+ * @dev: phy device
+ * @pdata: pointer to phy's private data structure
+ * @set_config: called to set phy's configuration
+ * @get_config: called to get phy's configuration
+ * @activate_card: perform smart card activation
+ * @deactivate_card: perform smart card de-activation
+ * @warm_reset: execute smart card warm reset sequence
+ * @register_card_activity_cb: register call back to phy device.
+ * This call back will be called on card insert or remove event
+ *
+ * smart card controller uses this interface to communicate with
+ * smart card via phy.Some smart card phy has multiple slots for
+ * cards. This inerface also enables controller to communicate with
+ * one or more smart card connected over phy.
+ */
+struct sc_phy {
+ /* phy's device pointer */
+ struct device *dev;
+
+ /* phy's private data */
+ void *pdata;
+
+ /* notify data, passed by interface user as a part of
+ * register_notify API. Data should be passed back when
+ * notification raised to the interface user
+ */
+ void *notify_data;
+
+ int (*set_config)(struct sc_phy *phy, u8 slot,
+ enum sc_phy_config attr, int value);
+ int (*get_config)(struct sc_phy *phy, u8 slot, enum
+ sc_phy_config attr);
+ int (*activate_card)(struct sc_phy *phy, u8 slot);
+ int (*deactivate_card)(struct sc_phy *phy, u8 slot);
+ int (*warm_reset)(struct sc_phy *phy, u8 slot);
+ int (*register_notify)(struct sc_phy *phy,
+ struct notifier_block *nb, void *notify_data);
+ int (*unregister_notify)(struct sc_phy *phy,
+ struct notifier_block *nb);
+};
+
+#endif /* __SC_PHY_H__ */
--
1.7.1
^ permalink raw reply related
* [PATCH v2 0/5] Smart Card(SC) interface, TI USIM & NxP SC phy driver
From: Satish Patel @ 2014-01-20 4:33 UTC (permalink / raw)
To: linux-arm-kernel
Changes from v1:
* RFC(v1) comments are fixed
** removed "gpio_to_irq" as GPIO controller process cell from DT and
give it to DT node
** comments on documentation
** few other comments on null checks are resolved
* BWT timing configuration is added to ti-usim driver
v1 cover letter link#
https://lkml.org/lkml/2014/1/6/250
Satish Patel (5):
sc_phy:SmartCard(SC) PHY interface to SC controller
misc: tda8026: Add NXP TDA8026 PHY driver
char: ti-usim: Add driver for USIM module on AM43xx
ARM: dts: AM43xx: DT entries added for ti-usim
ARM: dts: AM43xx-epos-evm: DT entries for ti-usim and phy
Documentation/devicetree/bindings/misc/tda8026.txt | 19 +
.../devicetree/bindings/ti-usim/ti-usim.txt | 31 +
Documentation/sc_phy.txt | 171 ++
arch/arm/boot/dts/am4372.dtsi | 10 +
arch/arm/boot/dts/am43x-epos-evm.dts | 43 +
drivers/char/Kconfig | 7 +
drivers/char/Makefile | 1 +
drivers/char/ti-usim-hw.h | 863 +++++++++
drivers/char/ti-usim.c | 1859 ++++++++++++++++++++
drivers/misc/Kconfig | 7 +
drivers/misc/Makefile | 1 +
drivers/misc/tda8026.c | 1255 +++++++++++++
include/linux/sc_phy.h | 132 ++
include/linux/ti-usim.h | 98 +
14 files changed, 4497 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/misc/tda8026.txt
create mode 100644 Documentation/devicetree/bindings/ti-usim/ti-usim.txt
create mode 100644 Documentation/sc_phy.txt
create mode 100644 drivers/char/ti-usim-hw.h
create mode 100644 drivers/char/ti-usim.c
create mode 100644 drivers/misc/tda8026.c
create mode 100644 include/linux/sc_phy.h
create mode 100644 include/linux/ti-usim.h
^ permalink raw reply
* [PATCH] arm64: mm: use ubfm for dcache_line_size
From: Jingoo Han @ 2014-01-20 4:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140117111307.GE16003@mudshark.cambridge.arm.com>
On Friday, January 17, 2014 8:13 PM, Will Deacon wrote:
> On Fri, Jan 17, 2014 at 08:04:32AM +0000, Jingoo Han wrote:
> > Use 'ubfm' for the bitfield move instruction; thus, single
> > instruction can be used instead of two instructions, when
> > getting the minimum D-cache line size from CTR_EL0 register.
> >
> > Signed-off-by: Jingoo Han <jg1.han@samsung.com>
> > ---
> > arch/arm64/mm/proc-macros.S | 3 +--
> > 1 file changed, 1 insertion(+), 2 deletions(-)
> >
> > diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S
> > index 8957b82..c31f41e 100644
> > --- a/arch/arm64/mm/proc-macros.S
> > +++ b/arch/arm64/mm/proc-macros.S
> > @@ -38,8 +38,7 @@
> > */
> > .macro dcache_line_size, reg, tmp
> > mrs \tmp, ctr_el0 // read CTR
> > - lsr \tmp, \tmp, #16
> > - and \tmp, \tmp, #0xf // cache line size encoding
> > + ubfm \tmp, \tmp, #0x16, 0x19 // cache line size encoding
>
> 0x16 and 0x19. Are you sure?
>
> You can also grep for other occurences of this pattern and change those too
> (pgtable stuff in head.S, clidr in cache.S).
I will not change those things, due to the following reasons.
So, I will send v3 patch that fixes only dcache_line_size in
proc-macros.S.
1. pgtable stuff in head.S
'ubfx' can be used instead of 'ubfm'. Also, additional definition
such as 'PGDIR_WIDTH' is necessary.
./arch/arm64/kernel/head.S
@@ -371,8 +371,7 @@ ENDPROC(__calc_phys_offset)
* Corrupts: tmp1, tmp2
*/
.macro create_pgd_entry, pgd, tbl, virt, tmp1, tmp2
- lsr \tmp1, \virt, #PGDIR_SHIFT
- and \tmp1, \tmp1, #PTRS_PER_PGD - 1 // PGD index
+ ubfx \tmp1, \virt, #PGDIR_SHIFT, #PGDIR_WIDTH // PGD index
./arch/arm64/include/asm/pgtable-2level-hwdef.h
@@ -32,6 +32,7 @@
+#define PGDIR_WIDTH 13
./arch/arm64/include/asm/pgtable-3level-hwdef.h
@@ -32,6 +32,7 @@
+#define PGDIR_WIDTH 9
2. clidr in cache.S
'ubfm' can be used; however, additional 'lsl' is necessary.
ENTRY(__flush_dcache_all)
dsb sy // ensure ordering with previous memory accesses
mrs x0, clidr_el1 // read clidr
- and x3, x0, #0x7000000 // extract loc from clidr
- lsr x3, x3, #23 // left align loc bit field
+ ubfm x3, x0, #24, #26
+ lsl x3, x3, #1
cbz x3, finished // if loc is 0, then no need to clean
mov x10, #0 // start clean at cache level 0
Best regards,
Jingoo Han
^ permalink raw reply
* [PATCH v3 16/24] drm/i2c: tda998x: add DT documentation
From: Olof Johansson @ 2014-01-20 4:06 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195844.0c975b90@armhf>
Hi,
On Sun, Jan 19, 2014 at 10:58 AM, Jean-Francois Moine <moinejf@free.fr> wrote:
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> .../devicetree/bindings/drm/i2c/tda998x.txt | 24 ++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/drm/i2c/tda998x.txt
Please cc bindings for review to devicetree at vger.kernel.org (cc:d here now)
> diff --git a/Documentation/devicetree/bindings/drm/i2c/tda998x.txt b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
> new file mode 100644
> index 0000000..72da71d
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/drm/i2c/tda998x.txt
> @@ -0,0 +1,24 @@
> +Device-Tree bindings for the NXP TDA998x HDMI transmitter
> +
> +Required properties;
> + - compatible: must be "nxp,tda998x"
> +
> +Optional properties:
> + - interrupts: interrupt number for HDMI exchanges - default: by polling
What are HDMI exchanges, and how do they differ from other interrupts?
> +
> + - pinctrl-0: pin control group to be used for this controller (IRQ).
> +
> + - pinctrl-names: must contain a "default" entry.
> +
> + - video-ports: 24 bits value - default: <0x230145>
What is this?
-Olof
^ permalink raw reply
* [PATCH net-next v3 0/8] net: stmmac: Add Allwinner A20 GMAC ethernet
From: David Miller @ 2014-01-20 4:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389965087-21971-1-git-send-email-wens@csie.org>
From: Chen-Yu Tsai <wens@csie.org>
Date: Fri, 17 Jan 2014 21:24:39 +0800
> This is v3 of the Allwinner A20 GMAC glue layer for stmmac.
> I have split the series into stmmac driver changes for net-next,
> and clock and DT patches for their respective trees.
Series applied, thanks.
^ permalink raw reply
* How to support SDIO wifi/bt in DT
From: Olof Johansson @ 2014-01-20 3:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119233008.GA4867@quad.lixom.net>
On Sun, Jan 19, 2014 at 3:30 PM, Olof Johansson <olof@lixom.net> wrote:
> On Mon, Jan 20, 2014 at 12:09:25AM +0100, Alexandre Belloni wrote:
>> On Sun, Jan 19, 2014 at 11:29:55AM -0800, Olof Johansson wrote :
>> > The "multi-slot" concept I was talking about was one host controller
>> > for several physical slots. Seems like at91 implements _and_ uses this
>> > but nobody else. However, it seems that they allocate one mmc host per
>> > slot, so the same model still works.
>> >
>>
>> I think we need to be extra careful about the DT bindings then because
>> adding a property in a slot is requiring handling that in the at91
>> driver whereas adding a common property for the various mmc hosts can be
>> done in the core code.
>>
>> From what I remember, it would be really good to put the DT parsing for
>> the slots in the core code but I only had a really quick look at that.
>
> The patches I have here do it all in the core per host with common
> bindings. I hope to post them tonight.
Done. See RFC series at:
http://marc.info/?l=linux-mmc&m=139019015915702&w=2
-Olof
^ permalink raw reply
* [PATCH 3/3] ARM: dts: exynos5250-snow: Enable wifi power-on
From: Olof Johansson @ 2014-01-20 3:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390190215-22700-1-git-send-email-olof@lixom.net>
This adds the required specifications of reset-gpios on snow to enable wifi.
The wifi module is always powered, but it has three more signals needed:
- Enable line (active high)
- Reset line (active low)
- 32kHz reference clock
The enable and reset lines are in practice quite similar (i.e. both
need to be 1 for the module to be enabled), so treat them both as
"reset gpios". The clock is specified using the new clock property on
MMC, which due to lack of append options right now has to duplicate the
first two clocks that are SoC generic.
Also, fix the pinctrl setting (that has moved from slot node to host
controller node), and add the two gpio lines to the pinctrl setup.
Signed-off-by: Olof Johansson <olof@lixom.net>
---
arch/arm/boot/dts/exynos5250-cros-common.dtsi | 17 ++++++++++++++++-
arch/arm/boot/dts/exynos5250-snow.dts | 11 +++++++++--
2 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/arch/arm/boot/dts/exynos5250-cros-common.dtsi b/arch/arm/boot/dts/exynos5250-cros-common.dtsi
index 2c1560d..b72f342 100644
--- a/arch/arm/boot/dts/exynos5250-cros-common.dtsi
+++ b/arch/arm/boot/dts/exynos5250-cros-common.dtsi
@@ -34,6 +34,20 @@
samsung,pin-pud = <0>;
samsung,pin-drv = <0>;
};
+
+ wifi_en: wifi-en {
+ samsung,pins = "gpx0-1";
+ samsung,pin-function = <1>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ wifi_rst: wifi-rst {
+ samsung,pins = "gpx0-2";
+ samsung,pin-function = <1>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
};
i2c at 12C60000 {
@@ -41,7 +55,7 @@
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <378000>;
- max77686 at 09 {
+ pmic: max77686 at 09 {
compatible = "maxim,max77686";
interrupt-parent = <&gpx3>;
interrupts = <2 0>;
@@ -284,6 +298,7 @@
num-slots = <1>;
supports-highspeed;
broken-cd;
+ cap-sdio-irq;
card-detect-delay = <200>;
samsung,dw-mshc-ciu-div = <3>;
samsung,dw-mshc-sdr-timing = <2 3>;
diff --git a/arch/arm/boot/dts/exynos5250-snow.dts b/arch/arm/boot/dts/exynos5250-snow.dts
index 7e45eea..c150483 100644
--- a/arch/arm/boot/dts/exynos5250-snow.dts
+++ b/arch/arm/boot/dts/exynos5250-snow.dts
@@ -11,6 +11,7 @@
/dts-v1/;
#include "exynos5250.dtsi"
#include "exynos5250-cros-common.dtsi"
+#include <dt-bindings/gpio/gpio.h>
/ {
model = "Google Snow";
@@ -186,9 +187,15 @@
*/
mmc at 12230000 {
status = "okay";
+ clocks = <&clock 280>, <&clock 139>, <&pmic 2>;
+ clock-names = "biu", "ciu", "card_ext_clock";
+ card-reset-gpios = <&gpx0 1 GPIO_ACTIVE_LOW>, /* WIFI_EN */
+ <&gpx0 2 GPIO_ACTIVE_LOW>; /* WIFI_RSTn */
+ pinctrl-names = "default";
+ pinctrl-0 = <&sd3_clk &sd3_cmd &sd3_bus4 &wifi_en &wifi_rst>;
slot at 0 {
- pinctrl-names = "default";
- pinctrl-0 = <&sd3_clk &sd3_cmd &sd3_bus4>;
+ reg = <0>;
+ bus-width = <4>;
};
};
--
1.7.10.4
^ permalink raw reply related
* [PATCH 2/3] mmc: dw_mmc: call mmc_of_parse to fill in common options
From: Olof Johansson @ 2014-01-20 3:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390190215-22700-1-git-send-email-olof@lixom.net>
The shared of parse function fills in common options for capabilities,
etc, but it needs to be called from each driver that wants to make use
of it. dw_mmc was missing the call.
Signed-off-by: Olof Johansson <olof@lixom.net>
---
drivers/mmc/host/dw_mmc.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index a776f24..7119f63 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2137,6 +2137,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (!mmc)
return -ENOMEM;
+ mmc_of_parse(mmc);
+
slot = mmc_priv(mmc);
slot->id = id;
slot->mmc = mmc;
--
1.7.10.4
^ permalink raw reply related
* [PATCH 1/3] mmc: add support for power-on sequencing through DT
From: Olof Johansson @ 2014-01-20 3:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390190215-22700-1-git-send-email-olof@lixom.net>
This patch enables support for power-on sequencing of SDIO peripherals through DT.
In general, it's quite common that wifi modules and other similar
peripherals have several signals in addition to the SDIO interface that
needs wiggling before the module will power on. It's common to have a
reference clock, one or several power rails and one or several lines
for reset/enable type functions.
The binding as written today introduces a number of reset gpios,
a regulator and a clock specifier. The code will handle up to 2 gpio
reset lines, but it's trivial to increase to more than that if needed
at some point.
Implementation-wise, the MMC core has been changed to handle this during
host power up, before the host interface is powered on. I have not yet
implemented the power-down side, I wanted people to have a chance for
reporting back w.r.t. issues (or comments on the bindings) first.
I have not tested the regulator portion, since the system and module
I'm working on doesn't need one (Samsung Chromebook with Marvell
8797-based wifi). Testing of those portions (and reporting back) would
be appreciated.
Signed-off-by: Olof Johansson <olof@lixom.net>
---
Documentation/devicetree/bindings/mmc/mmc.txt | 11 +++++++
drivers/mmc/core/core.c | 42 +++++++++++++++++++++++++
drivers/mmc/core/host.c | 30 +++++++++++++++++-
include/linux/mmc/host.h | 5 +++
4 files changed, 87 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
index 458b57f..962e0ee 100644
--- a/Documentation/devicetree/bindings/mmc/mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/mmc.txt
@@ -5,6 +5,8 @@ these definitions.
Interpreted by the OF core:
- reg: Registers location and length.
- interrupts: Interrupts used by the MMC controller.
+- clocks: Clocks needed for the host controller, if any.
+- clock-names: Goes with clocks above.
Card detection:
If no property below is supplied, host native card detect is used.
@@ -30,6 +32,15 @@ Optional properties:
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
- full-pwr-cycle: full power cycle of the card is supported
+Card power and reset control:
+The following properties can be specified for cases where the MMC
+peripheral needs additional reset, regulator and clock lines. It is for
+example common for WiFi/BT adapters to have these separate from the main
+MMC bus:
+ - card-reset-gpios: Specify GPIOs for card reset (reset active low)
+ - card-external-vcc-supply: Regulator to drive (independent) card VCC
+ - clock with name "card_ext_clock": External clock provided to the card
+
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted"
line levels. We choose to follow the SDHCI standard, which specifies both those
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 098374b..c43e6c8 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -13,11 +13,13 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/pagemap.h>
#include <linux/err.h>
+#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/scatterlist.h>
#include <linux/log2.h>
@@ -1519,6 +1521,43 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
mmc_host_clk_release(host);
}
+static void mmc_card_power_up(struct mmc_host *host)
+{
+ int i;
+ struct gpio_desc **gds = host->card_reset_gpios;
+
+ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
+ if (gds[i]) {
+ dev_dbg(host->parent, "Asserting reset line %d", i);
+ gpiod_set_value(gds[i], 1);
+ }
+ }
+
+ if (host->card_regulator) {
+ dev_dbg(host->parent, "Enabling external regulator");
+ if (regulator_enable(host->card_regulator))
+ dev_err(host->parent, "Failed to enable external regulator");
+ }
+
+ if (host->card_clk) {
+ dev_dbg(host->parent, "Enabling external clock");
+ clk_prepare_enable(host->card_clk);
+ }
+
+ /* 2ms delay to let clocks and power settle */
+ mmc_delay(20);
+
+ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
+ if (gds[i]) {
+ dev_dbg(host->parent, "Deasserting reset line %d", i);
+ gpiod_set_value(gds[i], 0);
+ }
+ }
+
+ /* 2ms delay to after reset release */
+ mmc_delay(20);
+}
+
/*
* Apply power to the MMC stack. This is a two-stage process.
* First, we enable power to the card without the clock running.
@@ -1535,6 +1574,9 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
if (host->ios.power_mode == MMC_POWER_ON)
return;
+ /* Power up the card/module first, if needed */
+ mmc_card_power_up(host);
+
mmc_host_clk_hold(host);
host->ios.vdd = fls(ocr) - 1;
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 49bc403..e6b850b 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -12,14 +12,18 @@
* MMC host class device management
*/
+#include <linux/kernel.h>
+#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/idr.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pagemap.h>
#include <linux/export.h>
#include <linux/leds.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/suspend.h>
@@ -312,7 +316,7 @@ int mmc_of_parse(struct mmc_host *host)
u32 bus_width;
bool explicit_inv_wp, gpio_inv_wp = false;
enum of_gpio_flags flags;
- int len, ret, gpio;
+ int i, len, ret, gpio;
if (!host->parent || !host->parent->of_node)
return 0;
@@ -415,6 +419,30 @@ int mmc_of_parse(struct mmc_host *host)
if (explicit_inv_wp ^ gpio_inv_wp)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
+ /* Parse card power/reset/clock control */
+ if (of_find_property(np, "card-reset-gpios", NULL)) {
+ struct gpio_desc *gpd;
+ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) {
+ gpd = devm_gpiod_get_index(host->parent, "card-reset", i);
+ if (IS_ERR(gpd))
+ break;
+ gpiod_direction_output(gpd, 0);
+ host->card_reset_gpios[i] = gpd;
+ }
+
+ gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios));
+ if (!IS_ERR(gpd)) {
+ dev_warn(host->parent, "More reset gpios than we can handle");
+ gpiod_put(gpd);
+ }
+ }
+
+ host->card_clk = of_clk_get_by_name(np, "card_ext_clock");
+ if (IS_ERR(host->card_clk))
+ host->card_clk = NULL;
+
+ host->card_regulator = regulator_get(host->parent, "card-external-vcc");
+
if (of_find_property(np, "cap-sd-highspeed", &len))
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_find_property(np, "cap-mmc-highspeed", &len))
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 99f5709..6781887 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -297,6 +297,11 @@ struct mmc_host {
unsigned long clkgate_delay;
#endif
+ /* card specific properties to deal with power and reset */
+ struct regulator *card_regulator; /* External VCC needed by the card */
+ struct gpio_desc *card_reset_gpios[2]; /* External resets, active low */
+ struct clk *card_clk; /* External clock needed by the card */
+
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_segs; /* see blk_queue_max_segments */
--
1.7.10.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox