* Re: [PATCH v6 07/13] media: dt-bindings: add bindings for i.MX7 media driver
From: Rob Herring @ 2018-05-22 16:10 UTC (permalink / raw)
To: Rui Miguel Silva
Cc: devel, devicetree, sakari.ailus, Greg Kroah-Hartman, Ryan Harkin,
Philipp Zabel, Steve Longerbeam, Fabio Estevam, mchehab,
Shawn Guo, linux-clk, linux-media
In-Reply-To: <20180522145245.3143-8-rui.silva@linaro.org>
On Tue, May 22, 2018 at 03:52:39PM +0100, Rui Miguel Silva wrote:
> Add bindings documentation for i.MX7 media drivers.
> The imx7 MIPI CSI2 and imx7 CMOS Sensor Interface.
>
> Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
> ---
> .../devicetree/bindings/media/imx7-csi.txt | 44 ++++++++++
> .../bindings/media/imx7-mipi-csi2.txt | 82 +++++++++++++++++++
> 2 files changed, 126 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/media/imx7-csi.txt
> create mode 100644 Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v6 3/6] dt-bindings: clock: renesas,rzn1-clocks: document RZ/N1 clock driver
From: Rob Herring @ 2018-05-22 16:09 UTC (permalink / raw)
To: Michel Pollet
Cc: linux-renesas-soc, Simon Horman, phil.edworthy, Michel Pollet,
Magnus Damm, Mark Rutland, Michael Turquette, Stephen Boyd,
Geert Uytterhoeven, devicetree, linux-kernel, linux-clk
In-Reply-To: <1526983321-41949-4-git-send-email-michel.pollet@bp.renesas.com>
On Tue, May 22, 2018 at 11:01:23AM +0100, Michel Pollet wrote:
> The Renesas RZ/N1 Family (Part #R9A06G0xx) requires a driver
> to provide the SoC clock infrastructure for Linux.
>
> This documents the driver bindings.
>
> Signed-off-by: Michel Pollet <michel.pollet@bp.renesas.com>
> ---
> .../bindings/clock/renesas,rzn1-clocks.txt | 44 ++++++++++++++++++++++
> 1 file changed, 44 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/clock/renesas,rzn1-clocks.txt
>
> diff --git a/Documentation/devicetree/bindings/clock/renesas,rzn1-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rzn1-clocks.txt
> new file mode 100644
> index 0000000..0c41137
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/renesas,rzn1-clocks.txt
> @@ -0,0 +1,44 @@
> +* Renesas RZ/N1 Clock Driver
> +
> +This driver provides the clock infrastructure used by all the other drivers.
Bindings document h/w not drivers.
> +
> +One of the 'special' feature of this infrastructure is that Linux doesn't
Bindings are not just for Linux.
> +necessary 'own' all the clocks on the SoC, some other OS runs on
> +the Cortex-M3 core and that OS can access and claim it's own clocks.
How is this relevant to the binding?
> +
> +Required Properties:
> +
> + - compatible: Must be
> + - "renesas,r9a06g032-clocks" for the RZ/N1D
> + and "renesas,rzn1-clocks" as a fallback.
Is "clocks" how the chip reference manual refers to this block?
> + - reg: Base address and length of the memory resource used by the driver
> + - #clock-cells: Must be 1
> +
> +Examples
> +--------
> +
> + - Clock driver device node:
> +
> + clock: clocks@4000c000 {
clock-controller@...
> + compatible = "renesas,r9a06g032-clocks",
> + "renesas,rzn1-clocks";
> + reg = <0x4000c000 0x1000>;
> + status = "okay";
Don't show status in examples. (Plus, I doubt you ever want to have this
disabled, so you don't need the property in your dts files either).
> + #clock-cells = <1>;
> + };
> +
> +
> + - Other drivers can use the clocks as in:
s/drivers/nodes/
> +
> + uart0: serial@40060000 {
> + compatible = "snps,dw-apb-uart";
> + reg = <0x40060000 0x400>;
> + interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
> + reg-shift = <2>;
> + reg-io-width = <4>;
> + clocks = <&clock RZN1_CLK_UART0>;
> + clock-names = "baudclk";
> + };
> + Note the use of RZN1_CLK_UART0 -- these constants are declared in
> + the rzn1-clocks.h header file. These are not hardware based constants
> + and are Linux specific.
No, they are not Linux specific. They are part of the DT ABI.
While it is not a requirement to base them on some h/w numbering, it is
preferred if you can. That usually only works if you can base them on
bit positions or register offsets.
Rob
^ permalink raw reply
* Re: [PATCH v6 2/6] dt-bindings: Add the rzn1-clocks.h file
From: Rob Herring @ 2018-05-22 16:00 UTC (permalink / raw)
To: Michel Pollet
Cc: linux-renesas-soc, Simon Horman, phil.edworthy, Michel Pollet,
Magnus Damm, Mark Rutland, Michael Turquette, Stephen Boyd,
Geert Uytterhoeven, devicetree, linux-kernel, linux-clk
In-Reply-To: <1526983321-41949-3-git-send-email-michel.pollet@bp.renesas.com>
On Tue, May 22, 2018 at 11:01:22AM +0100, Michel Pollet wrote:
> This adds the constants necessary to use the renesas,rzn1-clocks driver.
>
> Signed-off-by: Michel Pollet <michel.pollet@bp.renesas.com>
> ---
> include/dt-bindings/clock/rzn1-clocks.h | 187 ++++++++++++++++++++++++++++++++
> 1 file changed, 187 insertions(+)
> create mode 100644 include/dt-bindings/clock/rzn1-clocks.h
Reviewed-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v6 2/5] Documentation: dt-bindings: phy: add phy_config for Rockchip USB Type-C PHY
From: Rob Herring @ 2018-05-22 15:58 UTC (permalink / raw)
To: Lin Huang
Cc: devicetree, airlied, linux-kernel, briannorris, dianders, kishon,
linux-rockchip, dri-devel, zyw, daniel.vetter, linux-arm-kernel
In-Reply-To: <1526895424-22894-2-git-send-email-hl@rock-chips.com>
On Mon, May 21, 2018 at 05:37:01PM +0800, Lin Huang wrote:
> If want to do training outside DP Firmware, need phy voltage swing
> and pre_emphasis value.
>
> Signed-off-by: Lin Huang <hl@rock-chips.com>
> ---
> Changes in v2:
> - None
> Changes in v3:
> - modify property description and add this property to Example
> Changes in v4:
> - None
> Changes in v5:
> - None
> Changes in v6:
> - change rockchip,phy_config to rockchip,phy-config and descript it in detail.
>
> .../devicetree/bindings/phy/phy-rockchip-typec.txt | 36 +++++++++++++++++++++-
> 1 file changed, 35 insertions(+), 1 deletion(-)
Reviewed-by: Rob Herring <robh@kernel.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply
* Re: [PATCH v5 5/8] dt-bindings: remoteproc: Add Q6v5 Modem PIL binding for SDM845
From: Rob Herring @ 2018-05-22 15:56 UTC (permalink / raw)
To: Sibi Sankar
Cc: bjorn.andersson, p.zabel, linux-remoteproc, linux-kernel,
devicetree, georgi.djakov, jassisinghbrar, ohad, mark.rutland,
kyan, sricharan, akdwived, linux-arm-msm, tsoni
In-Reply-To: <20180521172714.8551-6-sibis@codeaurora.org>
On Mon, May 21, 2018 at 10:57:11PM +0530, Sibi Sankar wrote:
> Add new compatible string for Qualcomm SDM845 SoCs
>
> Signed-off-by: Sibi Sankar <sibis@codeaurora.org>
> ---
> Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt | 1 +
> 1 file changed, 1 insertion(+)
Please add acks/reviewed-bys when posting new versions.
Rob
^ permalink raw reply
* Re: [PATCH v9 11/15] dt-bindings: cpufreq: Document operating-points-v2-kryo-cpu
From: Rob Herring @ 2018-05-22 15:55 UTC (permalink / raw)
To: Ilia Lin
Cc: mturquette, sboyd, mark.rutland, viresh.kumar, nm, lgirdwood,
broonie, andy.gross, david.brown, catalin.marinas, will.deacon,
rjw, linux-clk, devicetree, linux-kernel, linux-pm, linux-arm-msm,
linux-soc, linux-arm-kernel, rnayak, amit.kucheria,
nicolas.dechesne, celster, tfinkel
In-Reply-To: <1526901932-9514-12-git-send-email-ilialin@codeaurora.org>
On Mon, May 21, 2018 at 02:25:28PM +0300, Ilia Lin wrote:
> The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
> to provide the OPP framework with required information.
> This is used to determine the voltage and frequency value for each OPP of
> operating-points-v2 table when it is parsed by the OPP framework.
>
> This change adds documentation for the DT bindings.
> The "operating-points-v2-kryo-cpu" DT extends the "operating-points-v2"
> with following parameters:
> - nvmem-cells (NVMEM area containig the speedbin information)
> - opp-supported-hw: A single 32 bit bitmap value,
> representing compatible HW:
> 0: MSM8996 V3, speedbin 0
> 1: MSM8996 V3, speedbin 1
> 2: MSM8996 V3, speedbin 2
> 3: unused
> 4: MSM8996 SG, speedbin 0
> 5: MSM8996 SG, speedbin 1
> 6: MSM8996 SG, speedbin 2
> 7-31: unused
>
> Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Please add acks/reviewed-bys when posting new versions.
Rob
^ permalink raw reply
* Re: [PATCH v2 3/3] ARM: dts: stm32: Add ADC support to stm32mp157c
From: Fabrice Gasnier @ 2018-05-22 15:51 UTC (permalink / raw)
To: Jonathan Cameron, alexandre.torgue
Cc: mark.rutland, devicetree, benjamin.gaignard, lars,
mcoquelin.stm32, linux-iio, linux-kernel, robh+dt, pmeerw,
knaack.h, linux-arm-kernel
In-Reply-To: <20180507182317.7406c682@archlinux>
On 05/07/2018 07:23 PM, Jonathan Cameron wrote:
> On Wed, 2 May 2018 09:44:51 +0200
> Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
>
>> stm32mp157c has an ADC block with two physical ADCs.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> The driver support is now queued up in the IIO tree and should
> be in Linux next later this week.
Hi,
Many Thanks Jonathan :-)
Alex, I just sent an updated version (v3) of this patch (with additional
dmas).
Best Regards,
Fabrice
>
> Thanks,
>
> Jonathan
>
>> ---
>> arch/arm/boot/dts/stm32mp157c.dtsi | 32 ++++++++++++++++++++++++++++++++
>> 1 file changed, 32 insertions(+)
>>
>> diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
>> index bc3eddc..7758a90 100644
>> --- a/arch/arm/boot/dts/stm32mp157c.dtsi
>> +++ b/arch/arm/boot/dts/stm32mp157c.dtsi
>> @@ -160,6 +160,38 @@
>> status = "disabled";
>> };
>>
>> + adc: adc@48003000 {
>> + compatible = "st,stm32mp1-adc-core";
>> + reg = <0x48003000 0x400>;
>> + interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
>> + <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&rcc ADC12>, <&rcc ADC12_K>;
>> + clock-names = "bus", "adc";
>> + interrupt-controller;
>> + #interrupt-cells = <1>;
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + status = "disabled";
>> +
>> + adc1: adc@0 {
>> + compatible = "st,stm32mp1-adc";
>> + #io-channel-cells = <1>;
>> + reg = <0x0>;
>> + interrupt-parent = <&adc>;
>> + interrupts = <0>;
>> + status = "disabled";
>> + };
>> +
>> + adc2: adc@100 {
>> + compatible = "st,stm32mp1-adc";
>> + #io-channel-cells = <1>;
>> + reg = <0x100>;
>> + interrupt-parent = <&adc>;
>> + interrupts = <1>;
>> + status = "disabled";
>> + };
>> + };
>> +
>> rcc: rcc@50000000 {
>> compatible = "st,stm32mp1-rcc", "syscon";
>> reg = <0x50000000 0x1000>;
>
^ permalink raw reply
* [PATCH v3] ARM: dts: stm32: Add ADC support to stm32mp157c
From: Fabrice Gasnier @ 2018-05-22 15:45 UTC (permalink / raw)
To: alexandre.torgue
Cc: robh+dt, mcoquelin.stm32, mark.rutland, linux-arm-kernel,
devicetree, linux-kernel, fabrice.gasnier
stm32mp157c has an ADC block with two physical ADCs.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v3:
- Add dmas since dmamux1 has been added on top of stm32-next
---
arch/arm/boot/dts/stm32mp157c.dtsi | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index b66f673..66d7496 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -600,6 +600,42 @@
clocks = <&rcc DMAMUX>;
};
+ adc: adc@48003000 {
+ compatible = "st,stm32mp1-adc-core";
+ reg = <0x48003000 0x400>;
+ interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rcc ADC12>, <&rcc ADC12_K>;
+ clock-names = "bus", "adc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ adc1: adc@0 {
+ compatible = "st,stm32mp1-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ dmas = <&dmamux1 9 0x400 0x01>;
+ dma-names = "rx";
+ status = "disabled";
+ };
+
+ adc2: adc@100 {
+ compatible = "st,stm32mp1-adc";
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ interrupt-parent = <&adc>;
+ interrupts = <1>;
+ dmas = <&dmamux1 10 0x400 0x01>;
+ dma-names = "rx";
+ status = "disabled";
+ };
+ };
+
rcc: rcc@50000000 {
compatible = "st,stm32mp1-rcc", "syscon";
reg = <0x50000000 0x1000>;
--
1.9.1
^ permalink raw reply related
* Re: [PATCH 1/2] arm64: dts: renesas: r8a77980: add GEther support
From: Sergei Shtylyov @ 2018-05-22 15:44 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Mark Rutland,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Magnus Damm, Simon Horman, Catalin Marinas, Will Deacon,
Linux-Renesas, Rob Herring, Linux ARM
In-Reply-To: <CAMuHMdW9UzOGV_zh3NxugYrj0j0qszX+NFoZtFvZRD9CNMHt+w@mail.gmail.com>
On 05/22/2018 02:48 PM, Geert Uytterhoeven wrote:
>> Define the generic R8A77980 part of the GEther device node.
>>
>> Based on the original (and large) patch by Vladimir Barinov.
>>
>> Signed-off-by: Vladimir Barinov <vladimir.barinov@cogentembedded.com>
>> Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>
>
> Thanks for your patch!
>
> With the below addressed:
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Thanks!
>> --- renesas.orig/arch/arm64/boot/dts/renesas/r8a77980.dtsi
>> +++ renesas/arch/arm64/boot/dts/renesas/r8a77980.dtsi
>> @@ -417,6 +417,17 @@
>> dma-channels = <16>;
>> };
>>
>> + gether: ethernet@e7400000 {
>> + compatible = "renesas,gether-r8a77980";
>> + reg = <0 0xe7400000 0 0x1000>;
>> + interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&cpg CPG_MOD 813>;
>> + power-domains = <&sysc R8A77980_PD_ALWAYS_ON>;
>
> resets = <&cpg 813>;
As usual...
>
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> + status = "disabled";
>
> Any default phy-mode needed?
A default "phy-mode" IMO make sense when the MAC supports a single
PHY interface mode. In this case, both RMII and RGMII are supported, so
I coulsn't choose a default...
>> + };
>> +
>> mmc0: mmc@ee140000 {
>> compatible = "renesas,sdhi-r8a77980",
>> "renesas,rcar-gen3-sdhi";
>
>
> Gr{oetje,eeting}s,
>
> Geert
>
^ permalink raw reply
* Re: [PATCH 4/8] mailbox: tegra-hsp: Refactor in preparation of mailboxes
From: Jon Hunter @ 2018-05-22 15:36 UTC (permalink / raw)
To: Mikko Perttunen, robh+dt, mark.rutland, jassisinghbrar, gregkh,
thierry.reding
Cc: araza, devicetree, linux-serial, linux-tegra, linux-arm-kernel,
linux-kernel
In-Reply-To: <20180508114403.14499-5-mperttunen@nvidia.com>
On 08/05/18 12:43, Mikko Perttunen wrote:
> The HSP driver is currently in many places written with the assumption
> of only supporting doorbells. Prepare for the addition of shared
> mailbox support by removing these assumptions and cleaning up the code.
>
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
> drivers/mailbox/tegra-hsp.c | 124 +++++++++++++++++++++++++++++---------------
> 1 file changed, 82 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
> index 0cde356c11ab..16eb970f2c9f 100644
> --- a/drivers/mailbox/tegra-hsp.c
> +++ b/drivers/mailbox/tegra-hsp.c
> @@ -1,5 +1,5 @@
> /*
> - * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
> + * Copyright (c) 2016-2018, NVIDIA CORPORATION. All rights reserved.
> *
> * This program is free software; you can redistribute it and/or modify it
> * under the terms and conditions of the GNU General Public License,
> @@ -42,6 +42,7 @@ struct tegra_hsp_channel;
> struct tegra_hsp;
>
> struct tegra_hsp_channel {
> + unsigned int type;
> struct tegra_hsp *hsp;
> struct mbox_chan *chan;
> void __iomem *regs;
> @@ -55,6 +56,12 @@ struct tegra_hsp_doorbell {
> unsigned int index;
> };
>
> +static inline struct tegra_hsp_doorbell *
> +channel_to_doorbell(struct tegra_hsp_channel *channel)
> +{
> + return container_of(channel, struct tegra_hsp_doorbell, channel);
> +}
> +
> struct tegra_hsp_db_map {
> const char *name;
> unsigned int master;
> @@ -69,7 +76,7 @@ struct tegra_hsp {
> const struct tegra_hsp_soc *soc;
> struct mbox_controller mbox;
> void __iomem *regs;
> - unsigned int irq;
> + unsigned int doorbell_irq;
> unsigned int num_sm;
> unsigned int num_as;
> unsigned int num_ss;
> @@ -194,7 +201,7 @@ tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
> if (!db)
> return ERR_PTR(-ENOMEM);
>
> - offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16;
> + offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) * SZ_64K;
> offset += index * 0x100;
>
> db->channel.regs = hsp->regs + offset;
> @@ -218,18 +225,8 @@ static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell *db)
> kfree(db);
> }
>
> -static int tegra_hsp_doorbell_send_data(struct mbox_chan *chan, void *data)
> -{
> - struct tegra_hsp_doorbell *db = chan->con_priv;
> -
> - tegra_hsp_channel_writel(&db->channel, 1, HSP_DB_TRIGGER);
> -
> - return 0;
> -}
> -
> -static int tegra_hsp_doorbell_startup(struct mbox_chan *chan)
> +static int tegra_hsp_doorbell_startup(struct tegra_hsp_doorbell *db)
> {
> - struct tegra_hsp_doorbell *db = chan->con_priv;
> struct tegra_hsp *hsp = db->channel.hsp;
> struct tegra_hsp_doorbell *ccplex;
> unsigned long flags;
> @@ -260,9 +257,8 @@ static int tegra_hsp_doorbell_startup(struct mbox_chan *chan)
> return 0;
> }
>
> -static void tegra_hsp_doorbell_shutdown(struct mbox_chan *chan)
> +static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_doorbell *db)
> {
> - struct tegra_hsp_doorbell *db = chan->con_priv;
> struct tegra_hsp *hsp = db->channel.hsp;
> struct tegra_hsp_doorbell *ccplex;
> unsigned long flags;
> @@ -281,35 +277,61 @@ static void tegra_hsp_doorbell_shutdown(struct mbox_chan *chan)
> spin_unlock_irqrestore(&hsp->lock, flags);
> }
>
> -static const struct mbox_chan_ops tegra_hsp_doorbell_ops = {
> - .send_data = tegra_hsp_doorbell_send_data,
> - .startup = tegra_hsp_doorbell_startup,
> - .shutdown = tegra_hsp_doorbell_shutdown,
> +static int tegra_hsp_send_data(struct mbox_chan *chan, void *data)
> +{
> + struct tegra_hsp_channel *channel = chan->con_priv;
> + struct tegra_hsp_doorbell *db;
> +
> + switch (channel->type) {
> + case TEGRA_HSP_MBOX_TYPE_DB:
> + db = channel_to_doorbell(channel);
> + tegra_hsp_channel_writel(&db->channel, 1, HSP_DB_TRIGGER);
The above appears to be redundant. We go from channel to db and then end
up passing channels. Do we really need the 'db' struct above?
> + }
> +
> + return -EINVAL;
Does this function always return -EINVAL?
> +}
> +
> +static int tegra_hsp_startup(struct mbox_chan *chan)
> +{
> + struct tegra_hsp_channel *channel = chan->con_priv;
> +
> + switch (channel->type) {
> + case TEGRA_HSP_MBOX_TYPE_DB:
> + return tegra_hsp_doorbell_startup(channel_to_doorbell(channel));
> + }
> +
> + return -EINVAL;
> +}
> +
> +static void tegra_hsp_shutdown(struct mbox_chan *chan)
> +{
> + struct tegra_hsp_channel *channel = chan->con_priv;
> +
> + switch (channel->type) {
> + case TEGRA_HSP_MBOX_TYPE_DB:
> + tegra_hsp_doorbell_shutdown(channel_to_doorbell(channel));
> + break;
> + }
> +}
> +
> +static const struct mbox_chan_ops tegra_hsp_ops = {
> + .send_data = tegra_hsp_send_data,
> + .startup = tegra_hsp_startup,
> + .shutdown = tegra_hsp_shutdown,
> };
>
> -static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
> - const struct of_phandle_args *args)
> +static struct mbox_chan *tegra_hsp_doorbell_xlate(struct tegra_hsp *hsp,
> + unsigned int master)
> {
> struct tegra_hsp_channel *channel = ERR_PTR(-ENODEV);
> - struct tegra_hsp *hsp = to_tegra_hsp(mbox);
> - unsigned int type = args->args[0];
> - unsigned int master = args->args[1];
> struct tegra_hsp_doorbell *db;
> struct mbox_chan *chan;
> unsigned long flags;
> unsigned int i;
>
> - switch (type) {
> - case TEGRA_HSP_MBOX_TYPE_DB:
> - db = tegra_hsp_doorbell_get(hsp, master);
> - if (db)
> - channel = &db->channel;
> -
> - break;
> -
> - default:
> - break;
> - }
> + db = tegra_hsp_doorbell_get(hsp, master);
> + if (db)
> + channel = &db->channel;
>
> if (IS_ERR(channel))
> return ERR_CAST(channel);
> @@ -321,6 +343,7 @@ static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
> if (!chan->con_priv) {
> chan->con_priv = channel;
> channel->chan = chan;
> + channel->type = TEGRA_HSP_MBOX_TYPE_DB;
> break;
I see that you are making the above only used for doorbells, but don't
we still need to set the chan->con_priv for shared mailboxes as well?
Cheers
Jon
--
nvpublic
^ permalink raw reply
* Re: [PATCH 2/8] dt-bindings: serial: Add bindings for nvidia,tegra194-tcu
From: Jon Hunter @ 2018-05-22 15:15 UTC (permalink / raw)
To: Mikko Perttunen, robh+dt, mark.rutland, jassisinghbrar, gregkh,
thierry.reding
Cc: devicetree, araza, linux-kernel, linux-serial, linux-tegra,
linux-arm-kernel
In-Reply-To: <20180508114403.14499-3-mperttunen@nvidia.com>
On 08/05/18 12:43, Mikko Perttunen wrote:
> Add bindings for the Tegra Combined UART device used to talk to the
> UART console on Tegra194 systems.
>
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
> .../bindings/serial/nvidia,tegra194-tcu.txt | 35 ++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/serial/nvidia,tegra194-tcu.txt
>
> diff --git a/Documentation/devicetree/bindings/serial/nvidia,tegra194-tcu.txt b/Documentation/devicetree/bindings/serial/nvidia,tegra194-tcu.txt
> new file mode 100644
> index 000000000000..86763bc5d74f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/serial/nvidia,tegra194-tcu.txt
> @@ -0,0 +1,35 @@
> +NVIDIA Tegra Combined UART (TCU)
> +
> +The TCU is a system for sharing a hardware UART instance among multiple
> +systems withing the Tegra SoC. It is implemented through a mailbox-
> +based protocol where each "virtual UART" has a pair of mailboxes, one
> +for transmitting and one for receiving, that is used to communicate
> +with the hardware implementing the TCU.
> +
> +Required properties:
> +- name : Should be tcu
> +- compatible
> + Array of strings
> + One of:
> + - "nvidia,tegra194-tcu"
Nit. We should say what device the above compatibility is applicable for ...
- "nvidia,tegra194-tcu": for Tegra194
Cheers
Jon
--
nvpublic
^ permalink raw reply
* Re: [PATCH 1/8] dt-bindings: tegra186-hsp: Add shared interrupts
From: Jon Hunter @ 2018-05-22 15:15 UTC (permalink / raw)
To: Mikko Perttunen, robh+dt, mark.rutland, jassisinghbrar, gregkh,
thierry.reding
Cc: araza, devicetree, linux-serial, linux-tegra, linux-arm-kernel,
linux-kernel
In-Reply-To: <20180508114403.14499-2-mperttunen@nvidia.com>
On 08/05/18 12:43, Mikko Perttunen wrote:
> Non-doorbell interrupts are routed through "shared interrupts". These
> interrupts can be mapped to various internal interrupt lines. Add
> interrupt properties for shared interrupts to the tegra186-hsp device
> tree bindings.
Reading the Tegra documentation, although the doorbells have dedicated
interrupts, it appears that the doorbell interrupts can also be routed
via these shared interrupts.
> Signed-off-by: Mikko Perttunen <mperttunen@nvidia.com>
> ---
> Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
> index b99d25fc2f26..9edcdf82d719 100644
> --- a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
> +++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
> @@ -21,6 +21,8 @@ Required properties:
> Contains a list of names for the interrupts described by the interrupt
> property. May contain the following entries, in any order:
> - "doorbell"
> + - "sharedN", where 'N' is a number from zero up to the number of
> + external interrupts supported by the HSP instance minus one.
> Users of this binding MUST look up entries in the interrupt property
> by name, using this interrupt-names property to do so.
> - interrupts
How is the mapping of shared-mailboxes interrupts to the actual
'sharedN' interrupt managed?
Cheers
Jon
--
nvpublic
^ permalink raw reply
* RE: [patch v21 1/4] drivers: jtag: Add JTAG core driver
From: Oleksandr Shamray @ 2018-05-22 15:00 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Greg Kroah-Hartman, Arnd Bergmann, Linux Kernel Mailing List,
linux-arm Mailing List, devicetree, openbmc@lists.ozlabs.org,
Joel Stanley, Jiří Pírko, Tobias Klauser,
open list:SERIAL DRIVERS, Vadim Pasternak, system-sw-low-level,
Rob Herring, openocd-devel-owner@lists.sourceforge.net,
linux-api@vger.kernel.org
In-Reply-To: <CAHp75VctG5RBtLF3nT5e_j7_e9O12u=5gerJ6OMxWbABM9ft2w@mail.gmail.com>
Hi Andy.
Thanks for review.
Please read my answers inline.
> -----Original Message-----
> From: Andy Shevchenko [mailto:andy.shevchenko@gmail.com]
> Sent: 16 мая 2018 г. 0:22
> To: Oleksandr Shamray <oleksandrs@mellanox.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Arnd Bergmann
> <arnd@arndb.de>; Linux Kernel Mailing List
> <linux-kernel@vger.kernel.org>; linux-arm Mailing List
> <linux-arm-kernel@lists.infradead.org>; devicetree
> <devicetree@vger.kernel.org>; openbmc@lists.ozlabs.org; Joel Stanley
> <joel@jms.id.au>; Jiří Pírko <jiri@resnulli.us>; Tobias Klauser
> <tklauser@distanz.ch>; open list:SERIAL DRIVERS <linux-
> serial@vger.kernel.org>; Vadim Pasternak <vadimp@mellanox.com>;
> system-sw-low-level <system-sw-low-level@mellanox.com>; Rob Herring
> <robh+dt@kernel.org>; openocd-devel-owner@lists.sourceforge.net;
> linux- api@vger.kernel.org; David S. Miller <davem@davemloft.net>;
> Mauro Carvalho Chehab <mchehab@kernel.org>; Jiri Pirko
> <jiri@mellanox.com>
> Subject: Re: [patch v21 1/4] drivers: jtag: Add JTAG core driver
>
> On Tue, May 15, 2018 at 5:21 PM, Oleksandr Shamray
> <oleksandrs@mellanox.com> wrote:
> > Initial patch for JTAG driver
> > JTAG class driver provide infrastructure to support
> > hardware/software JTAG platform drivers. It provide user layer API
> > interface for flashing and debugging external devices which equipped
> > with JTAG interface using standard transactions.
> >
> > Driver exposes set of IOCTL to user space for:
> > - XFER:
> > - SIR (Scan Instruction Register, IEEE 1149.1 Data Register scan);
> > - SDR (Scan Data Register, IEEE 1149.1 Instruction Register scan);
> > - RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
> > number of clocks).
> > - SIOCFREQ/GIOCFREQ for setting and reading JTAG frequency.
> >
> > Driver core provides set of internal APIs for allocation and
> > registration:
> > - jtag_register;
> > - jtag_unregister;
> > - jtag_alloc;
> > - jtag_free;
> >
> > Platform driver on registration with jtag-core creates the next
> > entry in dev folder:
> > /dev/jtagX
>
> > 0xB0 all RATIO devices in development:
> > <mailto:vgo@ratio.de>
> > 0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
> > +0xB2 00-0f linux/jtag.h JTAG driver
> > +
> > +<mailto:oleksandrs@mellanox.com>
>
> Consider to preserve style (upper vs. lower).
JTAG in code is lower (jtag) cane but in descriptions and notes it is upper (JTAG).
In all places which do not correspond to this I will fix.
>
> > + This provides basic core functionality support for JTAG class devices.
> > + Hardware that is equipped with a JTAG microcontroller can be
> > + supported by using this driver's interfaces.
> > + This driver exposes a set of IOCTLs to the user space for
> > + the following commands:
> > + SDR: (Scan Data Register) Performs an IEEE 1149.1 Data Register scan
> > + SIR: (Scan Instruction Register) Performs an IEEE 1149.1 Instruction
> > + Register scan.
> > + RUNTEST: Forces the IEEE 1149.1 bus to a run state for a specified
> > + number of clocks or a specified time period.
>
> Something feels wrong with formatting here.
>
Will fix
> > +#define MAX_JTAG_NAME_LEN (sizeof("jtag") + 5)
>
> Interesting definition. Why not to set to 10 explicitly? And why 10?
> (16 sounds better)
>
5 - is a max len of JTAG device id in device name. I will add define to it.
In general I don't see the case for the system with hundreds JTAG interfaces.
I will limit maximum jtag master to 255 devices and change id len to 3
#define MAX_JTAG_ID_STR_LEN 5
#define MAX_JTAG_NAME_LEN (sizeof("jtag") + MAX_JTAG_ID_STR_LEN)
> > +struct jtag {
> > + struct miscdevice miscdev;
>
> > + struct device *dev;
>
> Doesn't miscdev parent contain exactly this one?
Yes.
Will fix.
>
> > + const struct jtag_ops *ops;
> > + int id;
> > + bool opened;
> > + struct mutex open_lock;
> > + unsigned long priv[0];
> > +};
>
> > + err = copy_to_user(u64_to_user_ptr(xfer.tdio),
> > + (void *)(xfer_data), data_size);
>
> Redundant parens in one case. Check the rest similar places.
>
Yes.
> > +static int jtag_open(struct inode *inode, struct file *file) {
>
> > + struct jtag *jtag = container_of(file->private_data, struct jtag,
> > + miscdev);
>
> I would don't care about length and put it on one line.
>
But following to LINUX kernel style, it should be no longer than 80 symbols.
It will not pass by ./scripts/checkpatch.pl
Will it be OK to send a patch which failed 80 symbols limitation check?
> > + if (jtag->opened) {
> > + jtag->opened = true;
> > + jtag->opened = false;
>
> Can it be opened non exclusively several times? If so, this needs to
> be a ref counter instead.
It can be opened only once.
>
> > + if (!ops->idle || !ops->mode_set || !ops->status_get || !ops->xfer)
> > + return NULL;
>
> Are all of them mandatory?
>
Yes, except "mode_set"
Will remove mode_set from check
> > +int jtag_register(struct jtag *jtag)
>
> Perhaps devm_ variant.
Jtag driver uses miscdevice and related misc_register and misc_deregister
calls for creation and destruction. There is no device object prior to
call to misc_register, which could be used in devm_jtag_register.
>
> > +#define jtag_u64_to_ptr(arg) ((void *)(uintptr_t)arg)
>
Redundant. Removed.
> Where is this used or supposed to be used?
>
> > +#define JTAG_MAX_XFER_DATA_LEN 65535
>
> Is this limitation from some spec?
> Otherwise why not to allow 64K?
>
It not limited by specification.
But we enforce an upper bound for the length here, to prevent users from draining
kernel memory with giant buffers.
So it was limited by size of unsigned short int (65535)
> > +/**
> > + * struct jtag_ops - callbacks for jtag control functions:
> > + *
> > + * @freq_get: get frequency function. Filled by device driver
> > + * @freq_set: set frequency function. Filled by device driver
> > + * @status_get: set status function. Filled by device driver
> > + * @idle: set JTAG to idle state function. Filled by device driver
> > + * @xfer: send JTAG xfer function. Filled by device driver */
>
> Perhaps you need to describe which of them are _really_ mandatory and
> which are optional.
>
Ok
> --
> With Best Regards,
> Andy Shevchenko
Best Regards,
Oleksandr Shamray
^ permalink raw reply
* RE: [patch v21 2/4] drivers: jtag: Add Aspeed SoC 24xx and 25xx families JTAG master driver
From: Oleksandr Shamray @ 2018-05-22 15:00 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Greg Kroah-Hartman, Arnd Bergmann, Linux Kernel Mailing List,
linux-arm Mailing List, devicetree, openbmc@lists.ozlabs.org,
Joel Stanley, Jiří Pírko, Tobias Klauser,
open list:SERIAL DRIVERS, Vadim Pasternak, system-sw-low-level,
Rob Herring, openocd-devel-owner@lists.sourceforge.net,
linux-api@vger.kernel.org
In-Reply-To: <CAHp75Ve7EYiWBiE73i0CmyJhPMOdSKsGzCr0SUvta3Uya=Ov1Q@mail.gmail.com>
Hi Andy.
Thanks for review.
Please read my answers inline.
> -----Original Message-----
> From: Andy Shevchenko [mailto:andy.shevchenko@gmail.com]
> Sent: 16 мая 2018 г. 0:00
> To: Oleksandr Shamray <oleksandrs@mellanox.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Arnd Bergmann
> <arnd@arndb.de>; Linux Kernel Mailing List
> <linux-kernel@vger.kernel.org>; linux-arm Mailing List
> <linux-arm-kernel@lists.infradead.org>; devicetree
> <devicetree@vger.kernel.org>; openbmc@lists.ozlabs.org; Joel Stanley
> <joel@jms.id.au>; Jiří Pírko <jiri@resnulli.us>; Tobias Klauser
> <tklauser@distanz.ch>; open list:SERIAL DRIVERS <linux-
> serial@vger.kernel.org>; Vadim Pasternak <vadimp@mellanox.com>;
> system-sw-low-level <system-sw-low-level@mellanox.com>; Rob Herring
> <robh+dt@kernel.org>; openocd-devel-owner@lists.sourceforge.net;
> linux- api@vger.kernel.org; David S. Miller <davem@davemloft.net>;
> Mauro Carvalho Chehab <mchehab@kernel.org>; Jiri Pirko
> <jiri@mellanox.com>
> Subject: Re: [patch v21 2/4] drivers: jtag: Add Aspeed SoC 24xx and
> 25xx families JTAG master driver
>
> On Tue, May 15, 2018 at 5:21 PM, Oleksandr Shamray
> <oleksandrs@mellanox.com> wrote:
> > Driver adds support of Aspeed 2500/2400 series SOC JTAG master
> controller.
> >
> > Driver implements the following jtag ops:
> > - freq_get;
> > - freq_set;
> > - status_get;
> > - idle;
> > - xfer;
> >
> > It has been tested on Mellanox system with BMC equipped with Aspeed
> > 2520 SoC for programming CPLD devices.
>
> > +#define ASPEED_JTAG_DATA 0x00
> > +#define ASPEED_JTAG_INST 0x04
> > +#define ASPEED_JTAG_CTRL 0x08
> > +#define ASPEED_JTAG_ISR 0x0C
> > +#define ASPEED_JTAG_SW 0x10
> > +#define ASPEED_JTAG_TCK 0x14
> > +#define ASPEED_JTAG_EC 0x18
> > +
> > +#define ASPEED_JTAG_DATA_MSB 0x01
> > +#define ASPEED_JTAG_DATA_CHUNK_SIZE 0x20
>
>
> > +#define ASPEED_JTAG_IOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN |\
> > + ASPEED_JTAG_CTL_ENG_OUT_EN
> > +|\
> > +
> > +ASPEED_JTAG_CTL_INST_LEN(len))
>
> Better to read
>
> #define MY_COOL_CONST_OR_MACRO(xxx) \
> ...
>
> > +#define ASPEED_JTAG_DOUT_LEN(len) (ASPEED_JTAG_CTL_ENG_EN
> |\
> > + ASPEED_JTAG_CTL_ENG_OUT_EN
> > + |\
> > +
> > +ASPEED_JTAG_CTL_DATA_LEN(len))
>
> Ditto.
Ok. Changed to:
#define ASPEED_JTAG_IOUT_LEN(len) \
(ASPEED_JTAG_CTL_ENG_EN | \
ASPEED_JTAG_CTL_ENG_OUT_EN | \
ASPEED_JTAG_CTL_INST_LEN(len))
#define ASPEED_JTAG_DOUT_LEN(len) \
(ASPEED_JTAG_CTL_ENG_EN | \
ASPEED_JTAG_CTL_ENG_OUT_EN | \
ASPEED_JTAG_CTL_DATA_LEN(len))
>
> > +static char *end_status_str[] = {"idle", "ir pause", "drpause"};
>
> > +static int aspeed_jtag_freq_set(struct jtag *jtag, u32 freq) {
> > + struct aspeed_jtag *aspeed_jtag = jtag_priv(jtag);
> > + unsigned long apb_frq;
> > + u32 tck_val;
> > + u16 div;
> > +
> > + apb_frq = clk_get_rate(aspeed_jtag->pclk);
>
> > + div = (apb_frq % freq == 0) ? (apb_frq / freq) - 1 :
> > + (apb_frq / freq);
>
> Isn't it the same as
>
> div = (apb_frq - 1) / freq;
>
> ?
>
Seems it is same. Thanks.
> > + tck_val = aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_TCK);
> > + aspeed_jtag_write(aspeed_jtag,
> > + (tck_val & ASPEED_JTAG_TCK_DIVISOR_MASK) | div,
> > + ASPEED_JTAG_TCK);
> > + return 0;
> > +}
>
> > +static void aspeed_jtag_sw_delay(struct aspeed_jtag *aspeed_jtag,
> > +int
> > +cnt) {
> > + int i;
> > +
> > + for (i = 0; i < cnt; i++)
> > + aspeed_jtag_read(aspeed_jtag, ASPEED_JTAG_SW);
>
> Isn't it readsl() (or how it's called, I don't remember).
>
No, readsl reads data into buffer. But in this place read used for make
software delay.
Aspeed jtag driver supports 2 modes:
1 - hw mode with the hardware controlled JTAG states
and pins
2 - with software controlled pins.
This part of code used in sw-mode and generates delay for JTAG bit-bang .
I will change it to ndelay().
> > +}
>
> > +static void aspeed_jtag_wait_instruction_pause(struct aspeed_jtag
> > +*aspeed_jtag) {
> > + wait_event_interruptible(aspeed_jtag->jtag_wq, aspeed_jtag->flag &
> > + ASPEED_JTAG_ISR_INST_PAUSE);
>
> In such cases I prefer to see a new line with a parameter in full.
> Check all places.
>
> > + aspeed_jtag->flag &= ~ASPEED_JTAG_ISR_INST_PAUSE; }
>
> > +static void aspeed_jtag_sm_cycle(struct aspeed_jtag *aspeed_jtag,
> > +const
> u8 *tms,
> > + int len) {
> > + int i;
> > +
> > + for (i = 0; i < len; i++)
> > + aspeed_jtag_tck_cycle(aspeed_jtag, tms[i], 0); }
> > +
> > +static void aspeed_jtag_run_test_idle_sw(struct aspeed_jtag
> *aspeed_jtag,
> > + struct jtag_run_test_idle
> > +*runtest) {
> > + static const u8 sm_pause_irpause[] = {1, 1, 1, 1, 0, 1, 0};
> > + static const u8 sm_pause_drpause[] = {1, 1, 1, 0, 1, 0};
> > + static const u8 sm_idle_irpause[] = {1, 1, 0, 1, 0};
> > + static const u8 sm_idle_drpause[] = {1, 0, 1, 0};
> > + static const u8 sm_pause_idle[] = {1, 1, 0};
> > + int i;
> > +
> > + /* SW mode from idle/pause-> to pause/idle */
> > + if (runtest->reset) {
> > + for (i = 0; i < ASPEED_JTAG_RESET_CNTR; i++)
> > + aspeed_jtag_tck_cycle(aspeed_jtag, 1, 0);
> > + }
>
> I would rather split this big switch to a few helper functions per
> each status of surrounding switch.
>
Ok.
Will do it.
> > +
> > + /* Stay on IDLE for at least TCK cycle */
> > + for (i = 0; i < runtest->tck; i++)
> > + aspeed_jtag_tck_cycle(aspeed_jtag, 0, 0); }
>
>
> > +/**
> > + * aspeed_jtag_run_test_idle:
> > + * JTAG reset: generates at least 9 TMS high and 1 TMS low to force
> > + * devices into Run-Test/Idle State.
> > + */
>
> It's rather broken kernel doc.
Deleted.
>
> > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_CTL_ENG_EN |
> > + ASPEED_JTAG_CTL_ENG_OUT_EN |
> > + ASPEED_JTAG_CTL_FORCE_TMS,
> > + ASPEED_JTAG_CTRL);
>
> > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_EC_GO_IDLE,
> > + ASPEED_JTAG_EC);
>
> > + aspeed_jtag_write(aspeed_jtag, ASPEED_JTAG_SW_MODE_EN |
> > + ASPEED_JTAG_SW_MODE_TDIO, ASPEED_JTAG_SW);
>
> Here you have permutations of flag some of which are repeatetive in
> the code. Perhaps make additional definitions instead.
> Check other similar places.
>
Ok. Will add defined for repeated flags
>
> > + char tdo;
>
> Indentation.
Ok.
>
> > + if (xfer->direction == JTAG_READ_XFER)
> > + tdi = UINT_MAX;
> > + else
> > + tdi = data[index];
>
> > + if (xfer->direction == JTAG_READ_XFER)
> > + tdi = UINT_MAX;
> > + else
> > + tdi = data[index];
>
> Take your time to think how the above duplication can be avoided.
>
In both cases data[] is different, so I should check it twice, but I will
change it to, macro like:
#define ASPEED_JTAG_GET_TDI(direction, data) \
(direction == JTAG_READ_XFER) ? UNIT_MAX : data
> > + }
> > + }
> > +
> > + tdo = aspeed_jtag_tck_cycle(aspeed_jtag, 1, tdi &
> ASPEED_JTAG_DATA_MSB);
> > + data[index] |= tdo << (shift_bits %
> > +ASPEED_JTAG_DATA_CHUNK_SIZE); }
>
>
> > + if (endstate != JTAG_STATE_IDLE) {
>
> Why not to use positive check?
>
Will restructure to have positive check
if (endstate == JTAG_STATE_IDLE) {
...
} else {
...
}
> > + int i;
> > +
> > + for (i = 0; i <= xfer->length / BITS_PER_BYTE; i++) {
> > + pos += snprintf(&dbg_str[pos], sizeof(dbg_str) - pos,
> > + "0x%02x ", xfer_data[i]);
> > + }
>
> Oh, NO! Consider reading printk-formats (for %*ph) and other
> documentation about available APIs.
Ok.
>
> > + if (!(aspeed_jtag->mode & JTAG_XFER_HW_MODE)) {
> > + /* SW mode */
>
> This is rather too complex to be in one function.
>
Will split to separate functions.
> > + } else {
>
> > + /* hw mode */
> > + aspeed_jtag_write(aspeed_jtag, 0, ASPEED_JTAG_SW);
> > + aspeed_jtag_xfer_hw(aspeed_jtag, xfer, data);
>
> For symmetry it might be another function.
>
> > + }
>
> > + dev_dbg(aspeed_jtag->dev, "status %x\n", status);
>
> Perhaps someone should become familiar with tracepoints?
>
> > + dev_err(aspeed_jtag->dev, "irq status:%x\n",
> > + status);
>
>
> Huh, really?! SPAM.
I will review and delete redundant debug messages.
>
> (I would drop it completely, though you may use ratelimited variant)
>
> > + ret = IRQ_NONE;
> > + }
> > + return ret;
> > +}
>
> > + clk_prepare_enable(aspeed_jtag->pclk);
>
> This might fail.
Will add error check
>
> > + dev_dbg(&pdev->dev, "IRQ %d.\n", aspeed_jtag->irq);
>
> Noise even for debug.
Agree.
>
> > + err = jtag_register(jtag);
>
> Perhaps we might have devm_ variant of this. Check how SPI framework
> deal with a such.
>
Jtag driver uses miscdevice and related misc_register and misc_deregister
calls for creation and destruction. There is no device object prior
to call to misc_register, which could be used in devm_jtag_register.
> > +static int aspeed_jtag_remove(struct platform_device *pdev) {
>
> > + struct jtag *jtag;
> > +
> > + jtag = platform_get_drvdata(pdev);
>
> Usually we put this on one line
+
>
> > + aspeed_jtag_deinit(pdev, jtag_priv(jtag));
> > + jtag_unregister(jtag);
> > + jtag_free(jtag);
> > + return 0;
> > +}
>
>
> --
> With Best Regards,
> Andy Shevchenko
Best Regards,
Oleksandr Shamray
^ permalink raw reply
* Re: [RESEND PATCH 2/5] mtd: rawnand: add NVIDIA Tegra NAND Flash controller driver
From: Stefan Agner @ 2018-05-22 14:53 UTC (permalink / raw)
To: boris.brezillon, dwmw2, computersforpeace, marek.vasut, robh+dt,
mark.rutland, thierry.reding, mturquette, sboyd
Cc: dev, miquel.raynal, richard, marcel, krzk, digetx,
benjamin.lindqvist, jonathanh, pdeschrijver, pgaikwad, mirza.krak,
linux-mtd, linux-tegra, devicetree, linux-kernel, linux-clk
In-Reply-To: <86fdf19ec92b732709732fb60199f16488b4b727.1526990589.git.stefan@agner.ch>
Hi,
I do have some questions for some areas I wanted to improve in the next
revision. But I would like to make sure that the way I would like to
implement aligns with the MTD subsystem.
On 22.05.2018 14:07, Stefan Agner wrote:
> Add support for the NAND flash controller found on NVIDIA
> Tegra 2 SoCs. This implementation does not make use of the
> command queue feature. Regular operations/data transfers are
> done in PIO mode. Page read/writes with hardware ECC make
> use of the DMA for data transfer.
>
> Signed-off-by: Lucas Stach <dev@lynxeye.de>
> Signed-off-by: Stefan Agner <stefan@agner.ch>
> ---
> MAINTAINERS | 7 +
> drivers/mtd/nand/raw/Kconfig | 6 +
> drivers/mtd/nand/raw/Makefile | 1 +
> drivers/mtd/nand/raw/tegra_nand.c | 915 ++++++++++++++++++++++++++++++
> 4 files changed, 929 insertions(+)
> create mode 100644 drivers/mtd/nand/raw/tegra_nand.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 58b9861ccf99..a65739681279 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13844,6 +13844,13 @@ M: Laxman Dewangan <ldewangan@nvidia.com>
> S: Supported
> F: drivers/input/keyboard/tegra-kbc.c
>
> +TEGRA NAND DRIVER
> +M: Stefan Agner <stefan@agner.ch>
> +M: Lucas Stach <dev@lynxeye.de>
> +S: Maintained
> +F: Documentation/devicetree/bindings/mtd/nvidia,tegra20-nand.txt
> +F: drivers/mtd/nand/tegra_nand.c
> +
> TEGRA PWM DRIVER
> M: Thierry Reding <thierry.reding@gmail.com>
> S: Supported
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index 19a2b283fbbe..bd56264233ca 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -534,4 +534,10 @@ config MTD_NAND_MTK
> Enables support for NAND controller on MTK SoCs.
> This controller is found on mt27xx, mt81xx, mt65xx SoCs.
>
> +config MTD_NAND_TEGRA
> + tristate "Support for NAND on NVIDIA Tegra"
> + depends on ARCH_TEGRA
> + help
> + Enables support for NAND flash on NVIDIA Tegra SoC based boards.
> +
> endif # MTD_NAND
> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
> index 165b7ef9e9a1..d5a5f9832b88 100644
> --- a/drivers/mtd/nand/raw/Makefile
> +++ b/drivers/mtd/nand/raw/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
> obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
> obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
> obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
> +obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
>
> nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
> nand-objs += nand_amd.o
> diff --git a/drivers/mtd/nand/raw/tegra_nand.c
> b/drivers/mtd/nand/raw/tegra_nand.c
> new file mode 100644
> index 000000000000..fa236e683fb8
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/tegra_nand.c
> @@ -0,0 +1,915 @@
> +/*
> + * Copyright (C) 2018 Stefan Agner <stefan@agner.ch>
> + * Copyright (C) 2014-2015 Lucas Stach <dev@lynxeye.de>
> + * Copyright (C) 2012 Avionic Design GmbH
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/err.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/mtd/partitions.h>
> +#include <linux/mtd/rawnand.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#define CMD 0x00
> +#define CMD_GO (1 << 31)
> +#define CMD_CLE (1 << 30)
> +#define CMD_ALE (1 << 29)
> +#define CMD_PIO (1 << 28)
> +#define CMD_TX (1 << 27)
> +#define CMD_RX (1 << 26)
> +#define CMD_SEC_CMD (1 << 25)
> +#define CMD_AFT_DAT (1 << 24)
> +#define CMD_TRANS_SIZE(x) (((x - 1) & 0xf) << 20)
> +#define CMD_A_VALID (1 << 19)
> +#define CMD_B_VALID (1 << 18)
> +#define CMD_RD_STATUS_CHK (1 << 17)
> +#define CMD_RBSY_CHK (1 << 16)
> +#define CMD_CE(x) (1 << (8 + ((x) & 0x7)))
> +#define CMD_CLE_SIZE(x) (((x - 1) & 0x3) << 4)
> +#define CMD_ALE_SIZE(x) (((x - 1) & 0xf) << 0)
> +
> +#define STATUS 0x04
> +
> +#define ISR 0x08
> +#define ISR_CORRFAIL_ERR (1 << 24)
> +#define ISR_UND (1 << 7)
> +#define ISR_OVR (1 << 6)
> +#define ISR_CMD_DONE (1 << 5)
> +#define ISR_ECC_ERR (1 << 4)
> +
> +#define IER 0x0c
> +#define IER_ERR_TRIG_VAL(x) (((x) & 0xf) << 16)
> +#define IER_UND (1 << 7)
> +#define IER_OVR (1 << 6)
> +#define IER_CMD_DONE (1 << 5)
> +#define IER_ECC_ERR (1 << 4)
> +#define IER_GIE (1 << 0)
> +
> +#define CFG 0x10
> +#define CFG_HW_ECC (1 << 31)
> +#define CFG_ECC_SEL (1 << 30)
> +#define CFG_ERR_COR (1 << 29)
> +#define CFG_PIPE_EN (1 << 28)
> +#define CFG_TVAL_4 (0 << 24)
> +#define CFG_TVAL_6 (1 << 24)
> +#define CFG_TVAL_8 (2 << 24)
> +#define CFG_SKIP_SPARE (1 << 23)
> +#define CFG_BUS_WIDTH_8 (0 << 21)
> +#define CFG_BUS_WIDTH_16 (1 << 21)
> +#define CFG_COM_BSY (1 << 20)
> +#define CFG_PS_256 (0 << 16)
> +#define CFG_PS_512 (1 << 16)
> +#define CFG_PS_1024 (2 << 16)
> +#define CFG_PS_2048 (3 << 16)
> +#define CFG_PS_4096 (4 << 16)
> +#define CFG_SKIP_SPARE_SIZE_4 (0 << 14)
> +#define CFG_SKIP_SPARE_SIZE_8 (1 << 14)
> +#define CFG_SKIP_SPARE_SIZE_12 (2 << 14)
> +#define CFG_SKIP_SPARE_SIZE_16 (3 << 14)
> +#define CFG_TAG_BYTE_SIZE(x) ((x) & 0xff)
> +
> +#define TIMING_1 0x14
> +#define TIMING_TRP_RESP(x) (((x) & 0xf) << 28)
> +#define TIMING_TWB(x) (((x) & 0xf) << 24)
> +#define TIMING_TCR_TAR_TRR(x) (((x) & 0xf) << 20)
> +#define TIMING_TWHR(x) (((x) & 0xf) << 16)
> +#define TIMING_TCS(x) (((x) & 0x3) << 14)
> +#define TIMING_TWH(x) (((x) & 0x3) << 12)
> +#define TIMING_TWP(x) (((x) & 0xf) << 8)
> +#define TIMING_TRH(x) (((x) & 0xf) << 4)
> +#define TIMING_TRP(x) (((x) & 0xf) << 0)
> +
> +#define RESP 0x18
> +
> +#define TIMING_2 0x1c
> +#define TIMING_TADL(x) ((x) & 0xf)
> +
> +#define CMD_1 0x20
> +#define CMD_2 0x24
> +#define ADDR_1 0x28
> +#define ADDR_2 0x2c
> +
> +#define DMA_CTRL 0x30
> +#define DMA_CTRL_GO (1 << 31)
> +#define DMA_CTRL_IN (0 << 30)
> +#define DMA_CTRL_OUT (1 << 30)
> +#define DMA_CTRL_PERF_EN (1 << 29)
> +#define DMA_CTRL_IE_DONE (1 << 28)
> +#define DMA_CTRL_REUSE (1 << 27)
> +#define DMA_CTRL_BURST_1 (2 << 24)
> +#define DMA_CTRL_BURST_4 (3 << 24)
> +#define DMA_CTRL_BURST_8 (4 << 24)
> +#define DMA_CTRL_BURST_16 (5 << 24)
> +#define DMA_CTRL_IS_DONE (1 << 20)
> +#define DMA_CTRL_EN_A (1 << 2)
> +#define DMA_CTRL_EN_B (1 << 1)
> +
> +#define DMA_CFG_A 0x34
> +#define DMA_CFG_B 0x38
> +
> +#define FIFO_CTRL 0x3c
> +#define FIFO_CTRL_CLR_ALL (1 << 3)
> +
> +#define DATA_PTR 0x40
> +#define TAG_PTR 0x44
> +#define ECC_PTR 0x48
> +
> +#define DEC_STATUS 0x4c
> +#define DEC_STATUS_A_ECC_FAIL (1 << 1)
> +#define DEC_STATUS_ERR_COUNT_MASK 0x00ff0000
> +#define DEC_STATUS_ERR_COUNT_SHIFT 16
> +
> +#define HWSTATUS_CMD 0x50
> +#define HWSTATUS_MASK 0x54
> +#define HWSTATUS_RDSTATUS_MASK(x) (((x) & 0xff) << 24)
> +#define HWSTATUS_RDSTATUS_VALUE(x) (((x) & 0xff) << 16)
> +#define HWSTATUS_RBSY_MASK(x) (((x) & 0xff) << 8)
> +#define HWSTATUS_RBSY_VALUE(x) (((x) & 0xff) << 0)
> +
> +#define DEC_STAT_RESULT 0xd0
> +#define DEC_STAT_BUF 0xd4
> +#define DEC_STAT_BUF_CORR_SEC_FLAG_MASK 0x00ff0000
> +#define DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT 16
> +#define DEC_STAT_BUF_MAX_CORR_CNT_MASK 0x00001f00
> +#define DEC_STAT_BUF_MAX_CORR_CNT_SHIFT 8
> +
> +struct tegra_nand {
> + void __iomem *regs;
> + struct clk *clk;
> + struct gpio_desc *wp_gpio;
> +
> + struct nand_chip chip;
> + struct device *dev;
> +
> + struct completion command_complete;
> + struct completion dma_complete;
> + bool last_read_error;
> +
> + dma_addr_t data_dma;
> + void *data_buf;
> + dma_addr_t oob_dma;
> + void *oob_buf;
> +
> + int cur_chip;
> +};
> +
> +static inline struct tegra_nand *to_tegra_nand(struct mtd_info *mtd)
> +{
> + struct nand_chip *chip = mtd_to_nand(mtd);
> +
> + return nand_get_controller_data(chip);
> +}
> +
> +static int tegra_nand_ooblayout_16_ecc(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 4;
> + oobregion->length = 4;
> +
> + return 0;
> +}
> +
> +static int tegra_nand_ooblayout_16_free(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 8;
> + oobregion->length = 8;
> +
> + return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops tegra_nand_oob_16_ops = {
> + .ecc = tegra_nand_ooblayout_16_ecc,
> + .free = tegra_nand_ooblayout_16_free,
> +};
> +
> +static int tegra_nand_ooblayout_64_ecc(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 4;
> + oobregion->length = 36;
> +
> + return 0;
> +}
> +
> +static int tegra_nand_ooblayout_64_free(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 40;
> + oobregion->length = 24;
> +
> + return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops tegra_nand_oob_64_ops = {
> + .ecc = tegra_nand_ooblayout_64_ecc,
> + .free = tegra_nand_ooblayout_64_free,
> +};
> +
> +static int tegra_nand_ooblayout_128_ecc(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 4;
> + oobregion->length = 72;
> +
> + return 0;
> +}
> +
> +static int tegra_nand_ooblayout_128_free(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 76;
> + oobregion->length = 52;
> +
> + return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops tegra_nand_oob_128_ops = {
> + .ecc = tegra_nand_ooblayout_128_ecc,
> + .free = tegra_nand_ooblayout_128_free,
> +};
> +
> +static int tegra_nand_ooblayout_224_ecc(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 4;
> + oobregion->length = 144;
> +
> + return 0;
> +}
> +
> +static int tegra_nand_ooblayout_224_free(struct mtd_info *mtd, int section,
> + struct mtd_oob_region *oobregion)
> +{
> + if (section > 0)
> + return -ERANGE;
> +
> + oobregion->offset = 148;
> + oobregion->length = 76;
> +
> + return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops tegra_nand_oob_224_ops = {
> + .ecc = tegra_nand_ooblayout_224_ecc,
> + .free = tegra_nand_ooblayout_224_free,
> +};
The layout mostly depends on algorithm used. So for Reed-Solomon with
maximum symbol error of 8 it is probably better to create something
like:
static const struct mtd_ooblayout_ops tegra_nand_oob_rs_8_ops = {
.ecc = tegra_nand_ooblayout_rs_8_ecc,
.free = tegra_nand_ooblayout_rs_8_free,
};
And then use mtd->writesize/mtd->oobsize to calculate offset/length?
> +
> +static irqreturn_t tegra_nand_irq(int irq, void *data)
> +{
> + struct tegra_nand *nand = data;
> + u32 isr, dma;
> +
> + isr = readl(nand->regs + ISR);
> + dma = readl(nand->regs + DMA_CTRL);
> + dev_dbg(nand->dev, "isr %08x\n", isr);
> +
> + if (!isr && !(dma & DMA_CTRL_IS_DONE))
> + return IRQ_NONE;
> +
> + if (isr & ISR_CORRFAIL_ERR)
> + nand->last_read_error = true;
> +
> + if (isr & ISR_CMD_DONE)
> + complete(&nand->command_complete);
> +
> + if (isr & ISR_UND)
> + dev_dbg(nand->dev, "FIFO underrun\n");
> +
> + if (isr & ISR_OVR)
> + dev_dbg(nand->dev, "FIFO overrun\n");
> +
> + /* handle DMA interrupts */
> + if (dma & DMA_CTRL_IS_DONE) {
> + writel(dma, nand->regs + DMA_CTRL);
> + complete(&nand->dma_complete);
> + }
> +
> + /* clear interrupts */
> + writel(isr, nand->regs + ISR);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int tegra_nand_cmd(struct nand_chip *chip,
> + const struct nand_subop *subop)
> +{
> + const struct nand_op_instr *instr;
> + const struct nand_op_instr *instr_data_in = NULL;
> + struct mtd_info *mtd = nand_to_mtd(chip);
> + struct tegra_nand *nand = to_tegra_nand(mtd);
> + unsigned int op_id = -1, trfr_in_sz = 0, trfr_out_sz = 0, offset = 0;
> + bool first_cmd = true;
> + bool force8bit;
> + u32 cmd = 0;
> + u32 value;
> +
> + for (op_id = 0; op_id < subop->ninstrs; op_id++) {
> + unsigned int naddrs, i;
> + const u8 *addrs;
> + u32 addr1 = 0, addr2 = 0;
> +
> + instr = &subop->instrs[op_id];
> +
> + switch (instr->type) {
> + case NAND_OP_CMD_INSTR:
> + if (first_cmd) {
> + cmd |= CMD_CLE;
> + writel(instr->ctx.cmd.opcode, nand->regs + CMD_1);
> + } else {
> + cmd |= CMD_SEC_CMD;
> + writel(instr->ctx.cmd.opcode, nand->regs + CMD_2);
> + }
> + first_cmd = false;
> + break;
> + case NAND_OP_ADDR_INSTR:
> + offset = nand_subop_get_addr_start_off(subop, op_id);
> + naddrs = nand_subop_get_num_addr_cyc(subop, op_id);
> + addrs = &instr->ctx.addr.addrs[offset];
> +
> + cmd |= CMD_ALE | CMD_ALE_SIZE(naddrs);
> + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
> + addr1 |= *addrs++ << (8 * i);
> + naddrs -= i;
> + for (i = 0; i < min_t(unsigned int, 4, naddrs); i++)
> + addr2 |= *addrs++ << (8 * i);
> + writel(addr1, nand->regs + ADDR_1);
> + writel(addr2, nand->regs + ADDR_2);
> + break;
> +
> + case NAND_OP_DATA_IN_INSTR:
> + trfr_in_sz = nand_subop_get_data_len(subop, op_id);
> + offset = nand_subop_get_data_start_off(subop, op_id);
> +
> + cmd |= CMD_TRANS_SIZE(trfr_in_sz) | CMD_PIO | CMD_RX | CMD_A_VALID;
> +
> + instr_data_in = instr;
> + break;
> +
> + case NAND_OP_DATA_OUT_INSTR:
> + trfr_out_sz = nand_subop_get_data_len(subop, op_id);
> + offset = nand_subop_get_data_start_off(subop, op_id);
> + trfr_out_sz = min_t(size_t, trfr_out_sz, 4);
> +
> + cmd |= CMD_TRANS_SIZE(trfr_out_sz) | CMD_PIO | CMD_TX | CMD_A_VALID;
> +
> + memcpy(&value, instr->ctx.data.buf.out + offset, trfr_out_sz);
> + writel(value, nand->regs + RESP);
> +
> + break;
> + case NAND_OP_WAITRDY_INSTR:
> + cmd |= CMD_RBSY_CHK;
> + break;
> +
> + }
> + }
> +
> +
> + cmd |= CMD_GO | CMD_CE(nand->cur_chip);
> + writel(cmd, nand->regs + CMD);
> + wait_for_completion(&nand->command_complete);
> +
> + if (instr_data_in) {
> + u32 value;
> + size_t n = min_t(size_t, trfr_in_sz, 4);
> +
> + value = readl(nand->regs + RESP);
> + memcpy(instr_data_in->ctx.data.buf.in + offset, &value, n);
> + }
> +
> + return 0;
> +}
> +
> +static const struct nand_op_parser tegra_nand_op_parser = NAND_OP_PARSER(
> + NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true)),
> + NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
> + NAND_OP_PARSER_PAT_DATA_OUT_ELEM(false, 4)),
> + NAND_OP_PARSER_PATTERN(tegra_nand_cmd,
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_ADDR_ELEM(true, 8),
> + NAND_OP_PARSER_PAT_CMD_ELEM(true),
> + NAND_OP_PARSER_PAT_WAITRDY_ELEM(true),
> + NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 4)),
> + );
> +
> +static int tegra_nand_exec_op(struct nand_chip *chip,
> + const struct nand_operation *op,
> + bool check_only)
> +{
> + return nand_op_parser_exec_op(chip, &tegra_nand_op_parser, op,
> + check_only);
> +}
> +static void tegra_nand_select_chip(struct mtd_info *mtd, int chip)
> +{
> + struct tegra_nand *nand = to_tegra_nand(mtd);
> +
> + nand->cur_chip = chip;
> +}
> +
> +static u32 tegra_nand_fill_address(struct mtd_info *mtd, struct
> nand_chip *chip,
> + int page)
> +{
> + struct tegra_nand *nand = to_tegra_nand(mtd);
> +
> + /* Lower 16-bits are column, always 0 */
> + writel(page << 16, nand->regs + ADDR_1);
> +
> + if (chip->options & NAND_ROW_ADDR_3) {
> + writel(page >> 16, nand->regs + ADDR_2);
> + return 5;
> + }
> +
> + return 4;
> +}
> +
> +static int tegra_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
> + uint8_t *buf, int oob_required, int page)
> +{
> + struct tegra_nand *nand = to_tegra_nand(mtd);
> + u32 value, addrs;
> +
> + writel(NAND_CMD_READ0, nand->regs + CMD_1);
> + writel(NAND_CMD_READSTART, nand->regs + CMD_2);
> +
> + addrs = tegra_nand_fill_address(mtd, chip, page);
> +
> + value = readl(nand->regs + CFG);
> + value |= CFG_HW_ECC | CFG_ERR_COR;
> + writel(value, nand->regs + CFG);
> +
> + writel(mtd->writesize - 1, nand->regs + DMA_CFG_A);
> + writel(nand->data_dma, nand->regs + DATA_PTR);
> +
> + if (oob_required) {
> + writel(mtd_ooblayout_count_freebytes(mtd) - 1,
> + nand->regs + DMA_CFG_B);
> + writel(nand->oob_dma, nand->regs + TAG_PTR);
> + } else {
> + writel(0, nand->regs + DMA_CFG_B);
> + writel(0, nand->regs + TAG_PTR);
> + }
> +
> + value = DMA_CTRL_GO | DMA_CTRL_IN | DMA_CTRL_PERF_EN |
> + DMA_CTRL_REUSE | DMA_CTRL_IE_DONE | DMA_CTRL_IS_DONE |
> + DMA_CTRL_BURST_8 | DMA_CTRL_EN_A;
> + if (oob_required)
> + value |= DMA_CTRL_EN_B;
> + writel(value, nand->regs + DMA_CTRL);
> +
> + value = CMD_CLE | CMD_ALE | CMD_ALE_SIZE(addrs) | CMD_SEC_CMD |
> + CMD_RBSY_CHK | CMD_GO | CMD_RX | CMD_TRANS_SIZE(9) |
> + CMD_A_VALID | CMD_CE(nand->cur_chip);
> + if (oob_required)
> + value |= CMD_B_VALID;
> + writel(value, nand->regs + CMD);
> +
> + wait_for_completion(&nand->command_complete);
> + wait_for_completion(&nand->dma_complete);
> +
> + if (oob_required) {
> + struct mtd_oob_region oobregion;
> +
> + mtd_ooblayout_free(mtd, 0, &oobregion);
> + memcpy(chip->oob_poi, nand->oob_buf + oobregion.offset,
> + mtd_ooblayout_count_freebytes(mtd));
> + }
> + memcpy(buf, nand->data_buf, mtd->writesize);
> +
> + value = readl(nand->regs + CFG);
> + value &= ~(CFG_HW_ECC | CFG_ERR_COR);
> + writel(value, nand->regs + CFG);
> +
> + value = readl(nand->regs + DEC_STATUS);
> + if (value & DEC_STATUS_A_ECC_FAIL) {
> + /*
> + * The ECC isn't smart enough to figure out if a page is
> + * completely erased and flags an error in this case. So we
> + * check the read data here to figure out if it's a legitimate
> + * error or a false positive.
> + */
> + int i, err;
> + int flips_threshold = chip->ecc.strength / 2;
> + int max_bitflips = 0;
> +
> + for (i = 0; i < chip->ecc.steps; i++) {
> + u8 *data = buf + (chip->ecc.size * i);
> + err = nand_check_erased_ecc_chunk(data, chip->ecc.size,
> + NULL, 0,
> + NULL, 0,
> + flips_threshold);
> + if (err < 0)
> + return err;
> +
> + max_bitflips += max_bitflips;
> + }
> +
> + return max_bitflips;
> + }
> +
> + if (nand->last_read_error) {
> + int max_corr_cnt, corr_sec_flag;
> +
> + value = readl(nand->regs + DEC_STAT_BUF);
> + corr_sec_flag = (value & DEC_STAT_BUF_CORR_SEC_FLAG_MASK) >>
> + DEC_STAT_BUF_CORR_SEC_FLAG_SHIFT;
> + max_corr_cnt = (value & DEC_STAT_BUF_MAX_CORR_CNT_MASK) >>
> + DEC_STAT_BUF_MAX_CORR_CNT_SHIFT;
> +
> + /*
> + * The value returned in the register is the maximum of
> + * bitflips encountered in any of the ECC regions. As there is
> + * no way to get the number of bitflips in a specific regions
> + * we are not able to deliver correct stats but instead
> + * overestimate the number of corrected bitflips by assuming
> + * that all regions where errors have been corrected
> + * encountered the maximum number of bitflips.
> + */
> + mtd->ecc_stats.corrected += max_corr_cnt * hweight8(corr_sec_flag);
> + nand->last_read_error = false;
> + return value;
> + }
> +
> + return 0;
> +}
> +
> +static int tegra_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
> + const uint8_t *buf, int oob_required, int page)
> +{
> + struct tegra_nand *nand = to_tegra_nand(mtd);
> + u32 value, addrs;
> +
> + writel(NAND_CMD_SEQIN, nand->regs + CMD_1);
> + writel(NAND_CMD_PAGEPROG, nand->regs + CMD_2);
> +
> + addrs = tegra_nand_fill_address(mtd, chip, page);
> +
> + value = readl(nand->regs + CFG);
> + value |= CFG_HW_ECC | CFG_ERR_COR;
> + writel(value, nand->regs + CFG);
> +
> + memcpy(nand->data_buf, buf, mtd->writesize);
> +
> + writel(mtd->writesize - 1, nand->regs + DMA_CFG_A);
> + writel(nand->data_dma, nand->regs + DATA_PTR);
> +
> + if (oob_required) {
> + struct mtd_oob_region oobregion;
> +
> + mtd_ooblayout_free(mtd, 0, &oobregion);
> + memcpy(nand->oob_buf, chip->oob_poi + oobregion.offset,
> + mtd_ooblayout_count_freebytes(mtd));
> + writel(mtd_ooblayout_count_freebytes(mtd) - 1,
> + nand->regs + DMA_CFG_B);
> + writel(nand->oob_dma, nand->regs + TAG_PTR);
> + } else {
> + writel(0, nand->regs + DMA_CFG_B);
> + writel(0, nand->regs + TAG_PTR);
> + }
> +
> + value = DMA_CTRL_GO | DMA_CTRL_OUT | DMA_CTRL_PERF_EN |
> + DMA_CTRL_IE_DONE | DMA_CTRL_IS_DONE |
> + DMA_CTRL_BURST_8 | DMA_CTRL_EN_A;
> + if (oob_required)
> + value |= DMA_CTRL_EN_B;
> + writel(value, nand->regs + DMA_CTRL);
> +
> + value = CMD_CLE | CMD_ALE | CMD_ALE_SIZE(addrs) | CMD_SEC_CMD |
> + CMD_AFT_DAT | CMD_RBSY_CHK | CMD_GO | CMD_TX | CMD_A_VALID |
> + CMD_TRANS_SIZE(9) | CMD_CE(nand->cur_chip);
> + if (oob_required)
> + value |= CMD_B_VALID;
> + writel(value, nand->regs + CMD);
> +
> + wait_for_completion(&nand->command_complete);
> + wait_for_completion(&nand->dma_complete);
> +
> + value = readl(nand->regs + CFG);
> + value &= ~(CFG_HW_ECC | CFG_ERR_COR);
> + writel(value, nand->regs + CFG);
> +
> + return 0;
> +}
> +
> +static void tegra_nand_setup_timing(struct tegra_nand *nand, int mode)
> +{
> + /*
> + * The period (and all other timings in this function) is in ps,
> + * so need to take care here to avoid integer overflows.
> + */
> + unsigned int rate = clk_get_rate(nand->clk) / 1000000;
> + unsigned int period = DIV_ROUND_UP(1000000, rate);
> + const struct nand_sdr_timings *timings;
> + u32 val, reg = 0;
> +
> + timings = onfi_async_timing_mode_to_sdr_timings(mode);
> +
> + val = DIV_ROUND_UP(max3(timings->tAR_min, timings->tRR_min,
> + timings->tRC_min), period);
> + if (val > 2)
> + val -= 3;
> + reg |= TIMING_TCR_TAR_TRR(val);
> +
> + val = DIV_ROUND_UP(max(max(timings->tCS_min, timings->tCH_min),
> + max(timings->tALS_min, timings->tALH_min)),
> + period);
> + if (val > 1)
> + val -= 2;
> + reg |= TIMING_TCS(val);
> +
> + val = DIV_ROUND_UP(max(timings->tRP_min, timings->tREA_max) + 6000,
> + period);
> + reg |= TIMING_TRP(val) | TIMING_TRP_RESP(val);
> +
> + reg |= TIMING_TWB(DIV_ROUND_UP(timings->tWB_max, period));
> + reg |= TIMING_TWHR(DIV_ROUND_UP(timings->tWHR_min, period));
> + reg |= TIMING_TWH(DIV_ROUND_UP(timings->tWH_min, period));
> + reg |= TIMING_TWP(DIV_ROUND_UP(timings->tWP_min, period));
> + reg |= TIMING_TRH(DIV_ROUND_UP(timings->tRHW_min, period));
> +
> + writel(reg, nand->regs + TIMING_1);
> +
> + val = DIV_ROUND_UP(timings->tADL_min, period);
> + if (val > 2)
> + val -= 3;
> + reg = TIMING_TADL(val);
> +
> + writel(reg, nand->regs + TIMING_2);
> +}
> +
> +static void tegra_nand_setup_chiptiming(struct tegra_nand *nand)
> +{
> + struct nand_chip *chip = &nand->chip;
> + int mode;
> +
> + mode = onfi_get_async_timing_mode(chip);
> + if (mode == ONFI_TIMING_MODE_UNKNOWN)
> + mode = chip->onfi_timing_mode_default;
> + else
> + mode = fls(mode);
> +
> + tegra_nand_setup_timing(nand, mode);
> +}
> +
> +static int tegra_nand_probe(struct platform_device *pdev)
> +{
> + struct reset_control *rst;
> + struct tegra_nand *nand;
> + struct nand_chip *chip;
> + struct mtd_info *mtd;
> + struct resource *res;
> + unsigned long value;
> + int irq, err = 0;
> +
> + nand = devm_kzalloc(&pdev->dev, sizeof(*nand), GFP_KERNEL);
> + if (!nand)
> + return -ENOMEM;
> +
> + nand->dev = &pdev->dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + nand->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(nand->regs))
> + return PTR_ERR(nand->regs);
> +
> + irq = platform_get_irq(pdev, 0);
> + err = devm_request_irq(&pdev->dev, irq, tegra_nand_irq, 0,
> + dev_name(&pdev->dev), nand);
> + if (err)
> + return err;
> +
> + rst = devm_reset_control_get(&pdev->dev, "nand");
> + if (IS_ERR(rst))
> + return PTR_ERR(rst);
> +
> + nand->clk = devm_clk_get(&pdev->dev, "nand");
> + if (IS_ERR(nand->clk))
> + return PTR_ERR(nand->clk);
> +
> + nand->wp_gpio = gpiod_get_optional(&pdev->dev, "wp-gpios",
> + GPIOD_OUT_HIGH);
> + if (IS_ERR(nand->wp_gpio))
> + return PTR_ERR(nand->wp_gpio);
> +
> + err = clk_prepare_enable(nand->clk);
> + if (err)
> + return err;
> +
> + reset_control_assert(rst);
> + udelay(2);
> + reset_control_deassert(rst);
> +
> + value = HWSTATUS_RDSTATUS_MASK(1) | HWSTATUS_RDSTATUS_VALUE(0) |
> + HWSTATUS_RBSY_MASK(NAND_STATUS_READY) |
> + HWSTATUS_RBSY_VALUE(NAND_STATUS_READY);
> + writel(NAND_CMD_STATUS, nand->regs + HWSTATUS_CMD);
> + writel(value, nand->regs + HWSTATUS_MASK);
> +
> + init_completion(&nand->command_complete);
> + init_completion(&nand->dma_complete);
> +
> + /* clear interrupts */
> + value = readl(nand->regs + ISR);
> + writel(value, nand->regs + ISR);
> +
> + writel(DMA_CTRL_IS_DONE, nand->regs + DMA_CTRL);
> +
> + /* enable interrupts */
> + value = IER_UND | IER_OVR | IER_CMD_DONE | IER_ECC_ERR | IER_GIE;
> + writel(value, nand->regs + IER);
> +
> + /* reset config */
> + writel(0, nand->regs + CFG);
> +
> + chip = &nand->chip;
> + mtd = nand_to_mtd(chip);
> +
> + mtd->dev.parent = &pdev->dev;
> + mtd->name = "tegra_nand";
> + mtd->owner = THIS_MODULE;
> +
> + nand_set_flash_node(chip, pdev->dev.of_node);
> + nand_set_controller_data(chip, nand);
> +
> + chip->options = NAND_NO_SUBPAGE_WRITE;
> + chip->exec_op = tegra_nand_exec_op;
> + chip->select_chip = tegra_nand_select_chip;
> + tegra_nand_setup_timing(nand, 0);
> +
> + err = nand_scan_ident(mtd, 1, NULL);
> + if (err)
> + goto err_disable_clk;
> +
> + if (chip->bbt_options & NAND_BBT_USE_FLASH)
> + chip->bbt_options |= NAND_BBT_NO_OOB;
> +
> + nand->data_buf = dmam_alloc_coherent(&pdev->dev, mtd->writesize,
> + &nand->data_dma, GFP_KERNEL);
> + if (!nand->data_buf) {
> + err = -ENOMEM;
> + goto err_disable_clk;
> + }
> +
> + nand->oob_buf = dmam_alloc_coherent(&pdev->dev, mtd->oobsize,
> + &nand->oob_dma, GFP_KERNEL);
> + if (!nand->oob_buf) {
> + err = -ENOMEM;
> + goto err_disable_clk;
> + }
> +
> + chip->ecc.mode = NAND_ECC_HW;
> + chip->ecc.size = 512;
> + chip->ecc.read_page = tegra_nand_read_page;
> + chip->ecc.write_page = tegra_nand_write_page;
> +
> + value = readl(nand->regs + CFG);
> + value |= CFG_PIPE_EN | CFG_SKIP_SPARE | CFG_SKIP_SPARE_SIZE_4 |
> + CFG_TAG_BYTE_SIZE(mtd_ooblayout_count_freebytes(mtd) - 1);
> +
> + if (chip->options & NAND_BUSWIDTH_16)
> + value |= CFG_BUS_WIDTH_16;
> +
> + switch (mtd->oobsize) {
> + case 16:
> + mtd_set_ooblayout(mtd, &tegra_nand_oob_16_ops);
> + chip->ecc.strength = 1;
> + chip->ecc.bytes = 4;
> + break;
> + case 64:
> + mtd_set_ooblayout(mtd, &tegra_nand_oob_64_ops);
> + chip->ecc.strength = 8;
> + chip->ecc.bytes = 18;
> + value |= CFG_ECC_SEL | CFG_TVAL_8;
> + break;
> + case 128:
> + mtd_set_ooblayout(mtd, &tegra_nand_oob_128_ops);
> + chip->ecc.strength = 8;
> + chip->ecc.bytes = 18;
> + value |= CFG_ECC_SEL | CFG_TVAL_8;
> + break;
> + case 224:
> + mtd_set_ooblayout(mtd, &tegra_nand_oob_224_ops);
> + chip->ecc.strength = 8;
> + chip->ecc.bytes = 18;
> + value |= CFG_ECC_SEL | CFG_TVAL_8;
> + break;
> + default:
> + dev_err(&pdev->dev, "unhandled OOB size %d\n", mtd->oobsize);
> + err = -ENODEV;
> + goto err_disable_clk;
> + }
This selects the algorithm used somewhat ad-hoc.
The controller supports Hamming/Reed-Solomon and BCH. I was thinking to
use the device tree property nand-ecc-algo for manual algorithm
selection (need to add rs...). Then use ONFI/JEDEC/parameter page/driver
information from ecc_strength_ds/ecc_step_ds to select an appropriate
ECC strength.
Or in case device tree nand-ecc-maximize just use the maximum possible
with given algorithm/OOB size.
Does that sound reasonable?
--
Stefan
> +
> + switch (mtd->writesize) {
> + case 256:
> + value |= CFG_PS_256;
> + break;
> + case 512:
> + value |= CFG_PS_512;
> + break;
> + case 1024:
> + value |= CFG_PS_1024;
> + break;
> + case 2048:
> + value |= CFG_PS_2048;
> + break;
> + case 4096:
> + value |= CFG_PS_4096;
> + break;
> + default:
> + dev_err(&pdev->dev, "unhandled writesize %d\n", mtd->writesize);
> + err = -ENODEV;
> + goto err_disable_clk;
> + }
> +
> + writel(value, nand->regs + CFG);
> +
> + tegra_nand_setup_chiptiming(nand);
> +
> + err = nand_scan_tail(mtd);
> + if (err)
> + goto err_disable_clk;
> +
> + err = mtd_device_register(mtd, NULL, 0);
> + if (err)
> + goto err_cleanup_nand;
> +
> + platform_set_drvdata(pdev, nand);
> +
> + return 0;
> +
> +err_cleanup_nand:
> + nand_cleanup(chip);
> +err_disable_clk:
> + clk_disable_unprepare(nand->clk);
> + return err;
> +}
> +
> +static int tegra_nand_remove(struct platform_device *pdev)
> +{
> + struct tegra_nand *nand = platform_get_drvdata(pdev);
> +
> + nand_release(nand_to_mtd(&nand->chip));
> +
> + clk_disable_unprepare(nand->clk);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id tegra_nand_of_match[] = {
> + { .compatible = "nvidia,tegra20-nand" },
> + { /* sentinel */ }
> +};
> +
> +static struct platform_driver tegra_nand_driver = {
> + .driver = {
> + .name = "tegra-nand",
> + .of_match_table = tegra_nand_of_match,
> + },
> + .probe = tegra_nand_probe,
> + .remove = tegra_nand_remove,
> +};
> +module_platform_driver(tegra_nand_driver);
> +
> +MODULE_DESCRIPTION("NVIDIA Tegra NAND driver");
> +MODULE_AUTHOR("Thierry Reding <thierry.reding@nvidia.com>");
> +MODULE_AUTHOR("Lucas Stach <dev@lynxeye.de>");
> +MODULE_AUTHOR("Stefan Agner <stefan@agner.ch>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DEVICE_TABLE(of, tegra_nand_of_match);
^ permalink raw reply
* Re: [PATCH v5 3/5] i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller
From: Karthik Ramasubramanian @ 2018-05-22 14:52 UTC (permalink / raw)
To: Wolfram Sang
Cc: corbet, andy.gross, david.brown, robh+dt, mark.rutland, linux-doc,
linux-arm-msm, devicetree, linux-i2c, evgreen, acourbot, swboyd,
dianders, Sagar Dharia, Girish Mahadevan
In-Reply-To: <20180521204924.4ldbke6udjshdz2k@katana>
On 5/21/2018 2:49 PM, Wolfram Sang wrote:
> Hi,
>
> On Fri, Mar 23, 2018 at 02:20:59PM -0600, Karthikeyan Ramasubramanian wrote:
>> This bus driver supports the GENI based i2c hardware controller in the
>> Qualcomm SOCs. The Qualcomm Generic Interface (GENI) is a programmable
>> module supporting a wide range of serial interfaces including I2C. The
>> driver supports FIFO mode and DMA mode of transfer and switches modes
>> dynamically depending on the size of the transfer.
>>
>> Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
>> Signed-off-by: Sagar Dharia <sdharia@codeaurora.org>
>> Signed-off-by: Girish Mahadevan <girishm@codeaurora.org>
>
> Is one of these people interested in maintaining this driver? Then, an
> entry for MAINTAINERS would be needed, too. (Same goes for
> drivers/soc/qcom/ IMHO, but this is not my realm, so just saying)
One of us will maintain this driver and I will update the MAINTAINERS
appropriately.
>
>> +static const struct geni_i2c_err_log gi2c_log[] = {
>> + [GP_IRQ0] = {-EINVAL, "Unknown I2C err GP_IRQ0"},
>> + [NACK] = {-ENOTCONN, "NACK: slv unresponsive, check its power/reset-ln"},
>> + [GP_IRQ2] = {-EINVAL, "Unknown I2C err GP IRQ2"},
>> + [BUS_PROTO] = {-EPROTO, "Bus proto err, noisy/unepxected start/stop"},
>> + [ARB_LOST] = {-EBUSY, "Bus arbitration lost, clock line undriveable"},
>> + [GP_IRQ5] = {-EINVAL, "Unknown I2C err GP IRQ5"},
>> + [GENI_OVERRUN] = {-EIO, "Cmd overrun, check GENI cmd-state machine"},
>> + [GENI_ILLEGAL_CMD] = {-EILSEQ, "Illegal cmd, check GENI cmd-state machine"},
>> + [GENI_ABORT_DONE] = {-ETIMEDOUT, "Abort after timeout successful"},
>> + [GENI_TIMEOUT] = {-ETIMEDOUT, "I2C TXN timed out"},
>> +};
>
> Please check Documentation/i2c/fault-codes for better -ERRNO values,
> especially for NACK and ARB_LOST.
I will check the fault-codes and fix the error codes here.
>
> Rest looks good from a glimpse.
>
> Thanks,
>
> Wolfram
>
Regards,
Karthik.
--
Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH v6 13/13] media: staging/imx: add i.MX7 entries to TODO file
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
Add some i.MX7 related entries to TODO file.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
drivers/staging/media/imx/TODO | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/staging/media/imx/TODO b/drivers/staging/media/imx/TODO
index aeeb15494a49..6f29b5ca5324 100644
--- a/drivers/staging/media/imx/TODO
+++ b/drivers/staging/media/imx/TODO
@@ -45,3 +45,12 @@
Which means a port must not contain mixed-use endpoints, they
must all refer to media links between V4L2 subdevices.
+
+- i.MX7: all of the above, since it uses the imx media core
+
+- i.MX7: use Frame Interval Monitor
+
+- i.MX7: runtime testing with parallel sensor, links setup and streaming
+
+- i.MX7: runtime testing with different formats, for the time only 10-bit bayer
+ is tested
--
2.17.0
^ permalink raw reply related
* [PATCH v6 12/13] media: imx7.rst: add documentation for i.MX7 media driver
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
Add rst document to describe the i.MX7 media driver and also a working
example from the Warp7 board usage with a OV2680 sensor.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
Documentation/media/v4l-drivers/imx7.rst | 157 ++++++++++++++++++++++
Documentation/media/v4l-drivers/index.rst | 1 +
2 files changed, 158 insertions(+)
create mode 100644 Documentation/media/v4l-drivers/imx7.rst
diff --git a/Documentation/media/v4l-drivers/imx7.rst b/Documentation/media/v4l-drivers/imx7.rst
new file mode 100644
index 000000000000..cd1195d391c5
--- /dev/null
+++ b/Documentation/media/v4l-drivers/imx7.rst
@@ -0,0 +1,157 @@
+i.MX7 Video Capture Driver
+==========================
+
+Introduction
+------------
+
+The i.MX7 contrary to the i.MX5/6 family does not contain an Image Processing
+Unit (IPU); because of that the capabilities to perform operations or
+manipulation of the capture frames are less feature rich.
+
+For image capture the i.MX7 has three units:
+- CMOS Sensor Interface (CSI)
+- Video Multiplexer
+- MIPI CSI-2 Receiver
+
+::
+ |\
+ MIPI Camera Input ---> MIPI CSI-2 --- > | \
+ | \
+ | M |
+ | U | ------> CSI ---> Capture
+ | X |
+ | /
+ Parallel Camera Input ----------------> | /
+ |/
+
+For additional information, please refer to the latest versions of the i.MX7
+reference manual [#f1]_.
+
+Entities
+--------
+
+imx7-mipi-csi2
+--------------
+
+This is the MIPI CSI-2 receiver entity. It has one sink pad to receive the pixel
+data from MIPI CSI-2 camera sensor. It has one source pad, corresponding to the
+virtual channel 0. This module is compliant to previous version of Samsung
+D-phy, and supports two D-PHY Rx Data lanes.
+
+csi_mux
+-------
+
+This is the video multiplexer. It has two sink pads to select from either camera
+sensor with a parallel interface or from MIPI CSI-2 virtual channel 0. It has
+a single source pad that routes to the CSI.
+
+csi
+---
+
+The CSI enables the chip to connect directly to external CMOS image sensor. CSI
+can interface directly with Parallel and MIPI CSI-2 buses. It has 256 x 64 FIFO
+to store received image pixel data and embedded DMA controllers to transfer data
+from the FIFO through AHB bus.
+
+This entity has one sink pad that receives from the csi_mux entity and a single
+source pad that routes video frames directly to memory buffers. This pad is
+routed to a capture device node.
+
+Usage Notes
+-----------
+
+To aid in configuration and for backward compatibility with V4L2 applications
+that access controls only from video device nodes, the capture device interfaces
+inherit controls from the active entities in the current pipeline, so controls
+can be accessed either directly from the subdev or from the active capture
+device interface. For example, the sensor controls are available either from the
+sensor subdevs or from the active capture device.
+
+Warp7 with OV2680
+-----------------
+
+On this platform an OV2680 MIPI CSI-2 module is connected to the internal MIPI
+CSI-2 receiver. The following example configures a video capture pipeline with
+an output of 800x600, and BGGR 10 bit bayer format:
+
+.. code-block:: none
+ # Setup links
+ media-ctl -l "'ov2680 1-0036':0 -> 'imx7-mipi-csis.0':0[1]"
+ media-ctl -l "'imx7-mipi-csis.0':1 -> 'csi_mux':1[1]"
+ media-ctl -l "'csi_mux':2 -> 'csi':0[1]"
+ media-ctl -l "'csi':1 -> 'csi capture':0[1]"
+
+ # Configure pads for pipeline
+ media-ctl -V "'ov2680 1-0036':0 [fmt:SBGGR10_1X10/800x600 field:none]"
+ media-ctl -V "'csi_mux':1 [fmt:SBGGR10_1X10/800x600 field:none]"
+ media-ctl -V "'csi_mux':2 [fmt:SBGGR10_1X10/800x600 field:none]"
+ media-ctl -V "'imx7-mipi-csis.0':0 [fmt:SBGGR10_1X10/800x600 field:none]"
+ media-ctl -V "'csi':0 [fmt:SBGGR10_1X10/800x600 field:none]"
+
+After this streaming can start. The v4l2-ctl tool can be used to select any of
+the resolutions supported by the sensor.
+
+.. code-block:: none
+ root@imx7s-warp:~# media-ctl -p
+ Media controller API version 4.17.0
+
+ Media device information
+ ------------------------
+ driver imx-media
+ model imx-media
+ serial
+ bus info
+ hw revision 0x0
+ driver version 4.17.0
+
+ Device topology
+ - entity 1: csi (2 pads, 2 links)
+ type V4L2 subdev subtype Unknown flags 0
+ device node name /dev/v4l-subdev0
+ pad0: Sink
+ [fmt:SBGGR10_1X10/800x600 field:none]
+ <- "csi_mux":2 [ENABLED]
+ pad1: Source
+ [fmt:SBGGR10_1X10/800x600 field:none]
+ -> "csi capture":0 [ENABLED]
+
+ - entity 4: csi capture (1 pad, 1 link)
+ type Node subtype V4L flags 0
+ device node name /dev/video0
+ pad0: Sink
+ <- "csi":1 [ENABLED]
+
+ - entity 10: csi_mux (3 pads, 2 links)
+ type V4L2 subdev subtype Unknown flags 0
+ device node name /dev/v4l-subdev1
+ pad0: Sink
+ [fmt:unknown/0x0]
+ pad1: Sink
+ [fmt:unknown/800x600 field:none]
+ <- "imx7-mipi-csis.0":1 [ENABLED]
+ pad2: Source
+ [fmt:unknown/800x600 field:none]
+ -> "csi":0 [ENABLED]
+
+ - entity 14: imx7-mipi-csis.0 (2 pads, 2 links)
+ type V4L2 subdev subtype Unknown flags 0
+ device node name /dev/v4l-subdev2
+ pad0: Sink
+ [fmt:SBGGR10_1X10/800x600 field:none]
+ <- "ov2680 1-0036":0 [ENABLED]
+ pad1: Source
+ [fmt:SBGGR10_1X10/800x600 field:none]
+ -> "csi_mux":1 [ENABLED]
+
+ - entity 17: ov2680 1-0036 (1 pad, 1 link)
+ type V4L2 subdev subtype Sensor flags 0
+ device node name /dev/v4l-subdev3
+ pad0: Source
+ [fmt:SBGGR10_1X10/800x600 field:none]
+ -> "imx7-mipi-csis.0":0 [ENABLED]
+
+
+References
+----------
+
+.. [#f1] https://www.nxp.com/docs/en/reference-manual/IMX7SRM.pdf
diff --git a/Documentation/media/v4l-drivers/index.rst b/Documentation/media/v4l-drivers/index.rst
index 679238e786a7..693295bbc53f 100644
--- a/Documentation/media/v4l-drivers/index.rst
+++ b/Documentation/media/v4l-drivers/index.rst
@@ -44,6 +44,7 @@ For more details see the file COPYING in the source distribution of Linux.
davinci-vpbe
fimc
imx
+ imx7
ivtv
max2175
meye
--
2.17.0
^ permalink raw reply related
* [PATCH v6 11/13] ARM: dts: imx7s-warp: add ov2680 sensor node
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
Warp7 comes with a Omnivision OV2680 sensor, add the node here to make complete
the camera data path for this system. Add the needed regulator to the analog
voltage supply, the port and endpoints in mipi_csi node and the pinctrl for the
reset gpio.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
arch/arm/boot/dts/imx7s-warp.dts | 44 ++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/arch/arm/boot/dts/imx7s-warp.dts b/arch/arm/boot/dts/imx7s-warp.dts
index cb175ee2fc9d..bf04e13afd02 100644
--- a/arch/arm/boot/dts/imx7s-warp.dts
+++ b/arch/arm/boot/dts/imx7s-warp.dts
@@ -91,6 +91,14 @@
regulator-always-on;
};
+ reg_peri_3p15v: regulator-peri-3p15v {
+ compatible = "regulator-fixed";
+ regulator-name = "peri_3p15v_reg";
+ regulator-min-microvolt = <3150000>;
+ regulator-max-microvolt = <3150000>;
+ regulator-always-on;
+ };
+
sound {
compatible = "simple-audio-card";
simple-audio-card,name = "imx7-sgtl5000";
@@ -218,6 +226,27 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
+
+ ov2680: camera@36 {
+ compatible = "ovti,ov2680";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ov2680>;
+ reg = <0x36>;
+ clocks = <&osc>;
+ clock-names = "xvclk";
+ reset-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
+ DOVDD-supply = <&sw2_reg>;
+ DVDD-supply = <&sw2_reg>;
+ AVDD-supply = <®_peri_3p15v>;
+
+ port {
+ ov2680_to_mipi: endpoint {
+ remote-endpoint = <&mipi_from_sensor>;
+ clock-lanes = <0>;
+ data-lanes = <1>;
+ };
+ };
+ };
};
&i2c4 {
@@ -352,6 +381,15 @@
#size-cells = <0>;
fsl,csis-hs-settle = <3>;
+ port@0 {
+ reg = <0>;
+
+ mipi_from_sensor: endpoint {
+ remote-endpoint = <&ov2680_to_mipi>;
+ data-lanes = <1>;
+ };
+ };
+
port@1 {
reg = <1>;
@@ -408,6 +446,12 @@
>;
};
+ pinctrl_ov2680: ov2660grp {
+ fsl,pins = <
+ MX7D_PAD_LPSR_GPIO1_IO03__GPIO1_IO3 0x14
+ >;
+ };
+
pinctrl_sai1: sai1grp {
fsl,pins = <
MX7D_PAD_SAI1_RX_DATA__SAI1_RX_DATA0 0x1f
--
2.17.0
^ permalink raw reply related
* [PATCH v6 10/13] ARM: dts: imx7: Add video mux, csi and mipi_csi and connections
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
This patch adds the device tree nodes for csi, video multiplexer and mipi-csi
besides the graph connecting the necessary endpoints to make the media capture
entities to work in imx7 Warp board.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
arch/arm/boot/dts/imx7s-warp.dts | 51 ++++++++++++++++++++++++++++++++
arch/arm/boot/dts/imx7s.dtsi | 27 +++++++++++++++++
2 files changed, 78 insertions(+)
diff --git a/arch/arm/boot/dts/imx7s-warp.dts b/arch/arm/boot/dts/imx7s-warp.dts
index 8a30b148534d..cb175ee2fc9d 100644
--- a/arch/arm/boot/dts/imx7s-warp.dts
+++ b/arch/arm/boot/dts/imx7s-warp.dts
@@ -310,6 +310,57 @@
status = "okay";
};
+&gpr {
+ csi_mux {
+ compatible = "video-mux";
+ mux-controls = <&mux 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@1 {
+ reg = <1>;
+
+ csi_mux_from_mipi_vc0: endpoint {
+ remote-endpoint = <&mipi_vc0_to_csi_mux>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+
+ csi_mux_to_csi: endpoint {
+ remote-endpoint = <&csi_from_csi_mux>;
+ };
+ };
+ };
+};
+
+&csi {
+ status = "okay";
+
+ port {
+ csi_from_csi_mux: endpoint {
+ remote-endpoint = <&csi_mux_to_csi>;
+ };
+ };
+};
+
+&mipi_csi {
+ clock-frequency = <166000000>;
+ status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ fsl,csis-hs-settle = <3>;
+
+ port@1 {
+ reg = <1>;
+
+ mipi_vc0_to_csi_mux: endpoint {
+ remote-endpoint = <&csi_mux_from_mipi_vc0>;
+ };
+ };
+};
+
&wdog1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_wdog>;
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 0b0b85438869..4fce2f46c766 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -46,6 +46,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/reset/imx7-reset.h>
#include "imx7d-pinfunc.h"
/ {
@@ -738,6 +739,17 @@
status = "disabled";
};
+ csi: csi@30710000 {
+ compatible = "fsl,imx7-csi";
+ reg = <0x30710000 0x10000>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7D_CLK_DUMMY>,
+ <&clks IMX7D_CSI_MCLK_ROOT_CLK>,
+ <&clks IMX7D_CLK_DUMMY>;
+ clock-names = "axi", "mclk", "dcic";
+ status = "disabled";
+ };
+
lcdif: lcdif@30730000 {
compatible = "fsl,imx7d-lcdif", "fsl,imx28-lcdif";
reg = <0x30730000 0x10000>;
@@ -747,6 +759,21 @@
clock-names = "pix", "axi";
status = "disabled";
};
+
+ mipi_csi: mipi-csi@30750000 {
+ compatible = "fsl,imx7-mipi-csi2";
+ reg = <0x30750000 0x10000>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7D_IPG_ROOT_CLK>,
+ <&clks IMX7D_MIPI_CSI_ROOT_CLK>,
+ <&clks IMX7D_MIPI_DPHY_ROOT_CLK>;
+ clock-names = "pclk", "wrap", "phy";
+ power-domains = <&pgc_mipi_phy>;
+ phy-supply = <®_1p0d>;
+ resets = <&src IMX7_RESET_MIPI_PHY_MRST>;
+ reset-names = "mrst";
+ status = "disabled";
+ };
};
aips3: aips-bus@30800000 {
--
2.17.0
^ permalink raw reply related
* [PATCH v6 09/13] ARM: dts: imx7s: add multiplexer controls
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
The IOMUXC General Purpose Register has bitfield to control video bus
multiplexer to control the CSI input between the MIPI-CSI2 and parallel
interface. Add that register and mask.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
Reviewed-by: Philipp Zabel <p.zabel@pengutronix.de>
---
arch/arm/boot/dts/imx7s.dtsi | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 5e6d2063b143..0b0b85438869 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -520,8 +520,14 @@
gpr: iomuxc-gpr@30340000 {
compatible = "fsl,imx7d-iomuxc-gpr",
- "fsl,imx6q-iomuxc-gpr", "syscon";
+ "fsl,imx6q-iomuxc-gpr", "syscon", "simple-mfd";
reg = <0x30340000 0x10000>;
+
+ mux: mux-controller {
+ compatible = "mmio-mux";
+ #mux-control-cells = <0>;
+ mux-reg-masks = <0x14 0x00000010>;
+ };
};
ocotp: ocotp-ctrl@30350000 {
--
2.17.0
^ permalink raw reply related
* [PATCH v6 08/13] ARM: dts: imx7s: add mipi phy power domain
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
Add power domain index 0 related with mipi-phy to imx7s.
While at it rename pcie power-domain node to remove pgc prefix.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
arch/arm/boot/dts/imx7s.dtsi | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 4d42335c0dee..5e6d2063b143 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -636,7 +636,13 @@
#address-cells = <1>;
#size-cells = <0>;
- pgc_pcie_phy: pgc-power-domain@1 {
+ pgc_mipi_phy: power-domain@0 {
+ #power-domain-cells = <0>;
+ reg = <0>;
+ power-supply = <®_1p0d>;
+ };
+
+ pgc_pcie_phy: power-domain@1 {
#power-domain-cells = <0>;
reg = <1>;
power-supply = <®_1p0d>;
--
2.17.0
^ permalink raw reply related
* [PATCH v6 07/13] media: dt-bindings: add bindings for i.MX7 media driver
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
Add bindings documentation for i.MX7 media drivers.
The imx7 MIPI CSI2 and imx7 CMOS Sensor Interface.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
.../devicetree/bindings/media/imx7-csi.txt | 44 ++++++++++
.../bindings/media/imx7-mipi-csi2.txt | 82 +++++++++++++++++++
2 files changed, 126 insertions(+)
create mode 100644 Documentation/devicetree/bindings/media/imx7-csi.txt
create mode 100644 Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt
diff --git a/Documentation/devicetree/bindings/media/imx7-csi.txt b/Documentation/devicetree/bindings/media/imx7-csi.txt
new file mode 100644
index 000000000000..aab4f5d72390
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/imx7-csi.txt
@@ -0,0 +1,44 @@
+Freescale i.MX7 CMOS Sensor Interface
+=====================================
+
+csi node
+--------
+
+This is device node for the CMOS Sensor Interface (CSI) which enables the chip
+to connect directly to external CMOS image sensors.
+
+Required properties:
+
+- compatible : "fsl,imx7-csi";
+- reg : base address and length of the register set for the device;
+- interrupts : should contain CSI interrupt;
+- clocks : list of clock specifiers, see
+ Documentation/devicetree/bindings/clock/clock-bindings.txt for details;
+- clock-names : must contain "axi", "mclk" and "dcic" entries, matching
+ entries in the clock property;
+
+The device node should contain one 'port' child node with one child 'endpoint'
+node, according to the bindings defined in Documentation/devicetree/bindings/
+media/video-interfaces.txt. In the following example a remote endpoint is a
+video multiplexer.
+
+example:
+
+ csi: csi@30710000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "fsl,imx7-csi";
+ reg = <0x30710000 0x10000>;
+ interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7D_CLK_DUMMY>,
+ <&clks IMX7D_CSI_MCLK_ROOT_CLK>,
+ <&clks IMX7D_CLK_DUMMY>;
+ clock-names = "axi", "mclk", "dcic";
+
+ port {
+ csi_from_csi_mux: endpoint {
+ remote-endpoint = <&csi_mux_to_csi>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt b/Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt
new file mode 100644
index 000000000000..7c5f20863724
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/imx7-mipi-csi2.txt
@@ -0,0 +1,82 @@
+Freescale i.MX7 Mipi CSI2
+=========================
+
+mipi_csi2 node
+--------------
+
+This is the device node for the MIPI CSI-2 receiver core in i.MX7 SoC. It is
+compatible with previous version of Samsung D-phy.
+
+Required properties:
+
+- compatible : "fsl,imx7-mipi-csi2";
+- reg : base address and length of the register set for the device;
+- interrupts : should contain MIPI CSIS interrupt;
+- clocks : list of clock specifiers, see
+ Documentation/devicetree/bindings/clock/clock-bindings.txt for details;
+- clock-names : must contain "pclk", "wrap" and "phy" entries, matching
+ entries in the clock property;
+- power-domains : a phandle to the power domain, see
+ Documentation/devicetree/bindings/power/power_domain.txt for details.
+- reset-names : should include following entry "mrst";
+- resets : a list of phandle, should contain reset entry of
+ reset-names;
+- phy-supply : from the generic phy bindings, a phandle to a regulator that
+ provides power to MIPI CSIS core;
+
+Optional properties:
+
+- clock-frequency : The IP's main (system bus) clock frequency in Hz, default
+ value when this property is not specified is 166 MHz;
+- fsl,csis-hs-settle : differential receiver (HS-RX) settle time;
+
+port node
+---------
+
+- reg : (required) can take the values 0 or 1, where 0 is the
+ related sink port and port 1 should be the source one;
+
+endpoint node
+-------------
+
+- data-lanes : (required) an array specifying active physical MIPI-CSI2
+ data input lanes and their mapping to logical lanes; the
+ array's content is unused, only its length is meaningful;
+
+example:
+
+ mipi_csi: mipi-csi@30750000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ compatible = "fsl,imx7-mipi-csi2";
+ reg = <0x30750000 0x10000>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX7D_IPG_ROOT_CLK>,
+ <&clks IMX7D_MIPI_CSI_ROOT_CLK>,
+ <&clks IMX7D_MIPI_DPHY_ROOT_CLK>;
+ clock-names = "pclk", "wrap", "phy";
+ clock-frequency = <166000000>;
+ power-domains = <&pgc_mipi_phy>;
+ phy-supply = <®_1p0d>;
+ resets = <&src IMX7_RESET_MIPI_PHY_MRST>;
+ reset-names = "mrst";
+ fsl,csis-hs-settle = <3>;
+
+ port@0 {
+ reg = <0>;
+
+ mipi_from_sensor: endpoint {
+ remote-endpoint = <&ov2680_to_mipi>;
+ data-lanes = <1>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ mipi_vc0_to_csi_mux: endpoint {
+ remote-endpoint = <&csi_mux_from_mipi_vc0>;
+ };
+ };
+ };
--
2.17.0
^ permalink raw reply related
* [PATCH v6 06/13] media: staging/imx7: add MIPI CSI-2 receiver subdev for i.MX7
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
Adds MIPI CSI-2 subdev for i.MX7 to connect with sensors with a MIPI CSI-2
interface.
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
drivers/staging/media/imx/Makefile | 1 +
drivers/staging/media/imx/imx7-mipi-csis.c | 1134 ++++++++++++++++++++
2 files changed, 1135 insertions(+)
create mode 100644 drivers/staging/media/imx/imx7-mipi-csis.c
diff --git a/drivers/staging/media/imx/Makefile b/drivers/staging/media/imx/Makefile
index 074f016d3519..d2d909a36239 100644
--- a/drivers/staging/media/imx/Makefile
+++ b/drivers/staging/media/imx/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_VIDEO_IMX_CSI) += imx-media-csi.o
obj-$(CONFIG_VIDEO_IMX_CSI) += imx6-mipi-csi2.o
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
+obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-mipi-csis.o
diff --git a/drivers/staging/media/imx/imx7-mipi-csis.c b/drivers/staging/media/imx/imx7-mipi-csis.c
new file mode 100644
index 000000000000..79cc413b4bbe
--- /dev/null
+++ b/drivers/staging/media/imx/imx7-mipi-csis.c
@@ -0,0 +1,1134 @@
+// SPDX-License-Identifier: GPL
+/*
+ * Freescale i.MX7 SoC series MIPI-CSI V3.3 receiver driver
+ *
+ * Copyright (C) 2018 Linaro Ltd
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "imx-media.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+#define CSIS_DRIVER_NAME "imx7-mipi-csis"
+#define CSIS_SUBDEV_NAME CSIS_DRIVER_NAME
+
+#define CSIS_PAD_SINK 0
+#define CSIS_PAD_SOURCE 1
+#define CSIS_PADS_NUM 2
+
+#define MIPI_CSIS_DEF_PIX_WIDTH 640
+#define MIPI_CSIS_DEF_PIX_HEIGHT 480
+
+/* Register map definition */
+
+/* CSIS common control */
+#define MIPI_CSIS_CMN_CTRL 0x04
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW BIT(16)
+#define MIPI_CSIS_CMN_CTRL_INTER_MODE BIT(10)
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL BIT(2)
+#define MIPI_CSIS_CMN_CTRL_RESET BIT(1)
+#define MIPI_CSIS_CMN_CTRL_ENABLE BIT(0)
+
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET 8
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_MASK (3 << 8)
+
+/* CSIS clock control */
+#define MIPI_CSIS_CLK_CTRL 0x08
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH3(x) (x << 28)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH2(x) (x << 24)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH1(x) (x << 20)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(x) (x << 16)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK (0xf << 4)
+#define MIPI_CSIS_CLK_CTRL_WCLK_SRC BIT(0)
+
+/* CSIS Interrupt mask */
+#define MIPI_CSIS_INTMSK 0x10
+#define MIPI_CSIS_INTMSK_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INTMSK_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INTMSK_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INTMSK_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INTMSK_FRAME_START BIT(24)
+#define MIPI_CSIS_INTMSK_FRAME_END BIT(20)
+#define MIPI_CSIS_INTMSK_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INTMSK_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INTMSK_ERR_OVER BIT(4)
+#define MIPI_CSIS_INTMSK_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INTMSK_ERR_ECC BIT(2)
+#define MIPI_CSIS_INTMSK_ERR_CRC BIT(1)
+#define MIPI_CSIS_INTMSK_ERR_UNKNOWN BIT(0)
+
+/* CSIS Interrupt source */
+#define MIPI_CSIS_INTSRC 0x14
+#define MIPI_CSIS_INTSRC_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INTSRC_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INTSRC_EVEN BIT(30)
+#define MIPI_CSIS_INTSRC_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INTSRC_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INTSRC_ODD (0x3 << 28)
+#define MIPI_CSIS_INTSRC_NON_IMAGE_DATA (0xf << 28)
+#define MIPI_CSIS_INTSRC_FRAME_START BIT(24)
+#define MIPI_CSIS_INTSRC_FRAME_END BIT(20)
+#define MIPI_CSIS_INTSRC_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INTSRC_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INTSRC_ERR_OVER BIT(4)
+#define MIPI_CSIS_INTSRC_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INTSRC_ERR_ECC BIT(2)
+#define MIPI_CSIS_INTSRC_ERR_CRC BIT(1)
+#define MIPI_CSIS_INTSRC_ERR_UNKNOWN BIT(0)
+#define MIPI_CSIS_INTSRC_ERRORS 0xfffff
+
+/* D-PHY status control */
+#define MIPI_CSIS_DPHYSTATUS 0x20
+#define MIPI_CSIS_DPHYSTATUS_ULPS_DAT BIT(8)
+#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_DAT BIT(4)
+#define MIPI_CSIS_DPHYSTATUS_ULPS_CLK BIT(1)
+#define MIPI_CSIS_DPHYSTATUS_STOPSTATE_CLK BIT(0)
+
+/* D-PHY common control */
+#define MIPI_CSIS_DPHYCTRL 0x24
+#define MIPI_CSIS_DPHYCTRL_HSS_MASK (0xff << 24)
+#define MIPI_CSIS_DPHYCTRL_HSS_OFFSET 24
+#define MIPI_CSIS_DPHYCTRL_SCLKS_MASK (0x3 << 22)
+#define MIPI_CSIS_DPHYCTRL_SCLKS_OFFSET 22
+#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_CLK BIT(6)
+#define MIPI_CSIS_DPHYCTRL_DPDN_SWAP_DAT BIT(5)
+#define MIPI_CSIS_DPHYCTRL_ENABLE_DAT BIT(1)
+#define MIPI_CSIS_DPHYCTRL_ENABLE_CLK BIT(0)
+#define MIPI_CSIS_DPHYCTRL_ENABLE (0x1f << 0)
+
+/* D-PHY Master and Slave Control register Low */
+#define MIPI_CSIS_DPHYBCTRL_L 0x30
+/* D-PHY Master and Slave Control register High */
+#define MIPI_CSIS_DPHYBCTRL_H 0x34
+/* D-PHY Slave Control register Low */
+#define MIPI_CSIS_DPHYSCTRL_L 0x38
+/* D-PHY Slave Control register High */
+#define MIPI_CSIS_DPHYSCTRL_H 0x3c
+
+/* ISP Configuration register */
+#define MIPI_CSIS_ISPCONFIG_CH0 0x40
+#define MIPI_CSIS_ISPCONFIG_CH1 0x50
+#define MIPI_CSIS_ISPCONFIG_CH2 0x60
+#define MIPI_CSIS_ISPCONFIG_CH3 0x70
+
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24)
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) (x << 24)
+#define MIPI_CSIS_ISPCFG_DOUBLE_CMPNT BIT(12)
+#define MIPI_CSIS_ISPCFG_ALIGN_32BIT BIT(11)
+#define MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT (0x1e << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW8 (0x2a << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW10 (0x2b << 2)
+#define MIPI_CSIS_ISPCFG_FMT_RAW12 (0x2c << 2)
+
+/* User defined formats, x = 1...4 */
+#define MIPI_CSIS_ISPCFG_FMT_USER(x) ((0x30 + x - 1) << 2)
+#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2)
+
+/* ISP Image Resolution register */
+#define MIPI_CSIS_ISPRESOL_CH0 0x44
+#define MIPI_CSIS_ISPRESOL_CH1 0x54
+#define MIPI_CSIS_ISPRESOL_CH2 0x64
+#define MIPI_CSIS_ISPRESOL_CH3 0x74
+#define CSIS_MAX_PIX_WIDTH 0xffff
+#define CSIS_MAX_PIX_HEIGHT 0xffff
+
+/* ISP SYNC register */
+#define MIPI_CSIS_ISPSYNC_CH0 0x48
+#define MIPI_CSIS_ISPSYNC_CH1 0x58
+#define MIPI_CSIS_ISPSYNC_CH2 0x68
+#define MIPI_CSIS_ISPSYNC_CH3 0x78
+
+#define MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET 18
+#define MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET 12
+#define MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET 0
+
+/* Non-image packet data buffers */
+#define MIPI_CSIS_PKTDATA_ODD 0x2000
+#define MIPI_CSIS_PKTDATA_EVEN 0x3000
+#define MIPI_CSIS_PKTDATA_SIZE SZ_4K
+
+#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
+
+enum {
+ ST_POWERED = 1,
+ ST_STREAMING = 2,
+ ST_SUSPENDED = 4,
+};
+
+struct mipi_csis_event {
+ u32 mask;
+ const char * const name;
+ unsigned int counter;
+};
+
+static const struct mipi_csis_event mipi_csis_events[] = {
+ /* Errors */
+ { MIPI_CSIS_INTSRC_ERR_SOT_HS, "SOT Error" },
+ { MIPI_CSIS_INTSRC_ERR_LOST_FS, "Lost Frame Start Error" },
+ { MIPI_CSIS_INTSRC_ERR_LOST_FE, "Lost Frame End Error" },
+ { MIPI_CSIS_INTSRC_ERR_OVER, "FIFO Overflow Error" },
+ { MIPI_CSIS_INTSRC_ERR_WRONG_CFG, "Wrong Configuration Error" },
+ { MIPI_CSIS_INTSRC_ERR_ECC, "ECC Error" },
+ { MIPI_CSIS_INTSRC_ERR_CRC, "CRC Error" },
+ { MIPI_CSIS_INTSRC_ERR_UNKNOWN, "Unknown Error" },
+ /* Non-image data receive events */
+ { MIPI_CSIS_INTSRC_EVEN_BEFORE, "Non-image data before even frame" },
+ { MIPI_CSIS_INTSRC_EVEN_AFTER, "Non-image data after even frame" },
+ { MIPI_CSIS_INTSRC_ODD_BEFORE, "Non-image data before odd frame" },
+ { MIPI_CSIS_INTSRC_ODD_AFTER, "Non-image data after odd frame" },
+ /* Frame start/end */
+ { MIPI_CSIS_INTSRC_FRAME_START, "Frame Start" },
+ { MIPI_CSIS_INTSRC_FRAME_END, "Frame End" },
+};
+
+#define MIPI_CSIS_NUM_EVENTS ARRAY_SIZE(mipi_csis_events)
+
+struct csis_hw_reset {
+ struct regmap *src;
+ u8 req_src;
+ u8 rst_bit;
+};
+
+struct csi_state {
+ struct mutex lock;
+ spinlock_t slock;
+ struct device *dev;
+ struct media_pad pads[CSIS_PADS_NUM];
+ struct v4l2_subdev mipi_sd;
+ struct v4l2_subdev *src_sd;
+
+ u8 index;
+ struct platform_device *pdev;
+ struct phy *phy;
+ void __iomem *regs;
+ struct clk *pclk_clk;
+ struct clk *wrap_clk;
+ struct clk *phy_clk;
+ int irq;
+ u32 flags;
+
+ u32 clk_frequency;
+ u32 hs_settle;
+
+ struct reset_control *mrst;
+
+ const struct csis_pix_format *csis_fmt;
+ struct v4l2_mbus_framefmt format_mbus;
+
+ struct v4l2_fwnode_bus_mipi_csi2 bus;
+
+ struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS];
+
+ struct v4l2_async_notifier subdev_notifier;
+
+ struct csis_hw_reset hw_reset;
+ struct regulator *mipi_phy_regulator;
+ bool sink_linked;
+};
+
+struct csis_pix_format {
+ unsigned int pix_width_alignment;
+ u32 code;
+ u32 fmt_reg;
+ u8 data_alignment;
+};
+
+static const struct csis_pix_format mipi_csis_formats[] = {
+ {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW10,
+ .data_alignment = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_VYUY8_2X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_RAW8,
+ .data_alignment = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .fmt_reg = MIPI_CSIS_ISPCFG_FMT_YCBCR422_8BIT,
+ .data_alignment = 16,
+ }
+};
+
+#define mipi_csis_write(__csis, __r, __v) writel(__v, __csis->regs + __r)
+#define mipi_csis_read(__csis, __r) readl(__csis->regs + __r)
+
+static void dump_regs(struct csi_state *state, const char *label)
+{
+ u32 cfg;
+ u32 i;
+ struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { 0x04, "CTRL" },
+ { 0x24, "DPHYCTRL" },
+ { 0x08, "CLKCTRL" },
+ { 0x20, "DPHYSTS" },
+ { 0x10, "INTMSK" },
+ { 0x40, "CONFIG_CH0" },
+ { 0xC0, "DBG_CONFIG" },
+ { 0x38, "DPHYSLAVE_L" },
+ { 0x3C, "DPHYSLAVE_H" },
+ };
+
+ v4l2_info(&state->mipi_sd, "--- %s ---\n", label);
+
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ cfg = mipi_csis_read(state, registers[i].offset);
+ v4l2_info(&state->mipi_sd, "%12s: 0x%08x 0x%p\n",
+ registers[i].name, cfg, state->regs);
+ }
+}
+
+static struct csi_state *mipi_sd_to_csis_state(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_state, mipi_sd);
+}
+
+static const struct csis_pix_format *find_csis_format(u32 code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mipi_csis_formats); i++)
+ if (code == mipi_csis_formats[i].code)
+ return &mipi_csis_formats[i];
+ return NULL;
+}
+
+static void mipi_csis_enable_interrupts(struct csi_state *state, bool on)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_INTMSK);
+
+ if (on)
+ val |= 0xffffffff;
+ else
+ val &= ~0xffffffff;
+ mipi_csis_write(state, MIPI_CSIS_INTMSK, val);
+}
+
+static void mipi_csis_sw_reset(struct csi_state *state)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL,
+ val | MIPI_CSIS_CMN_CTRL_RESET);
+ usleep_range(10, 20);
+}
+
+static int mipi_csis_phy_init(struct csi_state *state)
+{
+ state->mipi_phy_regulator = devm_regulator_get(state->dev, "phy");
+
+ return regulator_set_voltage(state->mipi_phy_regulator, 1000000,
+ 1000000);
+}
+
+static void mipi_csis_phy_reset(struct csi_state *state)
+{
+ reset_control_assert(state->mrst);
+
+ msleep(20);
+
+ reset_control_deassert(state->mrst);
+}
+
+static void mipi_csis_system_enable(struct csi_state *state, int on)
+{
+ u32 val, mask;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ if (on)
+ val |= MIPI_CSIS_CMN_CTRL_ENABLE;
+ else
+ val &= ~MIPI_CSIS_CMN_CTRL_ENABLE;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
+ val &= ~MIPI_CSIS_DPHYCTRL_ENABLE;
+ if (on) {
+ mask = (1 << (state->bus.num_data_lanes + 1)) - 1;
+ val |= (mask & MIPI_CSIS_DPHYCTRL_ENABLE);
+ }
+ mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
+}
+
+/* Called with the state.lock mutex held */
+static void __mipi_csis_set_format(struct csi_state *state)
+{
+ struct v4l2_mbus_framefmt *mf = &state->format_mbus;
+ u32 val;
+
+ /* Color format */
+ val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
+ val = (val & ~MIPI_CSIS_ISPCFG_FMT_MASK) | state->csis_fmt->fmt_reg;
+ mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
+
+ /* Pixel resolution */
+ val = mf->width | (mf->height << 16);
+ mipi_csis_write(state, MIPI_CSIS_ISPRESOL_CH0, val);
+}
+
+static void mipi_csis_set_hsync_settle(struct csi_state *state, int hs_settle)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_DPHYCTRL);
+
+ val = ((val & ~MIPI_CSIS_DPHYCTRL_HSS_MASK) | (hs_settle << 24));
+
+ mipi_csis_write(state, MIPI_CSIS_DPHYCTRL, val);
+}
+
+static void mipi_csis_set_params(struct csi_state *state)
+{
+ int lanes = state->bus.num_data_lanes;
+ u32 val;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK;
+ val |= (lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ __mipi_csis_set_format(state);
+
+ mipi_csis_set_hsync_settle(state, state->hs_settle);
+
+ val = mipi_csis_read(state, MIPI_CSIS_ISPCONFIG_CH0);
+ if (state->csis_fmt->data_alignment == 32)
+ val |= MIPI_CSIS_ISPCFG_ALIGN_32BIT;
+ else
+ val &= ~MIPI_CSIS_ISPCFG_ALIGN_32BIT;
+ mipi_csis_write(state, MIPI_CSIS_ISPCONFIG_CH0, val);
+
+ val = (0 << MIPI_CSIS_ISPSYNC_HSYNC_LINTV_OFFSET) |
+ (0 << MIPI_CSIS_ISPSYNC_VSYNC_SINTV_OFFSET) |
+ (0 << MIPI_CSIS_ISPSYNC_VSYNC_EINTV_OFFSET);
+ mipi_csis_write(state, MIPI_CSIS_ISPSYNC_CH0, val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_CLK_CTRL);
+ val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+ if (state->wrap_clk)
+ val |= MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+ else
+ val &= ~MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+
+ val |= MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(15);
+ val &= ~MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK;
+ mipi_csis_write(state, MIPI_CSIS_CLK_CTRL, val);
+
+ mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_L, 0x1f4);
+ mipi_csis_write(state, MIPI_CSIS_DPHYBCTRL_H, 0);
+
+ /* Update the shadow register. */
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL,
+ val | MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW |
+ MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL);
+}
+
+static void mipi_csis_clk_enable(struct csi_state *state)
+{
+ clk_prepare_enable(state->pclk_clk);
+ clk_prepare_enable(state->wrap_clk);
+ clk_prepare_enable(state->phy_clk);
+}
+
+static void mipi_csis_clk_disable(struct csi_state *state)
+{
+ clk_disable_unprepare(state->pclk_clk);
+ clk_disable_unprepare(state->wrap_clk);
+ clk_disable_unprepare(state->phy_clk);
+}
+
+static int mipi_csis_clk_get(struct csi_state *state)
+{
+ struct device *dev = &state->pdev->dev;
+ int ret;
+
+ state->pclk_clk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(state->pclk_clk)) {
+ dev_err(dev, "Could not get pclk clock\n");
+ return -ENODEV;
+ }
+
+ state->wrap_clk = devm_clk_get(dev, "wrap");
+ if (IS_ERR(state->wrap_clk)) {
+ dev_info(dev, "Could not get wrap clock, using pclk\n");
+ state->wrap_clk = NULL;
+ }
+
+ state->phy_clk = devm_clk_get(dev, "phy");
+ if (IS_ERR(state->phy_clk)) {
+ dev_err(dev, "Could not get mipi phy clock\n");
+ return -ENODEV;
+ }
+
+ /* Set clock rate */
+ ret = clk_set_rate(state->wrap_clk, state->clk_frequency);
+ if (ret < 0)
+ dev_err(dev, "set rate=%d failed: %d\n", state->clk_frequency,
+ ret);
+
+ return ret;
+}
+
+static void mipi_csis_start_stream(struct csi_state *state)
+{
+ mipi_csis_sw_reset(state);
+ mipi_csis_set_params(state);
+ mipi_csis_system_enable(state, true);
+ mipi_csis_enable_interrupts(state, true);
+}
+
+static void mipi_csis_stop_stream(struct csi_state *state)
+{
+ mipi_csis_enable_interrupts(state, false);
+ mipi_csis_system_enable(state, false);
+}
+
+static void mipi_csis_clear_counters(struct csi_state *state)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&state->slock, flags);
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++)
+ state->events[i].counter = 0;
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+static void mipi_csis_log_counters(struct csi_state *state, bool non_errors)
+{
+ int i = non_errors ? MIPI_CSIS_NUM_EVENTS : MIPI_CSIS_NUM_EVENTS - 4;
+ unsigned long flags;
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ for (i--; i >= 0; i--) {
+ if (state->events[i].counter > 0 || debug)
+ v4l2_info(&state->mipi_sd, "%s events: %d\n",
+ state->events[i].name,
+ state->events[i].counter);
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+/*
+ * V4L2 subdev operations
+ */
+static int mipi_csis_s_power(struct v4l2_subdev *mipi_sd, int on)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct device *dev = &state->pdev->dev;
+
+ v4l2_subdev_call(state->src_sd, core, s_power, on);
+
+ if (on)
+ return pm_runtime_get_sync(dev);
+
+ return pm_runtime_put_sync(dev);
+}
+
+static int mipi_csis_s_stream(struct v4l2_subdev *mipi_sd, int enable)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ int ret = 0;
+
+ if (enable) {
+ mipi_csis_clear_counters(state);
+ ret = pm_runtime_get_sync(&state->pdev->dev);
+ if (ret && ret != 1)
+ return ret;
+ }
+
+ mutex_lock(&state->lock);
+ if (enable) {
+ if (state->flags & ST_SUSPENDED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ mipi_csis_start_stream(state);
+ ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
+ if (ret < 0)
+ goto unlock;
+
+ mipi_csis_log_counters(state, true);
+
+ state->flags |= ST_STREAMING;
+ } else {
+ v4l2_subdev_call(state->src_sd, video, s_stream, 0);
+ mipi_csis_stop_stream(state);
+ state->flags &= ~ST_STREAMING;
+ if (debug > 0)
+ mipi_csis_log_counters(state, true);
+ }
+
+unlock:
+ mutex_unlock(&state->lock);
+ if (!enable)
+ pm_runtime_put(&state->pdev->dev);
+
+ return ret;
+}
+
+static int mipi_csis_link_setup(struct media_entity *entity,
+ const struct media_pad *local_pad,
+ const struct media_pad *remote_pad, u32 flags)
+{
+ struct v4l2_subdev *mipi_sd = media_entity_to_v4l2_subdev(entity);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct v4l2_subdev *remote_sd;
+ int ret = 0;
+
+ dev_dbg(state->dev, "link setup %s -> %s", remote_pad->entity->name,
+ local_pad->entity->name);
+
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ mutex_lock(&state->lock);
+
+ if (local_pad->flags & MEDIA_PAD_FL_SOURCE) {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (state->sink_linked) {
+ ret = -EBUSY;
+ goto out;
+ }
+ state->sink_linked = true;
+ } else {
+ state->sink_linked = false;
+ }
+ } else {
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (state->src_sd) {
+ ret = -EBUSY;
+ goto out;
+ }
+ state->src_sd = remote_sd;
+ } else {
+ state->src_sd = NULL;
+ }
+ }
+
+out:
+ mutex_unlock(&state->lock);
+ return ret;
+}
+
+static int mipi_csis_init_cfg(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg)
+{
+ struct v4l2_mbus_framefmt *mf;
+ int ret;
+ int i;
+
+ for (i = 0; i < CSIS_PADS_NUM; i++) {
+ mf = v4l2_subdev_get_try_format(mipi_sd, cfg, i);
+
+ ret = imx_media_init_mbus_fmt(mf, MIPI_CSIS_DEF_PIX_HEIGHT,
+ MIPI_CSIS_DEF_PIX_WIDTH, 0,
+ V4L2_FIELD_NONE, NULL);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct csis_pix_format const *mipi_csis_try_format(
+ struct v4l2_subdev *mipi_sd,
+ struct v4l2_mbus_framefmt *mf)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct csis_pix_format const *csis_fmt;
+
+ csis_fmt = find_csis_format(mf->code);
+ if (!csis_fmt)
+ csis_fmt = &mipi_csis_formats[0];
+
+ v4l_bound_align_image(&mf->width, 1, CSIS_MAX_PIX_WIDTH,
+ csis_fmt->pix_width_alignment,
+ &mf->height, 1, CSIS_MAX_PIX_HEIGHT, 1,
+ 0);
+
+ state->format_mbus.code = csis_fmt->code;
+ state->format_mbus.width = mf->width;
+ state->format_mbus.height = mf->height;
+
+ return csis_fmt;
+}
+
+static struct v4l2_mbus_framefmt *mipi_csis_get_format(struct csi_state *state,
+ struct v4l2_subdev_pad_config *cfg,
+ enum v4l2_subdev_format_whence which,
+ unsigned int pad)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&state->mipi_sd, cfg, pad);
+
+ return &state->format_mbus;
+}
+
+static int mipi_csis_set_fmt(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct csis_pix_format const *csis_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (sdformat->pad >= CSIS_PADS_NUM)
+ return -EINVAL;
+
+ fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad);
+
+ mutex_lock(&state->lock);
+ if (fmt && sdformat->pad == CSIS_PAD_SOURCE) {
+ sdformat->format = *fmt;
+ goto unlock;
+ }
+
+ csis_fmt = mipi_csis_try_format(mipi_sd, &sdformat->format);
+
+ sdformat->format = *fmt;
+
+ if (csis_fmt && sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ state->csis_fmt = csis_fmt;
+ else
+ cfg->try_fmt = sdformat->format;
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int mipi_csis_get_fmt(struct v4l2_subdev *mipi_sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ mutex_lock(&state->lock);
+
+ fmt = mipi_csis_get_format(state, cfg, sdformat->which, sdformat->pad);
+
+ sdformat->format = *fmt;
+
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int mipi_csis_log_status(struct v4l2_subdev *mipi_sd)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+
+ mutex_lock(&state->lock);
+ mipi_csis_log_counters(state, true);
+ if (debug && (state->flags & ST_POWERED))
+ dump_regs(state, __func__);
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
+{
+ struct csi_state *state = dev_id;
+ unsigned long flags;
+ u32 status;
+ int i;
+
+ status = mipi_csis_read(state, MIPI_CSIS_INTSRC);
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ /* Update the event/error counters */
+ if ((status & MIPI_CSIS_INTSRC_ERRORS) || debug) {
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) {
+ if (!(status & state->events[i].mask))
+ continue;
+ state->events[i].counter++;
+ }
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+
+ mipi_csis_write(state, MIPI_CSIS_INTSRC, status);
+
+ return IRQ_HANDLED;
+}
+
+static int mipi_csi_registered(struct v4l2_subdev *mipi_sd)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ int i, ret;
+
+ for (i = 0; i < CSIS_PADS_NUM; i++) {
+ state->pads[i].flags = (i == CSIS_PAD_SINK) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+ }
+
+ /* set a default mbus format */
+ ret = imx_media_init_mbus_fmt(&state->format_mbus,
+ MIPI_CSIS_DEF_PIX_HEIGHT,
+ MIPI_CSIS_DEF_PIX_WIDTH, 0,
+ V4L2_FIELD_NONE, NULL);
+ if (ret)
+ return ret;
+
+ return media_entity_pads_init(&mipi_sd->entity, CSIS_PADS_NUM,
+ state->pads);
+}
+
+static struct v4l2_subdev_core_ops mipi_csis_core_ops = {
+ .s_power = mipi_csis_s_power,
+ .log_status = mipi_csis_log_status,
+};
+
+static const struct media_entity_operations mipi_csis_entity_ops = {
+ .link_setup = mipi_csis_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static struct v4l2_subdev_video_ops mipi_csis_video_ops = {
+ .s_stream = mipi_csis_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = {
+ .init_cfg = mipi_csis_init_cfg,
+ .get_fmt = mipi_csis_get_fmt,
+ .set_fmt = mipi_csis_set_fmt,
+};
+
+static struct v4l2_subdev_ops mipi_csis_subdev_ops = {
+ .core = &mipi_csis_core_ops,
+ .video = &mipi_csis_video_ops,
+ .pad = &mipi_csis_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops mipi_csis_internal_ops = {
+ .registered = mipi_csi_registered,
+};
+
+static int mipi_csis_parse_dt(struct platform_device *pdev,
+ struct csi_state *state)
+{
+ struct device_node *node = pdev->dev.of_node;
+
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->clk_frequency))
+ state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
+
+ /* Get MIPI PHY resets */
+ state->mrst = devm_reset_control_get_exclusive(&pdev->dev, "mrst");
+ if (IS_ERR(state->mrst))
+ return PTR_ERR(state->mrst);
+
+ /* Get MIPI CSI-2 bus configration from the endpoint node. */
+ of_property_read_u32(node, "fsl,csis-hs-settle", &state->hs_settle);
+
+ return 0;
+}
+
+static int mipi_csis_pm_resume(struct device *dev, bool runtime);
+
+static int mipi_csis_parse_endpoint(struct device *dev,
+ struct v4l2_fwnode_endpoint *ep,
+ struct v4l2_async_subdev *asd)
+{
+ struct v4l2_subdev *mipi_sd = dev_get_drvdata(dev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+
+ if (!fwnode_device_is_available(asd->match.fwnode)) {
+ v4l2_err(mipi_sd, "remote is not available\n");
+ return -EINVAL;
+ }
+
+ if (ep->bus_type != V4L2_MBUS_CSI2)
+ v4l2_err(mipi_sd, "invalid bus type, must be MIPI CSI2\n");
+
+ state->bus = ep->bus.mipi_csi2;
+
+ dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes);
+ dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags);
+
+ return 0;
+}
+
+static int mipi_csis_subdev_init(struct v4l2_subdev *mipi_sd,
+ struct platform_device *pdev,
+ const struct v4l2_subdev_ops *ops)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ unsigned int sink_port = 0;
+ int ret;
+
+ v4l2_subdev_init(mipi_sd, ops);
+ mipi_sd->owner = THIS_MODULE;
+ snprintf(mipi_sd->name, sizeof(mipi_sd->name), "%s.%d",
+ CSIS_SUBDEV_NAME, state->index);
+
+ mipi_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ mipi_sd->ctrl_handler = NULL;
+
+ mipi_sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ mipi_sd->entity.ops = &mipi_csis_entity_ops;
+
+ mipi_sd->dev = &pdev->dev;
+
+ state->csis_fmt = &mipi_csis_formats[0];
+ state->format_mbus.code = mipi_csis_formats[0].code;
+ state->format_mbus.width = MIPI_CSIS_DEF_PIX_WIDTH;
+ state->format_mbus.height = MIPI_CSIS_DEF_PIX_HEIGHT;
+ state->format_mbus.field = V4L2_FIELD_NONE;
+
+ v4l2_set_subdevdata(mipi_sd, &pdev->dev);
+
+ ret = v4l2_async_register_fwnode_subdev(mipi_sd,
+ sizeof(struct v4l2_async_subdev), &sink_port, 1,
+ mipi_csis_parse_endpoint);
+ if (ret < 0)
+ dev_err(&pdev->dev, "async fwnode register failed: %d\n", ret);
+
+ return ret;
+}
+
+static int mipi_csis_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *mem_res;
+ struct csi_state *state;
+ int ret = -ENOMEM;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->lock);
+ spin_lock_init(&state->slock);
+
+ state->pdev = pdev;
+ state->dev = dev;
+
+ ret = mipi_csis_parse_dt(pdev, state);
+ if (ret < 0) {
+ dev_err(dev, "Failed to parse device tree: %d\n", ret);
+ return ret;
+ }
+
+ mipi_csis_phy_init(state);
+ mipi_csis_phy_reset(state);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ state->regs = devm_ioremap_resource(dev, mem_res);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+
+ state->irq = platform_get_irq(pdev, 0);
+ if (state->irq < 0) {
+ dev_err(dev, "Failed to get irq\n");
+ return state->irq;
+ }
+
+ ret = mipi_csis_clk_get(state);
+ if (ret < 0)
+ return ret;
+
+ mipi_csis_clk_enable(state);
+
+ ret = devm_request_irq(dev, state->irq, mipi_csis_irq_handler,
+ 0, dev_name(dev), state);
+ if (ret) {
+ dev_err(dev, "Interrupt request failed\n");
+ goto disable_clock;
+ }
+
+ platform_set_drvdata(pdev, &state->mipi_sd);
+
+ ret = mipi_csis_subdev_init(&state->mipi_sd, pdev,
+ &mipi_csis_subdev_ops);
+ if (ret < 0)
+ goto disable_clock;
+
+ state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&state->mipi_sd.entity, CSIS_PADS_NUM,
+ state->pads);
+ if (ret < 0)
+ goto unregister_subdev;
+
+ memcpy(state->events, mipi_csis_events, sizeof(state->events));
+
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = mipi_csis_pm_resume(dev, true);
+ if (ret < 0)
+ goto unregister_all;
+ }
+
+ dev_info(&pdev->dev, "lanes: %d, hs_settle: %d, wclk: %d, freq: %u\n",
+ state->bus.num_data_lanes, state->hs_settle,
+ state->wrap_clk ? 1 : 0, state->clk_frequency);
+ return 0;
+
+unregister_all:
+ media_entity_cleanup(&state->mipi_sd.entity);
+unregister_subdev:
+ v4l2_async_unregister_subdev(&state->mipi_sd);
+disable_clock:
+ mipi_csis_clk_disable(state);
+
+ return ret;
+}
+
+static int mipi_csis_pm_suspend(struct device *dev, bool runtime)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+ if (state->flags & ST_POWERED) {
+ mipi_csis_stop_stream(state);
+ ret = regulator_disable(state->mipi_phy_regulator);
+ if (ret)
+ goto unlock;
+ mipi_csis_clk_disable(state);
+ state->flags &= ~ST_POWERED;
+ if (!runtime)
+ state->flags |= ST_SUSPENDED;
+ }
+
+ unlock:
+ mutex_unlock(&state->lock);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int mipi_csis_pm_resume(struct device *dev, bool runtime)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+ if (!runtime && !(state->flags & ST_SUSPENDED))
+ goto unlock;
+
+ if (!(state->flags & ST_POWERED)) {
+ ret = regulator_enable(state->mipi_phy_regulator);
+ if (ret)
+ goto unlock;
+
+ state->flags |= ST_POWERED;
+ mipi_csis_clk_enable(state);
+ }
+ if (state->flags & ST_STREAMING)
+ mipi_csis_start_stream(state);
+
+ state->flags &= ~ST_SUSPENDED;
+
+ unlock:
+ mutex_unlock(&state->lock);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int mipi_csis_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, false);
+}
+
+static int mipi_csis_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, false);
+}
+
+static int mipi_csis_runtime_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, true);
+}
+
+static int mipi_csis_runtime_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, true);
+}
+
+static int mipi_csis_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *mipi_sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csis_state(mipi_sd);
+
+ v4l2_async_unregister_subdev(&state->mipi_sd);
+ v4l2_async_notifier_unregister(&state->subdev_notifier);
+
+ pm_runtime_disable(&pdev->dev);
+ mipi_csis_pm_suspend(&pdev->dev, true);
+ mipi_csis_clk_disable(state);
+ media_entity_cleanup(&state->mipi_sd.entity);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops mipi_csis_pm_ops = {
+ SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(mipi_csis_suspend, mipi_csis_resume)
+};
+
+static const struct of_device_id mipi_csis_of_match[] = {
+ { .compatible = "fsl,imx7-mipi-csi2", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mipi_csis_of_match);
+
+static struct platform_driver mipi_csis_driver = {
+ .probe = mipi_csis_probe,
+ .remove = mipi_csis_remove,
+ .driver = {
+ .of_match_table = mipi_csis_of_match,
+ .name = CSIS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &mipi_csis_pm_ops,
+ },
+};
+
+module_platform_driver(mipi_csis_driver);
+
+MODULE_DESCRIPTION("i.MX7 MIPI CSI-2 Receiver driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx7-mipi-csi2");
--
2.17.0
^ permalink raw reply related
* [PATCH v6 05/13] clk: imx7d: reset parent for mipi csi root
From: Rui Miguel Silva @ 2018-05-22 14:52 UTC (permalink / raw)
To: mchehab, sakari.ailus, Steve Longerbeam, Philipp Zabel,
Rob Herring
Cc: devel, devicetree, Greg Kroah-Hartman, Ryan Harkin,
Rui Miguel Silva, Fabio Estevam, Shawn Guo, linux-clk,
linux-media
In-Reply-To: <20180522145245.3143-1-rui.silva@linaro.org>
To guarantee that we do not get Overflow in image FIFO the outer bandwidth has
to be faster than inputer bandwidth. For that it must be possible to set a
faster frequency clock. So set new parent to sys_pfd3 clock for the mipi csi
block.
Acked-by: Shawn Guo <shawnguo@kernel.org>
Signed-off-by: Rui Miguel Silva <rui.silva@linaro.org>
---
drivers/clk/imx/clk-imx7d.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
index f7f4db2e6fa6..27877d05faa2 100644
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -891,6 +891,8 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
clk_set_parent(clks[IMX7D_PLL_AUDIO_MAIN_BYPASS], clks[IMX7D_PLL_AUDIO_MAIN]);
clk_set_parent(clks[IMX7D_PLL_VIDEO_MAIN_BYPASS], clks[IMX7D_PLL_VIDEO_MAIN]);
+ clk_set_parent(clks[IMX7D_MIPI_CSI_ROOT_SRC], clks[IMX7D_PLL_SYS_PFD3_CLK]);
+
/* use old gpt clk setting, gpt1 root clk must be twice as gpt counter freq */
clk_set_parent(clks[IMX7D_GPT1_ROOT_SRC], clks[IMX7D_OSC_24M_CLK]);
--
2.17.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox