Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/2] Add MK808 RK3066 device
From: Paweł Jarosz @ 2016-10-08 20:21 UTC (permalink / raw)
  To: linux-arm-kernel

Add MK808 RK3066 device

This patchset adds support for Rikomagic MK808 v1 device with RK901 wifi.

It is hdmi stick with two usb ports(host and otg), sdmmc, wifi and uart
onboard.

It is RK3066 based.

Pawe? Jarosz (2):
  devicetree: Add vendor prefix for Rikomagic
  ARM: dts: rockchip: Add rk3066 MK808 board

 Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/rk3066a-mk808.dts                | 196 +++++++++++++++++++++
 4 files changed, 202 insertions(+)
 create mode 100644 arch/arm/boot/dts/rk3066a-mk808.dts

-- 
2.7.4

^ permalink raw reply

* [PATCH] KVM: arm64: Correct trace exception name after SError handling
From: Christoffer Dall @ 2016-10-08 20:20 UTC (permalink / raw)
  To: linux-arm-kernel

After commit 9aecafc8 introduced ARM_EXCEPTION_EL1_SERROR we should
adjust the description of the exception type for trace events.

Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
 arch/arm64/include/asm/kvm_arm.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 4b5c977..b942ce4 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -207,7 +207,8 @@
 
 #define kvm_arm_exception_type	\
 	{0, "IRQ" }, 		\
-	{1, "TRAP" }
+	{1, "SError" }, 	\
+	{2, "TRAP" }
 
 #define ECN(x) { ESR_ELx_EC_##x, #x }
 
-- 
2.9.0

^ permalink raw reply related

* [PATCH v2] ARM64: dts: meson-gxbb-odroidc2: Enable USB Nodes
From: Kevin Hilman @ 2016-10-08 16:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475564803-9616-1-git-send-email-brian.kim@hardkernel.com>

Brian Kim <brian.kim@hardkernel.com> writes:

> Enable both gxbb USB controller and add a 5V regulator for the OTG port
> VBUS
>
> Signed-off-by: Brian Kim <brian.kim@hardkernel.com>
> ---
> This patch was written on Kevin Hilman's repository[1] and branch[2]:
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic.git
> [2] v4.8/integ

Applied to the v4.10/dt64 branch, and will be included shortly in the
integration branch.

Kevin

^ permalink raw reply

* [PATCH v2 2/2] ARM: dts: rockchip: Add rk3066 MK808 board
From: Heiko Stübner @ 2016-10-08 15:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <c5ca69d3-698a-5ef4-623b-204c2a50c28f@rock-chips.com>

Am Samstag, 8. Oktober 2016, 11:01:08 schrieb Shawn Lin:
> ? 2016/10/7 18:01, Heiko Stuebner ??:
> > Am Freitag, 7. Oktober 2016, 10:29:48 CEST schrieb Shawn Lin:
> >> Hi Pawe? ,
> >> 
> >> On 2016/10/7 1:38, Pawe? Jarosz wrote:
> >>> MK808 is a tv stick which has rockchip rk3066 CPU inside, two usb ports
> >>> - host and otg, micro sd card slot and onboard wifi RK901.
> >>> 
> >>> Signed-off-by: Pawe? Jarosz <paweljarosz3691@gmail.com>
> >>> ---
> >>> 
> >>> Changes in v2:
> >>> - included Heiko sugestion.
> >>> 
> >>>  Documentation/devicetree/bindings/arm/rockchip.txt |   4 +
> >>>  arch/arm/boot/dts/Makefile                         |   1 +
> >>>  arch/arm/boot/dts/rk3066a-mk808.dts                | 184
> >>>  +++++++++++++++++++++ 3 files changed, 189 insertions(+)
> >>>  create mode 100644 arch/arm/boot/dts/rk3066a-mk808.dts
> >>> 
> >>> diff --git a/Documentation/devicetree/bindings/arm/rockchip.txt
> >>> b/Documentation/devicetree/bindings/arm/rockchip.txt index
> >>> 55f388f..c09595b 100644
> >>> --- a/Documentation/devicetree/bindings/arm/rockchip.txt
> >>> +++ b/Documentation/devicetree/bindings/arm/rockchip.txt
> >>> @@ -17,6 +17,10 @@ Rockchip platforms device tree bindings
> >>> 
> >>>      Required root node properties:
> >>>        - compatible = "chipspark,rayeager-px2", "rockchip,rk3066a";
> >>> 
> >>> +- Rikomagic MK808 v1 board:
> >>> +    Required root node properties:
> >>> +      - compatible = "rikomagic,mk808", "rockchip,rk3066a";
> >>> +
> >>> 
> >>>  - Radxa Rock board:
> >>>      Required root node properties:
> >>>        - compatible = "radxa,rock", "rockchip,rk3188";
> >>> 
> >>> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> >>> index befcd26..f19cc1d 100644
> >>> --- a/arch/arm/boot/dts/Makefile
> >>> +++ b/arch/arm/boot/dts/Makefile
> >>> @@ -639,6 +639,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \
> >>> 
> >>>  	rk3036-kylin.dtb \
> >>>  	rk3066a-bqcurie2.dtb \
> >>>  	rk3066a-marsboard.dtb \
> >>> 
> >>> +	rk3066a-mk808.dtb \
> >>> 
> >>>  	rk3066a-rayeager.dtb \
> >>>  	rk3188-radxarock.dtb \
> >>>  	rk3228-evb.dtb \
> >>> 
> >>> diff --git a/arch/arm/boot/dts/rk3066a-mk808.dts
> >>> b/arch/arm/boot/dts/rk3066a-mk808.dts new file mode 100644
> >>> index 0000000..2878562
> >>> --- /dev/null
> >>> +++ b/arch/arm/boot/dts/rk3066a-mk808.dts
> >>> @@ -0,0 +1,184 @@
> >>> +/*
> >>> + * Copyright (c) 2016 Pawe? Jarosz <paweljarosz3691@gmail.com>
> >>> + *
> >>> + * This file is dual-licensed: you can use it either under the terms
> >>> + * of the GPL or the X11 license, at your option. Note that this dual
> >>> + * licensing only applies to this file, and not this project as a
> >>> + * whole.
> >>> + *
> >>> + *  a) This file 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.
> >>> + *
> >>> + *     This file is distributed in the hope that it will be useful,
> >>> + *     but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>> + *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>> + *     GNU General Public License for more details.
> >>> + *
> >>> + * Or, alternatively,
> >>> + *
> >>> + *  b) Permission is hereby granted, free of charge, to any person
> >>> + *     obtaining a copy of this software and associated documentation
> >>> + *     files (the "Software"), to deal in the Software without
> >>> + *     restriction, including without limitation the rights to use,
> >>> + *     copy, modify, merge, publish, distribute, sublicense, and/or
> >>> + *     sell copies of the Software, and to permit persons to whom the
> >>> + *     Software is furnished to do so, subject to the following
> >>> + *     conditions:
> >>> + *
> >>> + *     The above copyright notice and this permission notice shall be
> >>> + *     included in all copies or substantial portions of the Software.
> >>> + *
> >>> + *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> >>> + *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> >>> + *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> >>> + *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> >>> + *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> >>> + *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> >>> + *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> >>> + *     OTHER DEALINGS IN THE SOFTWARE.
> >>> + */
> >>> +
> >>> +/dts-v1/;
> >>> +#include "rk3066a.dtsi"
> >>> +
> >>> +/ {
> >>> +	model = "Rikomagic MK808";
> >>> +	compatible = "rikomagic,mk808", "rockchip,rk3066a";
> >>> +
> >>> +	chosen {
> >>> +		stdout-path = "serial2:115200n8";
> >>> +	};
> >>> +
> >>> +	memory at 60000000 {
> >>> +		device_type = "memory";
> >>> +		reg = <0x60000000 0x40000000>;
> >>> +	};
> >>> +
> >>> +	gpio-leds {
> >>> +		compatible = "gpio-leds";
> >>> +
> >>> +		blue {
> >>> +			label = "mk808:blue:power";
> >>> +			gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>;
> >>> +			default-state = "off";
> >>> +			linux,default-trigger = "default-on";
> >>> +		};
> >>> +	};
> >>> +
> >>> +	mmc_pwrseq: mmc-pwrseq {
> >>> +		compatible = "mmc-pwrseq-simple";
> >>> +		pinctrl-names = "default";
> >>> +		pinctrl-0 = <&sdmmc_pwr>;
> >> 
> >> sd slot does not contain a reset pin. So the power pin should be
> >> enough. just add sdmmc_pwr to mmc0's pinctrl-0 and let mmc driver
> >> control it should be enough. Typically pwrseq is for emmc and sdio.
> >> We don't need a pwerseq to control power for sd slot..
> >> 
> >> But it seems sdmmc_pwr is a GPIO, but not functional port.
> >> So I am interesting that why MK808 board doesn't use the mmc
> >> controller's default power pin but chosing another gpio, so finally
> >> we have to add thses code for your DT.
> > 
> > Actually, we always model the sd-mmc pwr-pin as gpio-based regulator (see
> > sdmmc-regulator for example in the rk3066a-rayeager.dts), and specifiy
> > this
> > regulator as vmmc. That way the mmc core really can control the power.
> 
> yes it did. There are two ways for us to control vmmc properly.
> If we use gpio-based regulator or PMIC, mmc core take over it
> to control the power correctly. But if we use the dafault functional
> port, namely SDMMC_PWREN, the dw_mmc driver could handle the control
> when doing set_ios.. Both of these two could work fine. But I always
> chose the latter one to simplify the dts.
> 
> Anyway, I won't insist on it if it looks okay to you. :)

yes, I'd really prefer the mmc subsystem explicitly controlling the cards 
power via a regulator :-) .


Heiko

^ permalink raw reply

* [PATCH v3 3/6] pwm: imx: support output polarity inversion
From: Lukasz Majewski @ 2016-10-08 14:32 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161007151129.6043-4-bhuvanchandra.dv@toradex.com>

Hi Bhuvanchandra,

> From: Lothar Wassmann <LW@KARO-electronics.de>
> 
> The i.MX pwm unit on i.MX27 and newer SoCs provides a configurable
> output polarity. This patch adds support to utilize this feature
> where available.
> 
> Signed-off-by: Lothar Wa?mann <LW@KARO-electronics.de>
> Signed-off-by: Lukasz Majewski <l.majewski@samsung.com>
> Signed-off-by: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com>
> Acked-by: Shawn Guo <shawn.guo@linaro.org>
> Reviewed-by: Sascha Hauer <s.hauer@pengutronix.de>

I've tested those patches on my iMX6q board on top of v4.7 linux kernel.

Tested-by: Lukasz Majewski <l.majewski@majess.pl>

Best regards,
?ukasz Majewski

> ---
>  Documentation/devicetree/bindings/pwm/imx-pwm.txt |  6 +--
>  drivers/pwm/pwm-imx.c                             | 51
> +++++++++++++++++++++-- 2 files changed, 51 insertions(+), 6
> deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/pwm/imx-pwm.txt
> b/Documentation/devicetree/bindings/pwm/imx-pwm.txt index
> e00c2e9..c61bdf8 100644 ---
> a/Documentation/devicetree/bindings/pwm/imx-pwm.txt +++
> b/Documentation/devicetree/bindings/pwm/imx-pwm.txt @@ -6,8 +6,8 @@
> Required properties:
>    - "fsl,imx1-pwm" for PWM compatible with the one integrated on
> i.MX1
>    - "fsl,imx27-pwm" for PWM compatible with the one integrated on
> i.MX27
>  - reg: physical base address and length of the controller's registers
> -- #pwm-cells: should be 2. See pwm.txt in this directory for a
> description of
> -  the cells format.
> +- #pwm-cells: 2 for i.MX1 and 3 for i.MX27 and newer SoCs. See
> pwm.txt
> +  in this directory for a description of the cells format.
>  - clocks : Clock specifiers for both ipg and per clocks.
>  - clock-names : Clock names should include both "ipg" and "per"
>  See the clock consumer binding,
> @@ -17,7 +17,7 @@ See the clock consumer binding,
>  Example:
>  
>  pwm1: pwm at 53fb4000 {
> -	#pwm-cells = <2>;
> +	#pwm-cells = <3>;
>  	compatible = "fsl,imx53-pwm", "fsl,imx27-pwm";
>  	reg = <0x53fb4000 0x4000>;
>  	clocks = <&clks IMX5_CLK_PWM1_IPG_GATE>,
> diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c
> index d600fd5..c37d223 100644
> --- a/drivers/pwm/pwm-imx.c
> +++ b/drivers/pwm/pwm-imx.c
> @@ -38,6 +38,7 @@
>  #define MX3_PWMCR_DOZEEN		(1 << 24)
>  #define MX3_PWMCR_WAITEN		(1 << 23)
>  #define MX3_PWMCR_DBGEN			(1 << 22)
> +#define MX3_PWMCR_POUTC			(1 << 18)
>  #define MX3_PWMCR_CLKSRC_IPG_HIGH	(2 << 16)
>  #define MX3_PWMCR_CLKSRC_IPG		(1 << 16)
>  #define MX3_PWMCR_SWR			(1 << 3)
> @@ -180,6 +181,9 @@ static int imx_pwm_config_v2(struct pwm_chip
> *chip, if (enable)
>  		cr |= MX3_PWMCR_EN;
>  
> +	if (pwm->args.polarity == PWM_POLARITY_INVERSED)
> +		cr |= MX3_PWMCR_POUTC;
> +
>  	writel(cr, imx->mmio_base + MX3_PWMCR);
>  
>  	return 0;
> @@ -240,27 +244,62 @@ static void imx_pwm_disable(struct pwm_chip
> *chip, struct pwm_device *pwm) clk_disable_unprepare(imx->clk_per);
>  }
>  
> -static struct pwm_ops imx_pwm_ops = {
> +static int imx_pwm_set_polarity(struct pwm_chip *chip, struct
> pwm_device *pwm,
> +				enum pwm_polarity polarity)
> +{
> +	struct imx_chip *imx = to_imx_chip(chip);
> +	u32 val;
> +
> +	if (polarity == pwm->args.polarity)
> +		return 0;
> +
> +	val = readl(imx->mmio_base + MX3_PWMCR);
> +
> +	if (polarity == PWM_POLARITY_INVERSED)
> +		val |= MX3_PWMCR_POUTC;
> +	else
> +		val &= ~MX3_PWMCR_POUTC;
> +
> +	writel(val, imx->mmio_base + MX3_PWMCR);
> +
> +	dev_dbg(imx->chip.dev, "%s: polarity set to %s\n", __func__,
> +		polarity == PWM_POLARITY_INVERSED ? "inverted" :
> "normal"); +
> +	return 0;
> +}
> +
> +static struct pwm_ops imx_pwm_ops_v1 = {
>  	.enable = imx_pwm_enable,
>  	.disable = imx_pwm_disable,
>  	.config = imx_pwm_config,
>  	.owner = THIS_MODULE,
>  };
>  
> +static struct pwm_ops imx_pwm_ops_v2 = {
> +	.enable = imx_pwm_enable,
> +	.disable = imx_pwm_disable,
> +	.set_polarity = imx_pwm_set_polarity,
> +	.config = imx_pwm_config,
> +	.owner = THIS_MODULE,
> +};
> +
>  struct imx_pwm_data {
>  	int (*config)(struct pwm_chip *chip,
>  		struct pwm_device *pwm, int duty_ns, int period_ns);
>  	void (*set_enable)(struct pwm_chip *chip, bool enable);
> +	struct pwm_ops *pwm_ops;
>  };
>  
>  static struct imx_pwm_data imx_pwm_data_v1 = {
>  	.config = imx_pwm_config_v1,
>  	.set_enable = imx_pwm_set_enable_v1,
> +	.pwm_ops = &imx_pwm_ops_v1,
>  };
>  
>  static struct imx_pwm_data imx_pwm_data_v2 = {
>  	.config = imx_pwm_config_v2,
>  	.set_enable = imx_pwm_set_enable_v2,
> +	.pwm_ops = &imx_pwm_ops_v2,
>  };
>  
>  static const struct of_device_id imx_pwm_dt_ids[] = {
> @@ -282,6 +321,8 @@ static int imx_pwm_probe(struct platform_device
> *pdev) if (!of_id)
>  		return -ENODEV;
>  
> +	data = of_id->data;
> +
>  	imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL);
>  	if (imx == NULL)
>  		return -ENOMEM;
> @@ -300,18 +341,22 @@ static int imx_pwm_probe(struct platform_device
> *pdev) return PTR_ERR(imx->clk_ipg);
>  	}
>  
> -	imx->chip.ops = &imx_pwm_ops;
> +	imx->chip.ops = data->pwm_ops;
>  	imx->chip.dev = &pdev->dev;
>  	imx->chip.base = -1;
>  	imx->chip.npwm = 1;
>  	imx->chip.can_sleep = true;
> +	if (data->pwm_ops->set_polarity) {
> +		dev_dbg(&pdev->dev, "PWM supports output
> inversion\n");
> +		imx->chip.of_xlate = of_pwm_xlate_with_flags;
> +		imx->chip.of_pwm_n_cells = 3;
> +	}
>  
>  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	imx->mmio_base = devm_ioremap_resource(&pdev->dev, r);
>  	if (IS_ERR(imx->mmio_base))
>  		return PTR_ERR(imx->mmio_base);
>  
> -	data = of_id->data;
>  	imx->config = data->config;
>  	imx->set_enable = data->set_enable;
>  

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 181 bytes
Desc: OpenPGP digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161008/a63e8653/attachment-0001.sig>

^ permalink raw reply

* [PATCH] clk: hi6220: use CLK_OF_DECLARE_DRIVER for sysctrl and mediactrl clock init
From: Shawn Guo @ 2016-10-08 13:38 UTC (permalink / raw)
  To: linux-arm-kernel

The hi6220-sysctrl and hi6220-mediactrl are not only clock provider but
also reset controller.  It worked fine that single sysctrl/mediactrl
device node in DT can be used to initialize clock driver and populate
platform device for reset controller.  But it stops working after
commit 989eafd0b609 ("clk: core: Avoid double initialization of clocks")
gets merged.  The commit sets flag OF_POPULATED during clock
initialization to skip the platform device populating for the same
device node.  On hi6220, it effectively makes hi6220-sysctrl reset
driver not probe any more.

The patch changes hi6220 sysctrl and mediactrl clock init macro from
CLK_OF_DECLARE to CLK_OF_DECLARE_DRIVER, so that the reset driver using
the same hardware block can continue working.

Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
---
It fixes an issue that is seen on linux-next, i.e. the new added
hi6220-sysctrl reset driver doesn't probe at all, and consequently the
mmc driver fails to register.

Shawn

 drivers/clk/hisilicon/clk-hi6220.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c
index fe364e63f8de..c0e8e1f196aa 100644
--- a/drivers/clk/hisilicon/clk-hi6220.c
+++ b/drivers/clk/hisilicon/clk-hi6220.c
@@ -195,7 +195,7 @@ static void __init hi6220_clk_sys_init(struct device_node *np)
 	hi6220_clk_register_divider(hi6220_div_clks_sys,
 			ARRAY_SIZE(hi6220_div_clks_sys), clk_data);
 }
-CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init);
+CLK_OF_DECLARE_DRIVER(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init);
 
 
 /* clocks in media controller */
@@ -252,7 +252,7 @@ static void __init hi6220_clk_media_init(struct device_node *np)
 	hi6220_clk_register_divider(hi6220_div_clks_media,
 				ARRAY_SIZE(hi6220_div_clks_media), clk_data);
 }
-CLK_OF_DECLARE(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init);
+CLK_OF_DECLARE_DRIVER(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init);
 
 
 /* clocks in pmctrl */
-- 
1.9.1

^ permalink raw reply related

* [PATCH] sdhci-esdhc-imx: Correct two register accesses
From: Dong Aisheng @ 2016-10-08 13:10 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475794457-17993-1-git-send-email-aaron.brice@datasoft.com>

On Fri, Oct 7, 2016 at 6:54 AM, Aaron Brice <aaron.brice@datasoft.com> wrote:
>  - The DMA error interrupt bit is in a different position as
>    compared to the sdhci standard.  This is accounted for in
>    many cases, but not handled in the case of clearing the
>    INT_STATUS register by writing a 1 to that location.
>  - The HOST_CONTROL register is very different as compared to
>    the sdhci standard.  This is accounted for in the write
>    case, but not when read back out (which it is in the sdhci
>    code).
>
> Signed-off-by: Dave Russell <david.russell@datasoft.com>
> Signed-off-by: Aaron Brice <aaron.brice@datasoft.com>
> ---
>  drivers/mmc/host/sdhci-esdhc-imx.c | 23 ++++++++++++++++++++++-
>  1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
> index 1f54fd8..d61ef16 100644
> --- a/drivers/mmc/host/sdhci-esdhc-imx.c
> +++ b/drivers/mmc/host/sdhci-esdhc-imx.c
> @@ -346,7 +346,8 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
>         struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
>         u32 data;
>
> -       if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
> +       if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
> +                       reg == SDHCI_INT_STATUS)) {
>                 if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
>                         /*
>                          * Clear and then set D3CD bit to avoid missing the
> @@ -555,6 +556,25 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
>         esdhc_clrset_le(host, 0xffff, val, reg);
>  }
>
> +static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
> +{
> +       u8 ret;
> +       u32 long_val;
> +
> +       switch (reg) {
> +       case SDHCI_HOST_CONTROL:
> +               long_val = readl(host->ioaddr + reg);
> +
> +               ret = long_val & SDHCI_CTRL_LED;
> +               ret |= (long_val >> 5) & SDHCI_CTRL_DMA_MASK;
> +               ret |= (long_val & ESDHC_CTRL_4BITBUS);
> +               ret |= (long_val & ESDHC_CTRL_8BITBUS) << 3;
> +               return ret;

Thanks for the effort.

One nitpick: would be more like to use 'val' instead of 'long_val' to
be consistent with exist using.
(i saw a few 'new_val' as well, maybe we could clean up them in the future,
but at least we could avoid inventing more from now)

Otherwise,
Acked-by: Dong Aisheng <aisheng.dong@nxp.com>

Regards
Dong Aisheng

> +       }
> +
> +       return readb(host->ioaddr + reg);
> +}
> +
>  static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
>  {
>         struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> @@ -947,6 +967,7 @@ static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
>  static struct sdhci_ops sdhci_esdhc_ops = {
>         .read_l = esdhc_readl_le,
>         .read_w = esdhc_readw_le,
> +       .read_b = esdhc_readb_le,
>         .write_l = esdhc_writel_le,
>         .write_w = esdhc_writew_le,
>         .write_b = esdhc_writeb_le,
> --
> 2.7.4
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH v10 11/11] ARM: multi_v7_defconfig: Enable STi and simple-card drivers.
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

This patch enables the STi ALSA drivers found on STi platforms
as well as the simple-card driver which is a dependency to have
working sound.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
Cc: arnaud.pouliquen at st.com
Cc: broonie at kernel.org
---
 arch/arm/configs/multi_v7_defconfig | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 46f174e..5d92092 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -644,6 +644,9 @@ CONFIG_SND_SOC_AK4642=m
 CONFIG_SND_SOC_SGTL5000=m
 CONFIG_SND_SOC_SPDIF=m
 CONFIG_SND_SOC_WM8978=m
+CONFIG_SND_SOC_STI=m
+CONFIG_SND_SOC_STI_SAS=m
+CONFIG_SND_SIMPLE_CARD=m
 CONFIG_USB=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_XHCI_MVEBU=y
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 10/11] ARM: multi_v7_defconfig: Enable STi FDMA driver
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

This DMA controller is found on all STi chipsets.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 33f7a8a..46f174e 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -784,6 +784,7 @@ CONFIG_DMA_OMAP=y
 CONFIG_QCOM_BAM_DMA=y
 CONFIG_XILINX_DMA=y
 CONFIG_DMA_SUN6I=y
+CONFIG_ST_FDMA=m
 CONFIG_STAGING=y
 CONFIG_SENSORS_ISL29018=y
 CONFIG_SENSORS_ISL29028=y
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 09/11] ARM: multi_v7_defconfig: Enable st_remoteproc driver.
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

The st231 remote coprocessors are found on all STi chipsets.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index c94611c..33f7a8a 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -818,6 +818,7 @@ CONFIG_ROCKCHIP_IOMMU=y
 CONFIG_TEGRA_IOMMU_GART=y
 CONFIG_TEGRA_IOMMU_SMMU=y
 CONFIG_REMOTEPROC=m
+CONFIG_ST_REMOTEPROC=m
 CONFIG_PM_DEVFREQ=y
 CONFIG_ARM_TEGRA_DEVFREQ=m
 CONFIG_MEMORY=y
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 08/11] ARM: multi_v7_defconfig: Enable remoteproc core
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

Now that remoteproc core is selectable it needs to be enabled
in the multi_v7 build.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 3ca4974..c94611c 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -817,6 +817,7 @@ CONFIG_HWSPINLOCK_QCOM=y
 CONFIG_ROCKCHIP_IOMMU=y
 CONFIG_TEGRA_IOMMU_GART=y
 CONFIG_TEGRA_IOMMU_SMMU=y
+CONFIG_REMOTEPROC=m
 CONFIG_PM_DEVFREQ=y
 CONFIG_ARM_TEGRA_DEVFREQ=m
 CONFIG_MEMORY=y
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 07/11] MAINTAINERS: Add FDMA driver files to STi section.
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

This patch adds the FDMA driver files to the STi
section of the maintainers file.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 9924036..f71a1d1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1749,6 +1749,7 @@ F:	drivers/char/hw_random/st-rng.c
 F:	drivers/clocksource/arm_global_timer.c
 F:	drivers/clocksource/clksrc_st_lpc.c
 F:	drivers/cpufreq/sti-cpufreq.c
+F:	drivers/dma/st_fdma*
 F:	drivers/i2c/busses/i2c-st.c
 F:	drivers/media/rc/st_rc.c
 F:	drivers/media/platform/sti/c8sectpfe/
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 06/11] dmaengine: st_fdma: Add STMicroelectronics FDMA engine driver support
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

This patch adds support for the Flexible Direct Memory Access (FDMA) core
driver. The FDMA is a slim core CPU with a dedicated firmware.
It is a general purpose DMA controller capable of supporting 16
independent DMA channels. Data moves maybe from memory to memory
or between memory and paced latency critical real time targets and it
is found on al STi based chipsets.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
 drivers/dma/Kconfig   |  13 +
 drivers/dma/Makefile  |   1 +
 drivers/dma/st_fdma.c | 899 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 913 insertions(+)
 create mode 100644 drivers/dma/st_fdma.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 739f797..8322d37 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -437,6 +437,19 @@ config STE_DMA40
 	help
 	  Support for ST-Ericsson DMA40 controller
 
+config ST_FDMA
+	tristate "ST FDMA dmaengine support"
+	depends on ARCH_STI
+	select ST_SLIM_REMOTEPROC
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Enable support for ST FDMA controller.
+	  It supports 16 independent DMA channels, accepts up to 32 DMA requests
+
+	  Say Y here if you have such a chipset.
+	  If unsure, say N.
+
 config STM32_DMA
 	bool "STMicroelectronics STM32 DMA support"
 	depends on ARCH_STM32
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index e4dc9ca..a4fa336 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o
 obj-$(CONFIG_TI_EDMA) += edma.o
 obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
 obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
+obj-$(CONFIG_ST_FDMA) += st_fdma.o
 
 obj-y += qcom/
 obj-y += xilinx/
diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
new file mode 100644
index 0000000..515e1d4
--- /dev/null
+++ b/drivers/dma/st_fdma.c
@@ -0,0 +1,899 @@
+/*
+ * DMA driver for STMicroelectronics STi FDMA controller
+ *
+ * Copyright (C) 2014 STMicroelectronics
+ *
+ * Author: Ludovic Barre <Ludovic.barre@st.com>
+ *	   Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/remoteproc.h>
+
+#include "st_fdma.h"
+
+static inline struct st_fdma_chan *to_st_fdma_chan(struct dma_chan *c)
+{
+	return container_of(c, struct st_fdma_chan, vchan.chan);
+}
+
+static struct st_fdma_desc *to_st_fdma_desc(struct virt_dma_desc *vd)
+{
+	return container_of(vd, struct st_fdma_desc, vdesc);
+}
+
+static int st_fdma_dreq_get(struct st_fdma_chan *fchan)
+{
+	struct st_fdma_dev *fdev = fchan->fdev;
+	u32 req_line_cfg = fchan->cfg.req_line;
+	u32 dreq_line;
+	int try = 0;
+
+	/*
+	 * dreq_mask is shared for n channels of fdma, so all accesses must be
+	 * atomic. if the dreq_mask is changed between ffz and set_bit,
+	 * we retry
+	 */
+	do {
+		if (fdev->dreq_mask == ~0L) {
+			dev_err(fdev->dev, "No req lines available\n");
+			return -EINVAL;
+		}
+
+		if (try || req_line_cfg >= ST_FDMA_NR_DREQS) {
+			dev_err(fdev->dev, "Invalid or used req line\n");
+			return -EINVAL;
+		} else {
+			dreq_line = req_line_cfg;
+		}
+
+		try++;
+	} while (test_and_set_bit(dreq_line, &fdev->dreq_mask));
+
+	dev_dbg(fdev->dev, "get dreq_line:%d mask:%#lx\n",
+		dreq_line, fdev->dreq_mask);
+
+	return dreq_line;
+}
+
+static void st_fdma_dreq_put(struct st_fdma_chan *fchan)
+{
+	struct st_fdma_dev *fdev = fchan->fdev;
+
+	dev_dbg(fdev->dev, "put dreq_line:%#x\n", fchan->dreq_line);
+	clear_bit(fchan->dreq_line, &fdev->dreq_mask);
+}
+
+static void st_fdma_xfer_desc(struct st_fdma_chan *fchan)
+{
+	struct virt_dma_desc *vdesc;
+	unsigned long nbytes, ch_cmd, cmd;
+
+	vdesc = vchan_next_desc(&fchan->vchan);
+	if (!vdesc)
+		return;
+
+	fchan->fdesc = to_st_fdma_desc(vdesc);
+	nbytes = fchan->fdesc->node[0].desc->nbytes;
+	cmd = FDMA_CMD_START(fchan->vchan.chan.chan_id);
+	ch_cmd = fchan->fdesc->node[0].pdesc | FDMA_CH_CMD_STA_START;
+
+	/* start the channel for the descriptor */
+	fnode_write(fchan, nbytes, FDMA_CNTN_OFST);
+	fchan_write(fchan, ch_cmd, FDMA_CH_CMD_OFST);
+	writel(cmd,
+		fchan->fdev->slim_rproc->peri + FDMA_CMD_SET_OFST);
+
+	dev_dbg(fchan->fdev->dev, "start chan:%d\n", fchan->vchan.chan.chan_id);
+}
+
+static void st_fdma_ch_sta_update(struct st_fdma_chan *fchan,
+				  unsigned long int_sta)
+{
+	unsigned long ch_sta, ch_err;
+	int ch_id = fchan->vchan.chan.chan_id;
+	struct st_fdma_dev *fdev = fchan->fdev;
+
+	ch_sta = fchan_read(fchan, FDMA_CH_CMD_OFST);
+	ch_err = ch_sta & FDMA_CH_CMD_ERR_MASK;
+	ch_sta &= FDMA_CH_CMD_STA_MASK;
+
+	if (int_sta & FDMA_INT_STA_ERR) {
+		dev_warn(fdev->dev, "chan:%d, error:%ld\n", ch_id, ch_err);
+		fchan->status = DMA_ERROR;
+		return;
+	}
+
+	switch (ch_sta) {
+	case FDMA_CH_CMD_STA_PAUSED:
+		fchan->status = DMA_PAUSED;
+		break;
+
+	case FDMA_CH_CMD_STA_RUNNING:
+		fchan->status = DMA_IN_PROGRESS;
+		break;
+	}
+}
+
+static irqreturn_t st_fdma_irq_handler(int irq, void *dev_id)
+{
+	struct st_fdma_dev *fdev = dev_id;
+	irqreturn_t ret = IRQ_NONE;
+	struct st_fdma_chan *fchan = &fdev->chans[0];
+	unsigned long int_sta, clr;
+
+	int_sta = fdma_read(fdev, FDMA_INT_STA_OFST);
+	clr = int_sta;
+
+	for (; int_sta != 0 ; int_sta >>= 2, fchan++) {
+		if (!(int_sta & (FDMA_INT_STA_CH | FDMA_INT_STA_ERR)))
+			continue;
+
+		spin_lock(&fchan->vchan.lock);
+		st_fdma_ch_sta_update(fchan, int_sta);
+
+		if (fchan->fdesc) {
+			if (!fchan->fdesc->iscyclic) {
+				list_del(&fchan->fdesc->vdesc.node);
+				vchan_cookie_complete(&fchan->fdesc->vdesc);
+				fchan->fdesc = NULL;
+				fchan->status = DMA_COMPLETE;
+			} else {
+				vchan_cyclic_callback(&fchan->fdesc->vdesc);
+			}
+
+			/* Start the next descriptor (if available) */
+			if (!fchan->fdesc)
+				st_fdma_xfer_desc(fchan);
+		}
+
+		spin_unlock(&fchan->vchan.lock);
+		ret = IRQ_HANDLED;
+	}
+
+	fdma_write(fdev, clr, FDMA_INT_CLR_OFST);
+
+	return ret;
+}
+
+static struct dma_chan *st_fdma_of_xlate(struct of_phandle_args *dma_spec,
+					 struct of_dma *ofdma)
+{
+	struct st_fdma_dev *fdev = ofdma->of_dma_data;
+	struct dma_chan *chan;
+	struct st_fdma_chan *fchan;
+	int ret;
+
+	if (dma_spec->args_count < 1)
+		return ERR_PTR(-EINVAL);
+
+	if (fdev->dma_device.dev->of_node != dma_spec->np)
+		return ERR_PTR(-EINVAL);
+
+	ret = rproc_boot(fdev->slim_rproc->rproc);
+	if (ret == -ENOENT)
+		return ERR_PTR(-EPROBE_DEFER);
+	else if (ret)
+		return ERR_PTR(ret);
+
+	chan = dma_get_any_slave_channel(&fdev->dma_device);
+	if (!chan)
+		goto err_chan;
+
+	fchan = to_st_fdma_chan(chan);
+
+	fchan->cfg.of_node = dma_spec->np;
+	fchan->cfg.req_line = dma_spec->args[0];
+	fchan->cfg.req_ctrl = 0;
+	fchan->cfg.type = ST_FDMA_TYPE_FREE_RUN;
+
+	if (dma_spec->args_count > 1)
+		fchan->cfg.req_ctrl = dma_spec->args[1]
+			& FDMA_REQ_CTRL_CFG_MASK;
+
+	if (dma_spec->args_count > 2)
+		fchan->cfg.type = dma_spec->args[2];
+
+	if (fchan->cfg.type == ST_FDMA_TYPE_FREE_RUN) {
+		fchan->dreq_line = 0;
+	} else {
+		fchan->dreq_line = st_fdma_dreq_get(fchan);
+		if (IS_ERR_VALUE(fchan->dreq_line)) {
+			chan = ERR_PTR(fchan->dreq_line);
+			goto err_chan;
+		}
+	}
+
+	dev_dbg(fdev->dev, "xlate req_line:%d type:%d req_ctrl:%#lx\n",
+		fchan->cfg.req_line, fchan->cfg.type, fchan->cfg.req_ctrl);
+
+	return chan;
+
+err_chan:
+	rproc_shutdown(fdev->slim_rproc->rproc);
+	return chan;
+
+}
+
+static void st_fdma_free_desc(struct virt_dma_desc *vdesc)
+{
+	struct st_fdma_desc *fdesc;
+	int i;
+
+	fdesc = to_st_fdma_desc(vdesc);
+	for (i = 0; i < fdesc->n_nodes; i++)
+		dma_pool_free(fdesc->fchan->node_pool, fdesc->node[i].desc,
+			      fdesc->node[i].pdesc);
+	kfree(fdesc);
+}
+
+static struct st_fdma_desc *st_fdma_alloc_desc(struct st_fdma_chan *fchan,
+					       int sg_len)
+{
+	struct st_fdma_desc *fdesc;
+	int i;
+
+	fdesc = kzalloc(sizeof(*fdesc) +
+			sizeof(struct st_fdma_sw_node) * sg_len, GFP_NOWAIT);
+	if (!fdesc)
+		return NULL;
+
+	fdesc->fchan = fchan;
+	fdesc->n_nodes = sg_len;
+	for (i = 0; i < sg_len; i++) {
+		fdesc->node[i].desc = dma_pool_alloc(fchan->node_pool,
+				GFP_NOWAIT, &fdesc->node[i].pdesc);
+		if (!fdesc->node[i].desc)
+			goto err;
+	}
+	return fdesc;
+
+err:
+	while (--i >= 0)
+		dma_pool_free(fchan->node_pool, fdesc->node[i].desc,
+			      fdesc->node[i].pdesc);
+	kfree(fdesc);
+	return NULL;
+}
+
+static int st_fdma_alloc_chan_res(struct dma_chan *chan)
+{
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+
+	/* Create the dma pool for descriptor allocation */
+	fchan->node_pool = dma_pool_create(dev_name(&chan->dev->device),
+					    fchan->fdev->dev,
+					    sizeof(struct st_fdma_hw_node),
+					    __alignof__(struct st_fdma_hw_node),
+					    0);
+
+	if (!fchan->node_pool) {
+		dev_err(fchan->fdev->dev, "unable to allocate desc pool\n");
+		return -ENOMEM;
+	}
+
+	dev_dbg(fchan->fdev->dev, "alloc ch_id:%d type:%d\n",
+		fchan->vchan.chan.chan_id, fchan->cfg.type);
+
+	return 0;
+}
+
+static void st_fdma_free_chan_res(struct dma_chan *chan)
+{
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+	struct rproc *rproc = fchan->fdev->slim_rproc->rproc;
+	unsigned long flags;
+
+	LIST_HEAD(head);
+
+	dev_dbg(fchan->fdev->dev, "%s: freeing chan:%d\n",
+		__func__, fchan->vchan.chan.chan_id);
+
+	if (fchan->cfg.type != ST_FDMA_TYPE_FREE_RUN)
+		st_fdma_dreq_put(fchan);
+
+	spin_lock_irqsave(&fchan->vchan.lock, flags);
+	fchan->fdesc = NULL;
+	spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+	dma_pool_destroy(fchan->node_pool);
+	fchan->node_pool = NULL;
+	memset(&fchan->cfg, 0, sizeof(struct st_fdma_cfg));
+
+	rproc_shutdown(rproc);
+}
+
+static struct dma_async_tx_descriptor *st_fdma_prep_dma_memcpy(
+	struct dma_chan *chan,	dma_addr_t dst, dma_addr_t src,
+	size_t len, unsigned long flags)
+{
+	struct st_fdma_chan *fchan;
+	struct st_fdma_desc *fdesc;
+	struct st_fdma_hw_node *hw_node;
+
+	if (!len)
+		return NULL;
+
+	fchan = to_st_fdma_chan(chan);
+
+	/* We only require a single descriptor */
+	fdesc = st_fdma_alloc_desc(fchan, 1);
+	if (!fdesc) {
+		dev_err(fchan->fdev->dev, "no memory for desc\n");
+		return NULL;
+	}
+
+	hw_node = fdesc->node[0].desc;
+	hw_node->next = 0;
+	hw_node->control = FDMA_NODE_CTRL_REQ_MAP_FREE_RUN;
+	hw_node->control |= FDMA_NODE_CTRL_SRC_INCR;
+	hw_node->control |= FDMA_NODE_CTRL_DST_INCR;
+	hw_node->control |= FDMA_NODE_CTRL_INT_EON;
+	hw_node->nbytes = len;
+	hw_node->saddr = src;
+	hw_node->daddr = dst;
+	hw_node->generic.length = len;
+	hw_node->generic.sstride = 0;
+	hw_node->generic.dstride = 0;
+
+	return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags);
+}
+
+static int config_reqctrl(struct st_fdma_chan *fchan,
+			  enum dma_transfer_direction direction)
+{
+	u32 maxburst = 0, addr = 0;
+	enum dma_slave_buswidth width;
+	int ch_id = fchan->vchan.chan.chan_id;
+	struct st_fdma_dev *fdev = fchan->fdev;
+
+	switch (direction) {
+
+	case DMA_DEV_TO_MEM:
+		fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_WNR;
+		maxburst = fchan->scfg.src_maxburst;
+		width = fchan->scfg.src_addr_width;
+		addr = fchan->scfg.src_addr;
+		break;
+
+	case DMA_MEM_TO_DEV:
+		fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_WNR;
+		maxburst = fchan->scfg.dst_maxburst;
+		width = fchan->scfg.dst_addr_width;
+		addr = fchan->scfg.dst_addr;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_OPCODE_MASK;
+
+	switch (width) {
+
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+		fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST1;
+		break;
+
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST2;
+		break;
+
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST4;
+		break;
+
+	case DMA_SLAVE_BUSWIDTH_8_BYTES:
+		fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_OPCODE_LD_ST8;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	fchan->cfg.req_ctrl &= ~FDMA_REQ_CTRL_NUM_OPS_MASK;
+	fchan->cfg.req_ctrl |= FDMA_REQ_CTRL_NUM_OPS(maxburst-1);
+	dreq_write(fchan, fchan->cfg.req_ctrl, FDMA_REQ_CTRL_OFST);
+
+	fchan->cfg.dev_addr = addr;
+	fchan->cfg.dir = direction;
+
+	dev_dbg(fdev->dev, "chan:%d config_reqctrl:%#x req_ctrl:%#lx\n",
+		ch_id, addr, fchan->cfg.req_ctrl);
+
+	return 0;
+}
+
+static void fill_hw_node(struct st_fdma_hw_node *hw_node,
+			struct st_fdma_chan *fchan,
+			enum dma_transfer_direction direction)
+{
+	if (direction == DMA_MEM_TO_DEV) {
+		hw_node->control |= FDMA_NODE_CTRL_SRC_INCR;
+		hw_node->control |= FDMA_NODE_CTRL_DST_STATIC;
+		hw_node->daddr = fchan->cfg.dev_addr;
+	} else {
+		hw_node->control |= FDMA_NODE_CTRL_SRC_STATIC;
+		hw_node->control |= FDMA_NODE_CTRL_DST_INCR;
+		hw_node->saddr = fchan->cfg.dev_addr;
+	}
+
+	hw_node->generic.sstride = 0;
+	hw_node->generic.dstride = 0;
+}
+
+static inline struct st_fdma_chan *st_fdma_prep_common(struct dma_chan *chan,
+		size_t len, enum dma_transfer_direction direction)
+{
+	struct st_fdma_chan *fchan;
+
+	if (!chan || !len)
+		return NULL;
+
+	fchan = to_st_fdma_chan(chan);
+
+	if (!is_slave_direction(direction)) {
+		dev_err(fchan->fdev->dev, "bad direction?\n");
+		return NULL;
+	}
+
+	return fchan;
+}
+
+static struct dma_async_tx_descriptor *st_fdma_prep_dma_cyclic(
+		struct dma_chan *chan, dma_addr_t buf_addr, size_t len,
+		size_t period_len, enum dma_transfer_direction direction,
+		unsigned long flags)
+{
+	struct st_fdma_chan *fchan;
+	struct st_fdma_desc *fdesc;
+	int sg_len, i;
+
+	fchan = st_fdma_prep_common(chan, len, direction);
+	if (!fchan)
+		return NULL;
+
+	if (!period_len)
+		return NULL;
+
+	if (config_reqctrl(fchan, direction)) {
+		dev_err(fchan->fdev->dev, "bad width or direction\n");
+		return NULL;
+	}
+
+	/* the buffer length must be a multiple of period_len */
+	if (len % period_len != 0) {
+		dev_err(fchan->fdev->dev, "len is not multiple of period\n");
+		return NULL;
+	}
+
+	sg_len = len / period_len;
+	fdesc = st_fdma_alloc_desc(fchan, sg_len);
+	if (!fdesc) {
+		dev_err(fchan->fdev->dev, "no memory for desc\n");
+		return NULL;
+	}
+
+	fdesc->iscyclic = true;
+
+	for (i = 0; i < sg_len; i++) {
+		struct st_fdma_hw_node *hw_node = fdesc->node[i].desc;
+
+		hw_node->next = fdesc->node[(i + 1) % sg_len].pdesc;
+
+		hw_node->control =
+			FDMA_NODE_CTRL_REQ_MAP_DREQ(fchan->dreq_line);
+		hw_node->control |= FDMA_NODE_CTRL_INT_EON;
+
+		fill_hw_node(hw_node, fchan, direction);
+
+		if (direction == DMA_MEM_TO_DEV)
+			hw_node->saddr = buf_addr + (i * period_len);
+		else
+			hw_node->daddr = buf_addr + (i * period_len);
+
+		hw_node->nbytes = period_len;
+		hw_node->generic.length = period_len;
+	}
+
+	return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *st_fdma_prep_slave_sg(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_transfer_direction direction,
+		unsigned long flags, void *context)
+{
+	struct st_fdma_chan *fchan;
+	struct st_fdma_desc *fdesc;
+	struct st_fdma_hw_node *hw_node;
+	struct scatterlist *sg;
+	int i;
+
+	fchan = st_fdma_prep_common(chan, sg_len, direction);
+	if (!fchan)
+		return NULL;
+
+	if (!sgl)
+		return NULL;
+
+	fdesc = st_fdma_alloc_desc(fchan, sg_len);
+	if (!fdesc) {
+		dev_err(fchan->fdev->dev, "no memory for desc\n");
+		return NULL;
+	}
+
+	fdesc->iscyclic = false;
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		hw_node = fdesc->node[i].desc;
+
+		hw_node->next = fdesc->node[(i + 1) % sg_len].pdesc;
+		hw_node->control = FDMA_NODE_CTRL_REQ_MAP_DREQ(fchan->dreq_line);
+
+		fill_hw_node(hw_node, fchan, direction);
+
+		if (direction == DMA_MEM_TO_DEV)
+			hw_node->saddr = sg_dma_address(sg);
+		else
+			hw_node->daddr = sg_dma_address(sg);
+
+		hw_node->nbytes = sg_dma_len(sg);
+		hw_node->generic.length = sg_dma_len(sg);
+	}
+
+	/* interrupt at end of last node */
+	hw_node->control |= FDMA_NODE_CTRL_INT_EON;
+
+	return vchan_tx_prep(&fchan->vchan, &fdesc->vdesc, flags);
+}
+
+static size_t st_fdma_desc_residue(struct st_fdma_chan *fchan,
+				   struct virt_dma_desc *vdesc,
+				   bool in_progress)
+{
+	struct st_fdma_desc *fdesc = fchan->fdesc;
+	size_t residue = 0;
+	dma_addr_t cur_addr = 0;
+	int i;
+
+	if (in_progress) {
+		cur_addr = fchan_read(fchan, FDMA_CH_CMD_OFST);
+		cur_addr &= FDMA_CH_CMD_DATA_MASK;
+	}
+
+	for (i = fchan->fdesc->n_nodes - 1 ; i >= 0; i--) {
+		if (cur_addr == fdesc->node[i].pdesc) {
+			residue += fnode_read(fchan, FDMA_CNTN_OFST);
+			break;
+		}
+		residue += fdesc->node[i].desc->nbytes;
+	}
+
+	return residue;
+}
+
+static enum dma_status st_fdma_tx_status(struct dma_chan *chan,
+					 dma_cookie_t cookie,
+					 struct dma_tx_state *txstate)
+{
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+	struct virt_dma_desc *vd;
+	enum dma_status ret;
+	unsigned long flags;
+
+	ret = dma_cookie_status(chan, cookie, txstate);
+	if (ret == DMA_COMPLETE || !txstate)
+		return ret;
+
+	spin_lock_irqsave(&fchan->vchan.lock, flags);
+	vd = vchan_find_desc(&fchan->vchan, cookie);
+	if (fchan->fdesc && cookie == fchan->fdesc->vdesc.tx.cookie)
+		txstate->residue = st_fdma_desc_residue(fchan, vd, true);
+	else if (vd)
+		txstate->residue = st_fdma_desc_residue(fchan, vd, false);
+	else
+		txstate->residue = 0;
+
+	spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+	return ret;
+}
+
+static void st_fdma_issue_pending(struct dma_chan *chan)
+{
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&fchan->vchan.lock, flags);
+
+	if (vchan_issue_pending(&fchan->vchan) && !fchan->fdesc)
+		st_fdma_xfer_desc(fchan);
+
+	spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+}
+
+static int st_fdma_pause(struct dma_chan *chan)
+{
+	unsigned long flags;
+	LIST_HEAD(head);
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+	int ch_id = fchan->vchan.chan.chan_id;
+	unsigned long cmd = FDMA_CMD_PAUSE(ch_id);
+
+	dev_dbg(fchan->fdev->dev, "pause chan:%d\n", ch_id);
+
+	spin_lock_irqsave(&fchan->vchan.lock, flags);
+	if (fchan->fdesc)
+		fdma_write(fchan->fdev, cmd, FDMA_CMD_SET_OFST);
+	spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+	return 0;
+}
+
+static int st_fdma_resume(struct dma_chan *chan)
+{
+	unsigned long flags;
+	unsigned long val;
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+	int ch_id = fchan->vchan.chan.chan_id;
+
+	dev_dbg(fchan->fdev->dev, "resume chan:%d\n", ch_id);
+
+	spin_lock_irqsave(&fchan->vchan.lock, flags);
+	if (fchan->fdesc) {
+		val = fchan_read(fchan, FDMA_CH_CMD_OFST);
+		val &= FDMA_CH_CMD_DATA_MASK;
+		fchan_write(fchan, val, FDMA_CH_CMD_OFST);
+	}
+	spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+
+	return 0;
+}
+
+static int st_fdma_terminate_all(struct dma_chan *chan)
+{
+	unsigned long flags;
+	LIST_HEAD(head);
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+	int ch_id = fchan->vchan.chan.chan_id;
+	unsigned long cmd = FDMA_CMD_PAUSE(ch_id);
+
+	dev_dbg(fchan->fdev->dev, "terminate chan:%d\n", ch_id);
+
+	spin_lock_irqsave(&fchan->vchan.lock, flags);
+	fdma_write(fchan->fdev, cmd, FDMA_CMD_SET_OFST);
+	fchan->fdesc = NULL;
+	vchan_get_all_descriptors(&fchan->vchan, &head);
+	spin_unlock_irqrestore(&fchan->vchan.lock, flags);
+	vchan_dma_desc_free_list(&fchan->vchan, &head);
+
+	return 0;
+}
+
+static int st_fdma_slave_config(struct dma_chan *chan,
+				struct dma_slave_config *slave_cfg)
+{
+	struct st_fdma_chan *fchan = to_st_fdma_chan(chan);
+
+	memcpy(&fchan->scfg, slave_cfg, sizeof(fchan->scfg));
+	return 0;
+}
+
+static const struct st_fdma_driverdata fdma_mpe31_stih407_11 = {
+	.name = "STiH407",
+	.id = 0,
+};
+
+static const struct st_fdma_driverdata fdma_mpe31_stih407_12 = {
+	.name = "STiH407",
+	.id = 1,
+};
+
+static const struct st_fdma_driverdata fdma_mpe31_stih407_13 = {
+	.name = "STiH407",
+	.id = 2,
+};
+
+static const struct of_device_id st_fdma_match[] = {
+	{ .compatible = "st,stih407-fdma-mpe31-11"
+	  , .data = &fdma_mpe31_stih407_11 },
+	{ .compatible = "st,stih407-fdma-mpe31-12"
+	  , .data = &fdma_mpe31_stih407_12 },
+	{ .compatible = "st,stih407-fdma-mpe31-13"
+	  , .data = &fdma_mpe31_stih407_13 },
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_fdma_match);
+
+static int st_fdma_parse_dt(struct platform_device *pdev,
+			const struct st_fdma_driverdata *drvdata,
+			struct st_fdma_dev *fdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (!np)
+		goto err;
+
+	ret = of_property_read_u32(np, "dma-channels", &fdev->nr_channels);
+	if (ret)
+		goto err;
+
+	snprintf(fdev->fw_name, FW_NAME_SIZE, "fdma_%s_%d.elf",
+		drvdata->name, drvdata->id);
+
+err:
+	return ret;
+}
+#define FDMA_DMA_BUSWIDTHS	(BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+				 BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+				 BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
+				 BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+
+static void st_fdma_free(struct st_fdma_dev *fdev)
+{
+	struct st_fdma_chan *fchan;
+	int i;
+
+	for (i = 0; i < fdev->nr_channels; i++) {
+		fchan = &fdev->chans[i];
+		list_del(&fchan->vchan.chan.device_node);
+		tasklet_kill(&fchan->vchan.task);
+	}
+}
+
+static int st_fdma_probe(struct platform_device *pdev)
+{
+	struct st_fdma_dev *fdev;
+	const struct of_device_id *match;
+	struct device_node *np = pdev->dev.of_node;
+	const struct st_fdma_driverdata *drvdata;
+	int ret, i;
+
+	match = of_match_device((st_fdma_match), &pdev->dev);
+	if (!match || !match->data) {
+		dev_err(&pdev->dev, "No device match found\n");
+		return -ENODEV;
+	}
+
+	drvdata = match->data;
+
+	fdev = devm_kzalloc(&pdev->dev, sizeof(*fdev), GFP_KERNEL);
+	if (!fdev)
+		return -ENOMEM;
+
+	ret = st_fdma_parse_dt(pdev, drvdata, fdev);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to find platform data\n");
+		goto err;
+	}
+
+	fdev->chans = devm_kcalloc(&pdev->dev, fdev->nr_channels,
+				   sizeof(struct st_fdma_chan), GFP_KERNEL);
+	if (!fdev->chans)
+		return -ENOMEM;
+
+	fdev->dev = &pdev->dev;
+	fdev->drvdata = drvdata;
+	platform_set_drvdata(pdev, fdev);
+
+	fdev->irq = platform_get_irq(pdev, 0);
+	if (fdev->irq < 0) {
+		dev_err(&pdev->dev, "Failed to get irq resource\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_irq(&pdev->dev, fdev->irq, st_fdma_irq_handler, 0,
+			       dev_name(&pdev->dev), fdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq (%d)\n", ret);
+		goto err;
+	}
+
+	fdev->slim_rproc = st_slim_rproc_alloc(pdev, fdev->fw_name);
+	if (!fdev->slim_rproc) {
+		ret = PTR_ERR(fdev->slim_rproc);
+		dev_err(&pdev->dev, "slim_rproc_alloc failed (%d)\n", ret);
+		goto err;
+	}
+
+	/* Initialise list of FDMA channels */
+	INIT_LIST_HEAD(&fdev->dma_device.channels);
+	for (i = 0; i < fdev->nr_channels; i++) {
+		struct st_fdma_chan *fchan = &fdev->chans[i];
+
+		fchan->fdev = fdev;
+		fchan->vchan.desc_free = st_fdma_free_desc;
+		vchan_init(&fchan->vchan, &fdev->dma_device);
+	}
+
+	/* Initialise the FDMA dreq (reserve 0 & 31 for FDMA use) */
+	fdev->dreq_mask = BIT(0) | BIT(31);
+
+	dma_cap_set(DMA_SLAVE, fdev->dma_device.cap_mask);
+	dma_cap_set(DMA_CYCLIC, fdev->dma_device.cap_mask);
+	dma_cap_set(DMA_MEMCPY, fdev->dma_device.cap_mask);
+
+	fdev->dma_device.dev = &pdev->dev;
+	fdev->dma_device.device_alloc_chan_resources = st_fdma_alloc_chan_res;
+	fdev->dma_device.device_free_chan_resources = st_fdma_free_chan_res;
+	fdev->dma_device.device_prep_dma_cyclic	= st_fdma_prep_dma_cyclic;
+	fdev->dma_device.device_prep_slave_sg = st_fdma_prep_slave_sg;
+	fdev->dma_device.device_prep_dma_memcpy = st_fdma_prep_dma_memcpy;
+	fdev->dma_device.device_tx_status = st_fdma_tx_status;
+	fdev->dma_device.device_issue_pending = st_fdma_issue_pending;
+	fdev->dma_device.device_terminate_all = st_fdma_terminate_all;
+	fdev->dma_device.device_config = st_fdma_slave_config;
+	fdev->dma_device.device_pause = st_fdma_pause;
+	fdev->dma_device.device_resume = st_fdma_resume;
+
+	fdev->dma_device.src_addr_widths = FDMA_DMA_BUSWIDTHS;
+	fdev->dma_device.dst_addr_widths = FDMA_DMA_BUSWIDTHS;
+	fdev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+	fdev->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+	ret = dma_async_device_register(&fdev->dma_device);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to register DMA device (%d)\n", ret);
+		goto err_rproc;
+	}
+
+	ret = of_dma_controller_register(np, st_fdma_of_xlate, fdev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Failed to register controller (%d)\n", ret);
+		goto err_dma_dev;
+	}
+
+	dev_info(&pdev->dev, "ST FDMA engine driver, irq:%d\n", fdev->irq);
+
+	return 0;
+
+err_dma_dev:
+	dma_async_device_unregister(&fdev->dma_device);
+err_rproc:
+	st_fdma_free(fdev);
+	st_slim_rproc_put(fdev->slim_rproc);
+err:
+	return ret;
+}
+
+static int st_fdma_remove(struct platform_device *pdev)
+{
+	struct st_fdma_dev *fdev = platform_get_drvdata(pdev);
+
+	devm_free_irq(&pdev->dev, fdev->irq, fdev);
+	st_slim_rproc_put(fdev->slim_rproc);
+	of_dma_controller_free(pdev->dev.of_node);
+	dma_async_device_unregister(&fdev->dma_device);
+
+	return 0;
+}
+
+static struct platform_driver st_fdma_platform_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.of_match_table = st_fdma_match,
+	},
+	.probe = st_fdma_probe,
+	.remove = st_fdma_remove,
+};
+module_platform_driver(st_fdma_platform_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("STMicroelectronics FDMA engine driver");
+MODULE_AUTHOR("Ludovic.barre <Ludovic.barre@st.com>");
+MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
+MODULE_ALIAS("platform: " DRIVER_NAME);
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 05/11] dmaengine: st_fdma: Add STMicroelectronics FDMA driver header file
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

This header file will also be used by the dma xbar driver in the
future.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
 drivers/dma/st_fdma.h | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 249 insertions(+)
 create mode 100644 drivers/dma/st_fdma.h

diff --git a/drivers/dma/st_fdma.h b/drivers/dma/st_fdma.h
new file mode 100644
index 0000000..c58e00d
--- /dev/null
+++ b/drivers/dma/st_fdma.h
@@ -0,0 +1,249 @@
+/*
+ * DMA driver header for STMicroelectronics STi FDMA controller
+ *
+ * Copyright (C) 2014 STMicroelectronics
+ *
+ * Author: Ludovic Barre <Ludovic.barre@st.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; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+#ifndef __DMA_ST_FDMA_H
+#define __DMA_ST_FDMA_H
+
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/io.h>
+#include <linux/remoteproc/st_slim_rproc.h>
+#include "virt-dma.h"
+
+#define ST_FDMA_NR_DREQS 32
+#define FW_NAME_SIZE 30
+#define DRIVER_NAME "st-fdma"
+
+/**
+ * struct st_fdma_generic_node - Free running/paced generic node
+ *
+ * @length: Length in bytes of a line in a 2D mem to mem
+ * @sstride: Stride, in bytes, between source lines in a 2D data move
+ * @dstride: Stride, in bytes, between destination lines in a 2D data move
+ */
+struct st_fdma_generic_node {
+	u32 length;
+	u32 sstride;
+	u32 dstride;
+};
+
+/**
+ * struct st_fdma_hw_node - Node structure used by fdma hw
+ *
+ * @next: Pointer to next node
+ * @control: Transfer Control Parameters
+ * @nbytes: Number of Bytes to read
+ * @saddr: Source address
+ * @daddr: Destination address
+ *
+ * @generic: generic node for free running/paced transfert type
+ * 2 others transfert type are possible, but not yet implemented
+ *
+ * The NODE structures must be aligned to a 32 byte boundary
+ */
+struct st_fdma_hw_node {
+	u32 next;
+	u32 control;
+	u32 nbytes;
+	u32 saddr;
+	u32 daddr;
+	union {
+		struct st_fdma_generic_node generic;
+	};
+} __aligned(32);
+
+/*
+ * node control parameters
+ */
+#define FDMA_NODE_CTRL_REQ_MAP_MASK	GENMASK(4, 0)
+#define FDMA_NODE_CTRL_REQ_MAP_FREE_RUN	0x0
+#define FDMA_NODE_CTRL_REQ_MAP_DREQ(n)	((n)&FDMA_NODE_CTRL_REQ_MAP_MASK)
+#define FDMA_NODE_CTRL_REQ_MAP_EXT		FDMA_NODE_CTRL_REQ_MAP_MASK
+#define FDMA_NODE_CTRL_SRC_MASK		GENMASK(6, 5)
+#define FDMA_NODE_CTRL_SRC_STATIC	BIT(5)
+#define FDMA_NODE_CTRL_SRC_INCR		BIT(6)
+#define FDMA_NODE_CTRL_DST_MASK		GENMASK(8, 7)
+#define FDMA_NODE_CTRL_DST_STATIC	BIT(7)
+#define FDMA_NODE_CTRL_DST_INCR		BIT(8)
+#define FDMA_NODE_CTRL_SECURE		BIT(15)
+#define FDMA_NODE_CTRL_PAUSE_EON	BIT(30)
+#define FDMA_NODE_CTRL_INT_EON		BIT(31)
+
+/**
+ * struct st_fdma_sw_node - descriptor structure for link list
+ *
+ * @pdesc: Physical address of desc
+ * @node: link used for putting this into a channel queue
+ */
+struct st_fdma_sw_node {
+	dma_addr_t pdesc;
+	struct st_fdma_hw_node *desc;
+};
+
+#define NAME_SZ 10
+
+struct st_fdma_driverdata {
+	u32 id;
+	char name[NAME_SZ];
+};
+
+struct st_fdma_desc {
+	struct virt_dma_desc vdesc;
+	struct st_fdma_chan *fchan;
+	bool iscyclic;
+	unsigned int n_nodes;
+	struct st_fdma_sw_node node[];
+};
+
+enum st_fdma_type {
+	ST_FDMA_TYPE_FREE_RUN,
+	ST_FDMA_TYPE_PACED,
+};
+
+struct st_fdma_cfg {
+	struct device_node *of_node;
+	enum st_fdma_type type;
+	dma_addr_t dev_addr;
+	enum dma_transfer_direction dir;
+	int req_line; /* request line */
+	long req_ctrl; /* Request control */
+};
+
+struct st_fdma_chan {
+	struct st_fdma_dev *fdev;
+	struct dma_pool *node_pool;
+	struct dma_slave_config scfg;
+	struct st_fdma_cfg cfg;
+
+	int dreq_line;
+
+	struct virt_dma_chan vchan;
+	struct st_fdma_desc *fdesc;
+	enum dma_status	status;
+};
+
+struct st_fdma_dev {
+	struct device *dev;
+	const struct st_fdma_driverdata *drvdata;
+	struct dma_device dma_device;
+
+	struct st_slim_rproc *slim_rproc;
+
+	int irq;
+
+	struct st_fdma_chan *chans;
+
+	spinlock_t dreq_lock;
+	unsigned long dreq_mask;
+
+	u32 nr_channels;
+	char fw_name[FW_NAME_SIZE];
+};
+
+/* Peripheral Registers*/
+
+#define FDMA_CMD_STA_OFST	0xFC0
+#define FDMA_CMD_SET_OFST	0xFC4
+#define FDMA_CMD_CLR_OFST	0xFC8
+#define FDMA_CMD_MASK_OFST	0xFCC
+#define FDMA_CMD_START(ch)		(0x1 << (ch << 1))
+#define FDMA_CMD_PAUSE(ch)		(0x2 << (ch << 1))
+#define FDMA_CMD_FLUSH(ch)		(0x3 << (ch << 1))
+
+#define FDMA_INT_STA_OFST	0xFD0
+#define FDMA_INT_STA_CH			0x1
+#define FDMA_INT_STA_ERR		0x2
+
+#define FDMA_INT_SET_OFST	0xFD4
+#define FDMA_INT_CLR_OFST	0xFD8
+#define FDMA_INT_MASK_OFST	0xFDC
+
+#define fdma_read(fdev, name) \
+	readl((fdev)->slim_rproc->peri + name)
+
+#define fdma_write(fdev, val, name) \
+	writel((val), (fdev)->slim_rproc->peri + name)
+
+/* fchan interface (dmem) */
+#define FDMA_CH_CMD_OFST	0x200
+#define FDMA_CH_CMD_STA_MASK		GENMASK(1, 0)
+#define FDMA_CH_CMD_STA_IDLE		(0x0)
+#define FDMA_CH_CMD_STA_START		(0x1)
+#define FDMA_CH_CMD_STA_RUNNING		(0x2)
+#define FDMA_CH_CMD_STA_PAUSED		(0x3)
+#define FDMA_CH_CMD_ERR_MASK		GENMASK(4, 2)
+#define FDMA_CH_CMD_ERR_INT		(0x0 << 2)
+#define FDMA_CH_CMD_ERR_NAND		(0x1 << 2)
+#define FDMA_CH_CMD_ERR_MCHI		(0x2 << 2)
+#define FDMA_CH_CMD_DATA_MASK		GENMASK(31, 5)
+#define fchan_read(fchan, name) \
+	readl((fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+			+ (fchan)->vchan.chan.chan_id * 0x4 \
+			+ name)
+
+#define fchan_write(fchan, val, name) \
+	writel((val), (fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+			+ (fchan)->vchan.chan.chan_id * 0x4 \
+			+ name)
+
+/* req interface */
+#define FDMA_REQ_CTRL_OFST	0x240
+#define dreq_write(fchan, val, name) \
+	writel((val), (fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+			+ fchan->dreq_line * 0x04 \
+			+ name)
+/* node interface */
+#define FDMA_NODE_SZ 128
+#define FDMA_PTRN_OFST		0x800
+#define FDMA_CNTN_OFST		0x808
+#define FDMA_SADDRN_OFST	0x80c
+#define FDMA_DADDRN_OFST	0x810
+#define fnode_read(fchan, name) \
+	readl((fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+			+ (fchan)->vchan.chan.chan_id * FDMA_NODE_SZ \
+			+ name)
+
+#define fnode_write(fchan, val, name) \
+	writel((val), (fchan)->fdev->slim_rproc->mem[ST_SLIM_DMEM].cpu_addr \
+			+ (fchan)->vchan.chan.chan_id * FDMA_NODE_SZ \
+			+ name)
+
+/*
+ * request control bits
+ */
+#define FDMA_REQ_CTRL_NUM_OPS_MASK	GENMASK(31, 24)
+#define FDMA_REQ_CTRL_NUM_OPS(n)	(FDMA_REQ_CTRL_NUM_OPS_MASK & \
+					((n) << 24))
+#define FDMA_REQ_CTRL_INITIATOR_MASK	BIT(22)
+#define FDMA_REQ_CTRL_INIT0		(0x0 << 22)
+#define FDMA_REQ_CTRL_INIT1		(0x1 << 22)
+#define FDMA_REQ_CTRL_INC_ADDR_ON	BIT(21)
+#define FDMA_REQ_CTRL_DATA_SWAP_ON	BIT(17)
+#define FDMA_REQ_CTRL_WNR		BIT(14)
+#define FDMA_REQ_CTRL_OPCODE_MASK	GENMASK(7, 4)
+#define FDMA_REQ_CTRL_OPCODE_LD_ST1	(0x0 << 4)
+#define FDMA_REQ_CTRL_OPCODE_LD_ST2	(0x1 << 4)
+#define FDMA_REQ_CTRL_OPCODE_LD_ST4	(0x2 << 4)
+#define FDMA_REQ_CTRL_OPCODE_LD_ST8	(0x3 << 4)
+#define FDMA_REQ_CTRL_OPCODE_LD_ST16	(0x4 << 4)
+#define FDMA_REQ_CTRL_OPCODE_LD_ST32	(0x5 << 4)
+#define FDMA_REQ_CTRL_OPCODE_LD_ST64	(0x6 << 4)
+#define FDMA_REQ_CTRL_HOLDOFF_MASK	GENMASK(2, 0)
+#define FDMA_REQ_CTRL_HOLDOFF(n)	((n) & FDMA_REQ_CTRL_HOLDOFF_MASK)
+
+/* bits used by client to configure request control */
+#define FDMA_REQ_CTRL_CFG_MASK (FDMA_REQ_CTRL_HOLDOFF_MASK | \
+				FDMA_REQ_CTRL_DATA_SWAP_ON | \
+				FDMA_REQ_CTRL_INC_ADDR_ON | \
+				FDMA_REQ_CTRL_INITIATOR_MASK)
+
+#endif	/* __DMA_ST_FDMA_H */
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 04/11] dmaengine: st_fdma: Add STMicroelectronics FDMA DT binding documentation
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

This patch adds the DT binding documentation for the FDMA constroller
found on STi based chipsets from STMicroelectronics.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/dma/st_fdma.txt | 87 +++++++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/st_fdma.txt

diff --git a/Documentation/devicetree/bindings/dma/st_fdma.txt b/Documentation/devicetree/bindings/dma/st_fdma.txt
new file mode 100644
index 0000000..495d853
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/st_fdma.txt
@@ -0,0 +1,87 @@
+* STMicroelectronics Flexible Direct Memory Access Device Tree bindings
+
+The FDMA is a general-purpose direct memory access controller capable of
+supporting 16 independent DMA channels. It accepts up to 32 DMA requests.
+The FDMA is based on a Slim processor which requires a firmware.
+
+* FDMA Controller
+
+Required properties:
+- compatible	: Should be one of
+		 - st,stih407-fdma-mpe31-11, "st,slim-rproc";
+		 - st,stih407-fdma-mpe31-12, "st,slim-rproc";
+		 - st,stih407-fdma-mpe31-13, "st,slim-rproc";
+- reg		: Should contain an entry for each name in reg-names
+- reg-names	: Must contain "slimcore", "dmem", "peripherals", "imem" entries
+- interrupts	: Should contain one interrupt shared by all channels
+- dma-channels	: Number of channels supported by the controller
+- #dma-cells	: Must be <3>. See DMA client section below
+- clocks	: Must contain an entry for each clock
+See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+
+Example:
+
+	fdma0: dma-controller at 8e20000 {
+		compatible = "st,stih407-fdma-mpe31-11", "st,slim-rproc";
+		reg = <0x8e20000 0x8000>,
+		      <0x8e30000 0x3000>,
+		      <0x8e37000 0x1000>,
+		      <0x8e38000 0x8000>;
+		reg-names = "slimcore", "dmem", "peripherals", "imem";
+		clocks = <&clk_s_c0_flexgen CLK_FDMA>,
+			 <&clk_s_c0_flexgen CLK_EXT2F_A9>,
+			 <&clk_s_c0_flexgen CLK_EXT2F_A9>,
+			 <&clk_s_c0_flexgen CLK_EXT2F_A9>;
+		interrupts = <GIC_SPI 5 IRQ_TYPE_NONE>;
+		dma-channels = <16>;
+		#dma-cells = <3>;
+	};
+
+* DMA client
+
+Required properties:
+- dmas: Comma separated list of dma channel requests
+- dma-names: Names of the aforementioned requested channels
+
+Each dmas request consists of 4 cells:
+1. A phandle pointing to the FDMA controller
+2. The request line number
+3. A 32bit mask specifying (see include/linux/platform_data/dma-st-fdma.h)
+ -bit 2-0: Holdoff value, dreq will be masked for
+	0x0: 0-0.5us
+	0x1: 0.5-1us
+	0x2: 1-1.5us
+ -bit 17: data swap
+	0x0: disabled
+	0x1: enabled
+ -bit 21: Increment Address
+	0x0: no address increment between transfers
+	0x1: increment address between transfers
+ -bit 22: 2 STBus Initiator Coprocessor interface
+	0x0: high priority port
+	0x1: low priority port
+4. transfers type
+ 0 free running
+ 1 paced
+
+Example:
+
+	sti_uni_player2: sti-uni-player at 2 {
+		compatible = "st,sti-uni-player";
+		status = "disabled";
+		#sound-dai-cells = <0>;
+		st,syscfg = <&syscfg_core>;
+		clocks = <&clk_s_d0_flexgen CLK_PCM_2>;
+		assigned-clocks = <&clk_s_d0_flexgen CLK_PCM_2>;
+		assigned-clock-parents = <&clk_s_d0_quadfs 2>;
+		assigned-clock-rates = <50000000>;
+		reg = <0x8D82000 0x158>;
+		interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>;
+		dmas = <&fdma0 4 0 1>;
+		dai-name = "Uni Player #1 (DAC)";
+		dma-names = "tx";
+		st,uniperiph-id = <2>;
+		st,version = <5>;
+		st,mode = "PCM";
+	};
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 03/11] remoteproc: Update Kconfig setup to 'depends on REMOTEPROC'
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

Make REMOTEPROC core a selectable kconfig option, and update
remoteproc client drivers to 'depends on' the core. This avoids
some nasty Kconfig recursive dependency issues. Also when using
menuconfig client drivers will be hidden until the core has been
enabled.

Documentation/kbuild/kconfig-language.txt:

  Note:
        select should be used with care. select will force
        a symbol to a value without visiting the dependencies.
        By abusing select you are able to select a symbol FOO even
        if FOO depends on BAR that is not set.
        In general use select only for non-visible symbols
        (no prompts anywhere) and for symbols with no dependencies.
        That will limit the usefulness but on the other hand avoid
        the illegal configurations all over.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
 drivers/remoteproc/Kconfig | 21 ++++++++++++---------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index a7bedc6..decdcbe 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -1,20 +1,21 @@
 menu "Remoteproc drivers"
 
-# REMOTEPROC gets selected by whoever wants it
 config REMOTEPROC
-	tristate
+	tristate "Support for Remote Processor subsystem"
 	depends on HAS_DMA
 	select CRC32
 	select FW_LOADER
 	select VIRTIO
 	select VIRTUALIZATION
 
+if REMOTEPROC
+
 config OMAP_REMOTEPROC
 	tristate "OMAP remoteproc support"
 	depends on HAS_DMA
 	depends on ARCH_OMAP4 || SOC_OMAP5
 	depends on OMAP_IOMMU
-	select REMOTEPROC
+	depends on REMOTEPROC
 	select MAILBOX
 	select OMAP2PLUS_MBOX
 	select RPMSG
@@ -34,7 +35,7 @@ config OMAP_REMOTEPROC
 config STE_MODEM_RPROC
 	tristate "STE-Modem remoteproc support"
 	depends on HAS_DMA
-	select REMOTEPROC
+	depends on REMOTEPROC
 	default n
 	help
 	  Say y or m here to support STE-Modem shared memory driver.
@@ -44,7 +45,7 @@ config STE_MODEM_RPROC
 config WKUP_M3_RPROC
 	tristate "AMx3xx Wakeup M3 remoteproc support"
 	depends on SOC_AM33XX || SOC_AM43XX
-	select REMOTEPROC
+	depends on REMOTEPROC
 	help
 	  Say y here to support Wakeup M3 remote processor on TI AM33xx
 	  and AM43xx family of SoCs.
@@ -57,8 +58,8 @@ config WKUP_M3_RPROC
 config DA8XX_REMOTEPROC
 	tristate "DA8xx/OMAP-L13x remoteproc support"
 	depends on ARCH_DAVINCI_DA8XX
+	depends on REMOTEPROC
 	select CMA if MMU
-	select REMOTEPROC
 	select RPMSG
 	help
 	  Say y here to support DA8xx/OMAP-L13x remote processors via the
@@ -84,9 +85,9 @@ config QCOM_Q6V5_PIL
 	tristate "Qualcomm Hexagon V5 Peripherial Image Loader"
 	depends on OF && ARCH_QCOM
 	depends on QCOM_SMEM
+	depends on REMOTEPROC
 	select MFD_SYSCON
 	select QCOM_MDT_LOADER
-	select REMOTEPROC
 	help
 	  Say y here to support the Qualcomm Peripherial Image Loader for the
 	  Hexagon V5 based remote processors.
@@ -94,7 +95,7 @@ config QCOM_Q6V5_PIL
 config ST_REMOTEPROC
 	tristate "ST remoteproc support"
 	depends on ARCH_STI
-	select REMOTEPROC
+	depends on REMOTEPROC
 	help
 	  Say y here to support ST's adjunct processors via the remote
 	  processor framework.
@@ -102,6 +103,8 @@ config ST_REMOTEPROC
 
 config ST_SLIM_REMOTEPROC
 	tristate
-	select REMOTEPROC
+	depends on REMOTEPROC
+
+endif # REMOTEPROC
 
 endmenu
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 02/11] MAINTAINERS: Add st slim core rproc driver to STi section.
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

This patch adds the slim core rproc driver to the STi section
of the MAINTAINERS file.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
Acked-by: Lee Jones <lee.jones@linaro.org>
---
 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index f593300..9924036 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1759,6 +1759,7 @@ F:	drivers/phy/phy-stih407-usb.c
 F:	drivers/phy/phy-stih41x-usb.c
 F:	drivers/pinctrl/pinctrl-st.c
 F:	drivers/remoteproc/st_remoteproc.c
+F:	drivers/remoteproc/st_slim_rproc.c
 F:	drivers/reset/sti/
 F:	drivers/rtc/rtc-st-lpc.c
 F:	drivers/tty/serial/st-asc.c
@@ -1767,6 +1768,7 @@ F:	drivers/usb/host/ehci-st.c
 F:	drivers/usb/host/ohci-st.c
 F:	drivers/watchdog/st_lpc_wdt.c
 F:	drivers/ata/ahci_st.c
+F:	include/linux/remoteproc/st_slim_rproc.h
 
 ARM/STM32 ARCHITECTURE
 M:	Maxime Coquelin <mcoquelin.stm32@gmail.com>
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 01/11] remoteproc: st_slim_rproc: add a slimcore rproc driver
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1475931154-1021-1-git-send-email-peter.griffin@linaro.org>

slim core is used as a basis for many IPs in the STi
chipsets such as fdma and demux. To avoid duplicating
the elf loading code in each device driver a slim
rproc driver has been created.

This driver is designed to be used by other device drivers
such as fdma, or demux whose IP is based around a slim core.
The device driver can call slim_rproc_alloc() to allocate
a slim rproc and slim_rproc_put() when finished.

This driver takes care of ioremapping the slim
registers (dmem, imem, slimcore, peripherals), whose offsets
and sizes can change between IP's. It also obtains and enables
any clocks used by the device. This approach avoids having
a double mapping of the registers as slim_rproc does not register
its own platform device. It also maps well to device tree
abstraction as it allows us to have one dt node for the whole
device.

All of the generic rproc elf loading code can be reused, and
we provide start() stop() hooks to start and stop the slim
core once the firmware has been loaded. This has been tested
successfully with fdma driver.

Signed-off-by: Peter Griffin <peter.griffin@linaro.org>
---
 drivers/remoteproc/Kconfig               |   4 +
 drivers/remoteproc/Makefile              |   1 +
 drivers/remoteproc/st_slim_rproc.c       | 364 +++++++++++++++++++++++++++++++
 include/linux/remoteproc/st_slim_rproc.h |  58 +++++
 4 files changed, 427 insertions(+)
 create mode 100644 drivers/remoteproc/st_slim_rproc.c
 create mode 100644 include/linux/remoteproc/st_slim_rproc.h

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 1a8bf76a..a7bedc6 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -100,4 +100,8 @@ config ST_REMOTEPROC
 	  processor framework.
 	  This can be either built-in or a loadable module.
 
+config ST_SLIM_REMOTEPROC
+	tristate
+	select REMOTEPROC
+
 endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 92d3758..db1dae7 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
 obj-$(CONFIG_QCOM_MDT_LOADER)		+= qcom_mdt_loader.o
 obj-$(CONFIG_QCOM_Q6V5_PIL)		+= qcom_q6v5_pil.o
 obj-$(CONFIG_ST_REMOTEPROC)		+= st_remoteproc.o
+obj-$(CONFIG_ST_SLIM_REMOTEPROC)	+= st_slim_rproc.o
diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c
new file mode 100644
index 0000000..1484e97
--- /dev/null
+++ b/drivers/remoteproc/st_slim_rproc.c
@@ -0,0 +1,364 @@
+/*
+ * SLIM core rproc driver
+ *
+ * Copyright (C) 2016 STMicroelectronics
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/remoteproc/st_slim_rproc.h>
+#include "remoteproc_internal.h"
+
+/* SLIM core registers */
+#define SLIM_ID_OFST		0x0
+#define SLIM_VER_OFST		0x4
+
+#define SLIM_EN_OFST		0x8
+#define SLIM_EN_RUN			BIT(0)
+
+#define SLIM_CLK_GATE_OFST	0xC
+#define SLIM_CLK_GATE_DIS		BIT(0)
+#define SLIM_CLK_GATE_RESET		BIT(2)
+
+#define SLIM_SLIM_PC_OFST	0x20
+
+/* DMEM registers */
+#define SLIM_REV_ID_OFST	0x0
+#define SLIM_REV_ID_MIN_MASK		GENMASK(15, 8)
+#define SLIM_REV_ID_MIN(id)		((id & SLIM_REV_ID_MIN_MASK) >> 8)
+#define SLIM_REV_ID_MAJ_MASK		GENMASK(23, 16)
+#define SLIM_REV_ID_MAJ(id)		((id & SLIM_REV_ID_MAJ_MASK) >> 16)
+
+
+/* peripherals registers */
+#define SLIM_STBUS_SYNC_OFST	0xF88
+#define SLIM_STBUS_SYNC_DIS		BIT(0)
+
+#define SLIM_INT_SET_OFST	0xFD4
+#define SLIM_INT_CLR_OFST	0xFD8
+#define SLIM_INT_MASK_OFST	0xFDC
+
+#define SLIM_CMD_CLR_OFST	0xFC8
+#define SLIM_CMD_MASK_OFST	0xFCC
+
+static const char *mem_names[ST_SLIM_MEM_MAX] = {
+	[ST_SLIM_DMEM]	= "dmem",
+	[ST_SLIM_IMEM]	= "imem",
+};
+
+static int slim_clk_get(struct st_slim_rproc *slim_rproc, struct device *dev)
+{
+	int clk, err;
+
+	for (clk = 0; clk < ST_SLIM_MAX_CLK; clk++) {
+		slim_rproc->clks[clk] = of_clk_get(dev->of_node, clk);
+		if (IS_ERR(slim_rproc->clks[clk])) {
+			err = PTR_ERR(slim_rproc->clks[clk]);
+			if (err == -EPROBE_DEFER)
+				goto err_put_clks;
+			slim_rproc->clks[clk] = NULL;
+			break;
+		}
+	}
+
+	return 0;
+
+err_put_clks:
+	while (--clk >= 0)
+		clk_put(slim_rproc->clks[clk]);
+
+	return err;
+}
+
+static void slim_clk_disable(struct st_slim_rproc *slim_rproc)
+{
+	int clk;
+
+	for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)
+		clk_disable_unprepare(slim_rproc->clks[clk]);
+}
+
+static int slim_clk_enable(struct st_slim_rproc *slim_rproc)
+{
+	int clk, ret;
+
+	for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++) {
+		ret = clk_prepare_enable(slim_rproc->clks[clk]);
+		if (ret)
+			goto err_disable_clks;
+	}
+
+	return 0;
+
+err_disable_clks:
+	while (--clk >= 0)
+		clk_disable_unprepare(slim_rproc->clks[clk]);
+
+	return ret;
+}
+
+/*
+ * Remoteproc slim specific device handlers
+ */
+static int slim_rproc_start(struct rproc *rproc)
+{
+	struct device *dev = &rproc->dev;
+	struct st_slim_rproc *slim_rproc = rproc->priv;
+	unsigned long hw_id, hw_ver, fw_rev;
+	u32 val;
+
+	/* disable CPU pipeline clock & reset CPU pipeline */
+	val = SLIM_CLK_GATE_DIS | SLIM_CLK_GATE_RESET;
+	writel(val, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
+
+	/* disable SLIM core STBus sync */
+	writel(SLIM_STBUS_SYNC_DIS, slim_rproc->peri + SLIM_STBUS_SYNC_OFST);
+
+	/* enable cpu pipeline clock */
+	writel(!SLIM_CLK_GATE_DIS,
+		slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
+
+	/* clear int & cmd mailbox */
+	writel(~0U, slim_rproc->peri + SLIM_INT_CLR_OFST);
+	writel(~0U, slim_rproc->peri + SLIM_CMD_CLR_OFST);
+
+	/* enable all channels cmd & int */
+	writel(~0U, slim_rproc->peri + SLIM_INT_MASK_OFST);
+	writel(~0U, slim_rproc->peri + SLIM_CMD_MASK_OFST);
+
+	/* enable cpu */
+	writel(SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);
+
+	hw_id = readl_relaxed(slim_rproc->slimcore + SLIM_ID_OFST);
+	hw_ver = readl_relaxed(slim_rproc->slimcore + SLIM_VER_OFST);
+
+	fw_rev = readl(slim_rproc->mem[ST_SLIM_DMEM].cpu_addr +
+			SLIM_REV_ID_OFST);
+
+	dev_info(dev, "fw rev:%ld.%ld on SLIM %ld.%ld\n",
+		 SLIM_REV_ID_MAJ(fw_rev), SLIM_REV_ID_MIN(fw_rev),
+		 hw_id, hw_ver);
+
+	return 0;
+}
+
+static int slim_rproc_stop(struct rproc *rproc)
+{
+	struct st_slim_rproc *slim_rproc = rproc->priv;
+	u32 val;
+
+	/* mask all (cmd & int) channels */
+	writel(0UL, slim_rproc->peri + SLIM_INT_MASK_OFST);
+	writel(0UL, slim_rproc->peri + SLIM_CMD_MASK_OFST);
+
+	/* disable cpu pipeline clock */
+	writel(SLIM_CLK_GATE_DIS, slim_rproc->slimcore + SLIM_CLK_GATE_OFST);
+
+	writel(!SLIM_EN_RUN, slim_rproc->slimcore + SLIM_EN_OFST);
+
+	val = readl(slim_rproc->slimcore + SLIM_EN_OFST);
+	if (val & SLIM_EN_RUN)
+		dev_warn(&rproc->dev, "Failed to disable SLIM");
+
+	dev_dbg(&rproc->dev, "slim stopped\n");
+
+	return 0;
+}
+
+static void *slim_rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+	struct st_slim_rproc *slim_rproc = rproc->priv;
+	void *va = NULL;
+	int i;
+
+	for (i = 0; i < ST_SLIM_MEM_MAX; i++) {
+		if (da != slim_rproc->mem[i].bus_addr)
+			continue;
+
+		if (len <= slim_rproc->mem[i].size) {
+			/* __force to make sparse happy with type conversion */
+			va = (__force void *)slim_rproc->mem[i].cpu_addr;
+			break;
+		}
+	}
+
+	dev_dbg(&rproc->dev, "da = 0x%llx len = 0x%x va = 0x%p\n", da, len, va);
+
+	return va;
+}
+
+static struct rproc_ops slim_rproc_ops = {
+	.start		= slim_rproc_start,
+	.stop		= slim_rproc_stop,
+	.da_to_va       = slim_rproc_da_to_va,
+};
+
+/*
+ * Firmware handler operations: sanity, boot address, load ...
+ */
+
+static struct resource_table empty_rsc_tbl = {
+	.ver = 1,
+	.num = 0,
+};
+
+static struct resource_table *slim_rproc_find_rsc_table(struct rproc *rproc,
+					       const struct firmware *fw,
+					       int *tablesz)
+{
+	*tablesz = sizeof(empty_rsc_tbl);
+	return &empty_rsc_tbl;
+}
+
+static struct rproc_fw_ops slim_rproc_fw_ops = {
+	.find_rsc_table = slim_rproc_find_rsc_table,
+};
+
+/**
+ * st_slim_rproc_alloc() - allocate and initialise slim rproc
+ * @pdev: Pointer to the platform_device struct
+ * @fw_name: Name of firmware for rproc to use
+ *
+ * Function for allocating and initialising a slim rproc for use by
+ * device drivers whose IP is based around the SLIM core. It
+ * obtains and enables any clocks required by the SLIM core and also
+ * ioremaps the various IO.
+ *
+ * Returns st_slim_rproc pointer or PTR_ERR() on error.
+ */
+
+struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev,
+				char *fw_name)
+{
+	struct device *dev = &pdev->dev;
+	struct st_slim_rproc *slim_rproc;
+	struct device_node *np = dev->of_node;
+	struct rproc *rproc;
+	struct resource *res;
+	int err, i;
+	const struct rproc_fw_ops *elf_ops;
+
+	if (!fw_name)
+		return ERR_PTR(-EINVAL);
+
+	if (!of_device_is_compatible(np, "st,slim-rproc"))
+		return ERR_PTR(-EINVAL);
+
+	rproc = rproc_alloc(dev, np->name, &slim_rproc_ops,
+			fw_name, sizeof(*slim_rproc));
+	if (!rproc)
+		return ERR_PTR(-ENOMEM);
+
+	rproc->has_iommu = false;
+
+	slim_rproc = rproc->priv;
+	slim_rproc->rproc = rproc;
+
+	elf_ops = rproc->fw_ops;
+	/* Use some generic elf ops */
+	slim_rproc_fw_ops.load = elf_ops->load;
+	slim_rproc_fw_ops.sanity_check = elf_ops->sanity_check;
+
+	rproc->fw_ops = &slim_rproc_fw_ops;
+
+	/* get imem and dmem */
+	for (i = 0; i < ARRAY_SIZE(mem_names); i++) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						mem_names[i]);
+
+		slim_rproc->mem[i].cpu_addr = devm_ioremap_resource(dev, res);
+		if (IS_ERR(slim_rproc->mem[i].cpu_addr)) {
+			dev_err(&pdev->dev, "devm_ioremap_resource failed\n");
+			err = PTR_ERR(slim_rproc->mem[i].cpu_addr);
+			goto err;
+		}
+		slim_rproc->mem[i].bus_addr = res->start;
+		slim_rproc->mem[i].size = resource_size(res);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slimcore");
+	slim_rproc->slimcore = devm_ioremap_resource(dev, res);
+	if (IS_ERR(slim_rproc->slimcore)) {
+		dev_err(&pdev->dev, "failed to ioremap slimcore IO\n");
+		err = PTR_ERR(slim_rproc->slimcore);
+		goto err;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "peripherals");
+	slim_rproc->peri = devm_ioremap_resource(dev, res);
+	if (IS_ERR(slim_rproc->peri)) {
+		dev_err(&pdev->dev, "failed to ioremap peripherals IO\n");
+		err = PTR_ERR(slim_rproc->peri);
+		goto err;
+	}
+
+	err = slim_clk_get(slim_rproc, dev);
+	if (err)
+		goto err;
+
+	err = slim_clk_enable(slim_rproc);
+	if (err) {
+		dev_err(dev, "Failed to enable clocks\n");
+		goto err_clk_put;
+	}
+
+	/* Register as a remoteproc device */
+	err = rproc_add(rproc);
+	if (err) {
+		dev_err(dev, "registration of slim remoteproc failed\n");
+		goto err_clk_dis;
+	}
+
+	return slim_rproc;
+
+err_clk_dis:
+	slim_clk_disable(slim_rproc);
+err_clk_put:
+	for (i = 0; i < ST_SLIM_MAX_CLK && slim_rproc->clks[i]; i++)
+		clk_put(slim_rproc->clks[i]);
+err:
+	rproc_put(rproc);
+	return ERR_PTR(err);
+}
+EXPORT_SYMBOL(st_slim_rproc_alloc);
+
+/**
+  * st_slim_rproc_put() - put slim rproc resources
+  * @slim_rproc: Pointer to the st_slim_rproc struct
+  *
+  * Function for calling respective _put() functions on slim_rproc resources.
+  *
+  */
+void st_slim_rproc_put(struct st_slim_rproc *slim_rproc)
+{
+	int clk;
+
+	if (!slim_rproc)
+		return;
+
+	slim_clk_disable(slim_rproc);
+
+	for (clk = 0; clk < ST_SLIM_MAX_CLK && slim_rproc->clks[clk]; clk++)
+		clk_put(slim_rproc->clks[clk]);
+
+	rproc_del(slim_rproc->rproc);
+	rproc_put(slim_rproc->rproc);
+}
+EXPORT_SYMBOL(st_slim_rproc_put);
+
+MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>");
+MODULE_DESCRIPTION("STMicroelectronics SLIM core rproc driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/remoteproc/st_slim_rproc.h b/include/linux/remoteproc/st_slim_rproc.h
new file mode 100644
index 0000000..4155556
--- /dev/null
+++ b/include/linux/remoteproc/st_slim_rproc.h
@@ -0,0 +1,58 @@
+/*
+ * SLIM core rproc driver header
+ *
+ * Copyright (C) 2016 STMicroelectronics
+ *
+ * Author: Peter Griffin <peter.griffin@linaro.org>
+ *
+ * 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.
+ */
+#ifndef _ST_REMOTEPROC_SLIM_H
+#define _ST_REMOTEPROC_SLIM_H
+
+#define ST_SLIM_MEM_MAX 2
+#define ST_SLIM_MAX_CLK 4
+
+enum {
+	ST_SLIM_DMEM,
+	ST_SLIM_IMEM,
+};
+
+/**
+ * struct st_slim_mem - slim internal memory structure
+ * @cpu_addr: MPU virtual address of the memory region
+ * @bus_addr: Bus address used to access the memory region
+ * @size: Size of the memory region
+ */
+struct st_slim_mem {
+	void __iomem *cpu_addr;
+	phys_addr_t bus_addr;
+	size_t size;
+};
+
+/**
+ * struct st_slim_rproc - SLIM slim core
+ * @rproc: rproc handle
+ * @mem: slim memory information
+ * @slimcore: slim slimcore regs
+ * @peri: slim peripheral regs
+ * @clks: slim clocks
+ */
+struct st_slim_rproc {
+	struct rproc *rproc;
+	struct st_slim_mem mem[ST_SLIM_MEM_MAX];
+	void __iomem *slimcore;
+	void __iomem *peri;
+
+	/* st_slim_rproc private */
+	struct clk *clks[ST_SLIM_MAX_CLK];
+};
+
+struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev,
+					char *fw_name);
+void st_slim_rproc_put(struct st_slim_rproc *slim_rproc);
+
+#endif
-- 
1.9.1

^ permalink raw reply related

* [PATCH v10 00/11] Add support for FDMA DMA controller and slim core rproc found on STi chipsets
From: Peter Griffin @ 2016-10-08 12:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Vinod and Bjorn,

This patchset adds support for the Flexible Direct Memory Access (FDMA) core
found on STi chipsets from STMicroelectronics. The FDMA is a slim core CPU
with a dedicated firmware. It is a general purpose DMA controller supporting
16 independent channels and data can be moved from memory to memory or between
memory and paced latency critical real time targets.

[..]

V10 series updates remoteproc kconfig subsystem to use 'depends on' rather
than select for the remoteproc core. Remoteproc client drivers now need to
'depends on' for the core part.

This avoids the Kconfig recursive dependency errors and reliance on the DRM
patch included with the v9 series. Patches are also included for enabling the
remoteproc core in the multi_v7 defconfig. All patches for DRM subsystem have
been removed.

Note there is no longer a depedency with the DRM_VIRTIO_GPU driver in this series,
only remoteproc and dmaengine subsystems.

regards,

Peter.

Changes since v9:
 - Update remoteproc Kconfig to 'depends on'
 - Add remoteproc core to multi_v7 defconfig
 - Remove drm patches
 - rebase v4.8

Changes since v8:
 - Add MODULE_ALIAS (Vinod)
 - devm_kzalloc to devm_kcalloc (Vinod)
 - quisce tasklet initialised by vchan_init() (Vinod)
 - Don't make SLIM rproc user selectable (Bjorn)
 - slim_rproc: Ensure clocks enabled before firmware load (Peter)
 - Various code style nits / commit message change (Lee)
 - Separate patch for '\n' kconfig removal (Vinod)
 - Fix clock bug when booting without clk_ignore_unused param

Changes since v7:
 - Rebase on v4.8-rc3 (Peter)
 - Double check that len is <= mem[i].size (Bjorn)
 - Remove if (!fw) checks (Bjorn)
 - Omit reference from fw_ops struct (Bjorn)
 - Call rproc_del() before rproc_put() (Bjorn)
 - Fix some odd line breaks (Bjorn)
 - Rename SLIM_* and slim_* with st prefix (Bjorn)

Changes since v6:
 - Fix recursive Kconfig warning (Patrice)
 - Fix various Intel zero day compiler warnings
 - Remove all changes related to late firmware load (now relies on initramfs) (Peter)

Changes since v5:
 - Remove optional rsc table and go back to dummy resource table (Bjorn)
 - Retry rproc_fw_config_virtio() from rproc_boot() if previously failed (Peter)
 - Fixup some kbuild warnings
 - rebase on v4.7-rc5
 - Remove some patches which were merged via ASoC tree

Changes since v4:
 - Make rsc table optional in remoteproc (Peter)
 - Various fixups to STi audio DT nodes (Arnaud)
 - Bulk rename of xp70 to slim (Peter)
 - Update my email to @linaro.org (Lee)
 - rebase on v4.7-rc2 (Peter)
 - Don't make ST_SLIM_REMOTEPROC user selectable (Bjorn)
 - -EPROBE_DEFER if rproc_boot fails when allocating DMA channel (Arnaud / Peter)
 - Drop some unnecessary headers (Vinod / Bjorn)
 - Change to writel now we have several mappings (Bjorn)
 - Remove io_res, rproc, and some unused structure fields / #define (Bjorn)
 - put clks in error path, also put clks before rproc_put() (Bjorn)
 - Make enum less generic (Bjorn)
 - Make slim_rproc_alloc() return a st_slim_rproc reference (Bjorn)
 - Alphabetical naming in Kconfig & Makefile (Vinod)
 - Add FDMA prefix to REQ_CTRL* (Vinod)
 - Print ret on some error paths (Vinod)
 - Add some acked-by (Peter)

Changes since v3:
 - Remove elf loading code from fdma driver (Vinod)
 - Remove fdma_ prefix for clock names (Arnd)
 - Make _xlate use dma_get_any_channel rather than request_channel (Arnd)
 - Make a common function for _prep_ routines (Vinod)
 - Make driver depend on COMPILE_TEST (Arnd)
 - Remove unnecessary st_fdma_filter_fn (Arnd)
 - Enable FDMA as a module (Arnd)
 - Drop fdma_ clock prefix (Arnd)
 - Fix description as well as example for st, prefix (Arnd)
 - Remove string concatenation from fdma register macros to ease grep'ability (Arnd)
 - Add a XP70 rproc driver for ELF firmware loading and start/stop control (Peter)
 - Add myself as a author of the driver (Peter)

Changes since v2:
 - Change to dma-controller (Arnd)
 - Remove platform data header file and simplifiy code (Arnd)
 - Remove FW_LOADER_USER_HELPER_FALLBACK and rework firmware loading to device config (Vinod)
 - Use SET_RUNTIME_PM_OPS helpers (Vinod)
 - Remove fdma-id dt prop and use compatibles to generate different fdma firmware names (Arnd / Lee)
 - Add sti-asoc-card DT nodes and pinmux config for uniperif player & reader (Peter)
 - Update sti-asoc-card DT binding documentation (Peter)
 - Enable STi audio drivers in multi_v7_defconfig (Peter)

Changes since v1:
 - split into smaller patches for easier / faster review (Vinod)
 - new fill_hw_mode() with common code (Vinod)
 - new config_reqctrl() called from *_prep() instead of device_config cb (Vinod)
 - fdma-xbar support removed (Peter)
 - rework firmware name mechanism so fwname isn't in DT (Peter / Lee)
 - st_fdma_seg_to_mem can be static (Paul)
 - EXPORT_SYMBOL st_fdma_filter_fn not required (Paul)
 - s/channel/channels (vinod)
 - better describe "Must be <3>" (vinod)
 - sizeof(*ehdr) (vinod)
 - print values on error debug (vinod)
 - empty line (Vinod)
 - Update to -EIO (Vinod)
 - Make st_fdma tristate (Paul)
 - Remove __exit tag from .remove (Maxime)
 - Update MAINTAINERS rule to fdma* (Lee)
 - Unit address should match reg property (Lee)

Peter Griffin (11):
  remoteproc: st_slim_rproc: add a slimcore rproc driver
  MAINTAINERS: Add st slim core rproc driver to STi section.
  remoteproc: Update Kconfig setup to 'depends on REMOTEPROC'
  dmaengine: st_fdma: Add STMicroelectronics FDMA DT binding
    documentation
  dmaengine: st_fdma: Add STMicroelectronics FDMA driver header file
  dmaengine: st_fdma: Add STMicroelectronics FDMA engine driver support
  MAINTAINERS: Add FDMA driver files to STi section.
  ARM: multi_v7_defconfig: Enable remoteproc core
  ARM: multi_v7_defconfig: Enable st_remoteproc driver.
  ARM: multi_v7_defconfig: Enable STi FDMA driver
  ARM: multi_v7_defconfig: Enable STi and simple-card drivers.

 Documentation/devicetree/bindings/dma/st_fdma.txt |  87 +++
 MAINTAINERS                                       |   3 +
 arch/arm/configs/multi_v7_defconfig               |   6 +
 drivers/dma/Kconfig                               |  13 +
 drivers/dma/Makefile                              |   1 +
 drivers/dma/st_fdma.c                             | 899 ++++++++++++++++++++++
 drivers/dma/st_fdma.h                             | 249 ++++++
 drivers/remoteproc/Kconfig                        |  23 +-
 drivers/remoteproc/Makefile                       |   1 +
 drivers/remoteproc/st_slim_rproc.c                | 364 +++++++++
 include/linux/remoteproc/st_slim_rproc.h          |  58 ++
 11 files changed, 1696 insertions(+), 8 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/dma/st_fdma.txt
 create mode 100644 drivers/dma/st_fdma.c
 create mode 100644 drivers/dma/st_fdma.h
 create mode 100644 drivers/remoteproc/st_slim_rproc.c
 create mode 100644 include/linux/remoteproc/st_slim_rproc.h

-- 
1.9.1

^ permalink raw reply

* [PATCH] clk: nxp: clk-lpc18xx-ccu: Unmap region obtained by of_iomap
From: Joachim Eastwood @ 2016-10-08 10:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474367987-15808-1-git-send-email-arvind.yadav.cs@gmail.com>

Hi Arvind,

On 20 September 2016 at 12:39, Arvind Yadav <arvind.yadav.cs@gmail.com> wrote:
> From: Arvind Yadav <arvind.yadav.cs@gmail.com>
>
> Free memory mapping, if lpc18xx_ccu_init is not successful.
>
> Signed-off-by: Arvind Yadav <arvind.yadav.cs@gmail.com>

Acked-by: Joachim Eastwood <manabian@gmail.com>

One comment below:

> ---
>  drivers/clk/nxp/clk-lpc18xx-ccu.c |    5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c
> index f7136b9..27781b4 100644
> --- a/drivers/clk/nxp/clk-lpc18xx-ccu.c
> +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c
> @@ -277,12 +277,15 @@ static void __init lpc18xx_ccu_init(struct device_node *np)
>         }
>
>         clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
> -       if (!clk_data)
> +       if (!clk_data) {
> +               iounmap(reg_base);
>                 return;
> +       }

You could also move the kzalloc() at the beginning of the function to
save a few lines. I am fine either way.

>
>         clk_data->num = of_property_count_strings(np, "clock-names");
>         clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL);
>         if (!clk_data->name) {
> +               iounmap(reg_base);
>                 kfree(clk_data);
>                 return;
>         }


regards,
Joachim Eastwood

^ permalink raw reply

* [PATCH 1/3] firmware: add lpc18xx boot rom driver
From: Joachim Eastwood @ 2016-10-08 10:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <9fec6e3b-fcd9-2251-c4c9-5b3f4fdc3c70@mleia.com>

Hi Vladimir,

On 7 October 2016 at 03:42, Vladimir Zapolskiy <vz@mleia.com> wrote:
> Hi Joachim,
>
> On 13.09.2016 22:51, Joachim Eastwood wrote:
>> Firmware driver for the boot ROM found on all NXP LPC18xx/43xx
>> devices. This driver makes it possible to retrieve device specific
>> information from either the ROM via API calls or from OTP memory.
>>
>> The boot ROM contains several APIs for on-chip devices. Note that not
>> all APIs are available on all devices. The API to retrieve device
>> information and internal Flash programming (IAP) is only available on
>> devices with Flash. Flashless devices retrieve device information from
>> OTP memory. The CHIPID register in CREG (syscon) is used to check if
>> IAP is available.
>>
>> For now this driver is only used to expose device information via a
>> 'SoC device'. Linux API for the IAP and OTP will be added later. These
>> two APIs will be used by a Flash MTD driver and a OTP NVMEM driver to
>> program the memory.
>>
>> Signed-off-by: Joachim Eastwood <manabian@gmail.com>
>
> please feel free to add to the series my
>
>   Tested-by: Vladimir Zapolskiy <vz@mleia.com>
>   Reviewed-by: Vladimir Zapolskiy <vz@mleia.com>
>
> I tested the change on a board powered by LPC4357.

Thanks!

> Nevertheless I have some nitpicks for your consideration.
>
> And the first one is that I'm not totally convinced that drivers/firmware
> is the proper place for the driver, I tend to think that it might be
> a good time to create drivers/soc/nxp/

As the driver stand now, I agree with you. But I do plan to add
firmware functionally later for Flash programming which will be used
for a MTD driver and OTP programming for the OTP nvmem driver. This is
also stated in the cover letter.

See this patch for the Flash ROM API:
https://github.com/manabian/linux-lpc/commit/f33b03e7b47515a1d48b42f8bb1476ad5c67025b

The reason why I want to merge a simple driver now is:
i) The driver useful for SoC identification
ii ) It's easier to review and the Flash API is really a separate thing
iii ) The Flash API can be reviewed/merged together with its user (MTD-driver)


>> ---
>>  drivers/firmware/Kconfig            |  12 ++
>>  drivers/firmware/Makefile           |   1 +
>>  drivers/firmware/nxp_lpc_boot_rom.c | 411 ++++++++++++++++++++++++++++++++++++
>>  3 files changed, 424 insertions(+)
>>  create mode 100644 drivers/firmware/nxp_lpc_boot_rom.c
>>
>
> [snip]
>
>> +/* LPC18xx/43xx CREG (syscon) defines */
>> +#define LPC18XX_CREG_CHIPID          0x200
>> +#define LPC18XX_FLASH_CHIPID0                0x4284e02b
>> +#define LPC18XX_FLASH_CHIPID1                0x7284e02b
>> +#define LPC18XX_FLASHLESS_CHIPID0    0x5284e02b
>> +#define LPC18XX_FLASHLESS_CHIPID1    0x6284e02b
>> +#define LPC43XX_FLASH_CHIPID0                0x4906002b
>> +#define LPC43XX_FLASH_CHIPID1                0x7906002b
>> +#define LPC43XX_FLASHLESS_CHIPID0    0x5906002b
>> +#define LPC43XX_FLASHLESS_CHIPID1    0x6906002b
>> +
>> +#define NXP_PART_LPC(_num, _id0, _id1, _sz0, _sz1)   \
>> +     {                                               \
>> +             .name = "LPC"#_num,                     \
>> +             .id[0] = _id0, .id[1] = _id1,           \
>> +             .flash_size[0] = _sz0 * 1024,           \
>> +             .flash_size[1] = _sz1 * 1024,           \
>> +     }
>> +
>> +
>
> checkpatch complains:
>
> CHECK: Please don't use multiple blank lines
> #145: FILE: drivers/firmware/nxp_lpc_boot_rom.c:75:

I'll fix up.

>
>> +struct nxp_lpc_part {
>> +     const char *name;
>> +     u16 flash_size[2];
>
> flash_size[2] is set by NXP_PART_LPC() macro but unused, however
> in my build environment (W=1 and sparse checks) I get tons of
> legitimate compile time warnings:

flash_size is currently unused, but will be used by the MTD via the
Flash ROM API. I only kept in this patch to avoid the churn later, but
if it causes warnings I'll remove it.

> drivers/firmware/nxp_lpc_boot_rom.c:93:9: warning: cast truncates bits from constant value (80000 becomes 0)
> drivers/firmware/nxp_lpc_boot_rom.c:93:9: warning: cast truncates bits from constant value (80000 becomes 0)
> drivers/firmware/nxp_lpc_boot_rom.c:94:9: warning: cast truncates bits from constant value (80000 becomes 0)
> drivers/firmware/nxp_lpc_boot_rom.c:94:9: warning: cast truncates bits from constant value (80000 becomes 0)
> ....
>
> drivers/firmware/nxp_lpc_boot_rom.c:93:2: warning: large integer implicitly truncated to unsigned type [-Woverflow]
>    NXP_PART_LPC(1857,  0xf001d830, 0x00, 512, 512),
>    ^
> drivers/firmware/nxp_lpc_boot_rom.c:93:2: warning: large integer implicitly truncated to unsigned type [-Woverflow]
> drivers/firmware/nxp_lpc_boot_rom.c:94:2: warning: large integer implicitly truncated to unsigned type [-Woverflow]
>    NXP_PART_LPC(18S57, 0xf001d860, 0x00, 512, 512),
>    ^
> ....
>
> and so on.
>
> Please consider either to store flash size in kB or change storage type to u32.

This was a last minute change and I didn't notice before sending it. I
have already fixed this up in my tree.


>> +     u32 id[2];
>> +};
>> +
>> +static const struct nxp_lpc_part nxp_lpc_parts[] = {
>> +     /* LPC18xx Flashless parts */
>> +     NXP_PART_LPC(1850,  0xf000d830, 0x00,   0,   0),
>> +     NXP_PART_LPC(18S50, 0xf000d860, 0x00,   0,   0),
>> +     NXP_PART_LPC(1830,  0xf000da30, 0x00,   0,   0),
>> +     NXP_PART_LPC(18S30, 0xf000da60, 0x00,   0,   0),
>> +     NXP_PART_LPC(1820,  0xf00adb3c, 0x00,   0,   0),
>> +     NXP_PART_LPC(18S20, 0xf00adb6c, 0x00,   0,   0),
>> +     NXP_PART_LPC(1810,  0xf00b5b3f, 0x00,   0,   0),
>> +     NXP_PART_LPC(18S10, 0xf00b5b6f, 0x00,   0,   0),
>> +     /* LPC18xx Flash parts */
>> +     NXP_PART_LPC(1857,  0xf001d830, 0x00, 512, 512),
>> +     NXP_PART_LPC(18S57, 0xf001d860, 0x00, 512, 512),
>> +     NXP_PART_LPC(1853,  0xf001d830, 0x44, 256, 256),
>> +     NXP_PART_LPC(1837,  0xf001da30, 0x00, 512, 512),
>> +     NXP_PART_LPC(18S37, 0xf001d860, 0x00, 512, 512),
>> +     NXP_PART_LPC(1833,  0xf001da30, 0x44, 256, 256),
>> +     NXP_PART_LPC(1827,  0xf001db3c, 0x00, 512, 512),
>> +     NXP_PART_LPC(1825,  0xf001db3c, 0x22, 384, 384),
>> +     NXP_PART_LPC(1823,  0xf00bdb3c, 0x44, 256, 256),
>> +     NXP_PART_LPC(1822,  0xf00bdb3c, 0x80, 512,   0),
>> +     NXP_PART_LPC(1817,  0xf001db3f, 0x00, 512, 512),
>> +     NXP_PART_LPC(1815,  0xf001db3f, 0x22, 384, 384),
>> +     NXP_PART_LPC(1813,  0xf00bdb3f, 0x44, 256, 256),
>> +     NXP_PART_LPC(1812,  0xf00bdb3f, 0x80, 512,   0),
>> +     /* LPC43xx Flashless parts */
>> +     NXP_PART_LPC(4370,  0x00000030, 0x00,   0,   0), /* LBGA256 */
>> +     NXP_PART_LPC(4370,  0x00000230, 0x00,   0,   0), /* TFBGA100 */
>> +     NXP_PART_LPC(43S70, 0x00000060, 0x00,   0,   0),
>> +     NXP_PART_LPC(4350,  0xa0000830, 0x00,   0,   0),
>> +     NXP_PART_LPC(43S50, 0xa0000860, 0x00,   0,   0),
>> +     NXP_PART_LPC(4330,  0xa0000a30, 0x00,   0,   0),
>> +     NXP_PART_LPC(43S30, 0xa0000a60, 0x00,   0,   0),
>> +     NXP_PART_LPC(4320,  0xa000cb3c, 0x00,   0,   0),
>> +     NXP_PART_LPC(43S20, 0xa000cb6c, 0x00,   0,   0),
>> +     NXP_PART_LPC(4310,  0xa00acb3f, 0x00,   0,   0),
>> +     /* LPC43xx parts with Flash */
>> +     NXP_PART_LPC(4367,  0x8001c030, 0x00, 512, 512),
>> +     NXP_PART_LPC(43S67, 0x8001c060, 0x00, 512, 512),
>> +     NXP_PART_LPC(4357,  0xa001c830, 0x00, 512, 512),
>> +     NXP_PART_LPC(43S57, 0xa001c860, 0x00, 512, 512), /* LBGA256 */
>> +     NXP_PART_LPC(43S57, 0xa001ca60, 0x00, 512, 512), /* LQFP208 */
>> +     NXP_PART_LPC(4353,  0xa001c830, 0x44, 256, 256),
>> +     NXP_PART_LPC(4337,  0xa001ca30, 0x00, 512, 512),
>> +     NXP_PART_LPC(43S37, 0xa001ca60, 0x00, 512, 512),
>> +     NXP_PART_LPC(4333,  0xa001ca30, 0x44, 256, 256),
>> +     NXP_PART_LPC(4327,  0xa001cb3c, 0x00, 512, 512),
>> +     NXP_PART_LPC(4325,  0xa001cb3c, 0x22, 384, 384),
>> +     NXP_PART_LPC(4323,  0xa00bcb3c, 0x44, 256, 256),
>> +     NXP_PART_LPC(4322,  0xa00bcb3c, 0x80, 512,   0),
>> +     NXP_PART_LPC(4317,  0xa001cb3f, 0x00, 512, 512),
>> +     NXP_PART_LPC(4315,  0xa001cb3f, 0x22, 384, 384),
>> +     NXP_PART_LPC(4313,  0xa00bcb3f, 0x44, 256, 256),
>> +     NXP_PART_LPC(4312,  0xa00bcb3f, 0x80, 512,   0),
>> +};
>> +
>> +struct iap_rom {
>> +     void (*entry)(u32 *, u32 *);
>> +};
>
> Here it might be simpler to declare a typedef instead of a struct.

This is done using a struct to keep it consistent with the other ROM
APIs that will be added later. The IAP is kinda special since it only
has one function while most other APIs have several functions.


regards,
Joachim Eastwood

^ permalink raw reply

* [PATCH 3/3] ARM: dts: lpc18xx: add boot rom node
From: Joachim Eastwood @ 2016-10-08 10:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <7d4f9e48-e93a-db9e-fba0-e1b64e7056d7@mleia.com>

Hi Vladimir,

On 7 October 2016 at 03:50, Vladimir Zapolskiy <vz@mleia.com> wrote:
> Hi Joachim,
>
> On 13.09.2016 22:51, Joachim Eastwood wrote:
>> Add node for the boot ROM found on all NXP LPC18xx/43xx devices.
>>
>> Signed-off-by: Joachim Eastwood <manabian@gmail.com>
>> ---
>>  arch/arm/boot/dts/lpc18xx.dtsi | 8 ++++++++
>>  1 file changed, 8 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/lpc18xx.dtsi b/arch/arm/boot/dts/lpc18xx.dtsi
>> index 631e6f6c..9f94f3e 100644
>> --- a/arch/arm/boot/dts/lpc18xx.dtsi
>> +++ b/arch/arm/boot/dts/lpc18xx.dtsi
>> @@ -81,6 +81,14 @@
>>                       status = "disabled";
>>               };
>>
>> +             boot_rom: firmware at 10400000 {
>> +                     compatible = "nxp,lpc1850-boot-rom";
>> +                     reg = <0x10400000 0x10000>;
>> +                     syscon = <&creg>;
>> +                     nvmem-cells = <&part_id>;
>
> nitpicking, this change has a compile time dependency on yours
> "nvmem: add lpc18xx OTP memory driver" series, I haven't noticed
> this info stated, but it may be an overlook on my part.

It's not really a hard dependency and I will ensure that the DTS
changes comes in the correct order.


>> +                     nvmem-cell-names = "PartID";
>> +             };
>> +
>>               dmac: dma-controller at 40002000 {
>>                       compatible = "arm,pl080", "arm,primecell";
>>                       arm,primecell-periphid = <0x00041080>;
>>
>
> Tested-by: Vladimir Zapolskiy <vz@mleia.com>
> Reviewed-by: Vladimir Zapolskiy <vz@mleia.com>

Thanks for testing and going through the changes.


regards,
Joachim Eastwood

^ permalink raw reply

* [PATCH 1/3] nvmem: add NXP LPC18xx OTP driver
From: Joachim Eastwood @ 2016-10-08 10:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <8ad4d5fb-0672-fbd7-6076-a91c89dee132@mleia.com>

Hi Vladimir,

On 7 October 2016 at 03:44, Vladimir Zapolskiy <vz@mleia.com> wrote:
> Hi Joachim, Srinivas,
>
> On 13.09.2016 19:12, Joachim Eastwood wrote:
>> Add simple read only driver for the internal OTP (One Time Programmable)
>> memory found on all NXP LPC18xx and LPC43xx devices.
>>
>> The OTP memory is split into 4 banks each with 4 32-bits word. Some of
>> the banks contain predefined data while others are for general purpose
>> and user programmable via the OTP API in ROM. Note that writing to the
>> OTP memory is not yet supported.
>>
>> Signed-off-by: Joachim Eastwood <manabian@gmail.com>
>> ---
>
> please feel free to add my
>
>   Tested-by: Vladimir Zapolskiy <vz@mleia.com>

Thanks for testing!


regards,
Joachim Eastwood

^ permalink raw reply

* [PATCH] ARM: dts: pxa: fix gpio0 and gpio1 interrupts
From: Robert Jarzmik @ 2016-10-08  9:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1474874531-26703-1-git-send-email-robert.jarzmik@free.fr>

Robert Jarzmik <robert.jarzmik@free.fr> writes:

> Since gpio-pxa was redesigned to differenciate gpio0, gpio1 and the
> gpio-mux interrupt as in the hardware IP, the device-tree description
> should be amended so that interrupts from gpio0 and gpio1 can be mapped
> to consumers.
>
> This is especially true on lubbock and mainstone devices where gpio0 is
> multiplexed on pxa_cplds for ethernet, sa1111, usb udc, and other
> devices.
>
> Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Applied to pxa/dt.

Cheers.

-- 
Robert

^ permalink raw reply

* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Ziji Hu @ 2016-10-08  9:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <25146173-d98b-f346-b333-4d7466960496@rock-chips.com>

Hi Shawn,

On 2016/10/8 10:44, Shawn Lin wrote:
> ? 2016/10/7 23:22, Gregory CLEMENT ??:
>> From: Ziji Hu <huziji@marvell.com>
>>
>> Marvell Xenon eMMC/SD/SDIO Host Controller contains PHY.
>> Three types of PHYs are supported.
>>
>> Add support to multiple types of PHYs init and configuration.
>> Add register definitions of PHYs.
>>
>> Signed-off-by: Hu Ziji <huziji@marvell.com>
>> Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
>> ---
>>  MAINTAINERS                        |    1 +-
>>  drivers/mmc/host/Makefile          |    2 +-
>>  drivers/mmc/host/sdhci-xenon-phy.c | 1141 +++++++++++++++++++++++++++++-
>>  drivers/mmc/host/sdhci-xenon-phy.h |  157 ++++-
>>  drivers/mmc/host/sdhci-xenon.c     |    4 +-
>>  drivers/mmc/host/sdhci-xenon.h     |   17 +-
>>  6 files changed, 1321 insertions(+), 1 deletion(-)
>>  create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c
>>  create mode 100644 drivers/mmc/host/sdhci-xenon-phy.h
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 859420e5dfd3..b5673c2ee5f2 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -7583,6 +7583,7 @@ M:    Ziji Hu <huziji@marvell.com>
>>  L:    linux-mmc at vger.kernel.org
>>  S:    Supported
>>  F:    drivers/mmc/host/sdhci-xenon.*
>> +F:    drivers/mmc/host/sdhci-xenon-phy.*
> 
> drivers/mmc/host/sdhci-xenon* shoube enough
> 
>>  F:    Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
>>
>>  MATROX FRAMEBUFFER DRIVER
>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> index 75eaf743486c..4f2854556ff7 100644
>> --- a/drivers/mmc/host/Makefile
>> +++ b/drivers/mmc/host/Makefile
>> @@ -82,4 +82,4 @@ ifeq ($(CONFIG_CB710_DEBUG),y)
>>  endif
>>
>>  obj-$(CONFIG_MMC_SDHCI_XENON)    += sdhci-xenon-driver.o
>> -sdhci-xenon-driver-y        += sdhci-xenon.o
>> +sdhci-xenon-driver-y        += sdhci-xenon.o sdhci-xenon-phy.o
>> diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c
>> new file mode 100644
>> index 000000000000..4eb8fea1bec9
>> --- /dev/null
>> +++ b/drivers/mmc/host/sdhci-xenon-phy.c
> 
> Well, it's legit to use phy API and move your phy
> operations to PHY subsystem. :)
> 

    Actually we tried to put the PHY code into Linux PHY framework.
    But it cannot fit in Linux common PHY framework.

    Our Xenon SDHC PHY register is a part of Xenon SDHC register set.
    Besides, during MMC initialization, MMC sequence has to call several PHY functions to complete timing setting.
    In those PHY setting functions, they have to access SDHC register and know current MMC setting, such as bus width, clock frequency and speed mode.
    As a result, we have to implement PHY under MMC directory.

    Thank you.

Best regards,
Hu Ziji    

>> @@ -0,0 +1,1141 @@
>> +/*
>> + * PHY support for Xenon SDHC
>> + *
>> + * Copyright (C) 2016 Marvell, All Rights Reserved.
>> + *
>> + * Author:    Hu Ziji <huziji@marvell.com>
>> + * Date:    2016-8-24
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/slab.h>
>> +#include <linux/delay.h>
>> +#include <linux/of_address.h>
>> +#include <linux/mmc/host.h>
>> +#include <linux/mmc/mmc.h>
>> +#include <linux/mmc/card.h>
>> +#include <linux/mmc/sdio.h>
>> +
>> +#include "sdhci.h"
>> +#include "sdhci-pltfm.h"
>> +#include "sdhci-xenon.h"
>> +
>> +static const char * const phy_types[] = {
>> +    "sdh phy",
>> +    "emmc 5.0 phy",
>> +    "emmc 5.1 phy"
>> +};
>> +
>> +enum phy_type_enum {
>> +    SDH_PHY,
>> +    EMMC_5_0_PHY,
>> +    EMMC_5_1_PHY,
>> +    NR_PHY_TYPES
>> +};
>> +
>> +struct soc_pad_ctrl_table {
>> +    const char *soc;
>> +    void (*set_soc_pad)(struct sdhci_host *host,
>> +                unsigned char signal_voltage);
>> +};
>> +
>> +struct soc_pad_ctrl {
>> +    /* Register address of SOC PHY PAD ctrl */
>> +    void __iomem    *reg;
>> +    /* SOC PHY PAD ctrl type */
>> +    enum soc_pad_ctrl_type pad_type;
>> +    /* SOC specific operation to set SOC PHY PAD */
>> +    void (*set_soc_pad)(struct sdhci_host *host,
>> +                unsigned char signal_voltage);
>> +};
>> +
>> +static struct xenon_emmc_phy_regs  xenon_emmc_5_0_phy_regs = {
>> +    .timing_adj    = EMMC_5_0_PHY_TIMING_ADJUST,
>> +    .func_ctrl    = EMMC_5_0_PHY_FUNC_CONTROL,
>> +    .pad_ctrl    = EMMC_5_0_PHY_PAD_CONTROL,
>> +    .pad_ctrl2    = EMMC_5_0_PHY_PAD_CONTROL2,
>> +    .dll_ctrl    = EMMC_5_0_PHY_DLL_CONTROL,
>> +    .logic_timing_adj = EMMC_5_0_PHY_LOGIC_TIMING_ADJUST,
>> +    .delay_mask    = EMMC_5_0_PHY_FIXED_DELAY_MASK,
>> +    .dll_update    = DLL_UPDATE_STROBE_5_0,
>> +};
>> +
>> +static struct xenon_emmc_phy_regs  xenon_emmc_5_1_phy_regs = {
>> +    .timing_adj    = EMMC_PHY_TIMING_ADJUST,
>> +    .func_ctrl    = EMMC_PHY_FUNC_CONTROL,
>> +    .pad_ctrl    = EMMC_PHY_PAD_CONTROL,
>> +    .pad_ctrl2    = EMMC_PHY_PAD_CONTROL2,
>> +    .dll_ctrl    = EMMC_PHY_DLL_CONTROL,
>> +    .logic_timing_adj = EMMC_PHY_LOGIC_TIMING_ADJUST,
>> +    .delay_mask    = EMMC_PHY_FIXED_DELAY_MASK,
>> +    .dll_update    = DLL_UPDATE,
>> +};
>> +
>> +static int xenon_delay_adj_test(struct mmc_card *card);
>> +
>> +/*
>> + * eMMC PHY configuration and operations
>> + */
>> +struct emmc_phy_params {
>> +    bool    slow_mode;
>> +
>> +    u8    znr;
>> +    u8    zpr;
>> +
>> +    /* Nr of consecutive Sampling Points of a Valid Sampling Window */
>> +    u8    nr_tun_times;
>> +    /* Divider for calculating Tuning Step */
>> +    u8    tun_step_divider;
>> +
>> +    struct soc_pad_ctrl pad_ctrl;
>> +};
>> +
>> +static void xenon_emmc_phy_strobe_delay_adj(struct sdhci_host *host,
>> +                        struct mmc_card *card);
>> +static int xenon_emmc_phy_fix_sampl_delay_adj(struct sdhci_host *host,
>> +                          struct mmc_card *card);
>> +static void xenon_emmc_phy_set(struct sdhci_host *host,
>> +                   unsigned char timing);
>> +static void xenon_emmc_set_soc_pad(struct sdhci_host *host,
>> +                   unsigned char signal_voltage);
>> +
>> +static const struct xenon_phy_ops emmc_phy_ops = {
>> +    .strobe_delay_adj = xenon_emmc_phy_strobe_delay_adj,
>> +    .fix_sampl_delay_adj = xenon_emmc_phy_fix_sampl_delay_adj,
>> +    .phy_set = xenon_emmc_phy_set,
>> +    .set_soc_pad = xenon_emmc_set_soc_pad,
>> +};
>> +
>> +static int alloc_emmc_phy(struct sdhci_xenon_priv *priv)
>> +{
>> +    struct emmc_phy_params *params;
>> +
>> +    params = kzalloc(sizeof(*params), GFP_KERNEL);
>> +    if (!params)
>> +        return -ENOMEM;
>> +
>> +    priv->phy_params = params;
>> +    priv->phy_ops = &emmc_phy_ops;
>> +    if (priv->phy_type == EMMC_5_0_PHY)
>> +        priv->emmc_phy_regs = &xenon_emmc_5_0_phy_regs;
>> +    else
>> +        priv->emmc_phy_regs = &xenon_emmc_5_1_phy_regs;
>> +
>> +    return 0;
>> +}
>> +
>> +static int xenon_emmc_phy_init(struct sdhci_host *host)
>> +{
>> +    u32 reg;
>> +    u32 wait, clock;
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
>> +
>> +    reg = sdhci_readl(host, phy_regs->timing_adj);
>> +    reg |= PHY_INITIALIZAION;
>> +    sdhci_writel(host, reg, phy_regs->timing_adj);
>> +
>> +    /* Add duration of FC_SYNC_RST */
>> +    wait = ((reg >> FC_SYNC_RST_DURATION_SHIFT) &
>> +            FC_SYNC_RST_DURATION_MASK);
>> +    /* Add interval between FC_SYNC_EN and FC_SYNC_RST */
>> +    wait += ((reg >> FC_SYNC_RST_EN_DURATION_SHIFT) &
>> +            FC_SYNC_RST_EN_DURATION_MASK);
>> +    /* Add duration of asserting FC_SYNC_EN */
>> +    wait += ((reg >> FC_SYNC_EN_DURATION_SHIFT) &
>> +            FC_SYNC_EN_DURATION_MASK);
>> +    /* Add duration of waiting for PHY */
>> +    wait += ((reg >> WAIT_CYCLE_BEFORE_USING_SHIFT) &
>> +            WAIT_CYCLE_BEFORE_USING_MASK);
>> +    /* 4 addtional bus clock and 4 AXI bus clock are required */
>> +    wait += 8;
>> +    wait <<= 20;
>> +
>> +    clock = host->clock;
>> +    if (!clock)
>> +        /* Use the possibly slowest bus frequency value */
>> +        clock = LOWEST_SDCLK_FREQ;
>> +    /* get the wait time */
>> +    wait /= clock;
>> +    wait++;
>> +    /* wait for host eMMC PHY init completes */
>> +    udelay(wait);
>> +
>> +    reg = sdhci_readl(host, phy_regs->timing_adj);
>> +    reg &= PHY_INITIALIZAION;
>> +    if (reg) {
>> +        dev_err(mmc_dev(host->mmc), "eMMC PHY init cannot complete after %d us\n",
>> +            wait);
>> +        return -ETIMEDOUT;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +#define ARMADA_3700_SOC_PAD_1_8V    0x1
>> +#define ARMADA_3700_SOC_PAD_3_3V    0x0
>> +
>> +static void armada_3700_soc_pad_voltage_set(struct sdhci_host *host,
>> +                        unsigned char signal_voltage)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct emmc_phy_params *params = priv->phy_params;
>> +
>> +    if (params->pad_ctrl.pad_type == SOC_PAD_FIXED_1_8V) {
>> +        writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
>> +    } else if (params->pad_ctrl.pad_type == SOC_PAD_SD) {
>> +        if (signal_voltage == MMC_SIGNAL_VOLTAGE_180)
>> +            writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
>> +        else if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
>> +            writel(ARMADA_3700_SOC_PAD_3_3V, params->pad_ctrl.reg);
>> +    }
>> +}
>> +
>> +static void xenon_emmc_set_soc_pad(struct sdhci_host *host,
>> +                   unsigned char signal_voltage)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct emmc_phy_params *params = priv->phy_params;
>> +
>> +    if (!params->pad_ctrl.reg)
>> +        return;
>> +
>> +    if (params->pad_ctrl.set_soc_pad)
>> +        params->pad_ctrl.set_soc_pad(host, signal_voltage);
>> +}
>> +
>> +static int emmc_phy_set_fix_sampl_delay(struct sdhci_host *host,
>> +                    unsigned int delay,
>> +                    bool invert,
>> +                    bool delay_90_degree)
>> +{
>> +    u32 reg;
>> +    unsigned long flags;
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
>> +    int ret = 0;
>> +
>> +    spin_lock_irqsave(&host->lock, flags);
>> +
>> +    /* Setup Sampling fix delay */
>> +    reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
>> +    reg &= ~phy_regs->delay_mask;
>> +    reg |= delay & phy_regs->delay_mask;
>> +    sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
>> +
>> +    if (priv->phy_type == EMMC_5_0_PHY) {
>> +        /* set 90 degree phase if necessary */
>> +        reg &= ~DELAY_90_DEGREE_MASK_EMMC5;
>> +        reg |= (delay_90_degree << DELAY_90_DEGREE_SHIFT_EMMC5);
>> +        sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
>> +    }
>> +
>> +    /* Disable SDCLK */
>> +    reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
>> +    reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
>> +    sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
>> +
>> +    udelay(200);
>> +
>> +    if (priv->phy_type == EMMC_5_1_PHY) {
>> +        /* set 90 degree phase if necessary */
>> +        reg = sdhci_readl(host, EMMC_PHY_FUNC_CONTROL);
>> +        reg &= ~ASYNC_DDRMODE_MASK;
>> +        reg |= (delay_90_degree << ASYNC_DDRMODE_SHIFT);
>> +        sdhci_writel(host, reg, EMMC_PHY_FUNC_CONTROL);
>> +    }
>> +
>> +    /* Setup Inversion of Sampling edge */
>> +    reg = sdhci_readl(host, phy_regs->timing_adj);
>> +    reg &= ~SAMPL_INV_QSP_PHASE_SELECT;
>> +    reg |= (invert << SAMPL_INV_QSP_PHASE_SELECT_SHIFT);
>> +    sdhci_writel(host, reg, phy_regs->timing_adj);
>> +
>> +    /* Enable SD internal clock */
>> +    ret = enable_xenon_internal_clk(host);
>> +    if (ret)
>> +        goto out;
>> +
>> +    /* Enable SDCLK */
>> +    reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
>> +    reg |= SDHCI_CLOCK_CARD_EN;
>> +    sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
>> +
>> +    udelay(200);
>> +
>> +    /*
>> +     * Has to re-initialize eMMC PHY here to active PHY
>> +     * because later get status cmd will be issued.
>> +     */
>> +    ret = xenon_emmc_phy_init(host);
>> +
>> +out:
>> +    spin_unlock_irqrestore(&host->lock, flags);
>> +    return ret;
>> +}
>> +
>> +static int emmc_phy_do_fix_sampl_delay(struct sdhci_host *host,
>> +                       struct mmc_card *card,
>> +                       unsigned int delay,
>> +                       bool invert, bool quarter)
>> +{
>> +    int ret;
>> +
>> +    emmc_phy_set_fix_sampl_delay(host, delay, invert, quarter);
>> +
>> +    ret = xenon_delay_adj_test(card);
>> +    if (ret) {
>> +        dev_dbg(mmc_dev(host->mmc),
>> +            "fail when sampling fix delay = %d, phase = %d degree\n",
>> +            delay, invert * 180 + quarter * 90);
>> +        return -1;
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int xenon_emmc_phy_fix_sampl_delay_adj(struct sdhci_host *host,
>> +                          struct mmc_card *card)
>> +{
>> +    enum sampl_fix_delay_phase phase;
>> +    int idx, nr_pair;
>> +    int ret;
>> +    unsigned int delay;
>> +    unsigned int min_delay, max_delay;
>> +    bool invert, quarter;
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
>> +    u32 coarse_step, fine_step;
>> +    const enum sampl_fix_delay_phase delay_edge[] = {
>> +        PHASE_0_DEGREE,
>> +        PHASE_180_DEGREE,
>> +        PHASE_90_DEGREE,
>> +        PHASE_270_DEGREE
>> +    };
>> +
>> +    coarse_step = phy_regs->delay_mask >> 1;
>> +    fine_step = coarse_step >> 2;
>> +
>> +    nr_pair = ARRAY_SIZE(delay_edge);
>> +
>> +    for (idx = 0; idx < nr_pair; idx++) {
>> +        phase = delay_edge[idx];
>> +        invert = (phase & 0x2) ? true : false;
>> +        quarter = (phase & 0x1) ? true : false;
>> +
>> +        /* increase delay value to get fix delay */
>> +        for (min_delay = 0;
>> +             min_delay <= phy_regs->delay_mask;
>> +             min_delay += coarse_step) {
>> +            ret = emmc_phy_do_fix_sampl_delay(host, card, min_delay,
>> +                              invert, quarter);
>> +            if (!ret)
>> +                break;
>> +        }
>> +
>> +        if (ret) {
>> +            dev_dbg(mmc_dev(host->mmc),
>> +                "Fail to set Sampling Fixed Delay with phase = %d degree\n",
>> +                phase * 90);
>> +            continue;
>> +        }
>> +
>> +        for (max_delay = min_delay + fine_step;
>> +             max_delay < phy_regs->delay_mask;
>> +             max_delay += fine_step) {
>> +            ret = emmc_phy_do_fix_sampl_delay(host, card, max_delay,
>> +                              invert, quarter);
>> +            if (ret) {
>> +                max_delay -= fine_step;
>> +                break;
>> +            }
>> +        }
>> +
>> +        if (!ret) {
>> +            ret = emmc_phy_do_fix_sampl_delay(host, card,
>> +                              phy_regs->delay_mask,
>> +                              invert, quarter);
>> +            if (!ret)
>> +                max_delay = phy_regs->delay_mask;
>> +        }
>> +
>> +        /*
>> +         * Sampling Fixed Delay line window should be large enough,
>> +         * thus the sampling point (the middle of the window)
>> +         * can work when environment varies.
>> +         * However, there is no clear conclusion how large the window
>> +         * should be.
>> +         */
>> +        if ((max_delay - min_delay) <=
>> +            EMMC_PHY_FIXED_DELAY_WINDOW_MIN) {
>> +            dev_info(mmc_dev(host->mmc),
>> +                 "The window size %d with phase = %d degree is too small\n",
>> +                 max_delay - min_delay, phase * 90);
>> +            continue;
>> +        }
>> +
>> +        delay = (min_delay + max_delay) / 2;
>> +        emmc_phy_set_fix_sampl_delay(host, delay, invert, quarter);
>> +        dev_dbg(mmc_dev(host->mmc),
>> +            "sampling fix delay = %d with phase = %d degree\n",
>> +            delay, phase * 90);
>> +        return 0;
>> +    }
>> +
>> +    return -EIO;
>> +}
>> +
>> +static int xenon_emmc_phy_enable_dll(struct sdhci_host *host)
>> +{
>> +    u32 reg;
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
>> +    u8 timeout;
>> +
>> +    if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR))
>> +        return -EINVAL;
>> +
>> +    reg = sdhci_readl(host, phy_regs->dll_ctrl);
>> +    if (reg & DLL_ENABLE)
>> +        return 0;
>> +
>> +    /* Enable DLL */
>> +    reg = sdhci_readl(host, phy_regs->dll_ctrl);
>> +    reg |= (DLL_ENABLE | DLL_FAST_LOCK);
>> +
>> +    /*
>> +     * Set Phase as 90 degree, which is most common value.
>> +     * Might set another value if necessary.
>> +     * The granularity is 1 degree.
>> +     */
>> +    reg &= ~((DLL_PHASE_MASK << DLL_PHSEL0_SHIFT) |
>> +            (DLL_PHASE_MASK << DLL_PHSEL1_SHIFT));
>> +    reg |= ((DLL_PHASE_90_DEGREE << DLL_PHSEL0_SHIFT) |
>> +            (DLL_PHASE_90_DEGREE << DLL_PHSEL1_SHIFT));
>> +
>> +    reg &= ~DLL_BYPASS_EN;
>> +    reg |= phy_regs->dll_update;
>> +    if (priv->phy_type == EMMC_5_1_PHY)
>> +        reg &= ~DLL_REFCLK_SEL;
>> +    sdhci_writel(host, reg, phy_regs->dll_ctrl);
>> +
>> +    /* Wait max 32 ms */
>> +    timeout = 32;
>> +    while (!(sdhci_readw(host, SDHC_SLOT_EXT_PRESENT_STATE) & LOCK_STATE)) {
>> +        if (!timeout) {
>> +            dev_err(mmc_dev(host->mmc), "Wait for DLL Lock time-out\n");
>> +            return -ETIMEDOUT;
>> +        }
>> +        timeout--;
>> +        mdelay(1);
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int __emmc_phy_config_tuning(struct sdhci_host *host)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct emmc_phy_params *params = priv->phy_params;
>> +    u32 reg, tuning_step;
>> +    int ret;
>> +    unsigned long flags;
>> +
>> +    if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR))
>> +        return -EINVAL;
>> +
>> +    spin_lock_irqsave(&host->lock, flags);
>> +
>> +    ret = xenon_emmc_phy_enable_dll(host);
>> +    if (ret) {
>> +        spin_unlock_irqrestore(&host->lock, flags);
>> +        return ret;
>> +    }
>> +
>> +    reg = sdhci_readl(host, SDHC_SLOT_DLL_CUR_DLY_VAL);
>> +    tuning_step = reg / params->tun_step_divider;
>> +    if (unlikely(tuning_step > TUNING_STEP_MASK)) {
>> +        dev_warn(mmc_dev(host->mmc),
>> +             "HS200 TUNING_STEP %d is larger than MAX value\n",
>> +             tuning_step);
>> +        tuning_step = TUNING_STEP_MASK;
>> +    }
>> +
>> +    reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
>> +    reg &= ~(TUN_CONSECUTIVE_TIMES_MASK << TUN_CONSECUTIVE_TIMES_SHIFT);
>> +    reg |= (params->nr_tun_times << TUN_CONSECUTIVE_TIMES_SHIFT);
>> +    reg &= ~(TUNING_STEP_MASK << TUNING_STEP_SHIFT);
>> +    reg |= (tuning_step << TUNING_STEP_SHIFT);
>> +    sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
>> +
>> +    spin_unlock_irqrestore(&host->lock, flags);
>> +    return 0;
>> +}
>> +
>> +static int xenon_emmc_phy_config_tuning(struct sdhci_host *host)
>> +{
>> +    return __emmc_phy_config_tuning(host);
>> +}
>> +
>> +static void xenon_emmc_phy_strobe_delay_adj(struct sdhci_host *host,
>> +                        struct mmc_card *card)
>> +{
>> +    u32 reg;
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    unsigned long flags;
>> +
>> +    if (host->clock <= MMC_HIGH_52_MAX_DTR)
>> +        return;
>> +
>> +    dev_dbg(mmc_dev(host->mmc), "starts HS400 strobe delay adjustment\n");
>> +
>> +    spin_lock_irqsave(&host->lock, flags);
>> +
>> +    xenon_emmc_phy_enable_dll(host);
>> +
>> +    /* Enable SDHC Data Strobe */
>> +    reg = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
>> +    reg |= ENABLE_DATA_STROBE;
>> +    sdhci_writel(host, reg, SDHC_SLOT_EMMC_CTRL);
>> +
>> +    /* Set Data Strobe Pull down */
>> +    if (priv->phy_type == EMMC_5_0_PHY) {
>> +        reg = sdhci_readl(host, EMMC_5_0_PHY_PAD_CONTROL);
>> +        reg |= EMMC5_FC_QSP_PD;
>> +        reg &= ~EMMC5_FC_QSP_PU;
>> +        sdhci_writel(host, reg, EMMC_5_0_PHY_PAD_CONTROL);
>> +    } else {
>> +        reg = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1);
>> +        reg |= EMMC5_1_FC_QSP_PD;
>> +        reg &= ~EMMC5_1_FC_QSP_PU;
>> +        sdhci_writel(host, reg, EMMC_PHY_PAD_CONTROL1);
>> +    }
>> +    spin_unlock_irqrestore(&host->lock, flags);
>> +}
>> +
>> +#define LOGIC_TIMING_VALUE    0x00AA8977
>> +
>> +static void xenon_emmc_phy_set(struct sdhci_host *host,
>> +                   unsigned char timing)
>> +{
>> +    u32 reg;
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    struct emmc_phy_params *params = priv->phy_params;
>> +    struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
>> +    struct mmc_card *card = priv->card_candidate;
>> +    unsigned long flags;
>> +
>> +    dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting starts\n");
>> +
>> +    spin_lock_irqsave(&host->lock, flags);
>> +
>> +    /* Setup pad, set bit[28] and bits[26:24] */
>> +    reg = sdhci_readl(host, phy_regs->pad_ctrl);
>> +    reg |= (FC_DQ_RECEN | FC_CMD_RECEN | FC_QSP_RECEN | OEN_QSN);
>> +    /*
>> +     * All FC_XX_RECEIVCE should be set as CMOS Type
>> +     */
>> +    reg |= FC_ALL_CMOS_RECEIVER;
>> +    sdhci_writel(host, reg, phy_regs->pad_ctrl);
>> +
>> +    /* Set CMD and DQ Pull Up */
>> +    if (priv->phy_type == EMMC_5_0_PHY) {
>> +        reg = sdhci_readl(host, EMMC_5_0_PHY_PAD_CONTROL);
>> +        reg |= (EMMC5_FC_CMD_PU | EMMC5_FC_DQ_PU);
>> +        reg &= ~(EMMC5_FC_CMD_PD | EMMC5_FC_DQ_PD);
>> +        sdhci_writel(host, reg, EMMC_5_0_PHY_PAD_CONTROL);
>> +    } else {
>> +        reg = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1);
>> +        reg |= (EMMC5_1_FC_CMD_PU | EMMC5_1_FC_DQ_PU);
>> +        reg &= ~(EMMC5_1_FC_CMD_PD | EMMC5_1_FC_DQ_PD);
>> +        sdhci_writel(host, reg, EMMC_PHY_PAD_CONTROL1);
>> +    }
>> +
>> +    if ((timing == MMC_TIMING_LEGACY) || !card)
>> +        goto phy_init;
>> +
>> +    /*
>> +     * FIXME: should depends on the specific board timing.
>> +     */
>> +    if ((timing == MMC_TIMING_MMC_HS400) ||
>> +        (timing == MMC_TIMING_MMC_HS200) ||
>> +        (timing == MMC_TIMING_UHS_SDR50) ||
>> +        (timing == MMC_TIMING_UHS_SDR104) ||
>> +        (timing == MMC_TIMING_UHS_DDR50) ||
>> +        (timing == MMC_TIMING_UHS_SDR25) ||
>> +        (timing == MMC_TIMING_MMC_DDR52)) {
>> +        reg = sdhci_readl(host, phy_regs->timing_adj);
>> +        reg &= ~OUTPUT_QSN_PHASE_SELECT;
>> +        sdhci_writel(host, reg, phy_regs->timing_adj);
>> +    }
>> +
>> +    /*
>> +     * If SDIO card, set SDIO Mode
>> +     * Otherwise, clear SDIO Mode and Slow Mode
>> +     */
>> +    if (mmc_card_sdio(card)) {
>> +        reg = sdhci_readl(host, phy_regs->timing_adj);
>> +        reg |= TIMING_ADJUST_SDIO_MODE;
>> +
>> +        if ((timing == MMC_TIMING_UHS_SDR25) ||
>> +            (timing == MMC_TIMING_UHS_SDR12) ||
>> +            (timing == MMC_TIMING_SD_HS) ||
>> +            (timing == MMC_TIMING_LEGACY))
>> +            reg |= TIMING_ADJUST_SLOW_MODE;
>> +
>> +        sdhci_writel(host, reg, phy_regs->timing_adj);
>> +    } else {
>> +        reg = sdhci_readl(host, phy_regs->timing_adj);
>> +        reg &= ~(TIMING_ADJUST_SDIO_MODE | TIMING_ADJUST_SLOW_MODE);
>> +        sdhci_writel(host, reg, phy_regs->timing_adj);
>> +    }
>> +
>> +    if (((timing == MMC_TIMING_UHS_SDR50) ||
>> +         (timing == MMC_TIMING_UHS_SDR25) ||
>> +         (timing == MMC_TIMING_UHS_SDR12) ||
>> +         (timing == MMC_TIMING_SD_HS) ||
>> +         (timing == MMC_TIMING_MMC_HS) ||
>> +         (timing == MMC_TIMING_LEGACY)) && params->slow_mode) {
>> +        reg = sdhci_readl(host, phy_regs->timing_adj);
>> +        reg |= TIMING_ADJUST_SLOW_MODE;
>> +        sdhci_writel(host, reg, phy_regs->timing_adj);
>> +    }
>> +
>> +    /*
>> +     * Set preferred ZNR and ZPR value
>> +     * The ZNR and ZPR value vary between different boards.
>> +     * Define them both in sdhci-xenon-emmc-phy.h.
>> +     */
>> +    reg = sdhci_readl(host, phy_regs->pad_ctrl2);
>> +    reg &= ~((ZNR_MASK << ZNR_SHIFT) | ZPR_MASK);
>> +    reg |= ((params->znr << ZNR_SHIFT) | params->zpr);
>> +    sdhci_writel(host, reg, phy_regs->pad_ctrl2);
>> +
>> +    /*
>> +     * When setting EMMC_PHY_FUNC_CONTROL register,
>> +     * SD clock should be disabled
>> +     */
>> +    reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
>> +    reg &= ~SDHCI_CLOCK_CARD_EN;
>> +    sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
>> +
>> +    if ((timing == MMC_TIMING_UHS_DDR50) ||
>> +        (timing == MMC_TIMING_MMC_HS400) ||
>> +        (timing == MMC_TIMING_MMC_DDR52)) {
>> +        reg = sdhci_readl(host, phy_regs->func_ctrl);
>> +        reg |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE;
>> +        sdhci_writel(host, reg, phy_regs->func_ctrl);
>> +    }
>> +
>> +    if (timing == MMC_TIMING_MMC_HS400) {
>> +        reg = sdhci_readl(host, phy_regs->func_ctrl);
>> +        reg &= ~DQ_ASYNC_MODE;
>> +        sdhci_writel(host, reg, phy_regs->func_ctrl);
>> +    }
>> +
>> +    /* Enable bus clock */
>> +    reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
>> +    reg |= SDHCI_CLOCK_CARD_EN;
>> +    sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
>> +
>> +    if (timing == MMC_TIMING_MMC_HS400)
>> +        /* Hardware team recommend a value for HS400 */
>> +        sdhci_writel(host, LOGIC_TIMING_VALUE,
>> +                 phy_regs->logic_timing_adj);
>> +
>> +phy_init:
>> +    xenon_emmc_phy_init(host);
>> +
>> +    spin_unlock_irqrestore(&host->lock, flags);
>> +
>> +    dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting completes\n");
>> +}
>> +
>> +static int get_dt_pad_ctrl_data(struct sdhci_host *host,
>> +                struct device_node *np,
>> +                struct emmc_phy_params *params)
>> +{
>> +    int ret = 0;
>> +    const char *name;
>> +    struct resource iomem;
>> +
>> +    if (of_device_is_compatible(np, "marvell,armada-3700-sdhci"))
>> +        params->pad_ctrl.set_soc_pad = armada_3700_soc_pad_voltage_set;
>> +    else
>> +        return 0;
>> +
>> +    if (of_address_to_resource(np, 1, &iomem)) {
>> +        dev_err(mmc_dev(host->mmc), "Unable to find SOC PAD ctrl register address for %s\n",
>> +            np->name);
>> +        return -EINVAL;
>> +    }
>> +
>> +    params->pad_ctrl.reg = devm_ioremap_resource(mmc_dev(host->mmc),
>> +                             &iomem);
>> +    if (IS_ERR(params->pad_ctrl.reg)) {
>> +        dev_err(mmc_dev(host->mmc), "Unable to get SOC PHY PAD ctrl regiser for %s\n",
>> +            np->name);
>> +        return PTR_ERR(params->pad_ctrl.reg);
>> +    }
>> +
>> +    ret = of_property_read_string(np, "xenon,pad-type", &name);
>> +    if (ret) {
>> +        dev_err(mmc_dev(host->mmc), "Unable to determine SOC PHY PAD ctrl type\n");
>> +        return ret;
>> +    }
>> +    if (!strcmp(name, "sd")) {
>> +        params->pad_ctrl.pad_type = SOC_PAD_SD;
>> +    } else if (!strcmp(name, "fixed-1-8v")) {
>> +        params->pad_ctrl.pad_type = SOC_PAD_FIXED_1_8V;
>> +    } else {
>> +        dev_err(mmc_dev(host->mmc), "Unsupported SOC PHY PAD ctrl type %s\n",
>> +            name);
>> +        return -EINVAL;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static int emmc_phy_parse_param_dt(struct sdhci_host *host,
>> +                   struct device_node *np,
>> +                   struct emmc_phy_params *params)
>> +{
>> +    u32 value;
>> +
>> +    if (of_property_read_bool(np, "xenon,phy-slow-mode"))
>> +        params->slow_mode = true;
>> +    else
>> +        params->slow_mode = false;
>> +
>> +    if (!of_property_read_u32(np, "xenon,phy-znr", &value))
>> +        params->znr = value & ZNR_MASK;
>> +    else
>> +        params->znr = ZNR_DEF_VALUE;
>> +
>> +    if (!of_property_read_u32(np, "xenon,phy-zpr", &value))
>> +        params->zpr = value & ZPR_MASK;
>> +    else
>> +        params->zpr = ZPR_DEF_VALUE;
>> +
>> +    if (!of_property_read_u32(np, "xenon,phy-nr-tun-times", &value))
>> +        params->nr_tun_times = value & TUN_CONSECUTIVE_TIMES_MASK;
>> +    else
>> +        params->nr_tun_times = TUN_CONSECUTIVE_TIMES;
>> +
>> +    if (!of_property_read_u32(np, "xenon,phy-tun-step-divider", &value))
>> +        params->tun_step_divider = value & 0xFF;
>> +    else
>> +        params->tun_step_divider = TUNING_STEP_DIVIDER;
>> +
>> +    return get_dt_pad_ctrl_data(host, np, params);
>> +}
>> +
>> +/*
>> + * SDH PHY configuration and operations
>> + */
>> +static int xenon_sdh_phy_set_fix_sampl_delay(struct sdhci_host *host,
>> +                         unsigned int delay, bool invert)
>> +{
>> +    u32 reg;
>> +    unsigned long flags;
>> +    int ret;
>> +
>> +    if (invert)
>> +        invert = 0x1;
>> +    else
>> +        invert = 0x0;
>> +
>> +    spin_lock_irqsave(&host->lock, flags);
>> +
>> +    /* Disable SDCLK */
>> +    reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
>> +    reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
>> +    sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
>> +
>> +    udelay(200);
>> +
>> +    /* Setup Sampling fix delay */
>> +    reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
>> +    reg &= ~(SDH_PHY_FIXED_DELAY_MASK |
>> +            (0x1 << FORCE_SEL_INVERSE_CLK_SHIFT));
>> +    reg |= ((delay & SDH_PHY_FIXED_DELAY_MASK) |
>> +            (invert << FORCE_SEL_INVERSE_CLK_SHIFT));
>> +    sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
>> +
>> +    /* Enable SD internal clock */
>> +    ret = enable_xenon_internal_clk(host);
>> +
>> +    /* Enable SDCLK */
>> +    reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
>> +    reg |= SDHCI_CLOCK_CARD_EN;
>> +    sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
>> +
>> +    udelay(200);
>> +
>> +    spin_unlock_irqrestore(&host->lock, flags);
>> +    return ret;
>> +}
>> +
>> +static int sdh_phy_do_fix_sampl_delay(struct sdhci_host *host,
>> +                      struct mmc_card *card,
>> +                      unsigned int delay, bool invert)
>> +{
>> +    int ret;
>> +
>> +    xenon_sdh_phy_set_fix_sampl_delay(host, delay, invert);
>> +
>> +    ret = xenon_delay_adj_test(card);
>> +    if (ret) {
>> +        dev_dbg(mmc_dev(host->mmc),
>> +            "fail when sampling fix delay = %d, phase = %d degree\n",
>> +            delay, invert * 180);
>> +        return -1;
>> +    }
>> +    return 0;
>> +}
>> +
>> +#define SDH_PHY_COARSE_FIX_DELAY    (SDH_PHY_FIXED_DELAY_MASK / 2)
>> +#define SDH_PHY_FINE_FIX_DELAY        (SDH_PHY_COARSE_FIX_DELAY / 4)
>> +
>> +static int xenon_sdh_phy_fix_sampl_delay_adj(struct sdhci_host *host,
>> +                         struct mmc_card *card)
>> +{
>> +    u32 reg;
>> +    bool dll_enable = false;
>> +    unsigned int min_delay, max_delay, delay;
>> +    const bool sampl_edge[] = {
>> +        false,
>> +        true,
>> +    };
>> +    int i, nr;
>> +    int ret;
>> +
>> +    if (host->clock > HIGH_SPEED_MAX_DTR) {
>> +        /* Enable DLL when SDCLK is higher than 50MHz */
>> +        reg = sdhci_readl(host, SDH_PHY_SLOT_DLL_CTRL);
>> +        if (!(reg & SDH_PHY_ENABLE_DLL)) {
>> +            reg |= (SDH_PHY_ENABLE_DLL | SDH_PHY_FAST_LOCK_EN);
>> +            sdhci_writel(host, reg, SDH_PHY_SLOT_DLL_CTRL);
>> +            mdelay(1);
>> +
>> +            reg = sdhci_readl(host, SDH_PHY_SLOT_DLL_PHASE_SEL);
>> +            reg |= SDH_PHY_DLL_UPDATE_TUNING;
>> +            sdhci_writel(host, reg, SDH_PHY_SLOT_DLL_PHASE_SEL);
>> +        }
>> +        dll_enable = true;
>> +    }
>> +
>> +    nr = dll_enable ? ARRAY_SIZE(sampl_edge) : 1;
>> +    for (i = 0; i < nr; i++) {
>> +        for (min_delay = 0; min_delay <= SDH_PHY_FIXED_DELAY_MASK;
>> +                min_delay += SDH_PHY_COARSE_FIX_DELAY) {
>> +            ret = sdh_phy_do_fix_sampl_delay(host, card, min_delay,
>> +                             sampl_edge[i]);
>> +            if (!ret)
>> +                break;
>> +        }
>> +
>> +        if (ret) {
>> +            dev_dbg(mmc_dev(host->mmc),
>> +                "Fail to set Fixed Sampling Delay with %s edge\n",
>> +                sampl_edge[i] ? "negative" : "positive");
>> +            continue;
>> +        }
>> +
>> +        for (max_delay = min_delay + SDH_PHY_FINE_FIX_DELAY;
>> +                max_delay < SDH_PHY_FIXED_DELAY_MASK;
>> +                max_delay += SDH_PHY_FINE_FIX_DELAY) {
>> +            ret = sdh_phy_do_fix_sampl_delay(host, card, max_delay,
>> +                             sampl_edge[i]);
>> +            if (ret) {
>> +                max_delay -= SDH_PHY_FINE_FIX_DELAY;
>> +                break;
>> +            }
>> +        }
>> +
>> +        if (!ret) {
>> +            delay = SDH_PHY_FIXED_DELAY_MASK;
>> +            ret = sdh_phy_do_fix_sampl_delay(host, card, delay,
>> +                             sampl_edge[i]);
>> +            if (!ret)
>> +                max_delay = SDH_PHY_FIXED_DELAY_MASK;
>> +        }
>> +
>> +        if ((max_delay - min_delay) <= SDH_PHY_FIXED_DELAY_WINDOW_MIN) {
>> +            dev_info(mmc_dev(host->mmc),
>> +                 "The window size %d with %s edge is too small\n",
>> +                 max_delay - min_delay,
>> +                 sampl_edge[i] ? "negative" : "positive");
>> +            continue;
>> +        }
>> +
>> +        delay = (min_delay + max_delay) / 2;
>> +        xenon_sdh_phy_set_fix_sampl_delay(host, delay, sampl_edge[i]);
>> +        dev_dbg(mmc_dev(host->mmc), "sampling fix delay = %d with %s edge\n",
>> +            delay, sampl_edge[i] ? "negative" : "positive");
>> +        return 0;
>> +    }
>> +    return -EIO;
>> +}
>> +
>> +static const struct xenon_phy_ops sdh_phy_ops = {
>> +    .fix_sampl_delay_adj = xenon_sdh_phy_fix_sampl_delay_adj,
>> +};
>> +
>> +static int alloc_sdh_phy(struct sdhci_xenon_priv *priv)
>> +{
>> +    priv->phy_params = NULL;
>> +    priv->phy_ops = &sdh_phy_ops;
>> +    return 0;
>> +}
>> +
>> +/*
>> + * Common functions for all PHYs
>> + */
>> +void xenon_soc_pad_ctrl(struct sdhci_host *host,
>> +            unsigned char signal_voltage)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +
>> +    if (priv->phy_ops->set_soc_pad)
>> +        priv->phy_ops->set_soc_pad(host, signal_voltage);
>> +}
>> +
>> +static int __xenon_emmc_delay_adj_test(struct mmc_card *card)
>> +{
>> +    int err;
>> +    u8 *ext_csd = NULL;
>> +
>> +    err = mmc_get_ext_csd(card, &ext_csd);
>> +    kfree(ext_csd);
>> +
>> +    return err;
>> +}
>> +
>> +static int __xenon_sdio_delay_adj_test(struct mmc_card *card)
>> +{
>> +    struct mmc_command cmd = {0};
>> +    int err;
>> +
>> +    cmd.opcode = SD_IO_RW_DIRECT;
>> +    cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
>> +
>> +    err = mmc_wait_for_cmd(card->host, &cmd, 0);
>> +    if (err)
>> +        return err;
>> +
>> +    if (cmd.resp[0] & R5_ERROR)
>> +        return -EIO;
>> +    if (cmd.resp[0] & R5_FUNCTION_NUMBER)
>> +        return -EINVAL;
>> +    if (cmd.resp[0] & R5_OUT_OF_RANGE)
>> +        return -ERANGE;
>> +    return 0;
>> +}
>> +
>> +static int __xenon_sd_delay_adj_test(struct mmc_card *card)
>> +{
>> +    struct mmc_command cmd = {0};
>> +    int err;
>> +
>> +    cmd.opcode = MMC_SEND_STATUS;
>> +    cmd.arg = card->rca << 16;
>> +    cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
>> +
>> +    err = mmc_wait_for_cmd(card->host, &cmd, 0);
>> +    return err;
>> +}
>> +
>> +static int xenon_delay_adj_test(struct mmc_card *card)
>> +{
>> +    WARN_ON(!card);
>> +    WARN_ON(!card->host);
>> +
>> +    if (mmc_card_mmc(card))
>> +        return __xenon_emmc_delay_adj_test(card);
>> +    else if (mmc_card_sd(card))
>> +        return __xenon_sd_delay_adj_test(card);
>> +    else if (mmc_card_sdio(card))
>> +        return __xenon_sdio_delay_adj_test(card);
>> +    else
>> +        return -EINVAL;
>> +}
>> +
>> +static void xenon_phy_set(struct sdhci_host *host, unsigned char timing)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +
>> +    if (priv->phy_ops->phy_set)
>> +        priv->phy_ops->phy_set(host, timing);
>> +}
>> +
>> +static void xenon_hs400_strobe_delay_adj(struct sdhci_host *host,
>> +                     struct mmc_card *card)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +
>> +    if (WARN_ON(!mmc_card_hs400(card)))
>> +        return;
>> +
>> +    /* Enable the DLL to automatically adjust HS400 strobe delay.
>> +     */
>> +    if (priv->phy_ops->strobe_delay_adj)
>> +        priv->phy_ops->strobe_delay_adj(host, card);
>> +}
>> +
>> +static int xenon_fix_sampl_delay_adj(struct sdhci_host *host,
>> +                     struct mmc_card *card)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +
>> +    if (priv->phy_ops->fix_sampl_delay_adj)
>> +        return priv->phy_ops->fix_sampl_delay_adj(host, card);
>> +
>> +    return 0;
>> +}
>> +
>> +/*
>> + * xenon_delay_adj should not be called inside IRQ context,
>> + * either Hard IRQ or Softirq.
>> + */
>> +static int xenon_hs_delay_adj(struct sdhci_host *host,
>> +                  struct mmc_card *card)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    int ret = 0;
>> +
>> +    if (WARN_ON(host->clock <= DEFAULT_SDCLK_FREQ))
>> +        return -EINVAL;
>> +
>> +    if (mmc_card_hs400(card)) {
>> +        xenon_hs400_strobe_delay_adj(host, card);
>> +        return 0;
>> +    }
>> +
>> +    if (((priv->phy_type == EMMC_5_1_PHY) ||
>> +         (priv->phy_type == EMMC_5_0_PHY)) &&
>> +         (mmc_card_hs200(card) ||
>> +         (host->timing == MMC_TIMING_UHS_SDR104))) {
>> +        ret = xenon_emmc_phy_config_tuning(host);
>> +        if (!ret)
>> +            return 0;
>> +    }
>> +
>> +    ret = xenon_fix_sampl_delay_adj(host, card);
>> +    if (ret)
>> +        dev_err(mmc_dev(host->mmc), "fails sampling fixed delay adjustment\n");
>> +    return ret;
>> +}
>> +
>> +int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios)
>> +{
>> +    struct mmc_host *mmc = host->mmc;
>> +    struct mmc_card *card;
>> +    int ret = 0;
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +
>> +    if (!host->clock) {
>> +        priv->clock = 0;
>> +        return 0;
>> +    }
>> +
>> +    /*
>> +     * The timing, frequency or bus width is changed,
>> +     * better to set eMMC PHY based on current setting
>> +     * and adjust Xenon SDHC delay.
>> +     */
>> +    if ((host->clock == priv->clock) &&
>> +        (ios->bus_width == priv->bus_width) &&
>> +        (ios->timing == priv->timing))
>> +        return 0;
>> +
>> +    xenon_phy_set(host, ios->timing);
>> +
>> +    /* Update the record */
>> +    priv->bus_width = ios->bus_width;
>> +    /* Temp stage from HS200 to HS400 */
>> +    if (((priv->timing == MMC_TIMING_MMC_HS200) &&
>> +         (ios->timing == MMC_TIMING_MMC_HS)) ||
>> +        ((ios->timing == MMC_TIMING_MMC_HS) &&
>> +         (priv->clock > host->clock))) {
>> +        priv->timing = ios->timing;
>> +        priv->clock = host->clock;
>> +        return 0;
>> +    }
>> +    priv->timing = ios->timing;
>> +    priv->clock = host->clock;
>> +
>> +    /* Legacy mode is a special case */
>> +    if (ios->timing == MMC_TIMING_LEGACY)
>> +        return 0;
>> +
>> +    card = priv->card_candidate;
>> +    if (unlikely(!card)) {
>> +        dev_warn(mmc_dev(mmc), "card is not present\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (host->clock > DEFAULT_SDCLK_FREQ)
>> +        ret = xenon_hs_delay_adj(host, card);
>> +    return ret;
>> +}
>> +
>> +static int add_xenon_phy(struct device_node *np, struct sdhci_host *host,
>> +             const char *phy_name)
>> +{
>> +    struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +    struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +    int i, ret;
>> +
>> +    for (i = 0; i < NR_PHY_TYPES; i++) {
>> +        if (!strcmp(phy_name, phy_types[i])) {
>> +            priv->phy_type = i;
>> +            break;
>> +        }
>> +    }
>> +    if (i == NR_PHY_TYPES) {
>> +        dev_err(mmc_dev(host->mmc),
>> +            "Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n",
>> +            phy_name);
>> +        priv->phy_type = EMMC_5_1_PHY;
>> +    }
>> +
>> +    if (priv->phy_type == SDH_PHY) {
>> +        return alloc_sdh_phy(priv);
>> +    } else if ((priv->phy_type == EMMC_5_0_PHY) ||
>> +            (priv->phy_type == EMMC_5_1_PHY)) {
>> +        ret = alloc_emmc_phy(priv);
>> +        if (ret)
>> +            return ret;
>> +        return emmc_phy_parse_param_dt(host, np, priv->phy_params);
>> +    }
>> +
>> +    return -EINVAL;
>> +}
>> +
>> +int xenon_phy_parse_dt(struct device_node *np, struct sdhci_host *host)
>> +{
>> +    const char *phy_type = NULL;
>> +
>> +    if (!of_property_read_string(np, "xenon,phy-type", &phy_type))
>> +        return add_xenon_phy(np, host, phy_type);
>> +
>> +    dev_err(mmc_dev(host->mmc), "Fail to get Xenon PHY type. Use default eMMC 5.1 PHY\n");
>> +    return add_xenon_phy(np, host, "emmc 5.1 phy");
>> +}
>> diff --git a/drivers/mmc/host/sdhci-xenon-phy.h b/drivers/mmc/host/sdhci-xenon-phy.h
>> new file mode 100644
>> index 000000000000..4373c71d3b7b
>> --- /dev/null
>> +++ b/drivers/mmc/host/sdhci-xenon-phy.h
>> @@ -0,0 +1,157 @@
>> +/* linux/drivers/mmc/host/sdhci-xenon-phy.h
>> + *
>> + * Author:    Hu Ziji <huziji@marvell.com>
>> + * Date:    2016-8-24
>> + *
>> + *  Copyright (C) 2016 Marvell, All Rights Reserved.
>> + *
>> + * 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.
>> + */
>> +#ifndef SDHCI_XENON_PHY_H_
>> +#define SDHCI_XENON_PHY_H_
>> +
>> +#include <linux/types.h>
>> +#include "sdhci.h"
>> +
>> +/* Register base for eMMC PHY 5.0 Version */
>> +#define EMMC_5_0_PHY_REG_BASE            0x0160
>> +/* Register base for eMMC PHY 5.1 Version */
>> +#define EMMC_PHY_REG_BASE            0x0170
>> +
>> +#define EMMC_PHY_TIMING_ADJUST            EMMC_PHY_REG_BASE
>> +#define EMMC_5_0_PHY_TIMING_ADJUST        EMMC_5_0_PHY_REG_BASE
>> +#define TIMING_ADJUST_SLOW_MODE            BIT(29)
>> +#define TIMING_ADJUST_SDIO_MODE            BIT(28)
>> +#define OUTPUT_QSN_PHASE_SELECT            BIT(17)
>> +#define SAMPL_INV_QSP_PHASE_SELECT        BIT(18)
>> +#define SAMPL_INV_QSP_PHASE_SELECT_SHIFT    18
>> +#define PHY_INITIALIZAION            BIT(31)
>> +#define WAIT_CYCLE_BEFORE_USING_MASK        0xF
>> +#define WAIT_CYCLE_BEFORE_USING_SHIFT        12
>> +#define FC_SYNC_EN_DURATION_MASK        0xF
>> +#define FC_SYNC_EN_DURATION_SHIFT        8
>> +#define FC_SYNC_RST_EN_DURATION_MASK        0xF
>> +#define FC_SYNC_RST_EN_DURATION_SHIFT        4
>> +#define FC_SYNC_RST_DURATION_MASK        0xF
>> +#define FC_SYNC_RST_DURATION_SHIFT        0
>> +
>> +#define EMMC_PHY_FUNC_CONTROL            (EMMC_PHY_REG_BASE + 0x4)
>> +#define EMMC_5_0_PHY_FUNC_CONTROL        (EMMC_5_0_PHY_REG_BASE + 0x4)
>> +#define ASYNC_DDRMODE_MASK            BIT(23)
>> +#define ASYNC_DDRMODE_SHIFT            23
>> +#define CMD_DDR_MODE                BIT(16)
>> +#define DQ_DDR_MODE_SHIFT            8
>> +#define DQ_DDR_MODE_MASK            0xFF
>> +#define DQ_ASYNC_MODE                BIT(4)
>> +
>> +#define EMMC_PHY_PAD_CONTROL            (EMMC_PHY_REG_BASE + 0x8)
>> +#define EMMC_5_0_PHY_PAD_CONTROL        (EMMC_5_0_PHY_REG_BASE + 0x8)
>> +#define REC_EN_SHIFT                24
>> +#define REC_EN_MASK                0xF
>> +#define FC_DQ_RECEN                BIT(24)
>> +#define FC_CMD_RECEN                BIT(25)
>> +#define FC_QSP_RECEN                BIT(26)
>> +#define FC_QSN_RECEN                BIT(27)
>> +#define OEN_QSN                    BIT(28)
>> +#define AUTO_RECEN_CTRL                BIT(30)
>> +#define FC_ALL_CMOS_RECEIVER            0xF000
>> +
>> +#define EMMC5_FC_QSP_PD                BIT(18)
>> +#define EMMC5_FC_QSP_PU                BIT(22)
>> +#define EMMC5_FC_CMD_PD                BIT(17)
>> +#define EMMC5_FC_CMD_PU                BIT(21)
>> +#define EMMC5_FC_DQ_PD                BIT(16)
>> +#define EMMC5_FC_DQ_PU                BIT(20)
>> +
>> +#define EMMC_PHY_PAD_CONTROL1            (EMMC_PHY_REG_BASE + 0xC)
>> +#define EMMC5_1_FC_QSP_PD            BIT(9)
>> +#define EMMC5_1_FC_QSP_PU            BIT(25)
>> +#define EMMC5_1_FC_CMD_PD            BIT(8)
>> +#define EMMC5_1_FC_CMD_PU            BIT(24)
>> +#define EMMC5_1_FC_DQ_PD            0xFF
>> +#define EMMC5_1_FC_DQ_PU            (0xFF << 16)
>> +
>> +#define EMMC_PHY_PAD_CONTROL2            (EMMC_PHY_REG_BASE + 0x10)
>> +#define EMMC_5_0_PHY_PAD_CONTROL2        (EMMC_5_0_PHY_REG_BASE + 0xC)
>> +#define ZNR_MASK                0x1F
>> +#define ZNR_SHIFT                8
>> +#define ZPR_MASK                0x1F
>> +/* Perferred ZNR and ZPR value vary between different boards.
>> + * The specific ZNR and ZPR value should be defined here
>> + * according to board actual timing.
>> + */
>> +#define ZNR_DEF_VALUE                0xF
>> +#define ZPR_DEF_VALUE                0xF
>> +
>> +#define EMMC_PHY_DLL_CONTROL            (EMMC_PHY_REG_BASE + 0x14)
>> +#define EMMC_5_0_PHY_DLL_CONTROL        (EMMC_5_0_PHY_REG_BASE + 0x10)
>> +#define DLL_ENABLE                BIT(31)
>> +#define DLL_UPDATE_STROBE_5_0            BIT(30)
>> +#define DLL_REFCLK_SEL                BIT(30)
>> +#define DLL_UPDATE                BIT(23)
>> +#define DLL_PHSEL1_SHIFT            24
>> +#define DLL_PHSEL0_SHIFT            16
>> +#define DLL_PHASE_MASK                0x3F
>> +#define DLL_PHASE_90_DEGREE            0x1F
>> +#define DLL_FAST_LOCK                BIT(5)
>> +#define DLL_GAIN2X                BIT(3)
>> +#define DLL_BYPASS_EN                BIT(0)
>> +
>> +#define EMMC_5_0_PHY_LOGIC_TIMING_ADJUST    (EMMC_5_0_PHY_REG_BASE + 0x14)
>> +#define EMMC_PHY_LOGIC_TIMING_ADJUST        (EMMC_PHY_REG_BASE + 0x18)
>> +
>> +enum sampl_fix_delay_phase {
>> +    PHASE_0_DEGREE = 0x0,
>> +    PHASE_90_DEGREE = 0x1,
>> +    PHASE_180_DEGREE = 0x2,
>> +    PHASE_270_DEGREE = 0x3,
>> +};
>> +
>> +#define SDH_PHY_SLOT_DLL_CTRL            (0x0138)
>> +#define SDH_PHY_ENABLE_DLL            BIT(1)
>> +#define SDH_PHY_FAST_LOCK_EN            BIT(5)
>> +
>> +#define SDH_PHY_SLOT_DLL_PHASE_SEL        (0x013C)
>> +#define SDH_PHY_DLL_UPDATE_TUNING        BIT(15)
>> +
>> +enum soc_pad_ctrl_type {
>> +    SOC_PAD_SD,
>> +    SOC_PAD_FIXED_1_8V,
>> +};
>> +
>> +/*
>> + * List offset of PHY registers and some special register values
>> + * in eMMC PHY 5.0 or eMMC PHY 5.1
>> + */
>> +struct xenon_emmc_phy_regs {
>> +    /* Offset of Timing Adjust register */
>> +    u16 timing_adj;
>> +    /* Offset of Func Control register */
>> +    u16 func_ctrl;
>> +    /* Offset of Pad Control register */
>> +    u16 pad_ctrl;
>> +    /* Offset of Pad Control register */
>> +    u16 pad_ctrl2;
>> +    /* Offset of DLL Control register */
>> +    u16 dll_ctrl;
>> +    /* Offset of Logic Timing Adjust register */
>> +    u16 logic_timing_adj;
>> +    /* Max value of eMMC Fixed Sampling Delay */
>> +    u32 delay_mask;
>> +    /* DLL Update Enable bit */
>> +    u32 dll_update;
>> +};
>> +
>> +struct xenon_phy_ops {
>> +    void (*strobe_delay_adj)(struct sdhci_host *host,
>> +                 struct mmc_card *card);
>> +    int (*fix_sampl_delay_adj)(struct sdhci_host *host,
>> +                   struct mmc_card *card);
>> +    void (*phy_set)(struct sdhci_host *host, unsigned char timing);
>> +    void (*set_soc_pad)(struct sdhci_host *host,
>> +                unsigned char signal_voltage);
>> +};
>> +#endif
>> diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
>> index 03ba183494d3..4d7d871544fc 100644
>> --- a/drivers/mmc/host/sdhci-xenon.c
>> +++ b/drivers/mmc/host/sdhci-xenon.c
>> @@ -224,6 +224,7 @@ static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>      spin_unlock_irqrestore(&host->lock, flags);
>>
>>      sdhci_set_ios(mmc, ios);
>> +    xenon_phy_adj(host, ios);
>>
>>      if (host->clock > DEFAULT_SDCLK_FREQ) {
>>          spin_lock_irqsave(&host->lock, flags);
>> @@ -309,6 +310,8 @@ static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
>>       */
>>      enable_xenon_internal_clk(host);
>>
>> +    xenon_soc_pad_ctrl(host, ios->signal_voltage);
>> +
>>      if (priv->card_candidate) {
>>          if (mmc_card_mmc(priv->card_candidate))
>>              return xenon_emmc_signal_voltage_switch(mmc, ios);
>> @@ -453,6 +456,7 @@ static int xenon_probe_dt(struct platform_device *pdev)
>>          sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
>>      }
>>
>> +    err = xenon_phy_parse_dt(np, host);
>>      return err;
>>  }
>>
>> diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
>> index c2370493fbe8..06e5261a563c 100644
>> --- a/drivers/mmc/host/sdhci-xenon.h
>> +++ b/drivers/mmc/host/sdhci-xenon.h
>> @@ -15,6 +15,7 @@
>>  #include <linux/mmc/card.h>
>>  #include <linux/of.h>
>>  #include "sdhci.h"
>> +#include "sdhci-xenon-phy.h"
>>
>>  /* Register Offset of SD Host Controller SOCP self-defined register */
>>  #define SDHC_SYS_CFG_INFO            0x0104
>> @@ -76,6 +77,7 @@
>>  #define MMC_TIMING_FAKE                0xFF
>>
>>  #define DEFAULT_SDCLK_FREQ            (400000)
>> +#define LOWEST_SDCLK_FREQ            (100000)
>>
>>  /* Xenon specific Mode Select value */
>>  #define XENON_SDHCI_CTRL_HS200            0x5
>> @@ -97,6 +99,15 @@ struct sdhci_xenon_priv {
>>      /* Slot idx */
>>      u8        slot_idx;
>>
>> +    int        phy_type;
>> +    /*
>> +     * Contains board-specific PHY parameters
>> +     * passed from device tree.
>> +     */
>> +    void        *phy_params;
>> +    const struct xenon_phy_ops *phy_ops;
>> +    struct xenon_emmc_phy_regs *emmc_phy_regs;
>> +
>>      /*
>>       * When initializing card, Xenon has to determine card type and
>>       * adjust Sampling Fixed delay.
>> @@ -131,4 +142,10 @@ static inline int enable_xenon_internal_clk(struct sdhci_host *host)
>>
>>      return 0;
>>  }
>> +
>> +int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios);
>> +int xenon_phy_parse_dt(struct device_node *np,
>> +               struct sdhci_host *host);
>> +void xenon_soc_pad_ctrl(struct sdhci_host *host,
>> +            unsigned char signal_voltage);
>>  #endif
>>
> 
> 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox