Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 5/5] ARM64: dts: meson-axg: enable the UART_A controller
From: Yixun Lan @ 2018-01-11  2:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180111023401.125658-1-yixun.lan@amlogic.com>

The UART_A is connected to a BT module on the S400 board.

Acked-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
---
 arch/arm64/boot/dts/amlogic/meson-axg-s400.dts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts
index 9c1b78028ccb..d56894dbb209 100644
--- a/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-axg-s400.dts
@@ -14,6 +14,7 @@
 
 	aliases {
 		serial0 = &uart_AO;
+		serial1 = &uart_A;
 	};
 };
 
@@ -24,6 +25,12 @@
 	pinctrl-names = "default";
 };
 
+&uart_A {
+	status = "okay";
+	pinctrl-0 = <&uart_a_pins>;
+	pinctrl-names = "default";
+};
+
 &uart_AO {
 	status = "okay";
 	pinctrl-0 = <&uart_ao_a_pins>;
-- 
2.15.1

^ permalink raw reply related

* [PATCH v3 0/2] phy: rockchip-emmc: fixes emmc-phy power on failed with rk3399 SoCs
From: Caesar Wang @ 2018-01-11  2:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Kishon,

Since the Shawn isn't available, I take over this series patches for now.

As the original bug had tracked on https://issuetracker.google.com/71561742.
In some cases, the mmc phy power on failed during booting up.
The log as below:
...
[   2.375333] rockchip_emmc_phy_power: caldone timeout.
[    2.377815] phy phy-ff770000.syscon:phy at f780.4: phy poweron failed --> -110
...
[    2.489295] mmc0: mmc_select_hs400es failed, error -110
[    2.489302] mmc0: error -110 whilst initialising MMC card
..

The actual emulate, the wait 5us for calpad busy trimming, that's no enough.
We need give the enough margin for it.

Verified on url =
        https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-4.4
This series patches can apply and bring up with kernel-next on rk3399 chromebook.

-Caesar


Changes in v3:
- As Doug commented on both upstream and gerrit.
  Change "5, 50" to "0, 50", and the message of print.
- As Doug commented on https://patchwork.kernel.org/patch/10154797,
  Change "1, 50" to "0, 50".

Changes in v2:
- print the return valut with regmap_read_poll_timeout failing.
- As Brian commented on https://patchwork.kernel.org/patch/10139891/,
  changed the note and added to print error value with
  regmap_read_poll_timeout API.

Shawn Lin (2):
  phy: rockchip-emmc: retry calpad busy trimming
  phy: rockchip-emmc: use regmap_read_poll_timeout to poll dllrdy

 drivers/phy/rockchip/phy-rockchip-emmc.c | 60 +++++++++++++++-----------------
 1 file changed, 28 insertions(+), 32 deletions(-)

-- 
2.7.4

^ permalink raw reply

* [PATCH v3 1/2] phy: rockchip-emmc: retry calpad busy trimming
From: Caesar Wang @ 2018-01-11  2:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515638427-15117-1-git-send-email-wxt@rock-chips.com>

From: Shawn Lin <shawn.lin@rock-chips.com>

It turns out that 5us isn't enough for all cases, so let's
retry some more times to wait for caldone.

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Tested-by: Ziyuan Xu <xzy.xu@rock-chips.com>
Signed-off-by: Caesar Wang <wxt@rock-chips.com>
---

Changes in v3:
- As Doug commented on both upstream and gerrit.
  Change "5, 50" to "0, 50", and the message of print.

Changes in v2:
- print the return valut with regmap_read_poll_timeout failing.

 drivers/phy/rockchip/phy-rockchip-emmc.c | 27 +++++++++++++++++----------
 1 file changed, 17 insertions(+), 10 deletions(-)

diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c
index f1b24f1..b0d1093 100644
--- a/drivers/phy/rockchip/phy-rockchip-emmc.c
+++ b/drivers/phy/rockchip/phy-rockchip-emmc.c
@@ -76,6 +76,10 @@
 #define PHYCTRL_OTAPDLYSEL_MASK		0xf
 #define PHYCTRL_OTAPDLYSEL_SHIFT	0x7
 
+#define PHYCTRL_IS_CALDONE(x) \
+	((((x) >> PHYCTRL_CALDONE_SHIFT) & \
+	  PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE)
+
 struct rockchip_emmc_phy {
 	unsigned int	reg_offset;
 	struct regmap	*reg_base;
@@ -90,6 +94,7 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
 	unsigned int freqsel = PHYCTRL_FREQSEL_200M;
 	unsigned long rate;
 	unsigned long timeout;
+	int ret;
 
 	/*
 	 * Keep phyctrl_pdb and phyctrl_endll low to allow
@@ -160,17 +165,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
 				   PHYCTRL_PDB_SHIFT));
 
 	/*
-	 * According to the user manual, it asks driver to
-	 * wait 5us for calpad busy trimming
+	 * According to the user manual, it asks driver to wait 5us for
+	 * calpad busy trimming. However it is documented that this value is
+	 * PVT(A.K.A process,voltage and temperature) relevant, so some
+	 * failure cases are found which indicates we should be more tolerant
+	 * to calpad busy trimming.
 	 */
-	udelay(5);
-	regmap_read(rk_phy->reg_base,
-		    rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
-		    &caldone);
-	caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
-	if (caldone != PHYCTRL_CALDONE_DONE) {
-		pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
-		return -ETIMEDOUT;
+	ret = regmap_read_poll_timeout(rk_phy->reg_base,
+				       rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+				       caldone, PHYCTRL_IS_CALDONE(caldone),
+				       0, 50);
+	if (ret) {
+		pr_err("%s: caldone failed, ret=%d\n", __func__, ret);
+		return ret;
 	}
 
 	/* Set the frequency of the DLL operation */
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 2/2] phy: rockchip-emmc: use regmap_read_poll_timeout to poll dllrdy
From: Caesar Wang @ 2018-01-11  2:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515638427-15117-1-git-send-email-wxt@rock-chips.com>

From: Shawn Lin <shawn.lin@rock-chips.com>

Just use the API instead of open-coding it, no functional change
intended.

Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Reviewed-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Caesar Wang <wxt@rock-chips.com>

---

Changes in v3:
- As Doug commented on https://patchwork.kernel.org/patch/10154797,
  Change "1, 50" to "0, 50".

Changes in v2:
- As Brian commented on https://patchwork.kernel.org/patch/10139891/,
  changed the note and added to print error value with
  regmap_read_poll_timeout API.

 drivers/phy/rockchip/phy-rockchip-emmc.c | 33 +++++++++++---------------------
 1 file changed, 11 insertions(+), 22 deletions(-)

diff --git a/drivers/phy/rockchip/phy-rockchip-emmc.c b/drivers/phy/rockchip/phy-rockchip-emmc.c
index b0d1093..b237360 100644
--- a/drivers/phy/rockchip/phy-rockchip-emmc.c
+++ b/drivers/phy/rockchip/phy-rockchip-emmc.c
@@ -79,6 +79,9 @@
 #define PHYCTRL_IS_CALDONE(x) \
 	((((x) >> PHYCTRL_CALDONE_SHIFT) & \
 	  PHYCTRL_CALDONE_MASK) == PHYCTRL_CALDONE_DONE)
+#define PHYCTRL_IS_DLLRDY(x) \
+	((((x) >> PHYCTRL_DLLRDY_SHIFT) & \
+	  PHYCTRL_DLLRDY_MASK) == PHYCTRL_DLLRDY_DONE)
 
 struct rockchip_emmc_phy {
 	unsigned int	reg_offset;
@@ -93,7 +96,6 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
 	unsigned int dllrdy;
 	unsigned int freqsel = PHYCTRL_FREQSEL_200M;
 	unsigned long rate;
-	unsigned long timeout;
 	int ret;
 
 	/*
@@ -217,28 +219,15 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off)
 	 * NOTE: There appear to be corner cases where the DLL seems to take
 	 * extra long to lock for reasons that aren't understood.  In some
 	 * extreme cases we've seen it take up to over 10ms (!).  We'll be
-	 * generous and give it 50ms.  We still busy wait here because:
-	 * - In most cases it should be super fast.
-	 * - This is not called lots during normal operation so it shouldn't
-	 *   be a power or performance problem to busy wait.  We expect it
-	 *   only at boot / resume.  In both cases, eMMC is probably on the
-	 *   critical path so busy waiting a little extra time should be OK.
+	 * generous and give it 50ms.
 	 */
-	timeout = jiffies + msecs_to_jiffies(50);
-	do {
-		udelay(1);
-
-		regmap_read(rk_phy->reg_base,
-			rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
-			&dllrdy);
-		dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
-		if (dllrdy == PHYCTRL_DLLRDY_DONE)
-			break;
-	} while (!time_after(jiffies, timeout));
-
-	if (dllrdy != PHYCTRL_DLLRDY_DONE) {
-		pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
-		return -ETIMEDOUT;
+	ret = regmap_read_poll_timeout(rk_phy->reg_base,
+				       rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
+				       dllrdy, PHYCTRL_IS_DLLRDY(dllrdy),
+				       0, 50 * USEC_PER_MSEC);
+	if (ret) {
+		pr_err("%s: dllrdy failed. ret=%d\n", __func__, ret);
+		return ret;
 	}
 
 	return 0;
-- 
2.7.4

^ permalink raw reply related

* [PATCH v5 01/44] dt-bindings: clock: Add new bindings for TI Davinci PLL clocks
From: David Lechner @ 2018-01-11  2:50 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAHCN7x+ZYezmEU_0mF=6_gF14DZxKnuDp1Cx=aC2=eN_QLNdJQ@mail.gmail.com>

On 01/10/2018 04:24 PM, Adam Ford wrote:
> 
> I am available tomorrow to build and test patches against the
> da850-evm.  I just need to know which version(s) to test.

Great. As per the cover letter:

You can find a working branch with everything included in the "common-clk-v5"
branch of https://github.com/dlech/ev3dev-kernel.git.

^ permalink raw reply

* [PATCH v5 01/44] dt-bindings: clock: Add new bindings for TI Davinci PLL clocks
From: David Lechner @ 2018-01-11  2:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5aacc350-6236-2e4f-35bb-a681fc9d47e7@ti.com>

On 01/10/2018 12:52 PM, Sekhar Nori wrote:
> On Wednesday 10 January 2018 08:31 AM, David Lechner wrote:
>> On 01/09/2018 06:35 AM, Sekhar Nori wrote:
>>> On Monday 08 January 2018 09:59 PM, David Lechner wrote:
>>>> On 01/08/2018 08:00 AM, Sekhar Nori wrote:
>>>>> On Monday 08 January 2018 07:47 AM, David Lechner wrote:
> 
>>>>>> diff --git
>>>>>> a/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>>>>> b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>>>>> new file mode 100644
>>>>>> index 0000000..99bf5da
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt
>>>>>> @@ -0,0 +1,47 @@
>>>>>> +Binding for TI DaVinci PLL Controllers
>>>>>> +
>>>>>> +The PLL provides clocks to most of the components on the SoC. In
>>>>>> addition
>>>>>> +to the PLL itself, this controller also contains bypasses, gates,
>>>>>> dividers,
>>>>>> +an multiplexers for various clock signals.
>>>>>> +
>>>>>> +Required properties:
>>>>>> +- compatible: shall be one of:
>>>>>> +??? - "ti,da850-pll0" for PLL0 on DA850/OMAP-L138/AM18XX
>>>>>> +??? - "ti,da850-pll1" for PLL1 on DA850/OMAP-L138/AM18XX
>>>>>
>>>>> These PLLs are same IP so they should use the same compatible. You can
>>>>> initialize both PLLs for DA850 based on the same compatible.
>>>>>
>>>>
>>>> But they are not exactly the same. For example, PLL0 has 7 PLLDIV clocks
>>>> while
>>>> PLL1 only has 3. PLL0 has PREDIV while PLL1 does not. PLL0 has certain
>>>> SYSCLKs
>>>> that are fixed-ratio but PLL1 does not have any of these. There are even
>>>> more
>>>> differences, but these are the ones we are actually using.
>>>
>>> We need each element of the PLLC to be modeled individually as a clock
>>> node.
>>
>> I gave this a good think while I have been working on this series
>> and I came to the conclusion that we really don't need to do this.
>> These components are all internal to the PLL IP block, so the
>> compatible string is enough to tell us what we have. They only
>> thing we need really in the device tree bindings are the connections
>> that are external to the IP block.
>>
>>
>>> That is, PLL should only model the multiplier, the dividers
>>> including post and prediv should be modeled as divider clocks (hopefully
>>> being able to use the clk-divider.c library). The sysclks can be
>>> fixed-factor-clock type clocks.
>>>
>>> Without this flexible mechanism, we cannot (at least later) model things
>>> like DIV4.5 clock which is the only clock which derives from the output
>>> of PLL multiplier before the post divider is applied.
>>>
>>> Since with DT there are are no retakes, we need to get this right the
>>> first time and modifying later will not be an option.
>>>
>>
>> So, the full device tree binding would look something like this:
>>
>> +
>> +??? pll0: clock-controller at 11000 {
>> +??????? compatible = "ti,da850-pll0";
>> +??????? reg = <0x11000 0x1000>;
>> +??????? clocks = <&ref_clk>, <&pll1_sysclk 3>, <&pll1_obsclk>;
>> +??????? clock-names = "oscin", pll1_sysclk3", "pll1_osbclk";
>> +??????? oscin-square-wave;
>> +
>> +??????? pll0_sysclk: sysclk {
>> +??????????? #clock-cells = <1>;
>> +??????? };
>> +
>> +??????? pll0_auxclk: auxclk {
>> +??????????? #clock-cells = <0>;
>> +??????? };
>> +
>> +??????? pll0_div45: div4.5 {
>> +??????????? #clock-cells = <0>;
>> +??????? };
>> +
>> +??????? pll0_obsclk: obsclk {
>> +??????????? #clock-cells = <0>;
>> +??????????? assigned-clocks = <&pll0_sysclk 1>;
>> +??????????? assigned-clock-names = "ocsrc";
>> +??????? };
>> +??? };
> 
> Well, I guess this will work as well. And I am probably biased towards
> the style I mentioned because AM335x and other TI OMAP processors
> follow that.
> 
> To make it easy to review that we have all bases covered, can you model
> the all PLLC0 and PLLC1 (input and output) clocks for the next version?

Sure thing.

> 
>>
>> There are three clocks coming into the IP block and there are 11 clocks
>> going out (sysclk is 7 clocks). And you can specify the board-specific
>> configuration, like having the "oscin-square-wave" flag when a square wave
>> is used instead of a crystal oscillator and you can assign the multiplexer
> 
> Ideally the OSCIN vs CLKIN selection should be another clock mux whose
> output is one of the input clocks to PLL controller. But I can see the
> difficulty in handling that as the mux itself is controlled by the PLL
> controller.
> 
>> input that will be used by obsclk. (And, this binding is totally compatible
>> with the binding I have already proposed - although, I see now it would
>> be better to go ahead and add the clocks-names property.)
> 
> Also, please add the oscin-square-wave to the binding definition too.
> 
> For the benefit of others reviewing and not familiar with the hardware,
> the users guide for DA850 is here:
> http://www.ti.com/lit/ug/spruh77c/spruh77c.pdf
> 
> and the PLL block diagram is on page 143 (Figure 8-1).
> 
> Thanks,
> Sekhar
> 

^ permalink raw reply

* [PATCH v5 0/2] Initial Allwinner V3s CSI Support
From: Yong Deng @ 2018-01-11  3:01 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset add initial support for Allwinner V3s CSI.

Allwinner V3s SoC features two CSI module. CSI0 is used for MIPI CSI-2
interface and CSI1 is used for parallel interface. This is not
documented in datasheet but by test and guess.

This patchset implement a v4l2 framework driver and add a binding 
documentation for it. 

Currently, the driver only support the parallel interface. And has been
tested with a BT1120 signal which generating from FPGA. The following
fetures are not support with this patchset:
  - ISP 
  - MIPI-CSI2
  - Master clock for camera sensor
  - Power regulator for the front end IC

Changes in v5:
  * Using the new SPDX tags.
  * Fix MODULE_LICENSE.
  * Add many default cases and warning messages.
  * Detail the parallel bus properties
  * Fix some spelling and syntax mistakes.

Changes in v4:
  * Deal with the CSI 'INNER QUEUE'.
    CSI will lookup the next dma buffer for next frame before the
    the current frame done IRQ triggered. This is not documented
    but reported by Ond?ej Jirman.
    The BSP code has workaround for this too. It skip to mark the
    first buffer as frame done for VB2 and pass the second buffer
    to CSI in the first frame done ISR call. Then in second frame
    done ISR call, it mark the first buffer as frame done for VB2
    and pass the third buffer to CSI. And so on. The bad thing is
    that the first buffer will be written twice and the first frame
    is dropped even the queued buffer is sufficient.
    So, I make some improvement here. Pass the next buffer to CSI
    just follow starting the CSI. In this case, the first frame
    will be stored in first buffer, second frame in second buffer.
    This mothed is used to avoid dropping the first frame, it
    would also drop frame when lacking of queued buffer.
  * Fix: using a wrong mbus_code when getting the supported formats
  * Change all fourcc to pixformat
  * Change some function names

Changes in v3:
  * Get rid of struct sun6i_csi_ops
  * Move sun6i-csi to new directory drivers/media/platform/sunxi
  * Merge sun6i_csi.c and sun6i_csi_v3s.c into sun6i_csi.c
  * Use generic fwnode endpoints parser
  * Only support a single subdev to make things simple
  * Many complaintion fix

Changes in v2: 
  * Change sunxi-csi to sun6i-csi
  * Rebase to media_tree master branch 

Following is the 'v4l2-compliance -s -f' output, I have test this
with both interlaced and progressive signal:

# ./v4l2-compliance -s -f
v4l2-compliance SHA   : 6049ea8bd64f9d78ef87ef0c2b3dc9b5de1ca4a1

Driver Info:
        Driver name   : sun6i-video
        Card type     : sun6i-csi
        Bus info      : platform:csi
        Driver version: 4.15.0
        Capabilities  : 0x84200001
                Video Capture
                Streaming
                Extended Pix Format
                Device Capabilities
        Device Caps   : 0x04200001
                Video Capture
                Streaming
                Extended Pix Format

Compliance test for device /dev/video0 (not using libv4l2):

Required ioctls:
        test VIDIOC_QUERYCAP: OK

Allow for multiple opens:
        test second video open: OK
        test VIDIOC_QUERYCAP: OK
        test VIDIOC_G/S_PRIORITY: OK
        test for unlimited opens: OK

Debug ioctls:
        test VIDIOC_DBG_G/S_REGISTER: OK (Not Supported)
        test VIDIOC_LOG_STATUS: OK (Not Supported)

Input ioctls:
        test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
        test VIDIOC_ENUMAUDIO: OK (Not Supported)
        test VIDIOC_G/S/ENUMINPUT: OK
        test VIDIOC_G/S_AUDIO: OK (Not Supported)
        Inputs: 1 Audio Inputs: 0 Tuners: 0

Output ioctls:
        test VIDIOC_G/S_MODULATOR: OK (Not Supported)
        test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
        test VIDIOC_ENUMAUDOUT: OK (Not Supported)
        test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
        test VIDIOC_G/S_AUDOUT: OK (Not Supported)
        Outputs: 0 Audio Outputs: 0 Modulators: 0

Input/Output configuration ioctls:
        test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
        test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
        test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
        test VIDIOC_G/S_EDID: OK (Not Supported)

Test input 0:

        Control ioctls:
                test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
                test VIDIOC_QUERYCTRL: OK (Not Supported)
                test VIDIOC_G/S_CTRL: OK (Not Supported)
                test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
                test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
                test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
                Standard Controls: 0 Private Controls: 0

        Format ioctls:
                test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK
                test VIDIOC_G/S_PARM: OK (Not Supported)
                test VIDIOC_G_FBUF: OK (Not Supported)
                test VIDIOC_G_FMT: OK
                test VIDIOC_TRY_FMT: OK
                test VIDIOC_S_FMT: OK
                test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
                test Cropping: OK (Not Supported)
                test Composing: OK (Not Supported)
                test Scaling: OK (Not Supported)

        Codec ioctls:
                test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
                test VIDIOC_G_ENC_INDEX: OK (Not Supported)
                test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)

        Buffer ioctls:
                test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK
                test VIDIOC_EXPBUF: OK

Test input 0:

Streaming ioctls:
        test read/write: OK (Not Supported)
        test MMAP: OK                                     
        test USERPTR: OK (Not Supported)
        test DMABUF: Cannot test, specify --expbuf-device

Stream using all formats:
        test MMAP for Format HM12, Frame Size 1280x720:
                Stride 1920, Field None: OK                                 
        test MMAP for Format NV12, Frame Size 1280x720:
                Stride 1920, Field None: OK                                 
        test MMAP for Format NV21, Frame Size 1280x720:
                Stride 1920, Field None: OK                                 
        test MMAP for Format YU12, Frame Size 1280x720:
                Stride 1920, Field None: OK                                 
        test MMAP for Format YV12, Frame Size 1280x720:
                Stride 1920, Field None: OK                                 
        test MMAP for Format NV16, Frame Size 1280x720:
                Stride 2560, Field None: OK                                 
        test MMAP for Format NV61, Frame Size 1280x720:
                Stride 2560, Field None: OK                                 
        test MMAP for Format 422P, Frame Size 1280x720:
                Stride 2560, Field None: OK                                 

Total: 54, Succeeded: 54, Failed: 0, Warnings: 0

Yong Deng (2):
  dt-bindings: media: Add Allwinner V3s Camera Sensor Interface (CSI)
  media: V3s: Add support for Allwinner CSI.

 .../devicetree/bindings/media/sun6i-csi.txt        |  59 ++
 MAINTAINERS                                        |   8 +
 drivers/media/platform/Kconfig                     |   1 +
 drivers/media/platform/Makefile                    |   2 +
 drivers/media/platform/sunxi/sun6i-csi/Kconfig     |   9 +
 drivers/media/platform/sunxi/sun6i-csi/Makefile    |   3 +
 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 908 +++++++++++++++++++++
 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 143 ++++
 .../media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h | 196 +++++
 .../media/platform/sunxi/sun6i-csi/sun6i_video.c   | 741 +++++++++++++++++
 .../media/platform/sunxi/sun6i-csi/sun6i_video.h   |  53 ++
 11 files changed, 2123 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/sun6i-csi.txt
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/Kconfig
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/Makefile
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h

-- 
1.8.3.1

^ permalink raw reply

* [PATCH v5 1/2] dt-bindings: media: Add Allwinner V3s Camera Sensor Interface (CSI)
From: Yong Deng @ 2018-01-11  3:03 UTC (permalink / raw)
  To: linux-arm-kernel

Add binding documentation for Allwinner V3s CSI.

Signed-off-by: Yong Deng <yong.deng@magewell.com>
---
 .../devicetree/bindings/media/sun6i-csi.txt        | 59 ++++++++++++++++++++++
 1 file changed, 59 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/sun6i-csi.txt

diff --git a/Documentation/devicetree/bindings/media/sun6i-csi.txt b/Documentation/devicetree/bindings/media/sun6i-csi.txt
new file mode 100644
index 0000000..2ff47a9
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/sun6i-csi.txt
@@ -0,0 +1,59 @@
+Allwinner V3s Camera Sensor Interface
+-------------------------------------
+
+Allwinner V3s SoC features two CSI module. CSI0 is used for MIPI CSI-2
+interface and CSI1 is used for parallel interface.
+
+Required properties:
+  - compatible: value must be "allwinner,sun8i-v3s-csi"
+  - reg: base address and size of the memory-mapped region.
+  - interrupts: interrupt associated to this IP
+  - clocks: phandles to the clocks feeding the CSI
+    * bus: the CSI interface clock
+    * mod: the CSI module clock
+    * ram: the CSI DRAM clock
+  - clock-names: the clock names mentioned above
+  - resets: phandles to the reset line driving the CSI
+
+Each CSI 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. As mentioned
+above, the endpoint's bus type should be MIPI CSI-2 for CSI0 and parallel or
+Bt656 for CSI1.
+
+Endpoint node properties for CSI1
+---------------------------------
+
+- remote-endpoint	: (required) a phandle to the bus receiver's endpoint
+			   node
+- bus-width:		: (required) must be 8, 10, 12 or 16
+- pclk-sample		: (optional) (default: sample on falling edge)
+- hsync-active		: (only required for parallel)
+- vsync-active		: (only required for parallel)
+
+Example:
+
+csi1: csi at 1cb4000 {
+	compatible = "allwinner,sun8i-v3s-csi";
+	reg = <0x01cb4000 0x1000>;
+	interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&ccu CLK_BUS_CSI>,
+		 <&ccu CLK_CSI1_SCLK>,
+		 <&ccu CLK_DRAM_CSI>;
+	clock-names = "bus", "mod", "ram";
+	resets = <&ccu RST_BUS_CSI>;
+
+	port {
+		/* Parallel bus endpoint */
+		csi1_ep: endpoint {
+			remote-endpoint = <&adv7611_ep>;
+			bus-width = <16>;
+
+			/* If hsync-active/vsync-active are missing,
+			   embedded BT.656 sync is used */
+			hsync-active = <0>; /* Active low */
+			vsync-active = <0>; /* Active low */
+			pclk-sample = <1>;  /* Rising */
+		};
+	};
+};
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH] ARM64: dts: meson-axg: add RMII pins for ethernet controller
From: Yixun Lan @ 2018-01-11  3:04 UTC (permalink / raw)
  To: linux-arm-kernel

Comparing to RGMII interface, the RMII interface require few pins.
So it's worth describing them here.

Signed-off-by: Yixun Lan <yixun.lan@amlogic.com>
---
 arch/arm64/boot/dts/amlogic/meson-axg.dtsi | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
index a80632641b39..ab4a0e8bc446 100644
--- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
@@ -251,6 +251,36 @@
 					gpio-ranges = <&pinctrl_periphs 0 0 86>;
 				};
 
+				eth_rmii_x_pins: eth-x-rmii {
+					mux {
+						groups = "eth_mdio_x",
+						       "eth_mdc_x",
+						       "eth_rgmii_rx_clk_x",
+						       "eth_rx_dv_x",
+						       "eth_rxd0_x",
+						       "eth_rxd1_x",
+						       "eth_txen_x",
+						       "eth_txd0_x",
+						       "eth_txd1_x";
+						function = "eth";
+					};
+				};
+
+				eth_rmii_y_pins: eth-y-rmii {
+					mux {
+						groups = "eth_mdio_y",
+						       "eth_mdc_y",
+						       "eth_rgmii_rx_clk_y",
+						       "eth_rx_dv_y",
+						       "eth_rxd0_y",
+						       "eth_rxd1_y",
+						       "eth_txen_y",
+						       "eth_txd0_y",
+						       "eth_txd1_y";
+						function = "eth";
+					};
+				};
+
 				eth_rgmii_x_pins: eth-x-rgmii {
 					mux {
 						groups = "eth_mdio_x",
-- 
2.15.1

^ permalink raw reply related

* [PATCH] clk: sunxi-ng: defaultly enable DE2 CCU for sun8i/sun50i
From: Icenowy Zheng @ 2018-01-11  3:04 UTC (permalink / raw)
  To: linux-arm-kernel

As DE2 support for more SoCs are introducing, there's many reports that
the DE2 is not functional due to DE2 CCU code not included in kernel.

Defaultly enable DE2 CCU for sun8i/sun50i to reduce this kind of
problems.

Signed-off-by: Icenowy Zheng <icenowy@aosc.io>
---
 drivers/clk/sunxi-ng/Kconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig
index 6427d0ebe2de..c2b04dcb6f18 100644
--- a/drivers/clk/sunxi-ng/Kconfig
+++ b/drivers/clk/sunxi-ng/Kconfig
@@ -60,6 +60,8 @@ config SUN8I_V3S_CCU
 
 config SUN8I_DE2_CCU
 	bool "Support for the Allwinner SoCs DE2 CCU"
+	default MACH_SUN8I || (ARM64 && ARCH_SUNXI)
+	depends on MACH_SUN8I || (ARM64 && ARCH_SUNXI) || COMPILE_TEST
 
 config SUN8I_R40_CCU
 	bool "Support for the Allwinner R40 CCU"
-- 
2.14.2

^ permalink raw reply related

* [PATCH v5 2/2] media: V3s: Add support for Allwinner CSI.
From: Yong Deng @ 2018-01-11  3:06 UTC (permalink / raw)
  To: linux-arm-kernel

Allwinner V3s SoC features two CSI module. CSI0 is used for MIPI CSI-2
interface and CSI1 is used for parallel interface. This is not
documented in datasheet but by test and guess.

This patch implement a v4l2 framework driver for it.

Currently, the driver only support the parallel interface. MIPI-CSI2,
ISP's support are not included in this patch.

Signed-off-by: Yong Deng <yong.deng@magewell.com>
---
 MAINTAINERS                                        |   8 +
 drivers/media/platform/Kconfig                     |   1 +
 drivers/media/platform/Makefile                    |   2 +
 drivers/media/platform/sunxi/sun6i-csi/Kconfig     |   9 +
 drivers/media/platform/sunxi/sun6i-csi/Makefile    |   3 +
 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c | 908 +++++++++++++++++++++
 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h | 143 ++++
 .../media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h | 196 +++++
 .../media/platform/sunxi/sun6i-csi/sun6i_video.c   | 741 +++++++++++++++++
 .../media/platform/sunxi/sun6i-csi/sun6i_video.h   |  53 ++
 10 files changed, 2064 insertions(+)
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/Kconfig
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/Makefile
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
 create mode 100644 drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 9501403..b792fe5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3783,6 +3783,14 @@ M:	Jaya Kumar <jayakumar.alsa@gmail.com>
 S:	Maintained
 F:	sound/pci/cs5535audio/
 
+CSI DRIVERS FOR ALLWINNER V3s
+M:	Yong Deng <yong.deng@magewell.com>
+L:	linux-media at vger.kernel.org
+T:	git git://linuxtv.org/media_tree.git
+S:	Maintained
+F:	drivers/media/platform/sunxi/sun6i-csi/
+F:	Documentation/devicetree/bindings/media/sun6i-csi.txt
+
 CW1200 WLAN driver
 M:	Solomon Peachy <pizza@shaftnet.org>
 S:	Maintained
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index fd0c998..41017e3 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -150,6 +150,7 @@ source "drivers/media/platform/am437x/Kconfig"
 source "drivers/media/platform/xilinx/Kconfig"
 source "drivers/media/platform/rcar-vin/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
 
 config VIDEO_TI_CAL
 	tristate "TI CAL (Camera Adaptation Layer) driver"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 003b0bb..e6e9ce7 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -97,3 +97,5 @@ obj-$(CONFIG_VIDEO_QCOM_CAMSS)		+= qcom/camss-8x16/
 obj-$(CONFIG_VIDEO_QCOM_VENUS)		+= qcom/venus/
 
 obj-y					+= meson/
+
+obj-$(CONFIG_VIDEO_SUN6I_CSI)		+= sunxi/sun6i-csi/
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Kconfig b/drivers/media/platform/sunxi/sun6i-csi/Kconfig
new file mode 100644
index 0000000..314188a
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_SUN6I_CSI
+	tristate "Allwinner V3s Camera Sensor Interface driver"
+	depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API && HAS_DMA
+	depends on ARCH_SUNXI || COMPILE_TEST
+	select VIDEOBUF2_DMA_CONTIG
+	select REGMAP_MMIO
+	select V4L2_FWNODE
+	---help---
+	   Support for the Allwinner Camera Sensor Interface Controller on V3s.
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Makefile b/drivers/media/platform/sunxi/sun6i-csi/Makefile
new file mode 100644
index 0000000..213cb6b
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/Makefile
@@ -0,0 +1,3 @@
+sun6i-csi-y += sun6i_video.o sun6i_csi.o
+
+obj-$(CONFIG_VIDEO_SUN6I_CSI) += sun6i-csi.o
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
new file mode 100644
index 0000000..9c341f0
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -0,0 +1,908 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+#include "sun6i_csi.h"
+#include "sun6i_csi_reg.h"
+
+#define MODULE_NAME	"sun6i-csi"
+
+struct sun6i_csi_dev {
+	struct sun6i_csi		csi;
+	struct device			*dev;
+
+	struct regmap			*regmap;
+	struct clk			*clk_mod;
+	struct clk			*clk_ram;
+	struct reset_control		*rstc_bus;
+
+	int				planar_offset[3];
+};
+
+static const u32 supported_pixformats[] = {
+	V4L2_PIX_FMT_SBGGR8,
+	V4L2_PIX_FMT_SGBRG8,
+	V4L2_PIX_FMT_SGRBG8,
+	V4L2_PIX_FMT_SRGGB8,
+	V4L2_PIX_FMT_SBGGR10,
+	V4L2_PIX_FMT_SGBRG10,
+	V4L2_PIX_FMT_SGRBG10,
+	V4L2_PIX_FMT_SRGGB10,
+	V4L2_PIX_FMT_SBGGR12,
+	V4L2_PIX_FMT_SGBRG12,
+	V4L2_PIX_FMT_SGRBG12,
+	V4L2_PIX_FMT_SRGGB12,
+	V4L2_PIX_FMT_YUYV,
+	V4L2_PIX_FMT_YVYU,
+	V4L2_PIX_FMT_UYVY,
+	V4L2_PIX_FMT_VYUY,
+	V4L2_PIX_FMT_HM12,
+	V4L2_PIX_FMT_NV12,
+	V4L2_PIX_FMT_NV21,
+	V4L2_PIX_FMT_YUV420,
+	V4L2_PIX_FMT_YVU420,
+	V4L2_PIX_FMT_NV16,
+	V4L2_PIX_FMT_NV61,
+	V4L2_PIX_FMT_YUV422P,
+};
+
+static inline struct sun6i_csi_dev *sun6i_csi_to_dev(struct sun6i_csi *csi)
+{
+	return container_of(csi, struct sun6i_csi_dev, csi);
+}
+
+int sun6i_csi_get_supported_pixformats(struct sun6i_csi *csi,
+				       const u32 **pixformats)
+{
+	if (pixformats != NULL)
+		*pixformats = supported_pixformats;
+
+	return ARRAY_SIZE(supported_pixformats);
+}
+
+/* TODO add 10&12 bit YUV, RGB support */
+bool sun6i_csi_is_format_support(struct sun6i_csi *csi,
+				 u32 pixformat, u32 mbus_code)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+
+	/*
+	 * Some video receivers have the ability to be compatible with
+	 * 8bit and 16bit bus width.
+	 * Identify the media bus format from device tree.
+	 */
+	if ((sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_PARALLEL
+	      || sdev->csi.v4l2_ep.bus_type == V4L2_MBUS_BT656)
+	     && sdev->csi.v4l2_ep.bus.parallel.bus_width == 16) {
+		switch (pixformat) {
+		case V4L2_PIX_FMT_HM12:
+		case V4L2_PIX_FMT_NV12:
+		case V4L2_PIX_FMT_NV21:
+		case V4L2_PIX_FMT_NV16:
+		case V4L2_PIX_FMT_NV61:
+		case V4L2_PIX_FMT_YUV420:
+		case V4L2_PIX_FMT_YVU420:
+		case V4L2_PIX_FMT_YUV422P:
+			switch (mbus_code) {
+			case MEDIA_BUS_FMT_UYVY8_1X16:
+			case MEDIA_BUS_FMT_VYUY8_1X16:
+			case MEDIA_BUS_FMT_YUYV8_1X16:
+			case MEDIA_BUS_FMT_YVYU8_1X16:
+				return true;
+			default:
+				dev_warn(sdev->dev,
+					 "Unsupported mbus code: 0x%x\n",
+					 mbus_code);
+				break;
+			}
+			break;
+		default:
+			dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n",
+				 pixformat);
+			break;
+		}
+		return false;
+	}
+
+	switch (pixformat) {
+	case V4L2_PIX_FMT_SBGGR8:
+		return (mbus_code == MEDIA_BUS_FMT_SBGGR8_1X8);
+	case V4L2_PIX_FMT_SGBRG8:
+		return (mbus_code == MEDIA_BUS_FMT_SGBRG8_1X8);
+	case V4L2_PIX_FMT_SGRBG8:
+		return (mbus_code == MEDIA_BUS_FMT_SGRBG8_1X8);
+	case V4L2_PIX_FMT_SRGGB8:
+		return (mbus_code == MEDIA_BUS_FMT_SRGGB8_1X8);
+	case V4L2_PIX_FMT_SBGGR10:
+		return (mbus_code == MEDIA_BUS_FMT_SBGGR10_1X10);
+	case V4L2_PIX_FMT_SGBRG10:
+		return (mbus_code == MEDIA_BUS_FMT_SGBRG10_1X10);
+	case V4L2_PIX_FMT_SGRBG10:
+		return (mbus_code == MEDIA_BUS_FMT_SGRBG10_1X10);
+	case V4L2_PIX_FMT_SRGGB10:
+		return (mbus_code == MEDIA_BUS_FMT_SRGGB10_1X10);
+	case V4L2_PIX_FMT_SBGGR12:
+		return (mbus_code == MEDIA_BUS_FMT_SBGGR12_1X12);
+	case V4L2_PIX_FMT_SGBRG12:
+		return (mbus_code == MEDIA_BUS_FMT_SGBRG12_1X12);
+	case V4L2_PIX_FMT_SGRBG12:
+		return (mbus_code == MEDIA_BUS_FMT_SGRBG12_1X12);
+	case V4L2_PIX_FMT_SRGGB12:
+		return (mbus_code == MEDIA_BUS_FMT_SRGGB12_1X12);
+
+	case V4L2_PIX_FMT_YUYV:
+		return (mbus_code == MEDIA_BUS_FMT_YUYV8_2X8);
+	case V4L2_PIX_FMT_YVYU:
+		return (mbus_code == MEDIA_BUS_FMT_YVYU8_2X8);
+	case V4L2_PIX_FMT_UYVY:
+		return (mbus_code == MEDIA_BUS_FMT_UYVY8_2X8);
+	case V4L2_PIX_FMT_VYUY:
+		return (mbus_code == MEDIA_BUS_FMT_VYUY8_2X8);
+
+	case V4L2_PIX_FMT_HM12:
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+	case V4L2_PIX_FMT_YUV422P:
+		switch (mbus_code) {
+		case MEDIA_BUS_FMT_UYVY8_2X8:
+		case MEDIA_BUS_FMT_VYUY8_2X8:
+		case MEDIA_BUS_FMT_YUYV8_2X8:
+		case MEDIA_BUS_FMT_YVYU8_2X8:
+			return true;
+		default:
+			dev_warn(sdev->dev, "Unsupported mbus code: 0x%x\n",
+				 mbus_code);
+			break;
+		}
+		break;
+	default:
+		dev_warn(sdev->dev, "Unsupported pixformat: 0x%x\n", pixformat);
+		break;
+	}
+
+	return false;
+}
+
+int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+	struct regmap *regmap = sdev->regmap;
+	int ret;
+
+	if (!enable) {
+		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
+
+		clk_disable_unprepare(sdev->clk_ram);
+		clk_disable_unprepare(sdev->clk_mod);
+		reset_control_assert(sdev->rstc_bus);
+		return 0;
+	}
+
+	ret = clk_prepare_enable(sdev->clk_mod);
+	if (ret) {
+		dev_err(sdev->dev, "Enable csi clk err %d\n", ret);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(sdev->clk_ram);
+	if (ret) {
+		dev_err(sdev->dev, "Enable clk_dram_csi clk err %d\n", ret);
+		return ret;
+	}
+
+	ret = reset_control_deassert(sdev->rstc_bus);
+	if (ret) {
+		dev_err(sdev->dev, "reset err %d\n", ret);
+		return ret;
+	}
+
+	regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, CSI_EN_CSI_EN);
+
+	return 0;
+}
+
+static enum csi_input_fmt get_csi_input_format(u32 mbus_code, u32 pixformat)
+{
+	/* bayer */
+	if ((mbus_code & 0xF000) == 0x3000)
+		return CSI_INPUT_FORMAT_RAW;
+
+	switch (pixformat) {
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YVYU:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_VYUY:
+		return CSI_INPUT_FORMAT_RAW;
+	default:
+		break;
+	}
+
+	/* not support YUV420 input format yet */
+	pr_debug("Select YUV422 as default input format of CSI.\n");
+	return CSI_INPUT_FORMAT_YUV422;
+}
+
+static enum csi_output_fmt get_csi_output_format(u32 pixformat, u32 field)
+{
+	bool buf_interlaced = false;
+
+	if (field == V4L2_FIELD_INTERLACED
+	    || field == V4L2_FIELD_INTERLACED_TB
+	    || field == V4L2_FIELD_INTERLACED_BT)
+		buf_interlaced = true;
+
+	switch (pixformat) {
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
+		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+		return buf_interlaced ? CSI_FRAME_RAW_10 : CSI_FIELD_RAW_10;
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+		return buf_interlaced ? CSI_FRAME_RAW_12 : CSI_FIELD_RAW_12;
+
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YVYU:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_VYUY:
+		return buf_interlaced ? CSI_FRAME_RAW_8 : CSI_FIELD_RAW_8;
+
+	case V4L2_PIX_FMT_HM12:
+		return buf_interlaced ? CSI_FRAME_MB_YUV420 :
+					CSI_FIELD_MB_YUV420;
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+		return buf_interlaced ? CSI_FRAME_UV_CB_YUV420 :
+					CSI_FIELD_UV_CB_YUV420;
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		return buf_interlaced ? CSI_FRAME_PLANAR_YUV420 :
+					CSI_FIELD_PLANAR_YUV420;
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+		return buf_interlaced ? CSI_FRAME_UV_CB_YUV422 :
+					CSI_FIELD_UV_CB_YUV422;
+	case V4L2_PIX_FMT_YUV422P:
+		return buf_interlaced ? CSI_FRAME_PLANAR_YUV422 :
+					CSI_FIELD_PLANAR_YUV422;
+	default:
+		WARN(1, "Unsupported pixformat: 0x%x\n", pixformat);
+		break;
+	}
+
+	return CSI_FIELD_RAW_8;
+}
+
+static enum csi_input_seq get_csi_input_seq(u32 mbus_code, u32 pixformat)
+{
+
+	switch (pixformat) {
+	case V4L2_PIX_FMT_HM12:
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YUV422P:
+		switch (mbus_code) {
+		case MEDIA_BUS_FMT_UYVY8_2X8:
+		case MEDIA_BUS_FMT_UYVY8_1X16:
+			return CSI_INPUT_SEQ_UYVY;
+		case MEDIA_BUS_FMT_VYUY8_2X8:
+		case MEDIA_BUS_FMT_VYUY8_1X16:
+			return CSI_INPUT_SEQ_VYUY;
+		case MEDIA_BUS_FMT_YUYV8_2X8:
+		case MEDIA_BUS_FMT_YUYV8_1X16:
+			return CSI_INPUT_SEQ_YUYV;
+		case MEDIA_BUS_FMT_YVYU8_1X16:
+		case MEDIA_BUS_FMT_YVYU8_2X8:
+			return CSI_INPUT_SEQ_YVYU;
+		default:
+			WARN(1, "Unsupported mbus code: 0x%x\n", mbus_code);
+			break;
+		}
+		break;
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV61:
+	case V4L2_PIX_FMT_YVU420:
+		switch (mbus_code) {
+		case MEDIA_BUS_FMT_UYVY8_2X8:
+		case MEDIA_BUS_FMT_UYVY8_1X16:
+			return CSI_INPUT_SEQ_VYUY;
+		case MEDIA_BUS_FMT_VYUY8_2X8:
+		case MEDIA_BUS_FMT_VYUY8_1X16:
+			return CSI_INPUT_SEQ_UYVY;
+		case MEDIA_BUS_FMT_YUYV8_2X8:
+		case MEDIA_BUS_FMT_YUYV8_1X16:
+			return CSI_INPUT_SEQ_YVYU;
+		case MEDIA_BUS_FMT_YVYU8_1X16:
+		case MEDIA_BUS_FMT_YVYU8_2X8:
+			return CSI_INPUT_SEQ_YUYV;
+		default:
+			WARN(1, "Unsupported mbus code: 0x%x\n", mbus_code);
+			break;
+		}
+		break;
+	default:
+		WARN(1, "Unsupported pixformat: 0x%x\n", pixformat);
+		break;
+	}
+
+	return CSI_INPUT_SEQ_YUYV;
+}
+
+static void sun6i_csi_setup_bus(struct sun6i_csi_dev *sdev)
+{
+	struct v4l2_fwnode_endpoint *endpoint = &sdev->csi.v4l2_ep;
+	unsigned char bus_width;
+	u32 flags;
+	u32 cfg;
+
+	bus_width = endpoint->bus.parallel.bus_width;
+
+	regmap_read(sdev->regmap, CSI_IF_CFG_REG, &cfg);
+
+	cfg &= ~(CSI_IF_CFG_CSI_IF_MASK | CSI_IF_CFG_MIPI_IF_MASK |
+		 CSI_IF_CFG_IF_DATA_WIDTH_MASK |
+		 CSI_IF_CFG_CLK_POL_MASK | CSI_IF_CFG_VREF_POL_MASK |
+		 CSI_IF_CFG_HREF_POL_MASK | CSI_IF_CFG_FIELD_MASK);
+
+	switch (endpoint->bus_type) {
+	case V4L2_MBUS_PARALLEL:
+		cfg |= CSI_IF_CFG_MIPI_IF_CSI;
+
+		flags = endpoint->bus.parallel.flags;
+
+		cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_YUV422_16BIT :
+					   CSI_IF_CFG_CSI_IF_YUV422_INTLV;
+
+		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
+			cfg |= CSI_IF_CFG_FIELD_POSITIVE;
+
+		if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+			cfg |= CSI_IF_CFG_VREF_POL_POSITIVE;
+		if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+			cfg |= CSI_IF_CFG_HREF_POL_POSITIVE;
+
+		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
+		break;
+	case V4L2_MBUS_BT656:
+		cfg |= CSI_IF_CFG_MIPI_IF_CSI;
+
+		flags = endpoint->bus.parallel.flags;
+
+		cfg |= (bus_width == 16) ? CSI_IF_CFG_CSI_IF_BT1120 :
+					   CSI_IF_CFG_CSI_IF_BT656;
+
+		if (flags & V4L2_MBUS_FIELD_EVEN_LOW)
+			cfg |= CSI_IF_CFG_FIELD_POSITIVE;
+
+		if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
+			cfg |= CSI_IF_CFG_CLK_POL_FALLING_EDGE;
+		break;
+	default:
+		dev_warn(sdev->dev, "Unsupported bus type: %d\n",
+			 endpoint->bus_type);
+		break;
+	}
+
+	switch (bus_width) {
+	case 8:
+		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_8BIT;
+		break;
+	case 10:
+		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_10BIT;
+		break;
+	case 12:
+		cfg |= CSI_IF_CFG_IF_DATA_WIDTH_12BIT;
+		break;
+	case 16: /* No need to configure DATA_WIDTH for 16bit */
+		break;
+	default:
+		dev_warn(sdev->dev, "Unsupported bus width: %d\n", bus_width);
+		break;
+	}
+
+	regmap_write(sdev->regmap, CSI_IF_CFG_REG, cfg);
+}
+
+static void sun6i_csi_set_format(struct sun6i_csi_dev *sdev)
+{
+	struct sun6i_csi *csi = &sdev->csi;
+	u32 cfg;
+	u32 val;
+
+	regmap_read(sdev->regmap, CSI_CH_CFG_REG, &cfg);
+
+	cfg &= ~(CSI_CH_CFG_INPUT_FMT_MASK |
+		 CSI_CH_CFG_OUTPUT_FMT_MASK | CSI_CH_CFG_VFLIP_EN |
+		 CSI_CH_CFG_HFLIP_EN | CSI_CH_CFG_FIELD_SEL_MASK |
+		 CSI_CH_CFG_INPUT_SEQ_MASK);
+
+	val = get_csi_input_format(csi->config.code, csi->config.pixelformat);
+	cfg |= CSI_CH_CFG_INPUT_FMT(val);
+
+	val = get_csi_output_format(csi->config.pixelformat, csi->config.field);
+	cfg |= CSI_CH_CFG_OUTPUT_FMT(val);
+
+	val = get_csi_input_seq(csi->config.code, csi->config.pixelformat);
+	cfg |= CSI_CH_CFG_INPUT_SEQ(val);
+
+	if (csi->config.field == V4L2_FIELD_TOP)
+		cfg |= CSI_CH_CFG_FIELD_SEL_FIELD0;
+	else if (csi->config.field == V4L2_FIELD_BOTTOM)
+		cfg |= CSI_CH_CFG_FIELD_SEL_FIELD1;
+	else
+		cfg |= CSI_CH_CFG_FIELD_SEL_BOTH;
+
+	regmap_write(sdev->regmap, CSI_CH_CFG_REG, cfg);
+}
+
+static void sun6i_csi_set_window(struct sun6i_csi_dev *sdev)
+{
+	struct sun6i_csi_config *config = &sdev->csi.config;
+	u32 bytesperline_y;
+	u32 bytesperline_c;
+	int *planar_offset = sdev->planar_offset;
+	u32 width = config->width;
+	u32 height = config->height;
+	u32 hor_len = width;
+
+	switch (config->pixelformat) {
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YVYU:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_VYUY:
+		dev_dbg(sdev->dev,
+			"Horizontal length should be 2 times of width for packed YUV formats!\n");
+		hor_len = width * 2;
+		break;
+	default:
+		break;
+	}
+
+	regmap_write(sdev->regmap, CSI_CH_HSIZE_REG,
+		     CSI_CH_HSIZE_HOR_LEN(hor_len) |
+		     CSI_CH_HSIZE_HOR_START(0));
+	regmap_write(sdev->regmap, CSI_CH_VSIZE_REG,
+		     CSI_CH_VSIZE_VER_LEN(height) |
+		     CSI_CH_VSIZE_VER_START(0));
+
+	planar_offset[0] = 0;
+	switch (config->pixelformat) {
+	case V4L2_PIX_FMT_HM12:
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+		bytesperline_y = width;
+		bytesperline_c = width;
+		planar_offset[1] = bytesperline_y * height;
+		planar_offset[2] = -1;
+		break;
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		bytesperline_y = width;
+		bytesperline_c = width / 2;
+		planar_offset[1] = bytesperline_y * height;
+		planar_offset[2] = planar_offset[1] +
+				bytesperline_c * height / 2;
+		break;
+	case V4L2_PIX_FMT_YUV422P:
+		bytesperline_y = width;
+		bytesperline_c = width / 2;
+		planar_offset[1] = bytesperline_y * height;
+		planar_offset[2] = planar_offset[1] +
+				bytesperline_c * height;
+		break;
+	default: /* raw */
+		dev_dbg(sdev->dev,
+			"Calculating pixelformat(0x%x)'s bytesperline as a packed format\n",
+			config->pixelformat);
+		bytesperline_y = (sun6i_csi_get_bpp(config->pixelformat) *
+				  config->width) / 8;
+		bytesperline_c = 0;
+		planar_offset[1] = -1;
+		planar_offset[2] = -1;
+		break;
+	}
+
+	regmap_write(sdev->regmap, CSI_CH_BUF_LEN_REG,
+		     CSI_CH_BUF_LEN_BUF_LEN_C(bytesperline_c) |
+		     CSI_CH_BUF_LEN_BUF_LEN_Y(bytesperline_y));
+}
+
+int sun6i_csi_update_config(struct sun6i_csi *csi,
+			    struct sun6i_csi_config *config)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+
+	if (config == NULL)
+		return -EINVAL;
+
+	memcpy(&csi->config, config, sizeof(csi->config));
+
+	sun6i_csi_setup_bus(sdev);
+	sun6i_csi_set_format(sdev);
+	sun6i_csi_set_window(sdev);
+
+	return 0;
+}
+
+void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+	/* transform physical address to bus address */
+	dma_addr_t bus_addr = addr - PHYS_OFFSET;
+
+	regmap_write(sdev->regmap, CSI_CH_F0_BUFA_REG,
+		     (bus_addr + sdev->planar_offset[0]) >> 2);
+	if (sdev->planar_offset[1] != -1)
+		regmap_write(sdev->regmap, CSI_CH_F1_BUFA_REG,
+			     (bus_addr + sdev->planar_offset[1]) >> 2);
+	if (sdev->planar_offset[2] != -1)
+		regmap_write(sdev->regmap, CSI_CH_F2_BUFA_REG,
+			     (bus_addr + sdev->planar_offset[2]) >> 2);
+}
+
+void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable)
+{
+	struct sun6i_csi_dev *sdev = sun6i_csi_to_dev(csi);
+	struct regmap *regmap = sdev->regmap;
+
+	if (!enable) {
+		regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON, 0);
+		regmap_write(regmap, CSI_CH_INT_EN_REG, 0);
+		return;
+	}
+
+	regmap_write(regmap, CSI_CH_INT_STA_REG, 0xFF);
+	regmap_write(regmap, CSI_CH_INT_EN_REG,
+		     CSI_CH_INT_EN_HB_OF_INT_EN |
+		     CSI_CH_INT_EN_FIFO2_OF_INT_EN |
+		     CSI_CH_INT_EN_FIFO1_OF_INT_EN |
+		     CSI_CH_INT_EN_FIFO0_OF_INT_EN |
+		     CSI_CH_INT_EN_FD_INT_EN |
+		     CSI_CH_INT_EN_CD_INT_EN);
+
+	regmap_update_bits(regmap, CSI_CAP_REG, CSI_CAP_CH0_VCAP_ON,
+			   CSI_CAP_CH0_VCAP_ON);
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Controller and V4L2
+ */
+static int sun6i_csi_link_entity(struct sun6i_csi *csi,
+				 struct media_entity *entity)
+{
+	struct media_entity *sink;
+	struct media_pad *sink_pad;
+	int ret;
+	int i;
+
+	if (!entity->num_pads) {
+		dev_err(csi->dev, "%s: invalid entity\n", entity->name);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < entity->num_pads; i++) {
+		if (entity->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+			break;
+	}
+
+	if (i == entity->num_pads) {
+		dev_err(csi->dev, "%s: no source pad in external entity %s\n",
+			__func__, entity->name);
+		return -EINVAL;
+	}
+
+	sink = &csi->video.vdev.entity;
+	sink_pad = &csi->video.pad;
+
+	dev_dbg(csi->dev, "creating %s:%u -> %s:%u link\n",
+		entity->name, i, sink->name, sink_pad->index);
+	ret = media_create_pad_link(entity, i, sink, sink_pad->index,
+				    MEDIA_LNK_FL_ENABLED);
+	if (ret < 0) {
+		dev_err(csi->dev, "failed to create %s:%u -> %s:%u link\n",
+			entity->name, i, sink->name, sink_pad->index);
+		return ret;
+	}
+
+	return media_entity_call(sink, link_setup, sink_pad, &entity->pads[i],
+				 MEDIA_LNK_FL_ENABLED);
+}
+
+static int sun6i_subdev_notify_complete(struct v4l2_async_notifier *notifier)
+{
+	struct sun6i_csi *csi = container_of(notifier, struct sun6i_csi,
+					     notifier);
+	struct v4l2_device *v4l2_dev = &csi->v4l2_dev;
+	struct v4l2_subdev *sd;
+	int ret;
+
+	dev_dbg(csi->dev, "notify complete, all subdevs registered\n");
+
+	if (notifier->num_subdevs != 1)
+		return -EINVAL;
+
+	sd = list_first_entry(&v4l2_dev->subdevs, struct v4l2_subdev, list);
+	if (sd == NULL)
+		return -EINVAL;
+
+	ret = sun6i_csi_link_entity(csi, &sd->entity);
+	if (ret < 0)
+		return ret;
+
+	ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
+	if (ret < 0)
+		return ret;
+
+	return media_device_register(&csi->media_dev);
+}
+
+static const struct v4l2_async_notifier_operations sun6i_csi_async_ops = {
+	.complete = sun6i_subdev_notify_complete,
+};
+
+static int sun6i_csi_fwnode_parse(struct device *dev,
+				  struct v4l2_fwnode_endpoint *vep,
+				  struct v4l2_async_subdev *asd)
+{
+	struct sun6i_csi *csi = dev_get_drvdata(dev);
+
+	if (vep->base.port || vep->base.id) {
+		dev_warn(dev, "Only support a single port with one endpoint\n");
+		return -ENOTCONN;
+	}
+
+	switch (vep->bus_type) {
+	case V4L2_MBUS_PARALLEL:
+	case V4L2_MBUS_BT656:
+		csi->v4l2_ep = *vep;
+		return 0;
+	default:
+		dev_err(dev, "Unsupported media bus type\n");
+		return -ENOTCONN;
+	}
+}
+
+static void sun6i_csi_v4l2_cleanup(struct sun6i_csi *csi)
+{
+	v4l2_async_notifier_cleanup(&csi->notifier);
+	v4l2_async_notifier_unregister(&csi->notifier);
+	sun6i_video_cleanup(&csi->video);
+	v4l2_device_unregister(&csi->v4l2_dev);
+	media_device_unregister(&csi->media_dev);
+	media_device_cleanup(&csi->media_dev);
+}
+
+static int sun6i_csi_v4l2_init(struct sun6i_csi *csi)
+{
+	int ret;
+
+	csi->media_dev.dev = csi->dev;
+	strlcpy(csi->media_dev.model, "Allwinner Video Capture Device",
+		sizeof(csi->media_dev.model));
+	csi->media_dev.hw_revision = 0;
+
+	media_device_init(&csi->media_dev);
+
+	csi->v4l2_dev.mdev = &csi->media_dev;
+	ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
+	if (ret) {
+		dev_err(csi->dev, "V4L2 device registration failed (%d)\n",
+			ret);
+		goto clean_media;
+	}
+
+	ret = sun6i_video_init(&csi->video, csi, "sun6i-csi");
+	if (ret)
+		goto unreg_v4l2;
+
+	ret = v4l2_async_notifier_parse_fwnode_endpoints(
+		csi->dev, &csi->notifier, sizeof(struct v4l2_async_subdev),
+		sun6i_csi_fwnode_parse);
+	if (ret)
+		goto clean_video;
+
+	csi->notifier.ops = &sun6i_csi_async_ops;
+
+	ret = v4l2_async_notifier_register(&csi->v4l2_dev, &csi->notifier);
+	if (ret) {
+		dev_err(csi->dev, "notifier registration failed\n");
+		goto clean_notifier;
+	}
+
+	return 0;
+
+clean_notifier:
+	v4l2_async_notifier_cleanup(&csi->notifier);
+clean_video:
+	sun6i_video_cleanup(&csi->video);
+unreg_v4l2:
+	v4l2_device_unregister(&csi->v4l2_dev);
+clean_media:
+	media_device_cleanup(&csi->media_dev);
+
+	return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Resources and IRQ
+ */
+static irqreturn_t sun6i_csi_isr(int irq, void *dev_id)
+{
+	struct sun6i_csi_dev *sdev = (struct sun6i_csi_dev *)dev_id;
+	struct regmap *regmap = sdev->regmap;
+	u32 status;
+
+	regmap_read(regmap, CSI_CH_INT_STA_REG, &status);
+
+	if (!(status & 0xFF))
+		return IRQ_NONE;
+
+	if ((status & CSI_CH_INT_STA_FIFO0_OF_PD) ||
+	    (status & CSI_CH_INT_STA_FIFO1_OF_PD) ||
+	    (status & CSI_CH_INT_STA_FIFO2_OF_PD) ||
+	    (status & CSI_CH_INT_STA_HB_OF_PD)) {
+		regmap_write(regmap, CSI_CH_INT_STA_REG, status);
+		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN, 0);
+		regmap_update_bits(regmap, CSI_EN_REG, CSI_EN_CSI_EN,
+				   CSI_EN_CSI_EN);
+		return IRQ_HANDLED;
+	}
+
+	if (status & CSI_CH_INT_STA_FD_PD)
+		sun6i_video_frame_done(&sdev->csi.video);
+
+	regmap_write(regmap, CSI_CH_INT_STA_REG, status);
+
+	return IRQ_HANDLED;
+}
+
+static const struct regmap_config sun6i_csi_regmap_config = {
+	.reg_bits       = 32,
+	.reg_stride     = 4,
+	.val_bits       = 32,
+	.max_register	= 0x1000,
+};
+
+static int sun6i_csi_resource_request(struct sun6i_csi_dev *sdev,
+				      struct platform_device *pdev)
+{
+	struct resource *res;
+	void __iomem *io_base;
+	int ret;
+	int irq;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	io_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(io_base))
+		return PTR_ERR(io_base);
+
+	sdev->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", io_base,
+					    &sun6i_csi_regmap_config);
+	if (IS_ERR(sdev->regmap)) {
+		dev_err(&pdev->dev, "Failed to init register map\n");
+		return PTR_ERR(sdev->regmap);
+	}
+
+	sdev->clk_mod = devm_clk_get(&pdev->dev, "mod");
+	if (IS_ERR(sdev->clk_mod)) {
+		dev_err(&pdev->dev, "Unable to acquire csi clock\n");
+		return PTR_ERR(sdev->clk_mod);
+	}
+
+	sdev->clk_ram = devm_clk_get(&pdev->dev, "ram");
+	if (IS_ERR(sdev->clk_ram)) {
+		dev_err(&pdev->dev, "Unable to acquire dram-csi clock\n");
+		return PTR_ERR(sdev->clk_ram);
+	}
+
+	sdev->rstc_bus = devm_reset_control_get_shared(&pdev->dev, NULL);
+	if (IS_ERR(sdev->rstc_bus)) {
+		dev_err(&pdev->dev, "Cannot get reset controller\n");
+		return PTR_ERR(sdev->rstc_bus);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "No csi IRQ specified\n");
+		ret = -ENXIO;
+		return ret;
+	}
+
+	ret = devm_request_irq(&pdev->dev, irq, sun6i_csi_isr, 0, MODULE_NAME,
+			       sdev);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot request csi IRQ\n");
+		return ret;
+	}
+	return 0;
+}
+
+static int sun6i_csi_probe(struct platform_device *pdev)
+{
+	struct sun6i_csi_dev *sdev;
+	int ret;
+
+	sdev = devm_kzalloc(&pdev->dev, sizeof(*sdev), GFP_KERNEL);
+	if (!sdev)
+		return -ENOMEM;
+
+	sdev->dev = &pdev->dev;
+
+	ret = sun6i_csi_resource_request(sdev, pdev);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, sdev);
+
+	sdev->csi.dev = &pdev->dev;
+	ret = sun6i_csi_v4l2_init(&sdev->csi);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int sun6i_csi_remove(struct platform_device *pdev)
+{
+	struct sun6i_csi_dev *sdev = platform_get_drvdata(pdev);
+
+	sun6i_csi_v4l2_cleanup(&sdev->csi);
+
+	return 0;
+}
+
+static const struct of_device_id sun6i_csi_of_match[] = {
+	{ .compatible = "allwinner,sun8i-v3s-csi", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
+
+static struct platform_driver sun6i_csi_platform_driver = {
+	.probe = sun6i_csi_probe,
+	.remove = sun6i_csi_remove,
+	.driver = {
+		.name = MODULE_NAME,
+		.of_match_table = of_match_ptr(sun6i_csi_of_match),
+	},
+};
+module_platform_driver(sun6i_csi_platform_driver);
+
+MODULE_DESCRIPTION("Allwinner V3s Camera Sensor Interface driver");
+MODULE_AUTHOR("Yong Deng <yong.deng@magewell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
new file mode 100644
index 0000000..c1a1e3a
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.h
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#ifndef __SUN6I_CSI_H__
+#define __SUN6I_CSI_H__
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+
+#include "sun6i_video.h"
+
+struct sun6i_csi;
+
+/**
+ * struct sun6i_csi_config - configs for sun6i csi
+ * @pixelformat: v4l2 pixel format (V4L2_PIX_FMT_*)
+ * @code:	media bus format code (MEDIA_BUS_FMT_*)
+ * @field:	used interlacing type (enum v4l2_field)
+ * @width:	frame width
+ * @height:	frame height
+ */
+struct sun6i_csi_config {
+	u32		pixelformat;
+	u32		code;
+	u32		field;
+	u32		width;
+	u32		height;
+};
+
+struct sun6i_csi {
+	struct device			*dev;
+	struct v4l2_device		v4l2_dev;
+	struct media_device		media_dev;
+
+	struct v4l2_async_notifier	notifier;
+
+	/* video port settings */
+	struct v4l2_fwnode_endpoint	v4l2_ep;
+
+	struct sun6i_csi_config		config;
+
+	struct sun6i_video		video;
+};
+
+/**
+ * sun6i_csi_get_supported_pixformats() - get csi supported pixformats
+ * @csi:	pointer to the csi
+ * @pixformats:	supported pixformats return from csi
+ *
+ * @return the count of pixformats or error(< 0)
+ */
+int sun6i_csi_get_supported_pixformats(struct sun6i_csi *csi,
+				       const u32 **pixformats);
+
+/**
+ * sun6i_csi_is_format_support() - check if the format supported by csi
+ * @csi:	pointer to the csi
+ * @pixformat:	v4l2 pixel format (V4L2_PIX_FMT_*)
+ * @mbus_code:	media bus format code (MEDIA_BUS_FMT_*)
+ */
+bool sun6i_csi_is_format_support(struct sun6i_csi *csi, u32 pixformat,
+				 u32 mbus_code);
+
+/**
+ * sun6i_csi_set_power() - power on/off the csi
+ * @csi:	pointer to the csi
+ * @enable:	on/off
+ */
+int sun6i_csi_set_power(struct sun6i_csi *csi, bool enable);
+
+/**
+ * sun6i_csi_update_config() - update the csi register setttings
+ * @csi:	pointer to the csi
+ * @config:	see struct sun6i_csi_config
+ */
+int sun6i_csi_update_config(struct sun6i_csi *csi,
+			    struct sun6i_csi_config *config);
+
+/**
+ * sun6i_csi_update_buf_addr() - update the csi frame buffer address
+ * @csi:	pointer to the csi
+ * @addr:	frame buffer's physical address
+ */
+void sun6i_csi_update_buf_addr(struct sun6i_csi *csi, dma_addr_t addr);
+
+/**
+ * sun6i_csi_set_stream() - start/stop csi streaming
+ * @csi:	pointer to the csi
+ * @enable:	start/stop
+ */
+void sun6i_csi_set_stream(struct sun6i_csi *csi, bool enable);
+
+/* get bpp form v4l2 pixformat */
+static inline int sun6i_csi_get_bpp(unsigned int pixformat)
+{
+	switch (pixformat) {
+	case V4L2_PIX_FMT_SBGGR8:
+	case V4L2_PIX_FMT_SGBRG8:
+	case V4L2_PIX_FMT_SGRBG8:
+	case V4L2_PIX_FMT_SRGGB8:
+		return 8;
+	case V4L2_PIX_FMT_SBGGR10:
+	case V4L2_PIX_FMT_SGBRG10:
+	case V4L2_PIX_FMT_SGRBG10:
+	case V4L2_PIX_FMT_SRGGB10:
+		return 10;
+	case V4L2_PIX_FMT_SBGGR12:
+	case V4L2_PIX_FMT_SGBRG12:
+	case V4L2_PIX_FMT_SGRBG12:
+	case V4L2_PIX_FMT_SRGGB12:
+	case V4L2_PIX_FMT_HM12:
+	case V4L2_PIX_FMT_NV12:
+	case V4L2_PIX_FMT_NV21:
+	case V4L2_PIX_FMT_YUV420:
+	case V4L2_PIX_FMT_YVU420:
+		return 12;
+	case V4L2_PIX_FMT_YUYV:
+	case V4L2_PIX_FMT_YVYU:
+	case V4L2_PIX_FMT_UYVY:
+	case V4L2_PIX_FMT_VYUY:
+	case V4L2_PIX_FMT_NV16:
+	case V4L2_PIX_FMT_NV61:
+	case V4L2_PIX_FMT_YUV422P:
+		return 16;
+	case V4L2_PIX_FMT_RGB24:
+	case V4L2_PIX_FMT_BGR24:
+		return 24;
+	case V4L2_PIX_FMT_RGB32:
+	case V4L2_PIX_FMT_BGR32:
+		return 32;
+	default:
+		WARN(1, "Unsupported pixformat: 0x%x\n", pixformat);
+		break;
+	}
+
+	return 0;
+}
+
+#endif /* __SUN6I_CSI_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
new file mode 100644
index 0000000..b55b21f
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_reg.h
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#ifndef __SUN6I_CSI_REG_H__
+#define __SUN6I_CSI_REG_H__
+
+#include <linux/kernel.h>
+
+#define CSI_EN_REG			0x0
+#define CSI_EN_VER_EN				BIT(30)
+#define CSI_EN_CSI_EN				BIT(0)
+
+#define CSI_IF_CFG_REG			0x4
+#define CSI_IF_CFG_SRC_TYPE_MASK		BIT(21)
+#define CSI_IF_CFG_SRC_TYPE_PROGRESSED		((0 << 21) & CSI_IF_CFG_SRC_TYPE_MASK)
+#define CSI_IF_CFG_SRC_TYPE_INTERLACED		((1 << 21) & CSI_IF_CFG_SRC_TYPE_MASK)
+#define CSI_IF_CFG_FPS_DS_EN			BIT(20)
+#define CSI_IF_CFG_FIELD_MASK			BIT(19)
+#define CSI_IF_CFG_FIELD_NEGATIVE		((0 << 19) & CSI_IF_CFG_FIELD_MASK)
+#define CSI_IF_CFG_FIELD_POSITIVE		((1 << 19) & CSI_IF_CFG_FIELD_MASK)
+#define CSI_IF_CFG_VREF_POL_MASK		BIT(18)
+#define CSI_IF_CFG_VREF_POL_NEGATIVE		((0 << 18) & CSI_IF_CFG_VREF_POL_MASK)
+#define CSI_IF_CFG_VREF_POL_POSITIVE		((1 << 18) & CSI_IF_CFG_VREF_POL_MASK)
+#define CSI_IF_CFG_HREF_POL_MASK		BIT(17)
+#define CSI_IF_CFG_HREF_POL_NEGATIVE		((0 << 17) & CSI_IF_CFG_HREF_POL_MASK)
+#define CSI_IF_CFG_HREF_POL_POSITIVE		((1 << 17) & CSI_IF_CFG_HREF_POL_MASK)
+#define CSI_IF_CFG_CLK_POL_MASK			BIT(16)
+#define CSI_IF_CFG_CLK_POL_RISING_EDGE		((0 << 16) & CSI_IF_CFG_CLK_POL_MASK)
+#define CSI_IF_CFG_CLK_POL_FALLING_EDGE		((1 << 16) & CSI_IF_CFG_CLK_POL_MASK)
+#define CSI_IF_CFG_IF_DATA_WIDTH_MASK		GENMASK(10, 8)
+#define CSI_IF_CFG_IF_DATA_WIDTH_8BIT		((0 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
+#define CSI_IF_CFG_IF_DATA_WIDTH_10BIT		((1 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
+#define CSI_IF_CFG_IF_DATA_WIDTH_12BIT		((2 << 8) & CSI_IF_CFG_IF_DATA_WIDTH_MASK)
+#define CSI_IF_CFG_MIPI_IF_MASK			BIT(7)
+#define CSI_IF_CFG_MIPI_IF_CSI			(0 << 7)
+#define CSI_IF_CFG_MIPI_IF_MIPI			(1 << 7)
+#define CSI_IF_CFG_CSI_IF_MASK			GENMASK(4, 0)
+#define CSI_IF_CFG_CSI_IF_YUV422_INTLV		((0 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+#define CSI_IF_CFG_CSI_IF_YUV422_16BIT		((1 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+#define CSI_IF_CFG_CSI_IF_BT656			((4 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+#define CSI_IF_CFG_CSI_IF_BT1120		((5 << 0) & CSI_IF_CFG_CSI_IF_MASK)
+
+#define CSI_CAP_REG			0x8
+#define CSI_CAP_CH0_CAP_MASK_MASK		GENMASK(5, 2)
+#define CSI_CAP_CH0_CAP_MASK(count)		((count << 2) & CSI_CAP_CH0_CAP_MASK_MASK)
+#define CSI_CAP_CH0_VCAP_ON			BIT(1)
+#define CSI_CAP_CH0_SCAP_ON			BIT(0)
+
+#define CSI_SYNC_CNT_REG		0xc
+#define CSI_FIFO_THRS_REG		0x10
+#define CSI_BT656_HEAD_CFG_REG		0x14
+#define CSI_PTN_LEN_REG			0x30
+#define CSI_PTN_ADDR_REG		0x34
+#define CSI_VER_REG			0x3c
+
+#define CSI_CH_CFG_REG			0x44
+#define CSI_CH_CFG_INPUT_FMT_MASK		GENMASK(23, 20)
+#define CSI_CH_CFG_INPUT_FMT(fmt)		((fmt << 20) & CSI_CH_CFG_INPUT_FMT_MASK)
+#define CSI_CH_CFG_OUTPUT_FMT_MASK		GENMASK(19, 16)
+#define CSI_CH_CFG_OUTPUT_FMT(fmt)		((fmt << 16) & CSI_CH_CFG_OUTPUT_FMT_MASK)
+#define CSI_CH_CFG_VFLIP_EN			BIT(13)
+#define CSI_CH_CFG_HFLIP_EN			BIT(12)
+#define CSI_CH_CFG_FIELD_SEL_MASK		GENMASK(11, 10)
+#define CSI_CH_CFG_FIELD_SEL_FIELD0		((0 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
+#define CSI_CH_CFG_FIELD_SEL_FIELD1		((1 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
+#define CSI_CH_CFG_FIELD_SEL_BOTH		((2 << 10) & CSI_CH_CFG_FIELD_SEL_MASK)
+#define CSI_CH_CFG_INPUT_SEQ_MASK		GENMASK(9, 8)
+#define CSI_CH_CFG_INPUT_SEQ(seq)		((seq << 8) & CSI_CH_CFG_INPUT_SEQ_MASK)
+
+#define CSI_CH_SCALE_REG		0x4c
+#define CSI_CH_SCALE_QUART_EN			BIT(0)
+
+#define CSI_CH_F0_BUFA_REG		0x50
+
+#define CSI_CH_F1_BUFA_REG		0x58
+
+#define CSI_CH_F2_BUFA_REG		0x60
+
+#define CSI_CH_STA_REG			0x6c
+#define CSI_CH_STA_FIELD_STA_MASK		BIT(2)
+#define CSI_CH_STA_FIELD_STA_FIELD0		((0 << 2) & CSI_CH_STA_FIELD_STA_MASK)
+#define CSI_CH_STA_FIELD_STA_FIELD1		((1 << 2) & CSI_CH_STA_FIELD_STA_MASK)
+#define CSI_CH_STA_VCAP_STA			BIT(1)
+#define CSI_CH_STA_SCAP_STA			BIT(0)
+
+#define CSI_CH_INT_EN_REG		0x70
+#define CSI_CH_INT_EN_VS_INT_EN			BIT(7)
+#define CSI_CH_INT_EN_HB_OF_INT_EN		BIT(6)
+#define CSI_CH_INT_EN_MUL_ERR_INT_EN		BIT(5)
+#define CSI_CH_INT_EN_FIFO2_OF_INT_EN		BIT(4)
+#define CSI_CH_INT_EN_FIFO1_OF_INT_EN		BIT(3)
+#define CSI_CH_INT_EN_FIFO0_OF_INT_EN		BIT(2)
+#define CSI_CH_INT_EN_FD_INT_EN			BIT(1)
+#define CSI_CH_INT_EN_CD_INT_EN			BIT(0)
+
+#define CSI_CH_INT_STA_REG		0x74
+#define CSI_CH_INT_STA_VS_PD			BIT(7)
+#define CSI_CH_INT_STA_HB_OF_PD			BIT(6)
+#define CSI_CH_INT_STA_MUL_ERR_PD		BIT(5)
+#define CSI_CH_INT_STA_FIFO2_OF_PD		BIT(4)
+#define CSI_CH_INT_STA_FIFO1_OF_PD		BIT(3)
+#define CSI_CH_INT_STA_FIFO0_OF_PD		BIT(2)
+#define CSI_CH_INT_STA_FD_PD			BIT(1)
+#define CSI_CH_INT_STA_CD_PD			BIT(0)
+
+#define CSI_CH_FLD1_VSIZE_REG		0x78
+
+#define CSI_CH_HSIZE_REG		0x80
+#define CSI_CH_HSIZE_HOR_LEN_MASK		GENMASK(28, 16)
+#define CSI_CH_HSIZE_HOR_LEN(len)		((len << 16) & CSI_CH_HSIZE_HOR_LEN_MASK)
+#define CSI_CH_HSIZE_HOR_START_MASK		GENMASK(12, 0)
+#define CSI_CH_HSIZE_HOR_START(start)		((start << 0) & CSI_CH_HSIZE_HOR_START_MASK)
+
+#define CSI_CH_VSIZE_REG		0x84
+#define CSI_CH_VSIZE_VER_LEN_MASK		GENMASK(28, 16)
+#define CSI_CH_VSIZE_VER_LEN(len)		((len << 16) & CSI_CH_VSIZE_VER_LEN_MASK)
+#define CSI_CH_VSIZE_VER_START_MASK		GENMASK(12, 0)
+#define CSI_CH_VSIZE_VER_START(start)		((start << 0) & CSI_CH_VSIZE_VER_START_MASK)
+
+#define CSI_CH_BUF_LEN_REG		0x88
+#define CSI_CH_BUF_LEN_BUF_LEN_C_MASK		GENMASK(29, 16)
+#define CSI_CH_BUF_LEN_BUF_LEN_C(len)		((len << 16) & CSI_CH_BUF_LEN_BUF_LEN_C_MASK)
+#define CSI_CH_BUF_LEN_BUF_LEN_Y_MASK		GENMASK(13, 0)
+#define CSI_CH_BUF_LEN_BUF_LEN_Y(len)		((len << 0) & CSI_CH_BUF_LEN_BUF_LEN_Y_MASK)
+
+#define CSI_CH_FLIP_SIZE_REG		0x8c
+#define CSI_CH_FLIP_SIZE_VER_LEN_MASK		GENMASK(28, 16)
+#define CSI_CH_FLIP_SIZE_VER_LEN(len)		((len << 16) & CSI_CH_FLIP_SIZE_VER_LEN_MASK)
+#define CSI_CH_FLIP_SIZE_VALID_LEN_MASK		GENMASK(12, 0)
+#define CSI_CH_FLIP_SIZE_VALID_LEN(len)		((len << 0) & CSI_CH_FLIP_SIZE_VALID_LEN_MASK)
+
+#define CSI_CH_FRM_CLK_CNT_REG		0x90
+#define CSI_CH_ACC_ITNL_CLK_CNT_REG	0x94
+#define CSI_CH_FIFO_STAT_REG		0x98
+#define CSI_CH_PCLK_STAT_REG		0x9c
+
+/*
+ * csi input data format
+ */
+enum csi_input_fmt {
+	CSI_INPUT_FORMAT_RAW		= 0,
+	CSI_INPUT_FORMAT_YUV422		= 3,
+	CSI_INPUT_FORMAT_YUV420		= 4,
+};
+
+/*
+ * csi output data format
+ */
+enum csi_output_fmt {
+	/* only when input format is RAW */
+	CSI_FIELD_RAW_8			= 0,
+	CSI_FIELD_RAW_10		= 1,
+	CSI_FIELD_RAW_12		= 2,
+	CSI_FIELD_RGB565		= 4,
+	CSI_FIELD_RGB888		= 5,
+	CSI_FIELD_PRGB888		= 6,
+	CSI_FRAME_RAW_8			= 8,
+	CSI_FRAME_RAW_10		= 9,
+	CSI_FRAME_RAW_12		= 10,
+	CSI_FRAME_RGB565		= 12,
+	CSI_FRAME_RGB888		= 13,
+	CSI_FRAME_PRGB888		= 14,
+
+	/* only when input format is YUV422 */
+	CSI_FIELD_PLANAR_YUV422		= 0,
+	CSI_FIELD_PLANAR_YUV420		= 1,
+	CSI_FRAME_PLANAR_YUV420		= 2,
+	CSI_FRAME_PLANAR_YUV422		= 3,
+	CSI_FIELD_UV_CB_YUV422		= 4,
+	CSI_FIELD_UV_CB_YUV420		= 5,
+	CSI_FRAME_UV_CB_YUV420		= 6,
+	CSI_FRAME_UV_CB_YUV422		= 7,
+	CSI_FIELD_MB_YUV422		= 8,
+	CSI_FIELD_MB_YUV420		= 9,
+	CSI_FRAME_MB_YUV420		= 10,
+	CSI_FRAME_MB_YUV422		= 11,
+	CSI_FIELD_UV_CB_YUV422_10	= 12,
+	CSI_FIELD_UV_CB_YUV420_10	= 13,
+};
+
+/*
+ * csi YUV input data sequence
+ */
+enum csi_input_seq {
+	/* only when input format is YUV422 */
+	CSI_INPUT_SEQ_YUYV = 0,
+	CSI_INPUT_SEQ_YVYU,
+	CSI_INPUT_SEQ_UYVY,
+	CSI_INPUT_SEQ_VYUY,
+};
+
+#endif /* __SUN6I_CSI_REG_H__ */
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
new file mode 100644
index 0000000..a8885d2
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -0,0 +1,741 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#include <linux/of.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "sun6i_csi.h"
+#include "sun6i_video.h"
+
+struct sun6i_csi_buffer {
+	struct vb2_v4l2_buffer		vb;
+	struct list_head		list;
+
+	dma_addr_t			dma_addr;
+	bool				queued_to_csi;
+};
+
+static struct sun6i_csi_format *
+find_format_by_pixformat(struct sun6i_video *video, unsigned int pixformat)
+{
+	unsigned int num_formats = video->num_formats;
+	struct sun6i_csi_format *fmt;
+	unsigned int i;
+
+	for (i = 0; i < num_formats; i++) {
+		fmt = &video->formats[i];
+		if (fmt->pixformat == pixformat)
+			return fmt;
+	}
+
+	return NULL;
+}
+
+static struct v4l2_subdev *
+sun6i_video_remote_subdev(struct sun6i_video *video, u32 *pad)
+{
+	struct media_pad *remote;
+
+	remote = media_entity_remote_pad(&video->pad);
+
+	if (!remote || !is_media_entity_v4l2_subdev(remote->entity))
+		return NULL;
+
+	if (pad)
+		*pad = remote->index;
+
+	return media_entity_to_v4l2_subdev(remote->entity);
+}
+
+static int sun6i_video_queue_setup(struct vb2_queue *vq,
+				 unsigned int *nbuffers, unsigned int *nplanes,
+				 unsigned int sizes[],
+				 struct device *alloc_devs[])
+{
+	struct sun6i_video *video = vb2_get_drv_priv(vq);
+	unsigned int size = video->fmt.fmt.pix.sizeimage;
+
+	if (*nplanes)
+		return sizes[0] < size ? -EINVAL : 0;
+
+	*nplanes = 1;
+	sizes[0] = size;
+
+	return 0;
+}
+
+static int sun6i_video_buffer_prepare(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct sun6i_csi_buffer *buf =
+			container_of(vbuf, struct sun6i_csi_buffer, vb);
+	struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long size = video->fmt.fmt.pix.sizeimage;
+
+	if (vb2_plane_size(vb, 0) < size) {
+		v4l2_err(video->vdev.v4l2_dev, "buffer too small (%lu < %lu)\n",
+			 vb2_plane_size(vb, 0), size);
+		return -EINVAL;
+	}
+
+	vb2_set_plane_payload(vb, 0, size);
+
+	buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+	vbuf->field = video->fmt.fmt.pix.field;
+
+	return 0;
+}
+
+static int sun6i_pipeline_set_stream(struct sun6i_video *video, bool enable)
+{
+	struct media_entity *entity;
+	struct media_pad *pad;
+	struct v4l2_subdev *subdev;
+	int ret;
+
+	entity = &video->vdev.entity;
+	while (1) {
+		pad = &entity->pads[0];
+		if (!(pad->flags & MEDIA_PAD_FL_SINK))
+			break;
+
+		pad = media_entity_remote_pad(pad);
+		if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+			break;
+
+		entity = pad->entity;
+		subdev = media_entity_to_v4l2_subdev(entity);
+
+		ret = v4l2_subdev_call(subdev, video, s_stream, enable);
+		if (enable && ret < 0 && ret != -ENOIOCTLCMD)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int sun6i_video_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+	struct sun6i_video *video = vb2_get_drv_priv(vq);
+	struct sun6i_csi_buffer *buf;
+	struct sun6i_csi_buffer *next_buf;
+	struct sun6i_csi_config config;
+	unsigned long flags;
+	int ret;
+
+	video->sequence = 0;
+
+	ret = media_pipeline_start(&video->vdev.entity, &video->vdev.pipe);
+	if (ret < 0)
+		goto clear_dma_queue;
+
+	config.pixelformat = video->fmt.fmt.pix.pixelformat;
+	config.code = video->current_fmt->mbus_code;
+	config.field = video->fmt.fmt.pix.field;
+	config.width = video->fmt.fmt.pix.width;
+	config.height = video->fmt.fmt.pix.height;
+
+	ret = sun6i_csi_update_config(video->csi, &config);
+	if (ret < 0)
+		goto stop_media_pipeline;
+
+	spin_lock_irqsave(&video->dma_queue_lock, flags);
+
+	buf = list_first_entry(&video->dma_queue,
+			       struct sun6i_csi_buffer, list);
+	buf->queued_to_csi = true;
+	sun6i_csi_update_buf_addr(video->csi, buf->dma_addr);
+
+	sun6i_csi_set_stream(video->csi, true);
+
+	/*
+	 * CSI will lookup the next dma buffer for next frame before the
+	 * the current frame done IRQ triggered. This is not documented
+	 * but reported by Ond?ej Jirman.
+	 * The BSP code has workaround for this too. It skip to mark the
+	 * first buffer as frame done for VB2 and pass the second buffer
+	 * to CSI in the first frame done ISR call. Then in second frame
+	 * done ISR call, it mark the first buffer as frame done for VB2
+	 * and pass the third buffer to CSI. And so on. The bad thing is
+	 * that the first buffer will be written twice and the first frame
+	 * is dropped even the queued buffer is sufficient.
+	 * So, I make some improvement here. Pass the next buffer to CSI
+	 * just follow starting the CSI. In this case, the first frame
+	 * will be stored in first buffer, second frame in second buffer.
+	 * This method is used to avoid dropping the first frame, it
+	 * would also drop frame when lacking of queued buffer.
+	 */
+	next_buf = list_next_entry(buf, list);
+	next_buf->queued_to_csi = true;
+	sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
+
+	spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+
+	ret = sun6i_pipeline_set_stream(video, true);
+	if (ret < 0)
+		goto stop_csi_stream;
+
+	return 0;
+
+stop_csi_stream:
+	sun6i_csi_set_stream(video->csi, false);
+stop_media_pipeline:
+	media_pipeline_stop(&video->vdev.entity);
+clear_dma_queue:
+	spin_lock_irqsave(&video->dma_queue_lock, flags);
+	list_for_each_entry(buf, &video->dma_queue, list)
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+	INIT_LIST_HEAD(&video->dma_queue);
+	spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+
+	return ret;
+}
+
+static void sun6i_video_stop_streaming(struct vb2_queue *vq)
+{
+	struct sun6i_video *video = vb2_get_drv_priv(vq);
+	unsigned long flags;
+	struct sun6i_csi_buffer *buf;
+
+	sun6i_pipeline_set_stream(video, false);
+
+	sun6i_csi_set_stream(video->csi, false);
+
+	media_pipeline_stop(&video->vdev.entity);
+
+	/* Release all active buffers */
+	spin_lock_irqsave(&video->dma_queue_lock, flags);
+	list_for_each_entry(buf, &video->dma_queue, list)
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+	INIT_LIST_HEAD(&video->dma_queue);
+	spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+}
+
+static void sun6i_video_buffer_queue(struct vb2_buffer *vb)
+{
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+	struct sun6i_csi_buffer *buf =
+			container_of(vbuf, struct sun6i_csi_buffer, vb);
+	struct sun6i_video *video = vb2_get_drv_priv(vb->vb2_queue);
+	unsigned long flags;
+
+	spin_lock_irqsave(&video->dma_queue_lock, flags);
+	buf->queued_to_csi = false;
+	list_add_tail(&buf->list, &video->dma_queue);
+	spin_unlock_irqrestore(&video->dma_queue_lock, flags);
+}
+
+void sun6i_video_frame_done(struct sun6i_video *video)
+{
+	struct sun6i_csi_buffer *buf;
+	struct sun6i_csi_buffer *next_buf;
+	struct vb2_v4l2_buffer *vbuf;
+
+	spin_lock(&video->dma_queue_lock);
+
+	buf = list_first_entry(&video->dma_queue,
+			       struct sun6i_csi_buffer, list);
+	if (list_is_last(&buf->list, &video->dma_queue)) {
+		dev_dbg(video->csi->dev, "Frame droped!\n");
+		goto unlock;
+	}
+
+	next_buf = list_next_entry(buf, list);
+	/* If a new buffer (#next_buf) had not been queued to CSI, the old
+	 * buffer (#buf) is still holding by CSI for storing the next
+	 * frame. So, we queue a new buffer (#next_buf) to CSI then wait
+	 * for next ISR call.
+	 */
+	if (!next_buf->queued_to_csi) {
+		next_buf->queued_to_csi = true;
+		sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
+		dev_dbg(video->csi->dev, "Frame droped!\n");
+		goto unlock;
+	}
+
+	list_del(&buf->list);
+	vbuf = &buf->vb;
+	vbuf->vb2_buf.timestamp = ktime_get_ns();
+	vbuf->sequence = video->sequence;
+	vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
+
+	/* Prepare buffer for next frame but one.  */
+	if (!list_is_last(&next_buf->list, &video->dma_queue)) {
+		next_buf = list_next_entry(next_buf, list);
+		next_buf->queued_to_csi = true;
+		sun6i_csi_update_buf_addr(video->csi, next_buf->dma_addr);
+	} else {
+		dev_dbg(video->csi->dev, "Next frame will be dropped!\n");
+	}
+
+unlock:
+	video->sequence++;
+	spin_unlock(&video->dma_queue_lock);
+}
+
+static struct vb2_ops sun6i_csi_vb2_ops = {
+	.queue_setup		= sun6i_video_queue_setup,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+	.buf_prepare		= sun6i_video_buffer_prepare,
+	.start_streaming	= sun6i_video_start_streaming,
+	.stop_streaming		= sun6i_video_stop_streaming,
+	.buf_queue		= sun6i_video_buffer_queue,
+};
+
+static int vidioc_querycap(struct file *file, void *priv,
+				struct v4l2_capability *cap)
+{
+	struct sun6i_video *video = video_drvdata(file);
+
+	strlcpy(cap->driver, "sun6i-video", sizeof(cap->driver));
+	strlcpy(cap->card, video->vdev.name, sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
+		 video->csi->dev->of_node->name);
+
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *f)
+{
+	struct sun6i_video *video = video_drvdata(file);
+	u32 index = f->index;
+
+	if (index >= video->num_formats)
+		return -EINVAL;
+
+	f->pixelformat = video->formats[index].pixformat;
+
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *fmt)
+{
+	struct sun6i_video *video = video_drvdata(file);
+
+	*fmt = video->fmt;
+
+	return 0;
+}
+
+static int sun6i_video_try_fmt(struct sun6i_video *video, struct v4l2_format *f,
+			       struct sun6i_csi_format **current_fmt)
+{
+	struct sun6i_csi_format *csi_fmt;
+	struct v4l2_pix_format *pixfmt = &f->fmt.pix;
+	struct v4l2_subdev_format format;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = sun6i_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -ENXIO;
+
+	csi_fmt = find_format_by_pixformat(video, pixfmt->pixelformat);
+	if (csi_fmt == NULL) {
+		if (video->num_formats > 0) {
+			csi_fmt = &video->formats[0];
+			pixfmt->pixelformat = csi_fmt->pixformat;
+		} else
+			return -EINVAL;
+	}
+
+	format.pad = pad;
+	format.which = V4L2_SUBDEV_FORMAT_TRY;
+	v4l2_fill_mbus_format(&format.format, pixfmt, csi_fmt->mbus_code);
+	ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &format);
+	if (ret)
+		return ret;
+
+	v4l2_fill_pix_format(pixfmt, &format.format);
+
+	pixfmt->bytesperline = (pixfmt->width * csi_fmt->bpp) >> 3;
+	pixfmt->sizeimage = (pixfmt->width * csi_fmt->bpp * pixfmt->height) / 8;
+
+	if (current_fmt)
+		*current_fmt = csi_fmt;
+
+	return 0;
+}
+
+static int sun6i_video_set_fmt(struct sun6i_video *video, struct v4l2_format *f)
+{
+	struct v4l2_subdev_format format;
+	struct sun6i_csi_format *current_fmt;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	subdev = sun6i_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -ENXIO;
+
+	ret = sun6i_video_try_fmt(video, f, &current_fmt);
+	if (ret)
+		return ret;
+
+	format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
+			      current_fmt->mbus_code);
+	ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, &format);
+	if (ret < 0)
+		return ret;
+
+	video->fmt = *f;
+	video->current_fmt = current_fmt;
+
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+				struct v4l2_format *f)
+{
+	struct sun6i_video *video = video_drvdata(file);
+
+	if (vb2_is_busy(&video->vb2_vidq))
+		return -EBUSY;
+
+	return sun6i_video_set_fmt(video, f);
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+				  struct v4l2_format *f)
+{
+	struct sun6i_video *video = video_drvdata(file);
+
+	return sun6i_video_try_fmt(video, f, NULL);
+}
+
+static int vidioc_enum_input(struct file *file, void *fh,
+			 struct v4l2_input *inp)
+{
+	struct sun6i_video *video = video_drvdata(file);
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	int ret;
+
+	if (inp->index != 0)
+		return -EINVAL;
+
+	subdev = sun6i_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -ENXIO;
+
+	ret = v4l2_subdev_call(subdev, video, g_input_status, &inp->status);
+	if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
+		return ret;
+
+	inp->type = V4L2_INPUT_TYPE_CAMERA;
+
+	inp->capabilities = 0;
+	inp->std = 0;
+	if (v4l2_subdev_has_op(subdev, pad, dv_timings_cap))
+		inp->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+
+	strlcpy(inp->name, subdev->name, sizeof(inp->name));
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	*i = 0;
+
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int i)
+{
+	if (i != 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops sun6i_video_ioctl_ops = {
+	.vidioc_querycap		= vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap	= vidioc_enum_fmt_vid_cap,
+	.vidioc_g_fmt_vid_cap		= vidioc_g_fmt_vid_cap,
+	.vidioc_s_fmt_vid_cap		= vidioc_s_fmt_vid_cap,
+	.vidioc_try_fmt_vid_cap		= vidioc_try_fmt_vid_cap,
+
+	.vidioc_enum_input		= vidioc_enum_input,
+	.vidioc_s_input			= vidioc_s_input,
+	.vidioc_g_input			= vidioc_g_input,
+
+	.vidioc_reqbufs			= vb2_ioctl_reqbufs,
+	.vidioc_querybuf		= vb2_ioctl_querybuf,
+	.vidioc_qbuf			= vb2_ioctl_qbuf,
+	.vidioc_expbuf			= vb2_ioctl_expbuf,
+	.vidioc_dqbuf			= vb2_ioctl_dqbuf,
+	.vidioc_create_bufs		= vb2_ioctl_create_bufs,
+	.vidioc_prepare_buf		= vb2_ioctl_prepare_buf,
+	.vidioc_streamon		= vb2_ioctl_streamon,
+	.vidioc_streamoff		= vb2_ioctl_streamoff,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 file operations
+ */
+static int sun6i_video_open(struct file *file)
+{
+	struct sun6i_video *video = video_drvdata(file);
+	int ret;
+
+	if (mutex_lock_interruptible(&video->lock))
+		return -ERESTARTSYS;
+
+	ret = v4l2_fh_open(file);
+	if (ret < 0)
+		goto unlock;
+
+	ret = v4l2_pipeline_pm_use(&video->vdev.entity, 1);
+	if (ret < 0)
+		goto fh_release;
+
+	/* check if already powered */
+	if (!v4l2_fh_is_singular_file(file))
+		goto unlock;
+
+	ret = sun6i_csi_set_power(video->csi, true);
+	if (ret < 0)
+		goto fh_release;
+
+	mutex_unlock(&video->lock);
+	return 0;
+
+fh_release:
+	v4l2_fh_release(file);
+unlock:
+	mutex_unlock(&video->lock);
+	return ret;
+}
+
+static int sun6i_video_close(struct file *file)
+{
+	struct sun6i_video *video = video_drvdata(file);
+	bool last_fh;
+
+	mutex_lock(&video->lock);
+
+	last_fh = v4l2_fh_is_singular_file(file);
+
+	_vb2_fop_release(file, NULL);
+
+	v4l2_pipeline_pm_use(&video->vdev.entity, 0);
+
+	if (last_fh)
+		sun6i_csi_set_power(video->csi, false);
+
+	mutex_unlock(&video->lock);
+
+	return 0;
+}
+
+static const struct v4l2_file_operations sun6i_video_fops = {
+	.owner		= THIS_MODULE,
+	.open		= sun6i_video_open,
+	.release	= sun6i_video_close,
+	.unlocked_ioctl	= video_ioctl2,
+	.mmap		= vb2_fop_mmap,
+	.poll		= vb2_fop_poll
+};
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+static int sun6i_video_formats_init(struct sun6i_video *video)
+{
+	struct v4l2_subdev_mbus_code_enum mbus_code = { 0 };
+	struct sun6i_csi *csi = video->csi;
+	struct v4l2_format format;
+	struct v4l2_subdev *subdev;
+	u32 pad;
+	const u32 *pixformats;
+	int pixformat_count = 0;
+	u32 subdev_codes[32]; /* subdev format codes, 32 should be enough */
+	int codes_count = 0;
+	int num_fmts = 0;
+	int i, j;
+
+	subdev = sun6i_video_remote_subdev(video, &pad);
+	if (subdev == NULL)
+		return -ENXIO;
+
+	/* Get supported pixformats of CSI */
+	pixformat_count = sun6i_csi_get_supported_pixformats(csi, &pixformats);
+	if (pixformat_count <= 0)
+		return -ENXIO;
+
+	/* Get subdev formats codes */
+	mbus_code.pad = pad;
+	mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+	while (!v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL,
+				 &mbus_code)) {
+		if (codes_count >= ARRAY_SIZE(subdev_codes)) {
+			dev_warn(video->csi->dev,
+				 "subdev_codes array is full!\n");
+			break;
+		}
+		subdev_codes[codes_count] = mbus_code.code;
+		codes_count++;
+		mbus_code.index++;
+	}
+
+	if (!codes_count)
+		return -ENXIO;
+
+	/* Get supported formats count */
+	for (i = 0; i < codes_count; i++) {
+		for (j = 0; j < pixformat_count; j++) {
+			if (!sun6i_csi_is_format_support(csi, pixformats[j],
+					subdev_codes[i])) {
+				continue;
+			}
+			num_fmts++;
+		}
+	}
+
+	if (!num_fmts)
+		return -ENXIO;
+
+	video->num_formats = num_fmts;
+	video->formats = devm_kcalloc(video->csi->dev, num_fmts,
+			sizeof(struct sun6i_csi_format), GFP_KERNEL);
+	if (!video->formats)
+		return -ENOMEM;
+
+	/* Get supported formats */
+	num_fmts = 0;
+	for (i = 0; i < codes_count; i++) {
+		for (j = 0; j < pixformat_count; j++) {
+			if (!sun6i_csi_is_format_support(csi, pixformats[j],
+					subdev_codes[i])) {
+				continue;
+			}
+
+			video->formats[num_fmts].pixformat = pixformats[j];
+			video->formats[num_fmts].mbus_code = subdev_codes[i];
+			video->formats[num_fmts].bpp =
+					sun6i_csi_get_bpp(pixformats[j]);
+			num_fmts++;
+		}
+	}
+
+	/* setup default format */
+	format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	format.fmt.pix.width = 1280;
+	format.fmt.pix.height = 720;
+	format.fmt.pix.pixelformat = video->formats[0].pixformat;
+	sun6i_video_set_fmt(video, &format);
+
+	return 0;
+}
+
+static int sun6i_video_link_setup(struct media_entity *entity,
+				  const struct media_pad *local,
+				  const struct media_pad *remote, u32 flags)
+{
+	struct video_device *vdev = media_entity_to_video_device(entity);
+	struct sun6i_video *video = video_get_drvdata(vdev);
+
+	if (WARN_ON(video == NULL))
+		return 0;
+
+	return sun6i_video_formats_init(video);
+}
+
+static const struct media_entity_operations sun6i_video_media_ops = {
+	.link_setup = sun6i_video_link_setup,
+};
+
+int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
+		     const char *name)
+{
+	struct video_device *vdev = &video->vdev;
+	struct vb2_queue *vidq = &video->vb2_vidq;
+	int ret;
+
+	video->csi = csi;
+
+	/* Initialize the media entity... */
+	video->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+	vdev->entity.ops = &sun6i_video_media_ops;
+	ret = media_entity_pads_init(&vdev->entity, 1, &video->pad);
+	if (ret < 0)
+		return ret;
+
+	mutex_init(&video->lock);
+
+	INIT_LIST_HEAD(&video->dma_queue);
+	spin_lock_init(&video->dma_queue_lock);
+
+	video->sequence = 0;
+	video->num_formats = 0;
+
+	/* Initialize videobuf2 queue */
+	vidq->type			= V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	vidq->io_modes			= VB2_MMAP | VB2_DMABUF;
+	vidq->drv_priv			= video;
+	vidq->buf_struct_size		= sizeof(struct sun6i_csi_buffer);
+	vidq->ops			= &sun6i_csi_vb2_ops;
+	vidq->mem_ops			= &vb2_dma_contig_memops;
+	vidq->timestamp_flags		= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+	vidq->lock			= &video->lock;
+	/* Make sure non-dropped frame */
+	vidq->min_buffers_needed	= 3;
+	vidq->dev			= csi->dev;
+
+	ret = vb2_queue_init(vidq);
+	if (ret) {
+		v4l2_err(&csi->v4l2_dev, "vb2_queue_init failed: %d\n", ret);
+		goto error;
+	}
+
+	/* Register video device */
+	strlcpy(vdev->name, name, sizeof(vdev->name));
+	vdev->release		= video_device_release_empty;
+	vdev->fops		= &sun6i_video_fops;
+	vdev->ioctl_ops		= &sun6i_video_ioctl_ops;
+	vdev->vfl_type		= VFL_TYPE_GRABBER;
+	vdev->vfl_dir		= VFL_DIR_RX;
+	vdev->v4l2_dev		= &csi->v4l2_dev;
+	vdev->queue		= vidq;
+	vdev->lock		= &video->lock;
+	vdev->device_caps	= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+	video_set_drvdata(vdev, video);
+
+	ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+	if (ret < 0) {
+		v4l2_err(&csi->v4l2_dev,
+			 "video_register_device failed: %d\n", ret);
+		goto error;
+	}
+
+	return 0;
+
+error:
+	sun6i_video_cleanup(video);
+	return ret;
+}
+
+void sun6i_video_cleanup(struct sun6i_video *video)
+{
+	if (video_is_registered(&video->vdev))
+		video_unregister_device(&video->vdev);
+
+	media_entity_cleanup(&video->vdev.entity);
+}
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
new file mode 100644
index 0000000..9e7171b
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.h
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2011-2018 Magewell Electronics Co., Ltd. (Nanjing)
+ * All rights reserved.
+ * Author: Yong Deng <yong.deng@magewell.com>
+ */
+
+#ifndef __SUN6I_VIDEO_H__
+#define __SUN6I_VIDEO_H__
+
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-core.h>
+
+/*
+ * struct sun6i_csi_format - CSI media bus format information
+ * @pixformat: V4l2 pixformat for this format
+ * @mbus_code: V4L2 media bus format code.
+ * @bpp: Bytes per pixel (when stored in memory)
+ */
+struct sun6i_csi_format {
+	u32				pixformat;
+	u32				mbus_code;
+	u8				bpp;
+};
+
+struct sun6i_csi;
+
+struct sun6i_video {
+	struct video_device		vdev;
+	struct media_pad		pad;
+	struct sun6i_csi		*csi;
+
+	struct mutex			lock;
+
+	struct vb2_queue		vb2_vidq;
+	spinlock_t			dma_queue_lock;
+	struct list_head		dma_queue;
+
+	unsigned int			sequence;
+
+	struct sun6i_csi_format		*formats;
+	unsigned int			num_formats;
+	struct sun6i_csi_format		*current_fmt;
+	struct v4l2_format		fmt;
+};
+
+int sun6i_video_init(struct sun6i_video *video, struct sun6i_csi *csi,
+		     const char *name);
+void sun6i_video_cleanup(struct sun6i_video *video);
+
+void sun6i_video_frame_done(struct sun6i_video *video);
+
+#endif /* __SUN6I_VIDEO_H__ */
-- 
1.8.3.1

^ permalink raw reply related

* [PATCH v3 2/2] phy: rockchip-emmc: use regmap_read_poll_timeout to poll dllrdy
From: Doug Anderson @ 2018-01-11  3:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515638427-15117-3-git-send-email-wxt@rock-chips.com>

Hi,

On Wed, Jan 10, 2018 at 6:40 PM, Caesar Wang <wxt@rock-chips.com> wrote:
> From: Shawn Lin <shawn.lin@rock-chips.com>
>
> Just use the API instead of open-coding it, no functional change
> intended.
>
> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
> Reviewed-by: Brian Norris <briannorris@chromium.org>
> Signed-off-by: Caesar Wang <wxt@rock-chips.com>
>
> ---
>
> Changes in v3:
> - As Doug commented on https://patchwork.kernel.org/patch/10154797,
>   Change "1, 50" to "0, 50".
>
> Changes in v2:
> - As Brian commented on https://patchwork.kernel.org/patch/10139891/,
>   changed the note and added to print error value with
>   regmap_read_poll_timeout API.
>
>  drivers/phy/rockchip/phy-rockchip-emmc.c | 33 +++++++++++---------------------
>  1 file changed, 11 insertions(+), 22 deletions(-)

Reviewed-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply

* [PATCH v3 1/2] phy: rockchip-emmc: retry calpad busy trimming
From: Doug Anderson @ 2018-01-11  3:22 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515638427-15117-2-git-send-email-wxt@rock-chips.com>

Hi,

On Wed, Jan 10, 2018 at 6:40 PM, Caesar Wang <wxt@rock-chips.com> wrote:
> From: Shawn Lin <shawn.lin@rock-chips.com>
>
> It turns out that 5us isn't enough for all cases, so let's
> retry some more times to wait for caldone.
>
> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
> Tested-by: Ziyuan Xu <xzy.xu@rock-chips.com>
> Signed-off-by: Caesar Wang <wxt@rock-chips.com>
> ---
>
> Changes in v3:
> - As Doug commented on both upstream and gerrit.
>   Change "5, 50" to "0, 50", and the message of print.
>
> Changes in v2:
> - print the return valut with regmap_read_poll_timeout failing.
>
>  drivers/phy/rockchip/phy-rockchip-emmc.c | 27 +++++++++++++++++----------
>  1 file changed, 17 insertions(+), 10 deletions(-)

Reviewed-by: Douglas Anderson <dianders@chromium.org>

^ permalink raw reply

* [PATCH RFC v1] arm64: Handle traps from accessing CNTVCT/CNTFRQ for CONFIG_COMPAT
From: Nicolin Chen @ 2018-01-11  4:43 UTC (permalink / raw)
  To: linux-arm-kernel

CONFIG_COMPAT allows ARM64 machine to run 32-bit instructions.
Since the ARCH_TIMER_USR_VCT_ACCESS_EN might be disabled if a
timer workaround is detected, accessing cntvct via mrrc will
also trigger a trap.

So this patch adds support to handle this situation.

Tested with a user program generated by 32-bit compiler:
  int main()
  {
      unsigned long long cval;
      asm volatile("mrrc p15, 1, %Q0, %R0, c14" : "=r" (cval));
      return 0;
  }

Signed-off-by: Nicolin Chen <nicoleotsuka@gmail.com>
---

[ I also added cntfrq here for safety as theoretically it could
  trigger the trap as well. However, my another test case (with
  mrc insturction) doesn't seem to trigger a trap. So I would
  drop it in the next version if someone can confirm it's not
  required. Thanks -- Nicolin ]

 arch/arm64/include/asm/esr.h    | 25 +++++++++++++++++++
 arch/arm64/include/asm/ptrace.h | 15 ++++++++++++
 arch/arm64/kernel/entry.S       |  4 ++--
 arch/arm64/kernel/traps.c       | 53 +++++++++++++++++++++++++++++++++++++++--
 4 files changed, 93 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 014d7d8..55dea62 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -220,6 +220,31 @@
 		(((e) & ESR_ELx_SYS64_ISS_OP2_MASK) >>		\
 		 ESR_ELx_SYS64_ISS_OP2_SHIFT))
 
+/* ISS fields for MRC and MRS are very similar, so reuse SYS64 macros */
+#define ESR_ELx_CP15_32_ISS_MRC_MASK	ESR_ELx_SYS64_ISS_SYS_MASK
+#define ESR_ELx_CP15_32_ISS_MRC_CNTFRQ	(ESR_ELx_SYS64_ISS_SYS_VAL(0, 0, 0, 14, 0) | \
+					 ESR_ELx_SYS64_ISS_DIR_READ)
+
+/* ISS field definitions for CP15 MRRC/MCRR instructions (same for CP14) */
+#define ESR_ELx_CP15_64_ISS_OPC1_SHIFT	16
+#define ESR_ELx_CP15_64_ISS_OPC1_MASK	(UL(0xf) << ESR_ELx_CP15_64_ISS_OPC1_SHIFT)
+#define ESR_ELx_CP15_64_ISS_RT2_SHIFT	10
+#define ESR_ELx_CP15_64_ISS_RT2_MASK	(UL(0x1f) << ESR_ELx_CP15_64_ISS_RT2_SHIFT)
+#define ESR_ELx_CP15_64_ISS_RT_SHIFT	5
+#define ESR_ELx_CP15_64_ISS_RT_MASK	(UL(0x1f) << ESR_ELx_CP15_64_ISS_RT_SHIFT)
+#define ESR_ELx_CP15_64_ISS_CRM_SHIFT	1
+#define ESR_ELx_CP15_64_ISS_CRM_MASK	(UL(0xf) << ESR_ELx_CP15_64_ISS_CRM_SHIFT)
+#define ESR_ELx_CP15_64_ISS_MRRC_MASK	(ESR_ELx_CP15_64_ISS_OPC1_MASK | \
+					 ESR_ELx_CP15_64_ISS_CRM_MASK| \
+					 ESR_ELx_SYS64_ISS_DIR_MASK)
+
+#define ESR_ELx_CP15_64_ISS_MRRC_VAL(opc1, crm) \
+					(((opc1) << ESR_ELx_CP15_64_ISS_OPC1_SHIFT) | \
+					 ((crm) << ESR_ELx_CP15_64_ISS_CRM_SHIFT))
+
+#define ESR_ELx_CP15_64_ISS_MRRC_CNTVCT	(ESR_ELx_CP15_64_ISS_MRRC_VAL(1, 14) | \
+					 ESR_ELx_SYS64_ISS_DIR_READ)
+
 #ifndef __ASSEMBLY__
 #include <asm/types.h>
 
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
index 6069d66..50caf11 100644
--- a/arch/arm64/include/asm/ptrace.h
+++ b/arch/arm64/include/asm/ptrace.h
@@ -243,6 +243,21 @@ static inline void pt_regs_write_reg(struct pt_regs *regs, int r,
 		regs->regs[r] = val;
 }
 
+/*
+ * Write two registers given architectural register index r and r2.
+ * Used by 32-bit MRRC and MCRR instrutions for 64-bit results
+ */
+static inline void pt_regs_write_regs(struct pt_regs *regs, int r, int r2,
+				      unsigned long val)
+{
+	if (r != 31 && r2 != 31) {
+		/* Save lower 32 bits to register r */
+		regs->regs[r] = val & 0xffffffff;
+		/* Save higher 32 bits to register r2 */
+		regs->regs[r2] = val >> 32;
+	}
+}
+
 /* Valid only for Kernel mode traps. */
 static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
 {
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
index 6d14b8f..9d6cd95 100644
--- a/arch/arm64/kernel/entry.S
+++ b/arch/arm64/kernel/entry.S
@@ -636,9 +636,9 @@ el0_sync_compat:
 	cmp	x24, #ESR_ELx_EC_UNKNOWN	// unknown exception in EL0
 	b.eq	el0_undef
 	cmp	x24, #ESR_ELx_EC_CP15_32	// CP15 MRC/MCR trap
-	b.eq	el0_undef
+	b.eq	el0_sys
 	cmp	x24, #ESR_ELx_EC_CP15_64	// CP15 MRRC/MCRR trap
-	b.eq	el0_undef
+	b.eq	el0_sys
 	cmp	x24, #ESR_ELx_EC_CP14_MR	// CP14 MRC/MCR trap
 	b.eq	el0_undef
 	cmp	x24, #ESR_ELx_EC_CP14_LS	// CP14 LDC/STC trap
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 3d3588f..211cce7 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -454,6 +454,17 @@ static void cntvct_read_handler(unsigned int esr, struct pt_regs *regs)
 	arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
 }
 
+#ifdef CONFIG_COMPAT
+static void cntvct_read_32_handler(unsigned int esr, struct pt_regs *regs)
+{
+	int rt2 = (esr & ESR_ELx_CP15_64_ISS_RT2_MASK) >> ESR_ELx_CP15_64_ISS_RT2_SHIFT;
+	int rt = (esr & ESR_ELx_CP15_64_ISS_RT_MASK) >> ESR_ELx_CP15_64_ISS_RT_SHIFT;
+
+	pt_regs_write_regs(regs, rt, rt2, arch_counter_get_cntvct());
+	arm64_skip_faulting_instruction(regs, AARCH64_INSN_SIZE);
+}
+#endif
+
 static void cntfrq_read_handler(unsigned int esr, struct pt_regs *regs)
 {
 	int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
@@ -495,11 +506,49 @@ static struct sys64_hook sys64_hooks[] = {
 	{},
 };
 
+#ifdef CONFIG_COMPAT
+static struct sys64_hook cp15_32_hooks[] = {
+	{
+		/* Trap read access to CNTFRQ via 32-bit insturction MRC */
+		.esr_mask = ESR_ELx_CP15_32_ISS_MRC_MASK,
+		.esr_val = ESR_ELx_CP15_32_ISS_MRC_CNTFRQ,
+		.handler = cntfrq_read_handler,
+	},
+	{},
+};
+
+static struct sys64_hook cp15_64_hooks[] = {
+	{
+		/* Trap read access to CNTVCT via 32-bit insturction MRRC */
+		.esr_mask = ESR_ELx_CP15_64_ISS_MRRC_MASK,
+		.esr_val = ESR_ELx_CP15_64_ISS_MRRC_CNTVCT,
+		.handler = cntvct_read_32_handler,
+	},
+	{},
+};
+#endif
+
 asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs)
 {
-	struct sys64_hook *hook;
+	struct sys64_hook *hook = NULL;
+
+	switch (ESR_ELx_EC(esr)) {
+#ifdef CONFIG_COMPAT
+	case ESR_ELx_EC_CP15_32:
+		hook = cp15_32_hooks;
+		break;
+	case ESR_ELx_EC_CP15_64:
+		hook = cp15_64_hooks;
+		break;
+#endif
+	case ESR_ELx_EC_SYS64:
+		hook = sys64_hooks;
+		break;
+	default:
+		break;
+	}
 
-	for (hook = sys64_hooks; hook->handler; hook++)
+	for (; hook && hook->handler; hook++)
 		if ((hook->esr_mask & esr) == hook->esr_val) {
 			hook->handler(esr, regs);
 			return;
-- 
2.1.4

^ permalink raw reply related

* [PATCH, v3] arm: omap2: timer: fix a kmemleak caused in omap_get_timer_dt
From: Qi Hou @ 2018-01-11  4:54 UTC (permalink / raw)
  To: linux-arm-kernel

When more than one GP timers are used as kernel system timers and the
corresponding nodes in device-tree are marked with the same "disabled"
property, then the "attr" field of the property will be initialized
more than once as the property being added to sys file system via
__of_add_property_sysfs().

In __of_add_property_sysfs(), the "name" field of pp->attr.attr is set
directly to the return value of safe_name(), without taking care of
whether it's already a valid pointer to a memory block. If it is, its
old value will always be overwritten by the new one and the memory block
allocated before will a "ghost", then a kmemleak happened.

That the same "disabled" property being added to different nodes of device
tree would cause that kind of kmemleak overhead, at leat once.

To fix it, allocate the property dynamically, and delete static one.

Signed-off-by: Qi Hou <qi.hou@windriver.com>
---
 arch/arm/mach-omap2/timer.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index ece09c9..d61fbd7 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -156,12 +156,6 @@ static struct clock_event_device clockevent_gpt = {
 	.tick_resume		= omap2_gp_timer_shutdown,
 };
 
-static struct property device_disabled = {
-	.name = "status",
-	.length = sizeof("disabled"),
-	.value = "disabled",
-};
-
 static const struct of_device_id omap_timer_match[] __initconst = {
 	{ .compatible = "ti,omap2420-timer", },
 	{ .compatible = "ti,omap3430-timer", },
@@ -203,8 +197,17 @@ static struct device_node * __init omap_get_timer_dt(const struct of_device_id *
 				  of_get_property(np, "ti,timer-secure", NULL)))
 			continue;
 
-		if (!of_device_is_compatible(np, "ti,omap-counter32k"))
-			of_add_property(np, &device_disabled);
+		if (!of_device_is_compatible(np, "ti,omap-counter32k")) {
+			struct property *prop;
+
+			prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+			if (!prop)
+				return NULL;
+			prop->name = "status";
+			prop->value = "disabled";
+			prop->length = strlen(prop->value);
+			of_add_property(np, prop);
+		}
 		return np;
 	}
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH 3/9] soc: samsung: pmu: Add the PMU data of exynos5433 to support low-power state
From: Chanwoo Choi @ 2018-01-11  5:39 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJKOXPf027Nz4CsNt4i1yuiQVMtZpv8ncrbgP1D-y4YM8kn30A@mail.gmail.com>

On 2018? 01? 09? 21:23, Krzysztof Kozlowski wrote:
> On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
>> This patch adds the PMU (Power Management Unit) data of exynos5433 SoC
>> in order to support the various power modes. Each power mode has
>> the different value for reducing the power-consumption.
>>
>> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
>> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
>> ---
>>  arch/arm/mach-exynos/common.h               |   2 -
>>  drivers/soc/samsung/Makefile                |   3 +-
>>  drivers/soc/samsung/exynos-pmu.c            |   1 +
>>  drivers/soc/samsung/exynos-pmu.h            |   2 +
>>  drivers/soc/samsung/exynos5433-pmu.c        | 286 ++++++++++++++++++++++++++++
>>  include/linux/soc/samsung/exynos-regs-pmu.h | 148 ++++++++++++++
>>  6 files changed, 439 insertions(+), 3 deletions(-)
>>  create mode 100644 drivers/soc/samsung/exynos5433-pmu.c
>>
>> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
>> index 098f84a149a3..afbc143a3d5d 100644
>> --- a/arch/arm/mach-exynos/common.h
>> +++ b/arch/arm/mach-exynos/common.h
>> @@ -125,8 +125,6 @@ enum {
>>  void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
>>  void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode);
>>
>> -extern u32 exynos_get_eint_wake_mask(void);
>> -
> 
> This does not look good. Does it compile without warnings on ARMv7 platforms?

I'll try to consolidate suspend-related code. I'll rework.

> 
>>  #ifdef CONFIG_PM_SLEEP
>>  extern void __init exynos_pm_init(void);
>>  #else
>> diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
>> index 29f294baac6e..d2e637339a45 100644
>> --- a/drivers/soc/samsung/Makefile
>> +++ b/drivers/soc/samsung/Makefile
>> @@ -2,5 +2,6 @@
>>  obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
>>
>>  obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)   += exynos3250-pmu.o exynos4-pmu.o \
>> -                                       exynos5250-pmu.o exynos5420-pmu.o
>> +                                       exynos5250-pmu.o exynos5420-pmu.o \
>> +                                       exynos5433-pmu.o
>>  obj-$(CONFIG_EXYNOS_PM_DOMAINS) += pm_domains.o
>> diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c
>> index cfc9de518344..7112d7b2749b 100644
>> --- a/drivers/soc/samsung/exynos-pmu.c
>> +++ b/drivers/soc/samsung/exynos-pmu.c
>> @@ -97,6 +97,7 @@ void exynos_sys_powerup_conf(enum sys_powerdown mode)
>>                 .data = exynos_pmu_data_arm_ptr(exynos5420_pmu_data),
>>         }, {
>>                 .compatible = "samsung,exynos5433-pmu",
>> +               .data = exynos_pmu_data_arm_ptr(exynos5433_pmu_data),
>>         },
>>         { /*sentinel*/ },
>>  };
>> diff --git a/drivers/soc/samsung/exynos-pmu.h b/drivers/soc/samsung/exynos-pmu.h
>> index efbaf8929252..895c786a2f4c 100644
>> --- a/drivers/soc/samsung/exynos-pmu.h
>> +++ b/drivers/soc/samsung/exynos-pmu.h
>> @@ -28,6 +28,7 @@ struct exynos_pmu_data {
>>  };
>>
>>  extern void __iomem *pmu_base_addr;
>> +extern u32 exynos_get_eint_wake_mask(void);
>>
>>  #ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
>>  /* list of all exported SoC specific data */
>> @@ -36,6 +37,7 @@ struct exynos_pmu_data {
>>  extern const struct exynos_pmu_data exynos4412_pmu_data;
>>  extern const struct exynos_pmu_data exynos5250_pmu_data;
>>  extern const struct exynos_pmu_data exynos5420_pmu_data;
>> +extern const struct exynos_pmu_data exynos5433_pmu_data;
>>  #endif
>>
>>  extern void pmu_raw_writel(u32 val, u32 offset);
>> diff --git a/drivers/soc/samsung/exynos5433-pmu.c b/drivers/soc/samsung/exynos5433-pmu.c
>> new file mode 100644
>> index 000000000000..2571e61522f0
>> --- /dev/null
>> +++ b/drivers/soc/samsung/exynos5433-pmu.c
>> @@ -0,0 +1,286 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +//
>> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
>> +// Copyright (c) Jonghwa Lee <jonghwa3.lee@samsung.com>
>> +// Copyright (c) Chanwoo Choi <cw00.choi@samsung.com>
> 
> Did you want to add here authorship notice or personal copyrights?

Remove personal info.

> 
>> +//
>> +// EXYNOS5433 - CPU PMU (Power Management Unit) support
>> +
>> +#include <linux/soc/samsung/exynos-regs-pmu.h>
>> +#include <linux/soc/samsung/exynos-pmu.h>
>> +
>> +#include "exynos-pmu.h"
>> +
>> +static struct exynos_pmu_conf exynos5433_pmu_config[] = {
> 
> This should be also const.

OK.

> 
>> +       /* { .offset = address, .val = { AFTR, LPA, SLEEP } } */
>> +       { EXYNOS5433_ATLAS_CPU0_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU0_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_ATLAS_CPU1_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU1_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_ATLAS_CPU2_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU2_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_ATLAS_CPU3_SYS_PWR_REG,                    { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_ATLAS_CPU3_CENTRAL_SYS_PWR_REG,    { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_APOLLO_CPU0_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU0_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_APOLLO_CPU1_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU1_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_APOLLO_CPU2_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU2_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_APOLLO_CPU3_SYS_PWR_REG,                   { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_DIS_IRQ_APOLLO_CPU3_CENTRAL_SYS_PWR_REG,   { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_ATLAS_NONCPU_SYS_PWR_REG,                  { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_APOLLO_NONCPU_SYS_PWR_REG,                 { 0x0, 0x0, 0x8 } },
>> +       { EXYNOS5433_A5IS_SYS_PWR_REG,                          { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_DIS_IRQ_A5IS_LOCAL_SYS_PWR_REG,            { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_DIS_IRQ_A5IS_CENTRAL_SYS_PWR_REG,          { 0x0, 0x0, 0x0 } },
>> +       { EXYNOS5433_ATLAS_L2_SYS_PWR_REG,                      { 0x0, 0x0, 0x7 } },
>> +       { EXYNOS5433_APOLLO_L2_SYS_PWR_REG,                     { 0x0, 0x0, 0x7 } },
>> +       { EXYNOS5433_CLKSTOP_CMU_TOP_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_CLKRUN_CMU_TOP_SYS_PWR_REG,                { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_RESET_CMU_TOP_SYS_PWR_REG,                 { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_RESET_CPUCLKSTOP_SYS_PWR_REG,              { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_CLKSTOP_CMU_MIF_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_CLKRUN_CMU_MIF_SYS_PWR_REG,                { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_RESET_CMU_MIF_SYS_PWR_REG,                 { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_DDRPHY_DLLLOCK_SYS_PWR_REG,                { 0x1, 0x1, 0x1 } },
>> +       { EXYNOS5433_DISABLE_PLL_CMU_TOP_SYS_PWR_REG,           { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_DISABLE_PLL_AUD_PLL_SYS_PWR_REG,           { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_DISABLE_PLL_CMU_MIF_SYS_PWR_REG,           { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_TOP_BUS_SYS_PWR_REG,                       { 0x7, 0x0, 0x0 } },
>> +       { EXYNOS5433_TOP_RETENTION_SYS_PWR_REG,                 { 0x1, 0x0, 0x1 } },
>> +       { EXYNOS5433_TOP_PWR_SYS_PWR_REG,                       { 0x3, 0x0, 0x3 } },
>> +       { EXYNOS5433_TOP_BUS_MIF_SYS_PWR_REG,                   { 0x7, 0x0, 0x0 } },
>> +       { EXYNOS5433_TOP_RETENTION_MIF_SYS_PWR_REG,             { 0x1, 0x0, 0x1 } },
>> +       { EXYNOS5433_TOP_PWR_MIF_SYS_PWR_REG,                   { 0x3, 0x0, 0x3 } },
>> +       { EXYNOS5433_LOGIC_RESET_SYS_PWR_REG,                   { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_OSCCLK_GATE_SYS_PWR_REG,                   { 0x1, 0x0, 0x1 } },
>> +       { EXYNOS5433_SLEEP_RESET_SYS_PWR_REG,                   { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_LOGIC_RESET_MIF_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_OSCCLK_GATE_MIF_SYS_PWR_REG,               { 0x1, 0x0, 0x1 } },
>> +       { EXYNOS5433_SLEEP_RESET_MIF_SYS_PWR_REG,               { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_MEMORY_TOP_SYS_PWR_REG,                    { 0x3, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_LPDDR3_SYS_PWR_REG,          { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_JTAG_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_TOP_SYS_PWR_REG,             { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_UART_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_EBIA_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_EBIB_SYS_PWR_REG,            { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_SPI_SYS_PWR_REG,             { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_MIF_SYS_PWR_REG,             { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_ISOLATION_SYS_PWR_REG,                 { 0x1, 0x0, 0x1 } },
>> +       { EXYNOS5433_PAD_RETENTION_USBXTI_SYS_PWR_REG,          { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_RETENTION_BOOTLDO_SYS_PWR_REG,         { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_ISOLATION_MIF_SYS_PWR_REG,             { 0x1, 0x0, 0x1 } },
>> +       { EXYNOS5433_PAD_RETENTION_FSYSGENIO_SYS_PWR_REG,       { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_PAD_ALV_SEL_SYS_PWR_REG,                   { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_XXTI_SYS_PWR_REG,                          { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_XXTI26_SYS_PWR_REG,                        { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_EXT_REGULATOR_SYS_PWR_REG,                 { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_GPIO_MODE_SYS_PWR_REG,                     { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_GPIO_MODE_FSYS0_SYS_PWR_REG,               { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_GPIO_MODE_MIF_SYS_PWR_REG,                 { 0x1, 0x0, 0x0 } },
>> +       { EXYNOS5433_GPIO_MODE_AUD_SYS_PWR_REG,                 { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_GSCL_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_CAM0_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_MSCL_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_G3D_SYS_PWR_REG,                           { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_DISP_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_CAM1_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_AUD_SYS_PWR_REG,                           { 0xF, 0xF, 0x0 } },
>> +       { EXYNOS5433_FSYS_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_BUS2_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_G2D_SYS_PWR_REG,                           { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_ISP0_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_MFC_SYS_PWR_REG,                           { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_HEVC_SYS_PWR_REG,                          { 0xF, 0x0, 0x0 } },
>> +       { EXYNOS5433_RESET_SLEEP_FSYS_SYS_PWR_REG,              { 0x1, 0x1, 0x0 } },
>> +       { EXYNOS5433_RESET_SLEEP_BUS2_SYS_PWR_REG,              { 0x1, 0x1, 0x0 } },
>> +       { PMU_TABLE_END, },
>> +};
>> +
>> +static unsigned int const exynos5433_list_feed[] = {
>> +       EXYNOS5433_ATLAS_NONCPU_OPTION,
>> +       EXYNOS5433_APOLLO_NONCPU_OPTION,
>> +       EXYNOS5433_TOP_PWR_OPTION,
>> +       EXYNOS5433_TOP_PWR_MIF_OPTION,
>> +       EXYNOS5433_AUD_OPTION,
>> +       EXYNOS5433_CAM0_OPTION,
>> +       EXYNOS5433_DISP_OPTION,
>> +       EXYNOS5433_G2D_OPTION,
>> +       EXYNOS5433_G3D_OPTION,
>> +       EXYNOS5433_HEVC_OPTION,
>> +       EXYNOS5433_MSCL_OPTION,
>> +       EXYNOS5433_MFC_OPTION,
>> +       EXYNOS5433_GSCL_OPTION,
>> +       EXYNOS5433_FSYS_OPTION,
>> +       EXYNOS5433_ISP_OPTION,
>> +       EXYNOS5433_BUS2_OPTION,
>> +};
>> +
>> +static unsigned int const exynos5433_list_pad_retention[] = {
>> +       EXYNOS5433_PAD_RETENTION_LPDDR3_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_AUD_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_MMC2_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_TOP_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_UART_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_MMC0_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_MMC1_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_EBIA_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_EBIB_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_SPI_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_MIF_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_USBXTI_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_BOOTLDO_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_UFS_OPTION,
>> +       EXYNOS5433_PAD_RETENTION_FSYSGENIO_OPTION,
> 
> Looks like conflicting with existing
> drivers/pinctrl/samsung/pinctrl-exynos-arm64.c... and probably this
> should be part of pinctrl driver's suspend/resume paths.

OK. I'll remove it from this driver and then I'll handle PAD_RETENTION on pinctrl driver.

> 
>> +};
>> +
>> +static void exynos5433_set_wakeupmask(enum sys_powerdown mode)
>> +{
>> +       u32 intmask = 0;
>> +
>> +       pmu_raw_writel(exynos_get_eint_wake_mask(),
>> +                                       EXYNOS5433_EINT_WAKEUP_MASK);
>> +
>> +       /* Disable WAKEUP event monitor */
>> +       intmask = pmu_raw_readl(EXYNOS5433_WAKEUP_MASK);
>> +       intmask &= ~(1 << 31);
> 
> This should have a define. Maybe it is an already defined field like
> S5P_CORE_AUTOWAKEUP_EN or S5P_PS_HOLD_EN?

[31] bit of WAKEUP_MASK is the reserved bit on TRM.
But, when I checked it on released code from Samsung,
it is used to disable the wakeup event monitoring circuit.
I'll define it for readability.

> 
>> +       pmu_raw_writel(intmask, EXYNOS5433_WAKEUP_MASK);
>> +
>> +       pmu_raw_writel(0xFFFF0000, EXYNOS5433_WAKEUP_MASK2);
>> +       pmu_raw_writel(0xFFFF0000, EXYNOS5433_WAKEUP_MASK3);
> 
> Both need explaining what you are masking, preferably by appropriate
> comment and maybe also define for raw constants.

Initialize the reset value to EXYNOS5433_WAKEUP_MASK2/MASK3 which have
the 0xffff000 as the reset value on Exynos5433's TRM. I'll add the comment.

> 
>> +}
>> +
>> +static void exynos5433_pmu_central_seq(bool enable)
>> +{
>> +       unsigned int tmp;
>> +
>> +       tmp = pmu_raw_readl(EXYNOS5433_CENTRAL_SEQ_CONFIGURATION);
>> +       if (enable)
>> +               tmp &= ~EXYNOS5433_CENTRALSEQ_PWR_CFG;
>> +       else
>> +               tmp |= EXYNOS5433_CENTRALSEQ_PWR_CFG;
>> +       pmu_raw_writel(tmp, EXYNOS5433_CENTRAL_SEQ_CONFIGURATION);
>> +
>> +       tmp = pmu_raw_readl(EXYNOS5433_CENTRAL_SEQ_MIF_CONFIGURATION);
>> +       if (enable)
>> +               tmp &= ~EXYNOS5433_CENTRALSEQ_PWR_CFG;
>> +       else
>> +               tmp |= EXYNOS5433_CENTRALSEQ_PWR_CFG;
>> +       pmu_raw_writel(tmp, EXYNOS5433_CENTRAL_SEQ_MIF_CONFIGURATION);
>> +}
>> +
>> +static void exynos5433_pmu_pad_retention_release(void)
>> +{
>> +       unsigned int tmp;
>> +       int i;
> 
> unsigned int i

OK.

> 
>> +
>> +       for (i = 0 ; i < ARRAY_SIZE(exynos5433_list_pad_retention) ; i++) {
>> +               tmp = pmu_raw_readl(exynos5433_list_pad_retention[i]);
>> +               tmp |= EXYNOS5433_INITIATE_WAKEUP_FROM_LOWPOWER;
>> +               pmu_raw_writel(tmp, exynos5433_list_pad_retention[i]);
>> +       }
>> +}
>> +
>> +static void exynos5433_pmu_init(void)
>> +{
>> +       unsigned int tmp;
>> +       int i, cluster, cpu;
> 
> unsigned int i

OK.

> 
>> +
>> +       /* Enable non retention flip-flop reset for wakeup */
>> +       tmp = pmu_raw_readl(EXYNOS5433_PMU_SPARE0);
>> +       tmp |= EXYNOS5433_EN_NONRET_RESET;
>> +       pmu_raw_writel(tmp, EXYNOS5433_PMU_SPARE0);
> 
> This is spare register. Who is using it? Firmware? Please add its
> usage also in Documentation/arm/Samsung/Bootloader-interface.txt.

Unfortunately, when I checked the bootloader for the PMU_SPARE0 register,
the bootloader doesn't read/write for PMU_SPARE0. I fount this code
on code released by Samsung. The document doesn't include the detailed role.

> 
>> +
>> +        /* Enable only SC_FEEDBACK for the register list */
>> +       for (i = 0 ; i < ARRAY_SIZE(exynos5433_list_feed) ; i++) {
>> +               tmp = pmu_raw_readl(exynos5433_list_feed[i]);
>> +               tmp &= ~EXYNOS5_USE_SC_COUNTER;
>> +               tmp |= EXYNOS5_USE_SC_FEEDBACK;
>> +               pmu_raw_writel(tmp, exynos5433_list_feed[i]);
>> +       }
>> +
>> +       /*
>> +        * Disable automatic L2 flush, Disable L2 retention and
>> +        * Enable STANDBYWFIL2, ACE/ACP
>> +        */
>> +       for (cluster = 0; cluster < 2; cluster++) {
>> +               tmp = pmu_raw_readl(EXYNOS5433_ATLAS_L2_OPTION + (cluster * 0x20));
> 
> I would prefer to follow the convention for similar registers for cores, like:
> EXYNOS_ARM_CORE_CONFIGURATION
> EXYNOS_ARM_CORE_STATUS
> 
> This moves the offset into the header, along to other register offsets.

OK.

> 
>> +               tmp &= ~(EXYNOS5433_USE_AUTO_L2FLUSHREQ | EXYNOS5433_USE_RETENTION);
>> +
>> +               if (cluster == 0) {
>> +                       tmp |= (EXYNOS5433_USE_STANDBYWFIL2 |
>> +                               EXYNOS5433_USE_DEACTIVATE_ACE |
>> +                               EXYNOS5433_USE_DEACTIVATE_ACP);
>> +               }
>> +               pmu_raw_writel(tmp, EXYNOS5433_ATLAS_L2_OPTION + (cluster * 0x20));
>> +       }
>> +
>> +       /*
>> +        * Enable both SC_COUNTER and SC_FEEDBACK for the CPUs
>> +        * Use STANDBYWFI and SMPEN to indicate that core is ready to enter
>> +        * low power mode
>> +        */
>> +       for (cpu = 0; cpu < 8; cpu++) {
>> +               tmp = pmu_raw_readl(EXYNOS5433_CPU_OPTION(cpu));
>> +               tmp |= (EXYNOS5_USE_SC_FEEDBACK | EXYNOS5_USE_SC_COUNTER);
>> +               tmp |= EXYNOS5433_USE_SMPEN;
>> +               tmp |= EXYNOS5433_USE_STANDBYWFI;
>> +               tmp &= ~EXYNOS5433_USE_STANDBYWFE;
>> +               pmu_raw_writel(tmp, EXYNOS5433_CPU_OPTION(cpu));
>> +
>> +               tmp = pmu_raw_readl(EXYNOS5433_CPU_DURATION(cpu));
>> +               tmp |= EXYNOS5433_DUR_WAIT_RESET;
>> +               tmp &= ~EXYNOS5433_DUR_SCALL;
>> +               tmp |= EXYNOS5433_DUR_SCALL_VALUE;
>> +               pmu_raw_writel(tmp, EXYNOS5433_CPU_DURATION(cpu));
>> +       }
>> +
>> +       /* Skip atlas block power-off during automatic power down sequence */
>> +       tmp = pmu_raw_readl(EXYNOS5433_ATLAS_CPUSEQUENCER_OPTION);
>> +       tmp |= EXYNOS5433_SKIP_BLK_PWR_DOWN;
>> +       pmu_raw_writel(tmp, EXYNOS5433_ATLAS_CPUSEQUENCER_OPTION);
>> +
>> +       /* Limit in-rush current during local power up of cores */
>> +       tmp = pmu_raw_readl(EXYNOS5433_UP_SCHEDULER);
>> +       tmp |= EXYNOS5433_ENABLE_ATLAS_CPU;
>> +       pmu_raw_writel(tmp, EXYNOS5433_UP_SCHEDULER);
>> +}
>> +
>> +static void exynos5433_powerdown_conf(enum sys_powerdown mode)
>> +{
>> +       switch (mode) {
>> +       case SYS_SLEEP:
>> +               exynos5433_set_wakeupmask(mode);
>> +               exynos5433_pmu_central_seq(true);
>> +               break;
>> +       default:
>> +               break;
>> +       };
>> +}
>> +
>> +static void exynos5433_powerup_conf(enum sys_powerdown mode)
>> +{
>> +       unsigned int wakeup;
>> +
>> +       switch (mode) {
>> +       case SYS_SLEEP:
>> +               wakeup = pmu_raw_readl(EXYNOS5433_CENTRAL_SEQ_CONFIGURATION);
>> +               wakeup &= EXYNOS5433_CENTRALSEQ_PWR_CFG;
>> +               if (wakeup)
>> +                       exynos5433_pmu_pad_retention_release();
>> +               else
>> +                       exynos5433_pmu_central_seq(false);
> 
> I do not understand what you want to achieve here. Re-suspend?

The powerup_conf is unneeded anymore. So, I'll remove the powerup_conf.
Because 
- pad_retention should be handled in pinctrl driver according to your comment.
- exynos5433_pmu_central_seq(false) set the high for SYS_PWR_CFG field
of CENTRAL_SEQ_CONFIGURATION. But, When system-level low-power mode,
SYS_PWR_CFG field of CENTRAL_SEQ_CONFIGURATION register is automatically
cleared to high. So, exynos5433_pmu_central_seq(false) call is unneeded.

> 
>> +               break;
>> +       default:
>> +               break;
>> +       };
>> +}
>> +
>> +const struct exynos_pmu_data exynos5433_pmu_data = {
>> +       .pmu_config             = exynos5433_pmu_config,
>> +       .pmu_init               = exynos5433_pmu_init,
>> +       .powerdown_conf         = exynos5433_powerdown_conf,
>> +       .powerup_conf           = exynos5433_powerup_conf,
>> +};
>> diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h
>> index bebdde5dccd6..93a52d133ba1 100644
>> --- a/include/linux/soc/samsung/exynos-regs-pmu.h
>> +++ b/include/linux/soc/samsung/exynos-regs-pmu.h
>> @@ -645,7 +645,110 @@
>>                                          | EXYNOS5420_KFC_USE_STANDBY_WFI3)
>>
>>  /* For EXYNOS5433 */
>> +#define EXYNOS5433_UP_SCHEDULER                                        (0x0120)
>> +#define EXYNOS5433_CENTRAL_SEQ_CONFIGURATION                   (0x0200)
>> +#define EXYNOS5433_CENTRAL_SEQ_MIF_CONFIGURATION               (0x0240)
>> +#define EXYNOS5433_EINT_WAKEUP_MASK                            (0x060C)
>> +#define EXYNOS5433_WAKEUP_MASK                                 (0x0610)
>> +#define EXYNOS5433_WAKEUP_MASK2                                        (0x0614)
>> +#define EXYNOS5433_WAKEUP_MASK3                                        (0x0618)
>> +#define EXYNOS5433_EINT_WAKEUP_MASK1                           (0x062C)
>>  #define EXYNOS5433_USBHOST30_PHY_CONTROL                       (0x0728)
>> +#define EXYNOS5433_PMU_SPARE0                                  (0x0900)
>> +#define EXYNOS5433_ATLAS_CPU0_SYS_PWR_REG                      (0x1000)
>> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU0_CENTRAL_SYS_PWR_REG      (0x1008)
>> +#define EXYNOS5433_ATLAS_CPU1_SYS_PWR_REG                      (0x1010)
>> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU1_CENTRAL_SYS_PWR_REG      (0x1018)
>> +#define EXYNOS5433_ATLAS_CPU2_SYS_PWR_REG                      (0x1020)
>> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU2_CENTRAL_SYS_PWR_REG      (0x1028)
>> +#define EXYNOS5433_ATLAS_CPU3_SYS_PWR_REG                      (0x1030)
>> +#define EXYNOS5433_DIS_IRQ_ATLAS_CPU3_CENTRAL_SYS_PWR_REG      (0x1038)
>> +#define EXYNOS5433_APOLLO_CPU0_SYS_PWR_REG                     (0x1040)
>> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU0_CENTRAL_SYS_PWR_REG     (0x1048)
>> +#define EXYNOS5433_APOLLO_CPU1_SYS_PWR_REG                     (0x1050)
>> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU1_CENTRAL_SYS_PWR_REG     (0x1058)
>> +#define EXYNOS5433_APOLLO_CPU2_SYS_PWR_REG                     (0x1060)
>> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU2_CENTRAL_SYS_PWR_REG     (0x1068)
>> +#define EXYNOS5433_APOLLO_CPU3_SYS_PWR_REG                     (0x1070)
>> +#define EXYNOS5433_DIS_IRQ_APOLLO_CPU3_CENTRAL_SYS_PWR_REG     (0x1078)
>> +#define EXYNOS5433_ATLAS_NONCPU_SYS_PWR_REG                    (0x1080)
>> +#define EXYNOS5433_ATLAS_L2_SYS_PWR_REG                                (0x10C0)
>> +#define EXYNOS5433_APOLLO_L2_SYS_PWR_REG                       (0x10C4)
>> +#define EXYNOS5433_APOLLO_NONCPU_SYS_PWR_REG                   (0x1084)
>> +#define EXYNOS5433_A5IS_SYS_PWR_REG                            (0x10B0)
>> +#define EXYNOS5433_DIS_IRQ_A5IS_LOCAL_SYS_PWR_REG              (0x10B4)
>> +#define EXYNOS5433_DIS_IRQ_A5IS_CENTRAL_SYS_PWR_REG            (0x10B8)
>> +#define EXYNOS5433_CLKSTOP_CMU_TOP_SYS_PWR_REG                 (0x1100)
>> +#define EXYNOS5433_CLKRUN_CMU_TOP_SYS_PWR_REG                  (0x1104)
>> +#define EXYNOS5433_RESET_CMU_TOP_SYS_PWR_REG                   (0x110C)
>> +#define EXYNOS5433_RESET_CPUCLKSTOP_SYS_PWR_REG                        (0x111C)
>> +#define EXYNOS5433_CLKSTOP_CMU_MIF_SYS_PWR_REG                 (0x1120)
>> +#define EXYNOS5433_CLKRUN_CMU_MIF_SYS_PWR_REG                  (0x1124)
>> +#define EXYNOS5433_RESET_CMU_MIF_SYS_PWR_REG                   (0x112C)
>> +#define EXYNOS5433_DDRPHY_DLLLOCK_SYS_PWR_REG                  (0x1138)
>> +#define EXYNOS5433_DISABLE_PLL_CMU_TOP_SYS_PWR_REG             (0x1140)
>> +#define EXYNOS5433_DISABLE_PLL_AUD_PLL_SYS_PWR_REG             (0x1144)
>> +#define EXYNOS5433_DISABLE_PLL_CMU_MIF_SYS_PWR_REG             (0x1160)
>> +#define EXYNOS5433_TOP_BUS_SYS_PWR_REG                         (0x1180)
>> +#define EXYNOS5433_TOP_RETENTION_SYS_PWR_REG                   (0x1184)
>> +#define EXYNOS5433_TOP_PWR_SYS_PWR_REG                         (0x1188)
>> +#define EXYNOS5433_TOP_BUS_MIF_SYS_PWR_REG                     (0x1190)
>> +#define EXYNOS5433_TOP_RETENTION_MIF_SYS_PWR_REG               (0x1194)
>> +#define EXYNOS5433_TOP_PWR_MIF_SYS_PWR_REG                     (0x1198)
>> +#define EXYNOS5433_LOGIC_RESET_SYS_PWR_REG                     (0x11A0)
>> +#define EXYNOS5433_OSCCLK_GATE_SYS_PWR_REG                     (0x11A4)
>> +#define EXYNOS5433_SLEEP_RESET_SYS_PWR_REG                     (0x11A8)
>> +#define EXYNOS5433_LOGIC_RESET_MIF_SYS_PWR_REG                 (0x11B0)
>> +#define EXYNOS5433_OSCCLK_GATE_MIF_SYS_PWR_REG                 (0x11B4)
>> +#define EXYNOS5433_SLEEP_RESET_MIF_SYS_PWR_REG                 (0x11B8)
>> +#define EXYNOS5433_MEMORY_TOP_SYS_PWR_REG                      (0x11C0)
>> +#define EXYNOS5433_PAD_RETENTION_LPDDR3_SYS_PWR_REG            (0x1200)
>> +#define EXYNOS5433_PAD_RETENTION_JTAG_SYS_PWR_REG              (0x1208)
>> +#define EXYNOS5433_PAD_RETENTION_TOP_SYS_PWR_REG               (0x1220)
>> +#define EXYNOS5433_PAD_RETENTION_UART_SYS_PWR_REG              (0x1224)
>> +#define EXYNOS5433_PAD_RETENTION_EBIA_SYS_PWR_REG              (0x1230)
>> +#define EXYNOS5433_PAD_RETENTION_EBIB_SYS_PWR_REG              (0x1234)
>> +#define EXYNOS5433_PAD_RETENTION_SPI_SYS_PWR_REG               (0x1238)
>> +#define EXYNOS5433_PAD_RETENTION_MIF_SYS_PWR_REG               (0x123C)
>> +#define EXYNOS5433_PAD_ISOLATION_SYS_PWR_REG                   (0x1240)
>> +#define EXYNOS5433_PAD_RETENTION_USBXTI_SYS_PWR_REG            (0x1244)
>> +#define EXYNOS5433_PAD_RETENTION_BOOTLDO_SYS_PWR_REG           (0x1248)
>> +#define EXYNOS5433_PAD_ISOLATION_MIF_SYS_PWR_REG               (0x1250)
>> +#define EXYNOS5433_PAD_RETENTION_FSYSGENIO_SYS_PWR_REG         (0x1254)
>> +#define EXYNOS5433_PAD_ALV_SEL_SYS_PWR_REG                     (0x1260)
>> +#define EXYNOS5433_XXTI_SYS_PWR_REG                            (0x1284)
>> +#define EXYNOS5433_XXTI26_SYS_PWR_REG                          (0x1288)
>> +#define EXYNOS5433_EXT_REGULATOR_SYS_PWR_REG                   (0x12C0)
>> +#define EXYNOS5433_GPIO_MODE_SYS_PWR_REG                       (0x1300)
>> +#define EXYNOS5433_GPIO_MODE_FSYS0_SYS_PWR_REG                 (0x1304)
>> +#define EXYNOS5433_GPIO_MODE_MIF_SYS_PWR_REG                   (0x1320)
>> +#define EXYNOS5433_GPIO_MODE_AUD_SYS_PWR_REG                   (0x1340)
>> +#define EXYNOS5433_GSCL_SYS_PWR_REG                            (0x1400)
>> +#define EXYNOS5433_CAM0_SYS_PWR_REG                            (0x1404)
>> +#define EXYNOS5433_MSCL_SYS_PWR_REG                            (0x1408)
>> +#define EXYNOS5433_G3D_SYS_PWR_REG                             (0x140C)
>> +#define EXYNOS5433_DISP_SYS_PWR_REG                            (0x1410)
>> +#define EXYNOS5433_CAM1_SYS_PWR_REG                            (0x1414)
>> +#define EXYNOS5433_AUD_SYS_PWR_REG                             (0x1418)
>> +#define EXYNOS5433_FSYS_SYS_PWR_REG                            (0x141C)
>> +#define EXYNOS5433_BUS2_SYS_PWR_REG                            (0x1420)
>> +#define EXYNOS5433_G2D_SYS_PWR_REG                             (0x1424)
>> +#define EXYNOS5433_ISP0_SYS_PWR_REG                            (0x1428)
>> +#define EXYNOS5433_MFC_SYS_PWR_REG                             (0x1430)
>> +#define EXYNOS5433_HEVC_SYS_PWR_REG                            (0x1438)
>> +#define EXYNOS5433_RESET_SLEEP_FSYS_SYS_PWR_REG                        (0x15DC)
>> +#define EXYNOS5433_RESET_SLEEP_BUS2_SYS_PWR_REG                        (0x15E0)
>> +#define EXYNOS5433_ATLAS_CPU0_OPTION                           (0x2008)
>> +#define EXYNOS5433_CPU_OPTION(_nr)                             (EXYNOS5433_ATLAS_CPU0_OPTION + (_nr) * 0x80)
>> +#define EXYNOS5433_ATLAS_CPU0_DURATION0                                (0x2010)
>> +#define EXYNOS5433_CPU_DURATION(_nr)                           (EXYNOS5433_ATLAS_CPU0_DURATION0 + (_nr) * 0x80)
>> +#define EXYNOS5433_ATLAS_NONCPU_OPTION                         (0x2408)
>> +#define EXYNOS5433_APOLLO_NONCPU_OPTION                                (0x2428)
>> +#define EXYNOS5433_ATLAS_CPUSEQUENCER_OPTION                   (0x2488)
>> +#define EXYNOS5433_ATLAS_L2_OPTION                             (0x2608)
>> +#define EXYNOS5433_TOP_PWR_MIF_OPTION                          (0x2CC8)
>> +#define EXYNOS5433_TOP_PWR_OPTION                              (0x2C48)
>> +#define EXYNOS5433_PAD_RETENTION_LPDDR3_OPTION                 (0x3008)
>>  #define EXYNOS5433_PAD_RETENTION_AUD_OPTION                    (0x3028)
>>  #define EXYNOS5433_PAD_RETENTION_MMC2_OPTION                   (0x30C8)
>>  #define EXYNOS5433_PAD_RETENTION_TOP_OPTION                    (0x3108)
>> @@ -660,5 +763,50 @@
>>  #define EXYNOS5433_PAD_RETENTION_BOOTLDO_OPTION                        (0x3248)
>>  #define EXYNOS5433_PAD_RETENTION_UFS_OPTION                    (0x3268)
>>  #define EXYNOS5433_PAD_RETENTION_FSYSGENIO_OPTION              (0x32A8)
>> +#define EXYNOS5433_PS_HOLD_CONTROL                             (0x330C)
>> +#define EXYNOS5433_GSCL_OPTION                                 (0x4008)
>> +#define EXYNOS5433_CAM0_OPTION                                 (0x4028)
>> +#define EXYNOS5433_MSCL_OPTION                                 (0x4048)
>> +#define EXYNOS5433_G3D_OPTION                                  (0x4068)
>> +#define EXYNOS5433_DISP_OPTION                                 (0x4088)
>> +#define EXYNOS5433_AUD_OPTION                                  (0x40C8)
>> +#define EXYNOS5433_FSYS_OPTION                                 (0x40E8)
>> +#define EXYNOS5433_BUS2_OPTION                                 (0x4108)
>> +#define EXYNOS5433_G2D_OPTION                                  (0x4128)
>> +#define EXYNOS5433_ISP_OPTION                                  (0x4148)
>> +#define EXYNOS5433_MFC_OPTION                                  (0x4188)
>> +#define EXYNOS5433_HEVC_OPTION                                 (0x41C8)
>> +
>> +/* EXYNOS5433_PMU_SPARE0 */
>> +#define EXYNOS5433_EN_NONRET_RESET                             (1 << 0)
> 
> Use BIT(0) here and in other places.

OK.

[snip]

Thanks for the review.

-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

^ permalink raw reply

* [RFC PATCH 5/9] soc: samsung: pm: Add support for suspend-to-ram of Exynos5433
From: Chanwoo Choi @ 2018-01-11  5:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJKOXPdL7EbhaWkKCV7vMMnX2Z9vTr=r7UyDNx6E7ZLUikXy_Q@mail.gmail.com>

On 2018? 01? 09? 21:45, Krzysztof Kozlowski wrote:
> On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
>> This patch adds the specific exynos_pm_data instance for Exynos5433
>> in order to support the suspend-to-ram. Exynos5433 SoC need to write
>> the 'cpu_resume' poiter address and the specific magic number
>> for suspend mode.
>>
>> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
>> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
>> ---
>>  drivers/soc/samsung/exynos-pm.c | 38 ++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 38 insertions(+)
>>
>> diff --git a/drivers/soc/samsung/exynos-pm.c b/drivers/soc/samsung/exynos-pm.c
>> index 45d84bbe5e61..70d949ba5cab 100644
>> --- a/drivers/soc/samsung/exynos-pm.c
>> +++ b/drivers/soc/samsung/exynos-pm.c
>> @@ -12,6 +12,7 @@
>>  #include <linux/regulator/machine.h>
>>  #include <linux/syscore_ops.h>
>>  #include <linux/suspend.h>
>> +#include <linux/psci.h>
>>
>>  #include <asm/cpuidle.h>
>>  #include <asm/io.h>
>> @@ -123,7 +124,44 @@ static void exynos_pm_finish(void)
>>  #define exynos_pm_data_arm_ptr(data)   NULL
>>  #endif
>>
>> +static int exynos5433_pm_suspend(unsigned long unused)
>> +{
>> +       /*
>> +        * Exynos5433 uses PSCI v0.1 which provides the only one
>> +        * entry point (psci_ops.cpu_suspend) for both cpuidle and
>> +        * suspend-to-RAM. Also, PSCI v0.1 needs the specific 'power_state'
>> +        * parameter for the suspend mode. In order to enter suspend mode,
>> +        * Exynos5433 calls the 'psci_ops.cpu_suspend' with '0x3010000'
>> +        * power_state parameter.
>> +        *
>> +        * '0x3010000' means that both cluster and system are going to enter
>> +        * the power-down state as following:
>> +        * - [25:24] 0x3 : Indicate the cluster and system.
>> +        * - [16]    0x1 : Indicate power-down state.
> 
> Define them, it will be more readable (name of define given just as an example):
> 
> EXYNOS5433_PCSI_SUSPEND_SYSTEM (0x3 << 24)
> (or even split per system and cluser and use BIT() if this is real meaning)
> and probably reuse existing S5P_CENTRAL_LOWPWR_CFG

OK. I'll define it as the constant.

> 
>> +        */
>> +       return psci_ops.cpu_suspend(0x3010000, __pa_symbol(cpu_resume));
>> +}
>> +
>> +static int exynos5433_pm_suspend_enter(suspend_state_t state)
>> +{
>> +       if (!sysram_ns_base_addr)
>> +               return -EINVAL;
>> +
>> +       __raw_writel(virt_to_phys(cpu_resume), sysram_ns_base_addr + 0x8);
>> +       __raw_writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + 0xc);
> 
> Document them in Documentation/arm/Samsung/Bootloader-interface.txt.

OK.

> 
> Best regards,
> Krzysztof
> 
>> +
>> +       return cpu_suspend(0, exynos5433_pm_suspend);
>> +}
>> +
>> +const struct exynos_pm_data exynos5433_pm_data = {
>> +       .enter          = exynos5433_pm_suspend_enter,
>> +};
>> +
>>  static const struct of_device_id exynos_pm_of_device_ids[] = {
>> +       {
>> +               .compatible = "samsung,exynos5433-pmu",
>> +               .data = exynos_pm_data_arm_ptr(exynos5433_pm_data),
>> +       },
>>         { /*sentinel*/ },
>>  };
>>
>> --
>> 1.9.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> 


-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

^ permalink raw reply

* [RFC PATCH 4/9] soc: samsung: Add generic power-management driver for Exynos
From: Chanwoo Choi @ 2018-01-11  5:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJKOXPd5nZApbx-v2dZhuUy5ociUwNGHa_wAoz1b-XKbJy1nmg@mail.gmail.com>

Dear Krzysztof,

I'll try to consolidate the pm code for both arm and arm64.
So, drop this patch and then I'll start to move the code
from arch/arm/mach-exynos/* to drivers/soc/samsung/*.

But, I'm not sure it is possible to move all codes
to drivers/soc/samsung/*. I'll try it.

Best Regards,
Chanwoo Choi
Samsung Electronics

On 2018? 01? 09? 21:37, Krzysztof Kozlowski wrote:
> On Tue, Jan 9, 2018 at 8:59 AM, Chanwoo Choi <cw00.choi@samsung.com> wrote:
>> To enter suspend, Exynos SoC requires the some machine dependent procedures.
>> This patch introduces the generic power-management driver to support
>> those requirements and generic interface for power state management.
>>
>> Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
>> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
>> ---
>>  arch/arm/mach-exynos/common.h         |   1 -
>>  arch/arm/mach-exynos/exynos.c         |  23 +----
>>  drivers/soc/samsung/Makefile          |   2 +-
>>  drivers/soc/samsung/exynos-pm.c       | 176 ++++++++++++++++++++++++++++++++++
>>  include/linux/soc/samsung/exynos-pm.h |  21 ++++
>>  5 files changed, 199 insertions(+), 24 deletions(-)
>>  create mode 100644 drivers/soc/samsung/exynos-pm.c
>>  create mode 100644 include/linux/soc/samsung/exynos-pm.h
>>
>> diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
>> index afbc143a3d5d..ad482c0fc131 100644
>> --- a/arch/arm/mach-exynos/common.h
>> +++ b/arch/arm/mach-exynos/common.h
>> @@ -119,7 +119,6 @@ enum {
>>   * Magic values for bootloader indicating chosen low power mode.
>>   * See also Documentation/arm/Samsung/Bootloader-interface.txt
>>   */
>> -#define EXYNOS_SLEEP_MAGIC     0x00000bad
>>  #define EXYNOS_AFTR_MAGIC      0xfcba0d10
>>
>>  void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
>> diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
>> index fbd108ce8745..0d5265d175c4 100644
>> --- a/arch/arm/mach-exynos/exynos.c
>> +++ b/arch/arm/mach-exynos/exynos.c
>> @@ -12,6 +12,7 @@
>>  #include <linux/of_fdt.h>
> 
> of_address.h might be not needed anymore.
> 
>>  #include <linux/platform_device.h>
>>  #include <linux/irqchip.h>
>> +#include <linux/soc/samsung/exynos-pm.h>
>>  #include <linux/soc/samsung/exynos-regs-pmu.h>
>>
>>  #include <asm/cacheflush.h>
>> @@ -41,28 +42,6 @@
>>         .id                = -1,
>>  };
>>
>> -void __iomem *sysram_base_addr __ro_after_init;
>> -void __iomem *sysram_ns_base_addr __ro_after_init;
>> -
>> -void __init exynos_sysram_init(void)
>> -{
>> -       struct device_node *node;
>> -
>> -       for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") {
>> -               if (!of_device_is_available(node))
>> -                       continue;
>> -               sysram_base_addr = of_iomap(node, 0);
>> -               break;
>> -       }
>> -
>> -       for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") {
>> -               if (!of_device_is_available(node))
>> -                       continue;
>> -               sysram_ns_base_addr = of_iomap(node, 0);
>> -               break;
>> -       }
>> -}
>> -
>>  static void __init exynos_init_late(void)
>>  {
>>         if (of_machine_is_compatible("samsung,exynos5440"))
>> diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
>> index d2e637339a45..58ca5bdabf1f 100644
>> --- a/drivers/soc/samsung/Makefile
>> +++ b/drivers/soc/samsung/Makefile
>> @@ -1,5 +1,5 @@
>>  # SPDX-License-Identifier: GPL-2.0
>> -obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o
>> +obj-$(CONFIG_EXYNOS_PMU)       += exynos-pmu.o exynos-pm.o
>>
>>  obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)   += exynos3250-pmu.o exynos4-pmu.o \
>>                                         exynos5250-pmu.o exynos5420-pmu.o \
>> diff --git a/drivers/soc/samsung/exynos-pm.c b/drivers/soc/samsung/exynos-pm.c
>> new file mode 100644
>> index 000000000000..45d84bbe5e61
>> --- /dev/null
>> +++ b/drivers/soc/samsung/exynos-pm.c
>> @@ -0,0 +1,176 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +//
>> +// based on arch/arm/mach-exynos/suspend.c
>> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
>> +//
>> +// Exynos Power Management support driver
>> +
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_fdt.h>
>> +#include <linux/kernel.h>
>> +#include <linux/regulator/machine.h>
>> +#include <linux/syscore_ops.h>
>> +#include <linux/suspend.h>
>> +
>> +#include <asm/cpuidle.h>
>> +#include <asm/io.h>
>> +#include <asm/suspend.h>
>> +
>> +#include <linux/soc/samsung/exynos-pm.h>
>> +#include <linux/soc/samsung/exynos-pmu.h>
>> +
>> +/*
>> + * The struct exynos_pm_data contains the callbacks of
>> + * both struct platform_suspend_ops and syscore_ops.
>> + * This structure is listed according to the call order,
>> + * because the callback call order for the two structures is mixed.
>> + */
>> +struct exynos_pm_data {
>> +       int (*prepare)(void);                   /* for platform_suspend_ops */
>> +       int (*suspend)(void);                   /* for syscore_ops */
>> +       int (*enter)(suspend_state_t state);    /* for platform_suspend_ops */
>> +       void (*resume)(void);                   /* for syscore_ops */
>> +       void (*finish)(void);                   /* for platform_suspend_ops */
>> +};
>> +
>> +static struct platform_suspend_ops exynos_pm_suspend_ops;
>> +static struct syscore_ops exynos_pm_syscore_ops;
>> +static const struct exynos_pm_data *pm_data  __ro_after_init;
> 
> It is already const, so __initconst?
> 
>> +
>> +void __iomem *sysram_base_addr __ro_after_init;
>> +void __iomem *sysram_ns_base_addr __ro_after_init;
>> +
>> +static int exynos_pm_prepare(void)
>> +{
>> +       int ret;
>> +
>> +       /*
>> +        * REVISIT: It would be better if struct platform_suspend_ops
>> +        * .prepare handler get the suspend_state_t as a parameter to
>> +        * avoid hard-coding the suspend to mem state. It's safe to do
>> +        * it now only because the suspend_valid_only_mem function is
>> +        * used as the .valid callback used to check if a given state
>> +        * is supported by the platform anyways.
>> +        */
>> +       ret = regulator_suspend_prepare(PM_SUSPEND_MEM);
>> +       if (ret) {
>> +               pr_err("Failed to prepare regulators for suspend (%d)\n", ret);
>> +               return ret;
>> +       }
>> +
>> +       if (pm_data->prepare) {
>> +               ret = pm_data->prepare();
>> +               if (ret) {
>> +                       pr_err("Failed to prepare for suspend (%d)\n", ret);
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static int exynos_pm_suspend(void)
>> +{
>> +       if (pm_data->suspend)
>> +               return pm_data->suspend();
>> +
>> +       return 0;
>> +}
>> +
>> +static int exynos_pm_enter(suspend_state_t state)
>> +{
>> +       int ret;
>> +
>> +       exynos_sys_powerdown_conf(SYS_SLEEP);
>> +
>> +       ret = pm_data->enter(state);
>> +       if (ret) {
>> +               pr_err("Failed to enter sleep\n");
>> +               return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void exynos_pm_resume(void)
>> +{
>> +       exynos_sys_powerup_conf(SYS_SLEEP);
>> +
>> +       if (pm_data->resume)
>> +               pm_data->resume();
>> +}
>> +
>> +static void exynos_pm_finish(void)
>> +{
>> +       int ret;
>> +
>> +       ret = regulator_suspend_finish();
>> +       if (ret)
>> +               pr_warn("Failed to resume regulators from suspend (%d)\n", ret);
>> +
>> +       if (pm_data->finish)
>> +               pm_data->finish();
>> +}
>> +
>> +/*
>> + * Split the data between ARM architectures because it is relatively big
>> + * and useless on other arch.
>> + */
>> +#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
>> +#define exynos_pm_data_arm_ptr(data)   (&data)
>> +#else
>> +#define exynos_pm_data_arm_ptr(data)   NULL
>> +#endif
>> +
>> +static const struct of_device_id exynos_pm_of_device_ids[] = {
>> +       { /*sentinel*/ },
>> +};
>> +
>> +void __init exynos_sysram_init(void)
>> +{
>> +       struct device_node *np;
>> +
>> +       for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram") {
>> +               if (!of_device_is_available(np))
>> +                       continue;
>> +               sysram_base_addr = of_iomap(np, 0);
>> +               break;
>> +       }
>> +
>> +       for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram-ns") {
>> +               if (!of_device_is_available(np))
>> +                       continue;
>> +               sysram_ns_base_addr = of_iomap(np, 0);
>> +               break;
>> +       }
>> +}
>> +
>> +static int __init exynos_pm_init(void)
>> +{
>> +       const struct of_device_id *match;
>> +       struct device_node *np;
>> +
>> +       np = of_find_matching_node_and_match(NULL,
>> +                                       exynos_pm_of_device_ids, &match);
>> +       if (!np) {
>> +               pr_err("Failed to find PMU node for Exynos Power-Management\n");
>> +               return -ENODEV;
>> +       }
>> +       pm_data = (const struct exynos_pm_data *) match->data;
>> +
>> +       exynos_sysram_init();
>> +
>> +       exynos_pm_suspend_ops.valid     = suspend_valid_only_mem;
>> +       exynos_pm_suspend_ops.prepare   = exynos_pm_prepare;
>> +       exynos_pm_syscore_ops.suspend   = exynos_pm_suspend;
>> +       exynos_pm_suspend_ops.enter     = exynos_pm_enter;
>> +       exynos_pm_syscore_ops.resume    = exynos_pm_resume;
>> +       exynos_pm_suspend_ops.finish    = exynos_pm_finish;
>> +
>> +       register_syscore_ops(&exynos_pm_syscore_ops);
>> +       suspend_set_ops(&exynos_pm_suspend_ops);
>> +
>> +       return 0;
>> +}
>> +postcore_initcall(exynos_pm_init);
> 
> As I mentioned in cover letter, please move here first ARMv7 code. Now
> it looks like duplicating the existing code.
> 
>> diff --git a/include/linux/soc/samsung/exynos-pm.h b/include/linux/soc/samsung/exynos-pm.h
>> new file mode 100644
>> index 000000000000..b1afe95ed10c
>> --- /dev/null
>> +++ b/include/linux/soc/samsung/exynos-pm.h
>> @@ -0,0 +1,21 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +//
>> +// Copyright (c) 2018 Samsung Electronics Co., Ltd.
>> +//
>> +// Header for Exynos Power-Management support driver
> 
> Use header-style SPDX and comment.
> 
> Best regards,
> Krzysztof
> 
>> +
>> +#ifndef __LINUX_SOC_EXYNOS_PM_H
>> +#define __LINUX_SOC_EXYNOS_PM_H
>> +
>> +/*
>> + * Magic values for bootloader indicating chosen low power mode.
>> + * See also Documentation/arm/Samsung/Bootloader-interface.txt
>> + */
>> +#define EXYNOS_SLEEP_MAGIC     0x00000bad
>> +
>> +extern void __iomem *sysram_base_addr;
>> +extern void __iomem *sysram_ns_base_addr;
> 
> Since these are now global symbols, they need nice exynos prefix.
> Also, probably they should not be globally modifiable. Only
> exynos_sysram_init() should write there. Instead export a global
> accessor (get()) and rest should use that one.
> 
> Best regards,
> Krzysztof
> 
>> +
>> +extern void exynos_sysram_init(void);
>> +
>> +#endif /* __LINUX_SOC_EXYNOS_PMU_H */
>> --
>> 1.9.1
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> 
> 

^ permalink raw reply

* [PATCH 1/3] arm: spear600: Add missing interrupt-parent of rtc
From: Viresh Kumar @ 2018-01-11  5:58 UTC (permalink / raw)
  To: linux-arm-kernel

The interrupt-parent of rtc was missing, add it.

Fixes: 8113ba917dfa ("ARM: SPEAr: DT: Update device nodes")
Cc: stable at vger.kernel.org # v3.8+
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 arch/arm/boot/dts/spear600.dtsi | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/spear600.dtsi b/arch/arm/boot/dts/spear600.dtsi
index 6b32d20acc9f..00166eb9be86 100644
--- a/arch/arm/boot/dts/spear600.dtsi
+++ b/arch/arm/boot/dts/spear600.dtsi
@@ -194,6 +194,7 @@
 			rtc: rtc at fc900000 {
 				compatible = "st,spear600-rtc";
 				reg = <0xfc900000 0x1000>;
+				interrupt-parent = <&vic0>;
 				interrupts = <10>;
 				status = "disabled";
 			};
-- 
2.15.0.194.g9af6a3dea062

^ permalink raw reply related

* [PATCH 2/3] arm: spear13xx: Fix dmas cells
From: Viresh Kumar @ 2018-01-11  5:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <a062d9ed0d3365b578156a780202fa533e725374.1515650104.git.viresh.kumar@linaro.org>

The "dmas" cells for the designware DMA controller need to have only 3
properties apart from the phandle: request line, src master and
destination master. But the commit 6e8887f60f60 updated it incorrectly
while moving from platform code to DT. Fix it.

Cc: stable at vger.kernel.org # v3.10+
Fixes: 6e8887f60f60 ("ARM: SPEAr13xx: Pass generic DW DMAC platform data from DT")
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 Documentation/devicetree/bindings/dma/snps-dma.txt | 2 +-
 arch/arm/boot/dts/spear1340.dtsi                   | 4 ++--
 arch/arm/boot/dts/spear13xx.dtsi                   | 6 +++---
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/dma/snps-dma.txt b/Documentation/devicetree/bindings/dma/snps-dma.txt
index a122723907ac..99acc712f83a 100644
--- a/Documentation/devicetree/bindings/dma/snps-dma.txt
+++ b/Documentation/devicetree/bindings/dma/snps-dma.txt
@@ -64,6 +64,6 @@ described in the dma.txt file, using a four-cell specifier for each channel.
 		reg = <0xe0000000 0x1000>;
 		interrupts = <0 35 0x4>;
 		dmas = <&dmahost 12 0 1>,
-			<&dmahost 13 0 1 0>;
+			<&dmahost 13 1 0>;
 		dma-names = "rx", "rx";
 	};
diff --git a/arch/arm/boot/dts/spear1340.dtsi b/arch/arm/boot/dts/spear1340.dtsi
index 5f347054527d..d4dbc4098653 100644
--- a/arch/arm/boot/dts/spear1340.dtsi
+++ b/arch/arm/boot/dts/spear1340.dtsi
@@ -142,8 +142,8 @@
 				reg = <0xb4100000 0x1000>;
 				interrupts = <0 105 0x4>;
 				status = "disabled";
-				dmas = <&dwdma0 0x600 0 0 1>, /* 0xC << 11 */
-					<&dwdma0 0x680 0 1 0>; /* 0xD << 7 */
+				dmas = <&dwdma0 12 0 1>,
+					<&dwdma0 13 1 0>;
 				dma-names = "tx", "rx";
 			};
 
diff --git a/arch/arm/boot/dts/spear13xx.dtsi b/arch/arm/boot/dts/spear13xx.dtsi
index 17ea0abcdbd7..086b4b333249 100644
--- a/arch/arm/boot/dts/spear13xx.dtsi
+++ b/arch/arm/boot/dts/spear13xx.dtsi
@@ -100,7 +100,7 @@
 			reg = <0xb2800000 0x1000>;
 			interrupts = <0 29 0x4>;
 			status = "disabled";
-			dmas = <&dwdma0 0 0 0 0>;
+			dmas = <&dwdma0 0 0 0>;
 			dma-names = "data";
 		};
 
@@ -290,8 +290,8 @@
 				#size-cells = <0>;
 				interrupts = <0 31 0x4>;
 				status = "disabled";
-				dmas = <&dwdma0 0x2000 0 0 0>, /* 0x4 << 11 */
-					<&dwdma0 0x0280 0 0 0>;  /* 0x5 << 7 */
+				dmas = <&dwdma0 4 0 0>,
+					<&dwdma0 5 0 0>;
 				dma-names = "tx", "rx";
 			};
 
-- 
2.15.0.194.g9af6a3dea062

^ permalink raw reply related

* [PATCH 3/3] arm: spear13xx: Fix spics gpio controller's warning
From: Viresh Kumar @ 2018-01-11  5:58 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <a062d9ed0d3365b578156a780202fa533e725374.1515650104.git.viresh.kumar@linaro.org>

This fixes the following warning by also sending the flags argument for
gpio controllers:

Property 'cs-gpios', cell 6 is not a phandle reference in
/ahb/apb/spi at e0100000

Fixes: 8113ba917dfa ("ARM: SPEAr: DT: Update device nodes")
Cc: stable at vger.kernel.org # v3.8+
Reported-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
 arch/arm/boot/dts/spear1310-evb.dts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/spear1310-evb.dts b/arch/arm/boot/dts/spear1310-evb.dts
index 84101e4eebbf..0f5f379323a8 100644
--- a/arch/arm/boot/dts/spear1310-evb.dts
+++ b/arch/arm/boot/dts/spear1310-evb.dts
@@ -349,7 +349,7 @@
 			spi0: spi at e0100000 {
 				status = "okay";
 				num-cs = <3>;
-				cs-gpios = <&gpio1 7 0>, <&spics 0>, <&spics 1>;
+				cs-gpios = <&gpio1 7 0>, <&spics 0 0>, <&spics 1 0>;
 
 				stmpe610 at 0 {
 					compatible = "st,stmpe610";
-- 
2.15.0.194.g9af6a3dea062

^ permalink raw reply related

* [PATCH v2 1/4] dmaengine: xilinx_dma: populate dma caps properly
From: Vinod Koul @ 2018-01-11  6:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CY1PR02MB1692ED5378E474DB42A9CD7ADC100@CY1PR02MB1692.namprd02.prod.outlook.com>

On Tue, Jan 09, 2018 at 07:36:11AM +0000, Appana Durga Kedareswara Rao wrote:
> Hi,
> 
> 	Thanks for the review... 
> 
> >On Tue, Jan 09, 2018 at 04:48:10AM +0000, Appana Durga Kedareswara Rao
> >wrote:
> >> Hi,
> >>
> >> >On Mon, Jan 08, 2018 at 05:25:01PM +0000, Appana Durga Kedareswara
> >> >Rao
> >> >wrote:
> >> >> Hi,
> >> >>
> >> >> <Snip>
> >> >> >> >> +	xdev->common.dst_addr_widths = BIT(addr_width / 8);
> >> >> >> >> +	xdev->common.src_addr_widths = BIT(addr_width / 8);
> >> >> >> >
> >> >> >> >Do you not support trf of 1byte, 2 bytes, or 4 bytes wide transfers?
> >> >> >> >What is value of addr_width here typically? Usually controllers
> >> >> >> >can support different widths and this is a surprise that you
> >> >> >> >support only one value
> >> >> >>
> >> >> >> Controller supports address width of 32 and 64.
> >> >> >
> >> >> >Then this should have both 32 and 64 values here
> >> >>
> >> >> Address width is configurable parameter at the h/w level.
> >> >> Since this IP is a soft IP user can create a design with either
> >> >> 32-bit or 64-bit address configuration.
> >> >
> >> >and not both right?
> >>
> >> Yes not both at the same time...
> >> Axi dma controller can be configured for either 32-bit or 64-bit address...
> >
> >So my suspicion was correct.  I would suggest you to read up on the
> >documentation again. The src/dst_addr_widths has _nothing_ to do with 32/64
> >bit addresses used.
> >
> >It is the capability of the dma controller to do transfers with data width as 8bits,
> >16 bits, so on. iKey is "data width" and not address type.
> >This typically translates to DMA FIFO configuration of the controller!
> 
> Thanks for the detailed explanation... 

Welcome but I don't understand why you xilinx folks cant wrap your replies,
it is *very* hard to read on screens with 80chars

> I have gone through the spec again controller does supports 1 byte, 2
> byte, 4 byte up to 128 byte transfers.

I think you are talking about length and NOT width

> In order to do variable length transfers user needs to drive a valid value
> to the tkeep strobe signal at the h/w level.

bingo

> And user needs to configure the below parameters c_m_axis_mm2s_tdata_width
> or c_m_axis_s2mm_tdata_width With desired configuration at the h/w level.
> Controller supports data width of 8, 16, 32, 64, 128, 256, 512 and 1,024
> bits (i.e. c_m_axis_mm2s_tdata_width/ c_m_axis_s2mm_tdata_width parameters
> range)

holy cow, did you read that right. and 1024 bit width, how wide is your FIFO

I think you are off the rails here, please get your length and width
concepts right, they are NOT same and refer to different things and cannot be
used interchangably

> 
> At the s/w level currently we are getting c_m_axis_mm2s_tdata_width/ c_m_axis_s2mm_tdata_width
> Configuration as xlnx,datawidth property in the device-tree.
> 
> So proper values for the src/dst_addr width fields should be, datawidth property in bytes.
> Please correct me if I am wrong... 
> 
> Changes looks like below...
> Here width is in bytes based on the h/w configuration... 
> 
> --- a/drivers/dma/xilinx/xilinx_dma.c
> +++ b/drivers/dma/xilinx/xilinx_dma.c
> @@ -2411,6 +2411,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
>                 chan->direction = DMA_MEM_TO_DEV;
>                 chan->id = chan_id;
>                 chan->tdest = chan_id;
> +               xdev->common.directions = BIT(DMA_MEM_TO_DEV);
> +               xdev->common.src_addr_widths = BIT(width);
>  
>                 chan->ctrl_offset = XILINX_DMA_MM2S_CTRL_OFFSET;
>                 if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
> @@ -2428,6 +2430,8 @@ static int xilinx_dma_chan_probe(struct xilinx_dma_device *xdev,
>                 chan->direction = DMA_DEV_TO_MEM;
>                 chan->id = chan_id;
>                 chan->tdest = chan_id - xdev->nr_channels;
> +               xdev->common.directions |= BIT(DMA_DEV_TO_MEM);
> +               xdev->common.dst_addr_widths = BIT(width);
>  
>                 chan->ctrl_offset = XILINX_DMA_S2MM_CTRL_OFFSET;
>                 if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
> 
> 
> Regards,
> Kedar.
> 
> >
> >--
> >~Vinod
> --
> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
~Vinod

^ permalink raw reply

* [PATCH linux dev-4.10 0/6] Add support PECI and PECI hwmon drivers
From: Greg KH @ 2018-01-11  7:30 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e81095ff-d491-9977-25e6-7ba6d28d5e72@linux.intel.com>

On Wed, Jan 10, 2018 at 01:46:34PM -0800, Jae Hyun Yoo wrote:
> On 1/10/2018 12:27 PM, Greg KH wrote:
> > On Wed, Jan 10, 2018 at 11:30:05AM -0800, Jae Hyun Yoo wrote:
> > > On 1/10/2018 11:17 AM, Greg KH wrote:
> > > > On Wed, Jan 10, 2018 at 11:14:34AM -0800, Jae Hyun Yoo wrote:
> > > > > On 1/10/2018 2:17 AM, Greg KH wrote:
> > > > > > On Tue, Jan 09, 2018 at 02:31:20PM -0800, Jae Hyun Yoo wrote:
> > > > > > > From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
> > > > > > > 
> > > > > > > Hello,
> > > > > > > 
> > > > > > > This patch set provides support for PECI of AST2400/2500 which can give us PECI
> > > > > > > functionalities such as temperature monitoring, platform manageability,
> > > > > > > processor diagnostics and failure analysis. Also provides generic peci.h and
> > > > > > > peci_ioctl.h headers to provide compatibility to peci drivers that can be
> > > > > > > implemented later e.g. Nuvoton's BMC SoC family.
> > > > > > 
> > > > > > What is the "dev-4.10" in the subject for?  4.10 is really old and
> > > > > > obsolete :(
> > > > > > 
> > > > > > thanks,
> > > > > > 
> > > > > > greg k-h
> > > > > > 
> > > > > 
> > > > > I made this patch set on top of the v4.10 which OpenBmc project is currently
> > > > > using. I'll rebase this patch set onto the current kernel.org mainline.
> > > > 
> > > > What is "OpenBmc", and why are they using an obsolete and insecure
> > > > kernel for their project?  That seems like a very foolish thing to do...
> > > > 
> > > > thanks,
> > > > 
> > > > greg k-h
> > > > 
> > > 
> > > OpenBmc is an open source project to create a highly extensible framework
> > > for BMC (Board Management Controller) software for data-center computer
> > > systems:
> > > https://github.com/openbmc
> > > 
> > > Its current mainline is v4.10 but it is being kept upgrading so it will be
> > > upgraded to the latest stable or long-term version soon.
> > 
> > Why hasn't it been updated in the year since 4.10 was released?  That's
> > a _very_ long time to be running on a totally insecure kernel, and no
> > new development should ever be done on old kernels, that's even crazier
> > (as we can't go back in time and accept patches for new features to old
> > releases...)
> > 
> 
> Thanks for your pointing it out and I totally agree with you. Actually, we
> are preparing 4.13 update for now and an another update will be followed up.
> As I answered above, I'll rebase this patch set onto the latest kernel.org
> mainline. Sorry for my misunderstanding of upstream process.

4.13?  Why that kernel?  It too is obsolete and insecure and
unsupported.

What keeps you all from just always tracking the latest tree from Linus?
What is in your tree that is not upstream that requires you to have a
kernel tree at all?

And if you do have out-of-tree code, why not use a process that makes it
trivial to update the base kernel version so that you can keep up to
date very easily?  (hint, just using 'git' is not a good way to do
this...)

thanks,

greg k-h

^ permalink raw reply

* [PATCH 2/6] crypto: engine - Permit to enqueue all async requests
From: Fabien DESSENNE @ 2018-01-11  7:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <83fa00f2-6e93-d83f-fd9a-0048bd6d60ec@st.com>

(adding my tested by)


On 10/01/18 15:19, Fabien DESSENNE wrote:
> On 03/01/18 21:11, Corentin Labbe wrote:
>> The crypto engine could actually only enqueue hash and ablkcipher request.
>> This patch permit it to enqueue any type of crypto_async_request.
>>
>> Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>

Tested-by: Fabien Dessenne <fabien.dessenne@st.com>

>> ---
>>    crypto/crypto_engine.c  | 230 ++++++++++++++++++++++++------------------------
>>    include/crypto/engine.h |  59 +++++++------
>>    2 files changed, 148 insertions(+), 141 deletions(-)
>>
>> diff --git a/crypto/crypto_engine.c b/crypto/crypto_engine.c
>> index 61e7c4e02fd2..036270b61648 100644
>> --- a/crypto/crypto_engine.c
>> +++ b/crypto/crypto_engine.c
>> @@ -15,7 +15,6 @@
>>    #include <linux/err.h>
>>    #include <linux/delay.h>
>>    #include <crypto/engine.h>
>> -#include <crypto/internal/hash.h>
>>    #include <uapi/linux/sched/types.h>
>>    #include "internal.h"
>>    
>> @@ -34,11 +33,10 @@ static void crypto_pump_requests(struct crypto_engine *engine,
>>    				 bool in_kthread)
>>    {
>>    	struct crypto_async_request *async_req, *backlog;
>> -	struct ahash_request *hreq;
>> -	struct ablkcipher_request *breq;
>>    	unsigned long flags;
>>    	bool was_busy = false;
>> -	int ret, rtype;
>> +	int ret;
>> +	struct crypto_engine_reqctx *enginectx;
>>    
>>    	spin_lock_irqsave(&engine->queue_lock, flags);
>>    
>> @@ -94,7 +92,6 @@ static void crypto_pump_requests(struct crypto_engine *engine,
>>    
>>    	spin_unlock_irqrestore(&engine->queue_lock, flags);
>>    
>> -	rtype = crypto_tfm_alg_type(engine->cur_req->tfm);
>>    	/* Until here we get the request need to be encrypted successfully */
>>    	if (!was_busy && engine->prepare_crypt_hardware) {
>>    		ret = engine->prepare_crypt_hardware(engine);
>> @@ -104,57 +101,31 @@ static void crypto_pump_requests(struct crypto_engine *engine,
>>    		}
>>    	}
>>    
>> -	switch (rtype) {
>> -	case CRYPTO_ALG_TYPE_AHASH:
>> -		hreq = ahash_request_cast(engine->cur_req);
>> -		if (engine->prepare_hash_request) {
>> -			ret = engine->prepare_hash_request(engine, hreq);
>> -			if (ret) {
>> -				dev_err(engine->dev, "failed to prepare request: %d\n",
>> -					ret);
>> -				goto req_err;
>> -			}
>> -			engine->cur_req_prepared = true;
>> -		}
>> -		ret = engine->hash_one_request(engine, hreq);
>> -		if (ret) {
>> -			dev_err(engine->dev, "failed to hash one request from queue\n");
>> -			goto req_err;
>> -		}
>> -		return;
>> -	case CRYPTO_ALG_TYPE_ABLKCIPHER:
>> -		breq = ablkcipher_request_cast(engine->cur_req);
>> -		if (engine->prepare_cipher_request) {
>> -			ret = engine->prepare_cipher_request(engine, breq);
>> -			if (ret) {
>> -				dev_err(engine->dev, "failed to prepare request: %d\n",
>> -					ret);
>> -				goto req_err;
>> -			}
>> -			engine->cur_req_prepared = true;
>> -		}
>> -		ret = engine->cipher_one_request(engine, breq);
>> +	enginectx = crypto_tfm_ctx(async_req->tfm);
>> +
>> +	if (enginectx->op.prepare_request) {
>> +		ret = enginectx->op.prepare_request(engine, async_req);
>>    		if (ret) {
>> -			dev_err(engine->dev, "failed to cipher one request from queue\n");
>> +			dev_err(engine->dev, "failed to prepare request: %d\n",
>> +				ret);
>>    			goto req_err;
>>    		}
>> -		return;
>> -	default:
>> -		dev_err(engine->dev, "failed to prepare request of unknown type\n");
>> -		return;
>> +		engine->cur_req_prepared = true;
>> +	}
>> +	if (!enginectx->op.do_one_request) {
>> +		dev_err(engine->dev, "failed to do request\n");
>> +		ret = -EINVAL;
>> +		goto req_err;
>> +	}
>> +	ret = enginectx->op.do_one_request(engine, async_req);
>> +	if (ret) {
>> +		dev_err(engine->dev, "Failed to do one request from queue: %d\n", ret);
>> +		goto req_err;
>>    	}
>> +	return;
>>    
>>    req_err:
>> -	switch (rtype) {
>> -	case CRYPTO_ALG_TYPE_AHASH:
>> -		hreq = ahash_request_cast(engine->cur_req);
>> -		crypto_finalize_hash_request(engine, hreq, ret);
>> -		break;
>> -	case CRYPTO_ALG_TYPE_ABLKCIPHER:
>> -		breq = ablkcipher_request_cast(engine->cur_req);
>> -		crypto_finalize_cipher_request(engine, breq, ret);
>> -		break;
>> -	}
>> +	crypto_finalize_request(engine, async_req, ret);
>>    	return;
>>    
>>    out:
>> @@ -170,13 +141,12 @@ static void crypto_pump_work(struct kthread_work *work)
>>    }
>>    
>>    /**
>> - * crypto_transfer_cipher_request - transfer the new request into the
>> - * enginequeue
>> + * crypto_transfer_request - transfer the new request into the engine queue
>>     * @engine: the hardware engine
>>     * @req: the request need to be listed into the engine queue
>>     */
>> -int crypto_transfer_cipher_request(struct crypto_engine *engine,
>> -				   struct ablkcipher_request *req,
>> +static int crypto_transfer_request(struct crypto_engine *engine,
>> +				   struct crypto_async_request *req,
>>    				   bool need_pump)
>>    {
>>    	unsigned long flags;
>> @@ -189,7 +159,7 @@ int crypto_transfer_cipher_request(struct crypto_engine *engine,
>>    		return -ESHUTDOWN;
>>    	}
>>    
>> -	ret = ablkcipher_enqueue_request(&engine->queue, req);
>> +	ret = crypto_enqueue_request(&engine->queue, req);
>>    
>>    	if (!engine->busy && need_pump)
>>    		kthread_queue_work(engine->kworker, &engine->pump_requests);
>> @@ -197,85 +167,97 @@ int crypto_transfer_cipher_request(struct crypto_engine *engine,
>>    	spin_unlock_irqrestore(&engine->queue_lock, flags);
>>    	return ret;
>>    }
>> -EXPORT_SYMBOL_GPL(crypto_transfer_cipher_request);
>> +EXPORT_SYMBOL_GPL(crypto_transfer_request);
> Do not export this function which is a static one.
>
>>    
>>    /**
>> - * crypto_transfer_cipher_request_to_engine - transfer one request to list
>> + * crypto_transfer_request_to_engine - transfer one request to list
>>     * into the engine queue
>>     * @engine: the hardware engine
>>     * @req: the request need to be listed into the engine queue
>>     */
>> +static int crypto_transfer_request_to_engine(struct crypto_engine *engine,
>> +					     struct crypto_async_request *req)
>> +{
>> +	return crypto_transfer_request(engine, req, true);
>> +}
>> +
>> +/**
>> + * crypto_transfer_cipher_request_to_engine - transfer one ablkcipher_request
>> + * to list into the engine queue
>> + * @engine: the hardware engine
>> + * @req: the request need to be listed into the engine queue
>> + * TODO: Remove this function when skcipher conversion is finished
>> + */
>>    int crypto_transfer_cipher_request_to_engine(struct crypto_engine *engine,
>>    					     struct ablkcipher_request *req)
>>    {
>> -	return crypto_transfer_cipher_request(engine, req, true);
>> +	return crypto_transfer_request_to_engine(engine, &req->base);
>>    }
>>    EXPORT_SYMBOL_GPL(crypto_transfer_cipher_request_to_engine);
>>    
>>    /**
>> - * crypto_transfer_hash_request - transfer the new request into the
>> - * enginequeue
>> + * crypto_transfer_skcipher_request_to_engine - transfer one skcipher_request
>> + * to list into the engine queue
>>     * @engine: the hardware engine
>>     * @req: the request need to be listed into the engine queue
>>     */
>> -int crypto_transfer_hash_request(struct crypto_engine *engine,
>> -				 struct ahash_request *req, bool need_pump)
>> +int crypto_transfer_skcipher_request_to_engine(struct crypto_engine *engine,
>> +					       struct skcipher_request *req)
>>    {
>> -	unsigned long flags;
>> -	int ret;
>> -
>> -	spin_lock_irqsave(&engine->queue_lock, flags);
>> -
>> -	if (!engine->running) {
>> -		spin_unlock_irqrestore(&engine->queue_lock, flags);
>> -		return -ESHUTDOWN;
>> -	}
>> -
>> -	ret = ahash_enqueue_request(&engine->queue, req);
>> -
>> -	if (!engine->busy && need_pump)
>> -		kthread_queue_work(engine->kworker, &engine->pump_requests);
>> +	return crypto_transfer_request_to_engine(engine, &req->base);
>> +}
>> +EXPORT_SYMBOL_GPL(crypto_transfer_skcipher_request_to_engine);
>>    
>> -	spin_unlock_irqrestore(&engine->queue_lock, flags);
>> -	return ret;
>> +/**
>> + * crypto_transfer_akcipher_request_to_engine - transfer one akcipher_request
>> + * to list into the engine queue
>> + * @engine: the hardware engine
>> + * @req: the request need to be listed into the engine queue
>> + */
>> +int crypto_transfer_akcipher_request_to_engine(struct crypto_engine *engine,
>> +					       struct akcipher_request *req)
>> +{
>> +	return crypto_transfer_request_to_engine(engine, &req->base);
>>    }
>> -EXPORT_SYMBOL_GPL(crypto_transfer_hash_request);
>> +EXPORT_SYMBOL_GPL(crypto_transfer_akcipher_request_to_engine);
>>    
>>    /**
>> - * crypto_transfer_hash_request_to_engine - transfer one request to list
>> - * into the engine queue
>> + * crypto_transfer_hash_request_to_engine - transfer one ahash_request
>> + * to list into the engine queue
>>     * @engine: the hardware engine
>>     * @req: the request need to be listed into the engine queue
>>     */
>>    int crypto_transfer_hash_request_to_engine(struct crypto_engine *engine,
>>    					   struct ahash_request *req)
>>    {
>> -	return crypto_transfer_hash_request(engine, req, true);
>> +	return crypto_transfer_request_to_engine(engine, &req->base);
>>    }
>>    EXPORT_SYMBOL_GPL(crypto_transfer_hash_request_to_engine);
>>    
> Please add this EXPORTed function:
>
> crypto_transfer_aead_request_to_engine(struct crypto_engine *engine,
> struct aead_request *req)
>
>>    /**
>> - * crypto_finalize_cipher_request - finalize one request if the request is done
>> + * crypto_finalize_request - finalize one request if the request is done
>>     * @engine: the hardware engine
>>     * @req: the request need to be finalized
>>     * @err: error number
>>     */
>> -void crypto_finalize_cipher_request(struct crypto_engine *engine,
>> -				    struct ablkcipher_request *req, int err)
>> +void crypto_finalize_request(struct crypto_engine *engine,
> shall be static
>
>> +			     struct crypto_async_request *req, int err)
>>    {
>>    	unsigned long flags;
>>    	bool finalize_cur_req = false;
>>    	int ret;
>> +	struct crypto_engine_reqctx *enginectx;
>>    
>>    	spin_lock_irqsave(&engine->queue_lock, flags);
>> -	if (engine->cur_req == &req->base)
>> +	if (engine->cur_req == req)
>>    		finalize_cur_req = true;
>>    	spin_unlock_irqrestore(&engine->queue_lock, flags);
>>    
>>    	if (finalize_cur_req) {
>> +		enginectx = crypto_tfm_ctx(req->tfm);
>>    		if (engine->cur_req_prepared &&
>> -		    engine->unprepare_cipher_request) {
>> -			ret = engine->unprepare_cipher_request(engine, req);
>> +		    enginectx->op.unprepare_request) {
>> +			ret = enginectx->op.unprepare_request(engine, req);
>>    			if (ret)
>>    				dev_err(engine->dev, "failed to unprepare request\n");
>>    		}
>> @@ -285,46 +267,64 @@ void crypto_finalize_cipher_request(struct crypto_engine *engine,
>>    		spin_unlock_irqrestore(&engine->queue_lock, flags);
>>    	}
>>    
>> -	req->base.complete(&req->base, err);
>> +	req->complete(req, err);
>>    
>>    	kthread_queue_work(engine->kworker, &engine->pump_requests);
>>    }
>> -EXPORT_SYMBOL_GPL(crypto_finalize_cipher_request);
>>    
>>    /**
>> - * crypto_finalize_hash_request - finalize one request if the request is done
>> + * crypto_finalize_cipher_request - finalize one ablkcipher_request if
>> + * the request is done
>>     * @engine: the hardware engine
>>     * @req: the request need to be finalized
>>     * @err: error number
>>     */
>> -void crypto_finalize_hash_request(struct crypto_engine *engine,
>> -				  struct ahash_request *req, int err)
>> +void crypto_finalize_cipher_request(struct crypto_engine *engine,
>> +				    struct ablkcipher_request *req, int err)
>>    {
>> -	unsigned long flags;
>> -	bool finalize_cur_req = false;
>> -	int ret;
>> -
>> -	spin_lock_irqsave(&engine->queue_lock, flags);
>> -	if (engine->cur_req == &req->base)
>> -		finalize_cur_req = true;
>> -	spin_unlock_irqrestore(&engine->queue_lock, flags);
>> +	return crypto_finalize_request(engine, &req->base, err);
>> +}
>> +EXPORT_SYMBOL_GPL(crypto_finalize_cipher_request);
>>    
>> -	if (finalize_cur_req) {
>> -		if (engine->cur_req_prepared &&
>> -		    engine->unprepare_hash_request) {
>> -			ret = engine->unprepare_hash_request(engine, req);
>> -			if (ret)
>> -				dev_err(engine->dev, "failed to unprepare request\n");
>> -		}
>> -		spin_lock_irqsave(&engine->queue_lock, flags);
>> -		engine->cur_req = NULL;
>> -		engine->cur_req_prepared = false;
>> -		spin_unlock_irqrestore(&engine->queue_lock, flags);
>> -	}
>> +/**
>> + * crypto_finalize_skcipher_request - finalize one skcipher_request if
>> + * the request is done
>> + * @engine: the hardware engine
>> + * @req: the request need to be finalized
>> + * @err: error number
>> + */
>> +void crypto_finalize_skcipher_request(struct crypto_engine *engine,
>> +				      struct skcipher_request *req, int err)
>> +{
>> +	return crypto_finalize_request(engine, &req->base, err);
>> +}
>> +EXPORT_SYMBOL_GPL(crypto_finalize_skcipher_request);
>>    
>> -	req->base.complete(&req->base, err);
>> +/**
>> + * crypto_finalize_akcipher_request - finalize one akcipher_request if
>> + * the request is done
>> + * @engine: the hardware engine
>> + * @req: the request need to be finalized
>> + * @err: error number
>> + */
>> +void crypto_finalize_akcipher_request(struct crypto_engine *engine,
>> +				      struct akcipher_request *req, int err)
>> +{
>> +	return crypto_finalize_request(engine, &req->base, err);
>> +}
>> +EXPORT_SYMBOL_GPL(crypto_finalize_akcipher_request);
>>    
>> -	kthread_queue_work(engine->kworker, &engine->pump_requests);
>> +/**
>> + * crypto_finalize_hash_request - finalize one ahash_request if
>> + * the request is done
>> + * @engine: the hardware engine
>> + * @req: the request need to be finalized
>> + * @err: error number
>> + */
>> +void crypto_finalize_hash_request(struct crypto_engine *engine,
>> +				  struct ahash_request *req, int err)
>> +{
>> +	return crypto_finalize_request(engine, &req->base, err);
>>    }
>>    EXPORT_SYMBOL_GPL(crypto_finalize_hash_request);
> Add
> crypto_finalize_aead_request(struct crypto_engine *engine, struct
> aead_request *req, int err)
>
>>    
>> diff --git a/include/crypto/engine.h b/include/crypto/engine.h
>> index dd04c1699b51..1ea7cbe92eaf 100644
>> --- a/include/crypto/engine.h
>> +++ b/include/crypto/engine.h
>> @@ -17,7 +17,9 @@
>>    #include <linux/kernel.h>
>>    #include <linux/kthread.h>
>>    #include <crypto/algapi.h>
>> +#include <crypto/akcipher.h>
>>    #include <crypto/hash.h>
>> +#include <crypto/skcipher.h>
>>    
>>    #define ENGINE_NAME_LEN	30
>>    /*
>> @@ -37,12 +39,6 @@
>>     * @unprepare_crypt_hardware: there are currently no more requests on the
>>     * queue so the subsystem notifies the driver that it may relax the
>>     * hardware by issuing this call
>> - * @prepare_cipher_request: do some prepare if need before handle the current request
>> - * @unprepare_cipher_request: undo any work done by prepare_cipher_request()
>> - * @cipher_one_request: do encryption for current request
>> - * @prepare_hash_request: do some prepare if need before handle the current request
>> - * @unprepare_hash_request: undo any work done by prepare_hash_request()
>> - * @hash_one_request: do hash for current request
>>     * @kworker: kthread worker struct for request pump
>>     * @pump_requests: work struct for scheduling work to the request pump
>>     * @priv_data: the engine private data
>> @@ -65,19 +61,6 @@ struct crypto_engine {
>>    	int (*prepare_crypt_hardware)(struct crypto_engine *engine);
>>    	int (*unprepare_crypt_hardware)(struct crypto_engine *engine);
>>    
>> -	int (*prepare_cipher_request)(struct crypto_engine *engine,
>> -				      struct ablkcipher_request *req);
>> -	int (*unprepare_cipher_request)(struct crypto_engine *engine,
>> -					struct ablkcipher_request *req);
>> -	int (*prepare_hash_request)(struct crypto_engine *engine,
>> -				    struct ahash_request *req);
>> -	int (*unprepare_hash_request)(struct crypto_engine *engine,
>> -				      struct ahash_request *req);
>> -	int (*cipher_one_request)(struct crypto_engine *engine,
>> -				  struct ablkcipher_request *req);
>> -	int (*hash_one_request)(struct crypto_engine *engine,
>> -				struct ahash_request *req);
>> -
>>    	struct kthread_worker           *kworker;
>>    	struct kthread_work             pump_requests;
>>    
>> @@ -85,19 +68,43 @@ struct crypto_engine {
>>    	struct crypto_async_request	*cur_req;
>>    };
>>    
>> -int crypto_transfer_cipher_request(struct crypto_engine *engine,
>> -				   struct ablkcipher_request *req,
>> -				   bool need_pump);
>> +/*
>> + * struct crypto_engine_op - crypto hardware engine operations
>> + * @prepare__request: do some prepare if need before handle the current request
>> + * @unprepare_request: undo any work done by prepare_request()
>> + * @do_one_request: do encryption for current request
>> + */
>> +struct crypto_engine_op {
>> +	int (*prepare_request)(struct crypto_engine *engine,
>> +			       void *areq);
>> +	int (*unprepare_request)(struct crypto_engine *engine,
>> +				 void *areq);
>> +	int (*do_one_request)(struct crypto_engine *engine,
>> +			      void *areq);
>> +};
>> +
>> +struct crypto_engine_reqctx {
>> +	struct crypto_engine_op op;
>> +};
>> +
>> +int crypto_transfer_akcipher_request_to_engine(struct crypto_engine *engine,
>> +					       struct akcipher_request *req);
>>    int crypto_transfer_cipher_request_to_engine(struct crypto_engine *engine,
>> -					     struct ablkcipher_request *req);
>> -int crypto_transfer_hash_request(struct crypto_engine *engine,
>> -				 struct ahash_request *req, bool need_pump);
>> +				      struct ablkcipher_request *req);
>>    int crypto_transfer_hash_request_to_engine(struct crypto_engine *engine,
>> -					   struct ahash_request *req);
>> +					       struct ahash_request *req);
>> +int crypto_transfer_skcipher_request_to_engine(struct crypto_engine *engine,
>> +					       struct skcipher_request *req);
> + transfer_aead
>
>> +void crypto_finalize_request(struct crypto_engine *engine,
>> +			     struct crypto_async_request *req, int err);
> static (+move to? .c file?)
>
>> +void crypto_finalize_akcipher_request(struct crypto_engine *engine,
>> +				      struct akcipher_request *req, int err);
>>    void crypto_finalize_cipher_request(struct crypto_engine *engine,
>>    				    struct ablkcipher_request *req, int err);
>>    void crypto_finalize_hash_request(struct crypto_engine *engine,
>>    				  struct ahash_request *req, int err);
>> +void crypto_finalize_skcipher_request(struct crypto_engine *engine,
>> +				      struct skcipher_request *req, int err);
> + finalize_aead
>
>>    int crypto_engine_start(struct crypto_engine *engine);
>>    int crypto_engine_stop(struct crypto_engine *engine);
>>    struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt);

^ permalink raw reply

* [PATCH 5/6] crypto: stm32-hash: convert to the new crypto engine API
From: Fabien DESSENNE @ 2018-01-11  7:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <17d0497d-5c13-f93e-4249-8adff5e71b19@st.com>

(adding my tested my)


On 10/01/18 15:24, Fabien DESSENNE wrote:
>
> On 03/01/18 21:11, Corentin Labbe wrote:
>> This patch convert the stm32-hash driver to the new crypto engine API.
>>
>> Signed-off-by: Corentin Labbe <clabbe.montjoie@gmail.com>

Tested-by: Fabien Dessenne <fabien.dessenne@st.com>

>> ---
>>    drivers/crypto/stm32/stm32-hash.c | 18 +++++++++++++-----
>>    1 file changed, 13 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/crypto/stm32/stm32-hash.c b/drivers/crypto/stm32/stm32-hash.c
>> index 4ca4a264a833..9790c2c936c7 100644
>> --- a/drivers/crypto/stm32/stm32-hash.c
>> +++ b/drivers/crypto/stm32/stm32-hash.c
>> @@ -122,6 +122,7 @@ enum stm32_hash_data_format {
>>    #define HASH_DMA_THRESHOLD		50
>>    
>>    struct stm32_hash_ctx {
>> +	struct crypto_engine_reqctx enginectx;
>>    	struct stm32_hash_dev	*hdev;
>>    	unsigned long		flags;
>>    
>> @@ -828,6 +829,11 @@ static int stm32_hash_hw_init(struct stm32_hash_dev *hdev,
>>    	return 0;
>>    }
>>    
>> +static int stm32_hash_one_request(struct crypto_engine *engine,
>> +				  void *areq);
> merge these two lines in a single one
>
>> +static int stm32_hash_prepare_req(struct crypto_engine *engine,
>> +				  void *areq);
> merge these two lines in a single one
>
>> +
>>    static int stm32_hash_handle_queue(struct stm32_hash_dev *hdev,
>>    				   struct ahash_request *req)
>>    {
>> @@ -835,8 +841,9 @@ static int stm32_hash_handle_queue(struct stm32_hash_dev *hdev,
>>    }
>>    
>>    static int stm32_hash_prepare_req(struct crypto_engine *engine,
>> -				  struct ahash_request *req)
>> +				  void *areq)
> merge these two lines in a single one
>
>>    {
>> +	struct ahash_request *req = container_of(areq, struct ahash_request, base);
>   > 80 characters (CHECKPATCH)
>
>>    	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
>>    	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
>>    	struct stm32_hash_request_ctx *rctx;
>> @@ -855,8 +862,9 @@ static int stm32_hash_prepare_req(struct crypto_engine *engine,
>>    }
>>    
>>    static int stm32_hash_one_request(struct crypto_engine *engine,
>> -				  struct ahash_request *req)
>> +				  void *areq)
> merge these two lines in a single one
>
>>    {
>> +	struct ahash_request *req = container_of(areq, struct ahash_request, base);
>   > 80 characters (CHECKPATCH)
>
>>    	struct stm32_hash_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req));
>>    	struct stm32_hash_dev *hdev = stm32_hash_find_dev(ctx);
>>    	struct stm32_hash_request_ctx *rctx;
>> @@ -1033,6 +1041,9 @@ static int stm32_hash_cra_init_algs(struct crypto_tfm *tfm,
>>    	if (algs_hmac_name)
>>    		ctx->flags |= HASH_FLAGS_HMAC;
>>    
>> +	ctx->enginectx.op.do_one_request = stm32_hash_one_request;
>> +	ctx->enginectx.op.prepare_request = stm32_hash_prepare_req;
>> +	ctx->enginectx.op.unprepare_request = NULL;
>>    	return 0;
>>    }
>>    
>> @@ -1493,9 +1504,6 @@ static int stm32_hash_probe(struct platform_device *pdev)
>>    		goto err_engine;
>>    	}
>>    
>> -	hdev->engine->prepare_hash_request = stm32_hash_prepare_req;
>> -	hdev->engine->hash_one_request = stm32_hash_one_request;
>> -
>>    	ret = crypto_engine_start(hdev->engine);
>>    	if (ret)
>>    		goto err_engine_start;

^ permalink raw reply


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