Devicetree
 help / color / mirror / Atom feed
* [PATCH 1/2] arm64: dts: stratix10: Fix SPI nodes for Stratix10
From: thor.thayer @ 2018-05-29 18:13 UTC (permalink / raw)
  To: dinguyen
  Cc: robh+dt, mark.rutland, catalin.marinas, will.deacon, devicetree,
	linux-arm-kernel, Thor Thayer, stable

From: Thor Thayer <thor.thayer@linux.intel.com>

Remove the unused bus-num node and change num-chipselect
to num-cs to match SPI bindings.

Signed-off-by: Thor Thayer <thor.thayer@linux.intel.com>
Fixes: 78cd6a9d8e154 ("arm64: dts: Add base stratix 10 dtsi")
Cc: stable@vger.kernel.org
---
 arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
index d8c94d5ff4b4..47fa4b450324 100644
--- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
+++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
@@ -315,8 +315,7 @@
 			interrupts = <0 99 4>;
 			resets = <&rst SPIM0_RESET>;
 			reg-io-width = <4>;
-			num-chipselect = <4>;
-			bus-num = <0>;
+			num-cs = <4>;
 			status = "disabled";
 		};
 
@@ -328,8 +327,7 @@
 			interrupts = <0 100 4>;
 			resets = <&rst SPIM1_RESET>;
 			reg-io-width = <4>;
-			num-chipselect = <4>;
-			bus-num = <0>;
+			num-cs = <4>;
 			status = "disabled";
 		};
 
-- 
2.7.4

^ permalink raw reply related

* [PATCH 2/2] arm64: dts: stratix10: Add SPI clocks for Stratix10
From: thor.thayer @ 2018-05-29 18:13 UTC (permalink / raw)
  To: dinguyen
  Cc: mark.rutland, devicetree, Thor Thayer, catalin.marinas,
	will.deacon, robh+dt, linux-arm-kernel
In-Reply-To: <1527617612-2205-1-git-send-email-thor.thayer@linux.intel.com>

From: Thor Thayer <thor.thayer@linux.intel.com>

Add the SPI clocks to the Stratix10 device tree.

Signed-off-by: Thor Thayer <thor.thayer@linux.intel.com>
---
 arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
index 47fa4b450324..34658f135daf 100644
--- a/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
+++ b/arch/arm64/boot/dts/altera/socfpga_stratix10.dtsi
@@ -316,6 +316,8 @@
 			resets = <&rst SPIM0_RESET>;
 			reg-io-width = <4>;
 			num-cs = <4>;
+			clocks = <&clkmgr STRATIX10_L4_MAIN_CLK>,
+				 <&clkmgr STRATIX10_SPI_M_CLK>;
 			status = "disabled";
 		};
 
@@ -328,6 +330,8 @@
 			resets = <&rst SPIM1_RESET>;
 			reg-io-width = <4>;
 			num-cs = <4>;
+			clocks = <&clkmgr STRATIX10_L4_MAIN_CLK>,
+				 <&clkmgr STRATIX10_SPI_M_CLK>;
 			status = "disabled";
 		};
 
-- 
2.7.4

^ permalink raw reply related

* Re: [PATCH v3 4/5] clocksource: add driver for i.MX EPIT timer
From: Fabio Estevam @ 2018-05-29 18:16 UTC (permalink / raw)
  To: Clément Péron
  Cc: open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Rob Herring, Colin Didier, Sascha Hauer, Clément Peron,
	Vladimir Zapolskiy, Pengutronix Kernel Team, Fabio Estevam,
	linux-clk,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	NXP Linux Team
In-Reply-To: <20180529170436.22711-5-peron.clem@gmail.com>

On Tue, May 29, 2018 at 2:04 PM, Clément Péron <peron.clem@gmail.com> wrote:

> +static int __init epit_timer_init(struct device_node *np)
> +{
> +       struct epit_timer *epittm;
> +       struct clk *clk_ipg;
> +       int ret;
> +
> +       epittm = kzalloc(sizeof(*epittm), GFP_KERNEL);
> +       if (!epittm)
> +               return -ENOMEM;
> +
> +       epittm->base = of_iomap(np, 0);
> +       if (!epittm->base) {
> +               ret = -ENXIO;
> +               goto out_kfree;
> +       }
> +
> +       epittm->irq = irq_of_parse_and_map(np, 0);
> +       if (!epittm->irq) {
> +               ret = -EINVAL;
> +               goto out_iounmap;
> +       }
> +
> +       clk_ipg = of_clk_get_by_name(np, "ipg");
> +       if (IS_ERR(clk_ipg)) {
> +               pr_err("i.MX EPIT: unable to get clk_ipg\n");
> +               ret = PTR_ERR(clk_ipg);
> +               goto out_iounmap;
> +       }
> +
> +       ret = clk_prepare_enable(clk_ipg);
> +       if (ret) {
> +               pr_err("i.MX EPIT: unable to prepare+enable clk_ipg\n");
> +               goto out_clk_ipg_disable;

This should be: goto out_iounmap;

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 3/3] thermal: broadcom: Add Stingray thermal driver
From: kbuild test robot @ 2018-05-29 18:35 UTC (permalink / raw)
  Cc: kbuild-all, Zhang Rui, Eduardo Valentin, Rob Herring,
	Mark Rutland, devicetree, linux-kernel, bcm-kernel-feedback-list,
	Pramod Kumar, Srinath Mannam
In-Reply-To: <1527486084-4636-4-git-send-email-srinath.mannam@broadcom.com>

Hi Pramod,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on soc-thermal/next]
[also build test WARNING on v4.17-rc7 next-20180529]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Srinath-Mannam/Stingray-thermal-driver-support/20180529-145243
base:   https://git.kernel.org/pub/scm/linux/kernel/git/evalenti/linux-soc-thermal.git next
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

>> drivers/thermal/broadcom/sr-thermal.c:101:26: sparse: incorrect type in assignment (different address spaces) @@    expected void [noderef] <asn:2>*regs @@    got sn:2>*regs @@
   drivers/thermal/broadcom/sr-thermal.c:101:26:    expected void [noderef] <asn:2>*regs
   drivers/thermal/broadcom/sr-thermal.c:101:26:    got void *

vim +101 drivers/thermal/broadcom/sr-thermal.c

    88	
    89	static int sr_thermal_probe(struct platform_device *pdev)
    90	{
    91		struct device *dev = &pdev->dev;
    92		struct sr_thermal *sr_thermal;
    93		struct resource *res;
    94	
    95		sr_thermal = devm_kzalloc(dev, sizeof(*sr_thermal), GFP_KERNEL);
    96		if (!sr_thermal)
    97			return -ENOMEM;
    98		sr_thermal->dev = dev;
    99	
   100		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 > 101		sr_thermal->regs = devm_memremap(&pdev->dev, res->start,
   102						 resource_size(res), MEMREMAP_WB);
   103		if (IS_ERR(sr_thermal->regs)) {
   104			dev_err(dev, "failed to get io address\n");
   105			return PTR_ERR(sr_thermal->regs);
   106		}
   107	
   108		/* initialize tmon value to 0 */
   109		writel(0, sr_thermal->regs);
   110		sr_thermal->crit_temp = TMON_CRIT_TEMP;
   111	
   112		sr_thermal->tz = thermal_zone_device_register(dev_name(dev), 1, 1,
   113								 sr_thermal,
   114								 &sr_thermal_ops,
   115								 NULL, 1000, 1000);
   116		if (IS_ERR(sr_thermal->tz))
   117			return PTR_ERR(sr_thermal->tz);
   118	
   119		platform_set_drvdata(pdev, sr_thermal);
   120	
   121		return 0;
   122	}
   123	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply

* Re: [patch v25 4/4] Documentation: jtag: Add ABI documentation
From: Randy Dunlap @ 2018-05-29 18:54 UTC (permalink / raw)
  To: Oleksandr Shamray, gregkh, arnd
  Cc: system-sw-low-level, devicetree, jiri, vadimp, linux-api, openbmc,
	linux-kernel, openocd-devel-owner, robh+dt, joel, linux-serial,
	tklauser, mchehab, davem, linux-arm-kernel
In-Reply-To: <1527605618-15705-5-git-send-email-oleksandrs@mellanox.com>

On 05/29/2018 07:53 AM, Oleksandr Shamray wrote:
> Added document that describe the ABI for JTAG class drivrer

Sorry, there are still a few typos.  Please see below.

> ---
>  Documentation/ABI/testing/gpio-cdev |    1 -
>  Documentation/ABI/testing/jtag-dev  |   23 +++++++
>  Documentation/jtag/overview         |   27 +++++++++
>  Documentation/jtag/transactions     |  109 +++++++++++++++++++++++++++++++++++
>  MAINTAINERS                         |    1 +
>  5 files changed, 160 insertions(+), 1 deletions(-)
>  create mode 100644 Documentation/ABI/testing/jtag-dev
>  create mode 100644 Documentation/jtag/overview
>  create mode 100644 Documentation/jtag/transactions
> 

> diff --git a/Documentation/jtag/overview b/Documentation/jtag/overview
> new file mode 100644
> index 0000000..f179095
> --- /dev/null
> +++ b/Documentation/jtag/overview
> @@ -0,0 +1,27 @@
> +Linux kernel JTAG support
> +=========================
> +
> +JTAG is an industry standard for verifying hardware.JTAG provides access to

needs space:
                                              hardware. JTAG provides

> +many logic signals of a complex integrated circuit, including the device pins.
> +
> +A JTAG interface is a special interface added to a chip.
> +Depending on the version of JTAG, two, four, or five pins are added.
> +
> +The connector pins are:
> +	TDI (Test Data In)
> +	TDO (Test Data Out)
> +	TCK (Test Clock)
> +	TMS (Test Mode Select)
> +	TRST (Test Reset) optional
> +
> +JTAG interface is designed to have two parts - basic core driver and
> +hardware specific driver. The basic driver introduces a general interface
> +which is not dependent of specific hardware. It provides communication
> +between user space and hardware specific driver.
> +Each JTAG device is represented as a char device from (jtag0, jtag1, ...).
> +Access to a JTAG device is performed through IOCTL calls.
> +
> +Call flow example:
> +User: open  -> /dev/jatgX
> +User: ioctl -> /dev/jtagX -> JTAG core driver -> JTAG hardware specific driver
> +User: close -> /dev/jatgX

> diff --git a/Documentation/jtag/transactions b/Documentation/jtag/transactions
> new file mode 100644
> index 0000000..c5176a7
> --- /dev/null
> +++ b/Documentation/jtag/transactions
> @@ -0,0 +1,109 @@
> +The JTAG API
> +=============
> +
> +JTAG master devices can be accessed through a character misc-device.
> +Each JTAG master interface can be accessed by using /dev/jtagN.
> +
> +JTAG system calls set:
> +- SIR (Scan Instruction Register, IEEE 1149.1 Instruction Register scan);
> +- SDR (Scan Data Register, IEEE 1149.1 Data Register scan);
> +- RUNTEST (Forces the IEEE 1149.1 bus to a run state for a specified
> +number of clocks.
> +
> +open(), close()
> +-------
> +open() opens JTAG device.
> +
> +Open/Close  device:
> +- jtag_fd = open("/dev/jtag0", O_RDWR);
> +- close(jtag_fd);
> +
> +ioctl()
> +-------
> +All access operations to JTAG devices are erformed through ioctl interface.

                                             performed

> +The IOCTL interface supports these requests:
> +	JTAG_IOCRUNTEST - Force JTAG state machine to RUN_TEST/IDLE state
> +	JTAG_SIOCFREQ - Set JTAG TCK frequency
> +	JTAG_GIOCFREQ - Get JTAG TCK frequency
> +	JTAG_IOCXFER - send JTAG data Xfer
> +	JTAG_GIOCSTATUS - get current JTAG TAP status
> +	JTAG_SIOCMODE - set JTAG mode flags.



-- 
~Randy

^ permalink raw reply

* [PATCH 0/2] Add support for Xilinx CSI2 Receiver Subsystem
From: Vishal Sagar @ 2018-05-29 18:54 UTC (permalink / raw)
  To: hyun.kwon, laurent.pinchart, michal.simek, linux-media,
	devicetree
  Cc: sakari.ailus, hans.verkuil, mchehab, robh+dt, mark.rutland,
	dineshk, linux-arm-kernel, linux-kernel, Vishal Sagar

Xilinx MIPI CSI-2 Receiver Subsystem
------------------------------------

The Xilinx MIPI CSI-2 Receiver Subsystem Soft IP consists of a DPHY which
gets the data, an optional I2C, a CSI-2 Receiver which parses the data and
converts it into AXIS data.
This stream output maybe connected to a Xilinx Video Format Bridge.
The maximum number of lanes supported is fixed in the design.
The number of active lanes can be programmed.
For e.g. the design may set maximum lanes as 4 but if the camera sensor has
only 1 lane then the active lanes shall be set as 1.

The pixel format set in design acts as a filter allowing only the selected
data type or RAW8 data packets. The D-PHY register access can be gated in
the design. The base address of the DPHY depends on whether the internal
Xilinx I2C controller is enabled or not in design.

The device driver registers the MIPI CSI2 Rx Subsystem as a V4L2 sub device
having 2 pads. The sink pad is connected to the MIPI camera sensor and
output pad is connected to the video node.
Refer to xlnx,csi2rxss.txt for device tree node details.

This driver helps configure the number of active lanes to be set, setting
and handling interrupts and IP core enable. It logs the number of events
occurring according to their type between streaming ON and OFF.
It generates a v4l2 event for each short packet data received.
The application can then dequeue this event and get the requisite data
from the event structure.

It adds new V4L2 controls which are used to get the event counter values
and reset the subsystem.

The Xilinx CSI-2 Rx Subsystem outputs an AXI4 Stream data which can be
used for image processing. This data follows the video formats mentioned
in Xilinx UG934 when the Video Format Bridge and pixels per clock design
inputs are set. When the VFB is deselected then the video data width will
either be 32 or 64 bits.

Vishal Sagar (2):
  media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem
  media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver

 .../bindings/media/xilinx/xlnx,csi2rxss.txt        |  117 ++
 drivers/media/platform/xilinx/Kconfig              |   12 +
 drivers/media/platform/xilinx/Makefile             |    1 +
 drivers/media/platform/xilinx/xilinx-csi2rxss.c    | 1751 ++++++++++++++++++++
 include/uapi/linux/xilinx-csi2rxss.h               |   25 +
 include/uapi/linux/xilinx-v4l2-controls.h          |   14 +
 6 files changed, 1920 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
 create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
 create mode 100644 include/uapi/linux/xilinx-csi2rxss.h

--
2.7.4

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

^ permalink raw reply

* [PATCH 1/2] media: dt-bindings: media: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem
From: Vishal Sagar @ 2018-05-29 18:54 UTC (permalink / raw)
  To: hyun.kwon, laurent.pinchart, michal.simek, linux-media,
	devicetree
  Cc: sakari.ailus, hans.verkuil, mchehab, robh+dt, mark.rutland,
	dineshk, linux-arm-kernel, linux-kernel, Vishal Sagar
In-Reply-To: <1527620084-94864-1-git-send-email-vishal.sagar@xilinx.com>

Add bindings documentation for Xilinx MIPI CSI-2 Rx Subsystem.

The Xilinx MIPI CSI-2 Rx Subsystem consists of a DPHY, CSI-2 Rx, an
optional I2C controller and an optional Video Format Bridge (VFB). The
active lanes can be configured at run time if enabled in the IP. The
DPHY register interface may also be enabled.

Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
---
 .../bindings/media/xilinx/xlnx,csi2rxss.txt        | 117 +++++++++++++++++++++
 1 file changed, 117 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt

diff --git a/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
new file mode 100644
index 0000000..31ed721
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/xilinx/xlnx,csi2rxss.txt
@@ -0,0 +1,117 @@
+
+Xilinx MIPI CSI2 Receiver Subsystem Device Tree Bindings
+--------------------------------------------------------
+
+The Xilinx MIPI CSI2 Receiver Subsystem is used to capture MIPI CSI2 traffic
+from compliant camera sensors and send the output as AXI4 Stream video data
+for image processing.
+
+The subsystem consists of a MIPI DPHY in slave mode which captures the
+data packets. This is passed along the MIPI CSI2 Rx IP which extracts the
+packet data. This data is taken in by the Video Format Bridge (VFB),
+if selected, and converted into AXI4 Stream video data at selected
+pixels per clock as per AXI4-Stream Video IP and System Design UG934.
+
+For more details, please refer to PG232 MIPI CSI-2 Receiver Subsystem.
+https://www.xilinx.com/support/documentation/ip_documentation/mipi_csi2_rx_subsystem/v3_0/pg232-mipi-csi2-rx.pdf
+
+Required properties:
+
+- compatible: Must contain "xlnx,mipi-csi2-rx-subsystem-2.0" or
+  "xlnx,mipi-csi2-rx-subsystem-3.0"
+
+- reg: Physical base address and length of the registers set for the device.
+
+- interrupt-parent: specifies the phandle to the parent interrupt controller
+
+- interrupts: Property with a value describing the interrupt number.
+
+- xlnx,max-lanes: Maximum active lanes in the design.
+
+- xlnx,vc: Virtual Channel, specifies virtual channel number to be filtered.
+  If this is 4 then all virtual channels are allowed.
+
+- xlnx,csi-pxl-format: This denotes the CSI Data type selected in hw design.
+  Packets other than this data type (except for RAW8 and User defined data
+  types) will be filtered out. Possible values are RAW6, RAW7, RAW8, RAW10,
+  RAW12, RAW14, RGB444, RGB555, RGB565, RGB666, RGB888 and YUV4228bit.
+
+- xlnx,axis-tdata-width: AXI Stream width, This denotes the AXI Stream width.
+  It depends on Data type chosen, Video Format Bridge enabled/disabled and
+  pixels per clock. If VFB is disabled then its value is either 0x20 (32 bit)
+  or 0x40(64 bit) width.
+
+- xlnx,video-format, xlnx,video-width: Video format and width, as defined in
+  video.txt.
+
+- port: Video port, using the DT bindings defined in ../video-interfaces.txt.
+  The CSI 2 Rx Subsystem has a two ports, one input port for connecting to
+  camera sensor and other is output port.
+
+- data-lanes: The number of data lanes through which CSI2 Rx Subsystem is
+  connected to the camera sensor as per video-interfaces.txt
+
+Optional properties:
+
+- xlnx,en-active-lanes: Enable Active lanes configuration in Protocol
+  Configuration Register.
+
+- xlnx,dphy-present: This is equivalent to whether DPHY register interface is
+  enabled or not.
+
+- xlnx,iic-present: This shows whether subsystem's IIC is present or not. This
+  affects the base address of the DPHY.
+
+- xlnx,vfb: Video Format Bridge, Denotes if Video Format Bridge is selected
+  so that output is as per AXI stream documented in UG934.
+
+- xlnx,ppc: Pixels per clock, Number of pixels to be transferred per pixel
+  clock. This is valid only if xlnx,vfb property is present.
+
+Example:
+
+       csiss_1: csiss@a0020000 {
+               compatible = "xlnx,mipi-csi2-rx-subsystem-3.0";
+               reg = <0x0 0xa0020000 0x0 0x20000>;
+               interrupt-parent = <&gic>;
+               interrupts = <0 95 4>;
+
+               xlnx,max-lanes = <0x4>;
+               xlnx,en-active-lanes;
+               xlnx,dphy-present;
+               xlnx,iic-present;
+               xlnx,vc = <0x4>;
+               xlnx,csi-pxl-format = "RAW8";
+               xlnx,vfb;
+               xlnx,ppc = <0x4>;
+               xlnx,axis-tdata-width = <0x20>;
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@0 {
+                               reg = <0>;
+
+                               xlnx,video-format = <XVIP_VF_YUV_422>;
+                               xlnx,video-width = <8>;
+                               csiss_out: endpoint {
+                                       remote-endpoint = <&vcap_csiss_in>;
+                               };
+                       };
+                       port@1 {
+                               reg = <1>;
+
+                               xlnx,video-format = <XVIP_VF_YUV_422>;
+                               xlnx,video-width = <8>;
+
+                               csiss_in: endpoint {
+                                       data-lanes = <1 2 3 4>;
+                                       /* MIPI CSI2 Camera handle */
+                                       remote-endpoint = <&vs2016_out>;
+                               };
+
+                       };
+
+               };
+       };
--
2.7.4

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

^ permalink raw reply related

* [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver
From: Vishal Sagar @ 2018-05-29 18:54 UTC (permalink / raw)
  To: hyun.kwon, laurent.pinchart, michal.simek, linux-media,
	devicetree
  Cc: sakari.ailus, hans.verkuil, mchehab, robh+dt, mark.rutland,
	dineshk, linux-arm-kernel, linux-kernel, Vishal Sagar
In-Reply-To: <1527620084-94864-1-git-send-email-vishal.sagar@xilinx.com>

The Xilinx MIPI CSI-2 Rx Subsystem soft IP is used to capture images
from MIPI CSI-2 camera sensors and output AXI4-Stream video data ready
for image processing.

It supports upto 4 lanes, filtering based on Virtual Channel selected in
IP, an optional Xilinx IIC controller, optional register interface for
the DPHY, multiple color formats, short packet capture,

This driver helps configure the number of active lanes to be set,
setting and handling interrupts and IP core enable.
It logs the count of events occurring according to their type between
streaming ON and OFF. The short packet reception is notified to
application via v4l2_event.

Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
---
 drivers/media/platform/xilinx/Kconfig           |   12 +
 drivers/media/platform/xilinx/Makefile          |    1 +
 drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751 +++++++++++++++++++++++
 include/uapi/linux/xilinx-csi2rxss.h            |   25 +
 include/uapi/linux/xilinx-v4l2-controls.h       |   14 +
 5 files changed, 1803 insertions(+)
 create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
 create mode 100644 include/uapi/linux/xilinx-csi2rxss.h

diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index a5d21b7..06d5944 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -8,6 +8,18 @@ config VIDEO_XILINX

 if VIDEO_XILINX

+config VIDEO_XILINX_CSI2RXSS
+       tristate "Xilinx CSI2 Rx Subsystem"
+       depends on VIDEO_XILINX
+       help
+         Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
+         based driver that takes input from CSI2 Tx source and converts
+         it into an AXI4-Stream. It has a DPHY (whose register interface
+         can be enabled, an optional I2C controller and an optional Video
+         Format Bridge which converts the AXI4-Stream data to Xilinx Video
+         Bus formats based on UG934. The driver is used to set the number
+         of active lanes and get short packet data.
+
 config VIDEO_XILINX_TPG
        tristate "Xilinx Video Test Pattern Generator"
        depends on VIDEO_XILINX
diff --git a/drivers/media/platform/xilinx/Makefile b/drivers/media/platform/xilinx/Makefile
index e8a0f2a..768f079 100644
--- a/drivers/media/platform/xilinx/Makefile
+++ b/drivers/media/platform/xilinx/Makefile
@@ -1,5 +1,6 @@
 xilinx-video-objs += xilinx-dma.o xilinx-vip.o xilinx-vipp.o

 obj-$(CONFIG_VIDEO_XILINX) += xilinx-video.o
+obj-$(CONFIG_VIDEO_XILINX_CSI2RXSS) += xilinx-csi2rxss.o
 obj-$(CONFIG_VIDEO_XILINX_TPG) += xilinx-tpg.o
 obj-$(CONFIG_VIDEO_XILINX_VTC) += xilinx-vtc.o
diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
new file mode 100644
index 0000000..03f387c
--- /dev/null
+++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
@@ -0,0 +1,1751 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for Xilinx MIPI CSI2 Rx Subsystem
+ *
+ * Copyright (C) 2016 - 2018 Xilinx, Inc.
+ *
+ * Contacts: Vishal Sagar <vishal.sagar@xilinx.com>
+ *
+ */
+
+#include <dt-bindings/media/xilinx-vip.h>
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+#include <linux/v4l2-subdev.h>
+#include <linux/xilinx-csi2rxss.h>
+#include <linux/xilinx-v4l2-controls.h>
+#include <media/media-entity.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+#include "xilinx-vip.h"
+
+/*
+ * MIPI CSI2 Rx register map, bitmask and offsets
+ */
+#define XCSI_CCR_OFFSET                        0x00000000
+#define XCSI_CCR_SOFTRESET_SHIFT       1
+#define XCSI_CCR_COREENB_SHIFT         0
+#define XCSI_CCR_SOFTRESET_MASK                BIT(XCSI_CCR_SOFTRESET_SHIFT)
+#define XCSI_CCR_COREENB_MASK          BIT(XCSI_CCR_COREENB_SHIFT)
+
+#define XCSI_PCR_OFFSET                        0x00000004
+#define XCSI_PCR_MAXLANES_MASK         0x00000018
+#define XCSI_PCR_ACTLANES_MASK         0x00000003
+#define XCSI_PCR_MAXLANES_SHIFT                3
+#define XCSI_PCR_ACTLANES_SHIFT                0
+
+#define XCSI_CSR_OFFSET                        0x00000010
+#define XCSI_CSR_PKTCOUNT_SHIFT                16
+#define XCSI_CSR_SPFIFOFULL_SHIFT      3
+#define XCSI_CSR_SPFIFONE_SHIFT                2
+#define XCSI_CSR_SLBF_SHIFT            1
+#define XCSI_CSR_RIPCD_SHIFT           0
+#define XCSI_CSR_PKTCOUNT_MASK         0xFFFF0000
+#define XCSI_CSR_SPFIFOFULL_MASK       BIT(XCSI_CSR_SPFIFOFULL_SHIFT)
+#define XCSI_CSR_SPFIFONE_MASK         BIT(XCSI_CSR_SPFIFONE_SHIFT)
+#define XCSI_CSR_SLBF_MASK             BIT(XCSI_CSR_SLBF_SHIFT)
+#define XCSI_CSR_RIPCD_MASK            BIT(XCSI_CSR_RIPCD_SHIFT)
+
+#define XCSI_GIER_OFFSET               0x00000020
+#define XCSI_GIER_GIE_SHIFT            0
+#define XCSI_GIER_GIE_MASK             BIT(XCSI_GIER_GIE_SHIFT)
+#define XCSI_GIER_SET                  1
+#define XCSI_GIER_RESET                        0
+
+#define XCSI_ISR_OFFSET                        0x00000024
+#define XCSI_ISR_FR_SHIFT              31
+#define XCSI_ISR_ILC_SHIFT             21
+#define XCSI_ISR_SPFIFOF_SHIFT         20
+#define XCSI_ISR_SPFIFONE_SHIFT                19
+#define XCSI_ISR_SLBF_SHIFT            18
+#define XCSI_ISR_STOP_SHIFT            17
+#define XCSI_ISR_SOTERR_SHIFT          13
+#define XCSI_ISR_SOTSYNCERR_SHIFT      12
+#define XCSI_ISR_ECC2BERR_SHIFT                11
+#define XCSI_ISR_ECC1BERR_SHIFT                10
+#define XCSI_ISR_CRCERR_SHIFT          9
+#define XCSI_ISR_DATAIDERR_SHIFT       8
+#define XCSI_ISR_VC3FSYNCERR_SHIFT     7
+#define XCSI_ISR_VC3FLVLERR_SHIFT      6
+#define XCSI_ISR_VC2FSYNCERR_SHIFT     5
+#define XCSI_ISR_VC2FLVLERR_SHIFT      4
+#define XCSI_ISR_VC1FSYNCERR_SHIFT     3
+#define XCSI_ISR_VC1FLVLERR_SHIFT      2
+#define XCSI_ISR_VC0FSYNCERR_SHIFT     1
+#define XCSI_ISR_VC0FLVLERR_SHIFT      0
+#define XCSI_ISR_FR_MASK               BIT(XCSI_ISR_FR_SHIFT)
+#define XCSI_ISR_ILC_MASK              BIT(XCSI_ISR_ILC_SHIFT)
+#define XCSI_ISR_SPFIFOF_MASK          BIT(XCSI_ISR_SPFIFOF_SHIFT)
+#define XCSI_ISR_SPFIFONE_MASK         BIT(XCSI_ISR_SPFIFONE_SHIFT)
+#define XCSI_ISR_SLBF_MASK             BIT(XCSI_ISR_SLBF_SHIFT)
+#define XCSI_ISR_STOP_MASK             BIT(XCSI_ISR_STOP_SHIFT)
+#define XCSI_ISR_SOTERR_MASK           BIT(XCSI_ISR_SOTERR_SHIFT)
+#define XCSI_ISR_SOTSYNCERR_MASK       BIT(XCSI_ISR_SOTSYNCERR_SHIFT)
+#define XCSI_ISR_ECC2BERR_MASK         BIT(XCSI_ISR_ECC2BERR_SHIFT)
+#define XCSI_ISR_ECC1BERR_MASK         BIT(XCSI_ISR_ECC1BERR_SHIFT)
+#define XCSI_ISR_CRCERR_MASK           BIT(XCSI_ISR_CRCERR_SHIFT)
+#define XCSI_ISR_DATAIDERR_MASK                BIT(XCSI_ISR_DATAIDERR_SHIFT)
+#define XCSI_ISR_VC3FSYNCERR_MASK      BIT(XCSI_ISR_VC3FSYNCERR_SHIFT)
+#define XCSI_ISR_VC3FLVLERR_MASK       BIT(XCSI_ISR_VC3FLVLERR_SHIFT)
+#define XCSI_ISR_VC2FSYNCERR_MASK      BIT(XCSI_ISR_VC2FSYNCERR_SHIFT)
+#define XCSI_ISR_VC2FLVLERR_MASK       BIT(XCSI_ISR_VC2FLVLERR_SHIFT)
+#define XCSI_ISR_VC1FSYNCERR_MASK      BIT(XCSI_ISR_VC1FSYNCERR_SHIFT)
+#define XCSI_ISR_VC1FLVLERR_MASK       BIT(XCSI_ISR_VC1FLVLERR_SHIFT)
+#define XCSI_ISR_VC0FSYNCERR_MASK      BIT(XCSI_ISR_VC0FSYNCERR_SHIFT)
+#define XCSI_ISR_VC0FLVLERR_MASK       BIT(XCSI_ISR_VC0FLVLERR_SHIFT)
+#define XCSI_ISR_ALLINTR_MASK          0x803FFFFF
+
+#define XCSI_INTR_PROT_MASK    (XCSI_ISR_VC3FSYNCERR_MASK |    \
+                                XCSI_ISR_VC3FLVLERR_MASK |     \
+                                XCSI_ISR_VC2FSYNCERR_MASK |    \
+                                XCSI_ISR_VC2FLVLERR_MASK |     \
+                                XCSI_ISR_VC1FSYNCERR_MASK |    \
+                                XCSI_ISR_VC1FLVLERR_MASK |     \
+                                XCSI_ISR_VC0FSYNCERR_MASK |    \
+                                XCSI_ISR_VC0FLVLERR_MASK)
+
+#define XCSI_INTR_PKTLVL_MASK  (XCSI_ISR_ECC2BERR_MASK |       \
+                                XCSI_ISR_ECC1BERR_MASK |       \
+                                XCSI_ISR_CRCERR_MASK   |       \
+                                XCSI_ISR_DATAIDERR_MASK)
+
+#define XCSI_INTR_DPHY_MASK    (XCSI_ISR_SOTERR_MASK   |       \
+                                XCSI_ISR_SOTSYNCERR_MASK)
+
+#define XCSI_INTR_SPKT_MASK    (XCSI_ISR_SPFIFOF_MASK |        \
+                                XCSI_ISR_SPFIFONE_MASK)
+
+#define XCSI_INTR_FRAMERCVD_MASK       (XCSI_ISR_FR_MASK)
+
+#define XCSI_INTR_ERR_MASK     (XCSI_ISR_ILC_MASK |    \
+                                XCSI_ISR_SLBF_MASK |   \
+                                XCSI_ISR_STOP_MASK)
+
+#define XCSI_IER_OFFSET                        0x00000028
+#define XCSI_IER_FR_SHIFT              31
+#define XCSI_IER_ILC_SHIFT             21
+#define XCSI_IER_SPFIFOF_SHIFT         20
+#define XCSI_IER_SPFIFONE_SHIFT                19
+#define XCSI_IER_SLBF_SHIFT            18
+#define XCSI_IER_STOP_SHIFT            17
+#define XCSI_IER_SOTERR_SHIFT          13
+#define XCSI_IER_SOTSYNCERR_SHIFT      12
+#define XCSI_IER_ECC2BERR_SHIFT                11
+#define XCSI_IER_ECC1BERR_SHIFT                10
+#define XCSI_IER_CRCERR_SHIFT          9
+#define XCSI_IER_DATAIDERR_SHIFT       8
+#define XCSI_IER_VC3FSYNCERR_SHIFT     7
+#define XCSI_IER_VC3FLVLERR_SHIFT      6
+#define XCSI_IER_VC2FSYNCERR_SHIFT     5
+#define XCSI_IER_VC2FLVLERR_SHIFT      4
+#define XCSI_IER_VC1FSYNCERR_SHIFT     3
+#define XCSI_IER_VC1FLVLERR_SHIFT      2
+#define XCSI_IER_VC0FSYNCERR_SHIFT     1
+#define XCSI_IER_VC0FLVLERR_SHIFT      0
+#define XCSI_IER_FR_MASK               BIT(XCSI_IER_FR_SHIFT)
+#define XCSI_IER_ILC_MASK              BIT(XCSI_IER_ILC_SHIFT)
+#define XCSI_IER_SPFIFOF_MASK          BIT(XCSI_IER_SPFIFOF_SHIFT)
+#define XCSI_IER_SPFIFONE_MASK         BIT(XCSI_IER_SPFIFONE_SHIFT)
+#define XCSI_IER_SLBF_MASK             BIT(XCSI_IER_SLBF_SHIFT)
+#define XCSI_IER_STOP_MASK             BIT(XCSI_IER_STOP_SHIFT)
+#define XCSI_IER_SOTERR_MASK           BIT(XCSI_IER_SOTERR_SHIFT)
+#define XCSI_IER_SOTSYNCERR_MASK       BIT(XCSI_IER_SOTSYNCERR_SHIFT)
+#define XCSI_IER_ECC2BERR_MASK         BIT(XCSI_IER_ECC2BERR_SHIFT)
+#define XCSI_IER_ECC1BERR_MASK         BIT(XCSI_IER_ECC1BERR_SHIFT)
+#define XCSI_IER_CRCERR_MASK           BIT(XCSI_IER_CRCERR_SHIFT)
+#define XCSI_IER_DATAIDERR_MASK                BIT(XCSI_IER_DATAIDERR_SHIFT)
+#define XCSI_IER_VC3FSYNCERR_MASK      BIT(XCSI_IER_VC3FSYNCERR_SHIFT)
+#define XCSI_IER_VC3FLVLERR_MASK       BIT(XCSI_IER_VC3FLVLERR_SHIFT)
+#define XCSI_IER_VC2FSYNCERR_MASK      BIT(XCSI_IER_VC2FSYNCERR_SHIFT)
+#define XCSI_IER_VC2FLVLERR_MASK       BIT(XCSI_IER_VC2FLVLERR_SHIFT)
+#define XCSI_IER_VC1FSYNCERR_MASK      BIT(XCSI_IER_VC1FSYNCERR_SHIFT)
+#define XCSI_IER_VC1FLVLERR_MASK       BIT(XCSI_IER_VC1FLVLERR_SHIFT)
+#define XCSI_IER_VC0FSYNCERR_MASK      BIT(XCSI_IER_VC0FSYNCERR_SHIFT)
+#define XCSI_IER_VC0FLVLERR_MASK       BIT(XCSI_IER_VC0FLVLERR_SHIFT)
+#define XCSI_IER_ALLINTR_MASK          0x803FFFFF
+
+#define XCSI_SPKTR_OFFSET              0x00000030
+#define XCSI_SPKTR_DATA_SHIFT          8
+#define XCSI_SPKTR_VC_SHIFT            6
+#define XCSI_SPKTR_DT_SHIFT            0
+#define XCSI_SPKTR_DATA_MASK           0x00FFFF00
+#define XCSI_SPKTR_VC_MASK             0x000000C0
+#define XCSI_SPKTR_DT_MASK             0x0000003F
+
+#define XCSI_CLKINFR_OFFSET            0x0000003C
+#define XCSI_CLKINFR_STOP_SHIFT                1
+#define XCSI_CLKINFR_STOP_MASK         BIT(XCSI_CLKINFR_STOP_SHIFT)
+
+#define XCSI_L0INFR_OFFSET             0x00000040
+#define XCSI_L1INFR_OFFSET             0x00000044
+#define XCSI_L2INFR_OFFSET             0x00000048
+#define XCSI_L3INFR_OFFSET             0x0000004C
+#define XCSI_LXINFR_STOP_SHIFT         5
+#define XCSI_LXINFR_SOTERR_SHIFT       1
+#define XCSI_LXINFR_SOTSYNCERR_SHIFT   0
+#define XCSI_LXINFR_STOP_MASK          BIT(XCSI_LXINFR_STOP_SHIFT)
+#define XCSI_LXINFR_SOTERR_MASK                BIT(XCSI_LXINFR_SOTERR_SHIFT)
+#define XCSI_LXINFR_SOTSYNCERR_MASK    BIT(XCSI_LXINFR_SOTSYNCERR_SHIFT)
+
+#define XCSI_VC0INF1R_OFFSET           0x00000060
+#define XCSI_VC1INF1R_OFFSET           0x00000068
+#define XCSI_VC2INF1R_OFFSET           0x00000070
+#define XCSI_VC3INF1R_OFFSET           0x00000078
+#define XCSI_VCXINF1R_LINECOUNT_SHIFT  16
+#define XCSI_VCXINF1R_BYTECOUNT_SHIFT  0
+#define XCSI_VCXINF1R_LINECOUNT_MASK   0xFFFF0000
+#define XCSI_VCXINF1R_BYTECOUNT_MASK   0x0000FFFF
+
+#define XCSI_VC0INF2R_OFFSET           0x00000064
+#define XCSI_VC1INF2R_OFFSET           0x0000006C
+#define XCSI_VC2INF2R_OFFSET           0x00000074
+#define XCSI_VC3INF2R_OFFSET           0x0000007C
+#define XCSI_VCXINF2R_DATATYPE_SHIFT   0
+#define XCSI_VCXINF2R_DATATYPE_MASK    0x0000003F
+
+#define XDPHY_CTRLREG_OFFSET           0x0
+#define XDPHY_CTRLREG_DPHYEN_SHIFT     1
+#define XDPHY_CTRLREG_DPHYEN_MASK      BIT(XDPHY_CTRLREG_DPHYEN_SHIFT)
+
+#define XDPHY_CLKSTATREG_OFFSET                0x18
+#define XDPHY_CLKSTATREG_MODE_SHIFT    0
+#define XDPHY_CLKSTATREG_MODE_MASK     0x3
+#define XDPHY_LOW_POWER_MODE           0x0
+#define XDPHY_HI_SPEED_MODE            0x1
+#define XDPHY_ESC_MODE                 0x2
+
+/*
+ * Interrupt mask
+ */
+#define XCSI_INTR_MASK         (XCSI_ISR_ALLINTR_MASK & ~XCSI_ISR_STOP_MASK)
+/*
+ * Timeout for reset
+ */
+#define XCSI_TIMEOUT_VAL       (1000) /* us */
+
+/*
+ * Max string length for CSI Data type string
+ */
+#define MAX_XIL_CSIDT_STR_LENGTH 64
+
+/*
+ * Maximum number of short packet events per file handle.
+ */
+#define XCSI_MAX_SPKT          (512)
+
+/* Number of media pads */
+#define XILINX_CSI_MEDIA_PADS  (2)
+
+#define XCSI_DEFAULT_WIDTH     (1920)
+#define XCSI_DEFAULT_HEIGHT    (1080)
+
+/*
+ * Macro to return "true" or "false" string if bit is set
+ */
+#define XCSI_GET_BITSET_STR(val, mask) (val) & (mask) ? "true" : "false"
+
+enum csi_datatypes {
+       MIPI_CSI_DT_FRAME_START_CODE = 0x00,
+       MIPI_CSI_DT_FRAME_END_CODE,
+       MIPI_CSI_DT_LINE_START_CODE,
+       MIPI_CSI_DT_LINE_END_CODE,
+       MIPI_CSI_DT_SYNC_RSVD_04,
+       MIPI_CSI_DT_SYNC_RSVD_05,
+       MIPI_CSI_DT_SYNC_RSVD_06,
+       MIPI_CSI_DT_SYNC_RSVD_07,
+       MIPI_CSI_DT_GSPKT_08,
+       MIPI_CSI_DT_GSPKT_09,
+       MIPI_CSI_DT_GSPKT_0A,
+       MIPI_CSI_DT_GSPKT_0B,
+       MIPI_CSI_DT_GSPKT_0C,
+       MIPI_CSI_DT_GSPKT_0D,
+       MIPI_CSI_DT_GSPKT_0E,
+       MIPI_CSI_DT_GSPKT_0F,
+       MIPI_CSI_DT_GLPKT_10,
+       MIPI_CSI_DT_GLPKT_11,
+       MIPI_CSI_DT_GLPKT_12,
+       MIPI_CSI_DT_GLPKT_13,
+       MIPI_CSI_DT_GLPKT_14,
+       MIPI_CSI_DT_GLPKT_15,
+       MIPI_CSI_DT_GLPKT_16,
+       MIPI_CSI_DT_GLPKT_17,
+       MIPI_CSI_DT_YUV_420_8B,
+       MIPI_CSI_DT_YUV_420_10B,
+       MIPI_CSI_DT_YUV_420_8B_LEGACY,
+       MIPI_CSI_DT_YUV_RSVD,
+       MIPI_CSI_DT_YUV_420_8B_CSPS,
+       MIPI_CSI_DT_YUV_420_10B_CSPS,
+       MIPI_CSI_DT_YUV_422_8B,
+       MIPI_CSI_DT_YUV_422_10B,
+       MIPI_CSI_DT_RGB_444,
+       MIPI_CSI_DT_RGB_555,
+       MIPI_CSI_DT_RGB_565,
+       MIPI_CSI_DT_RGB_666,
+       MIPI_CSI_DT_RGB_888,
+       MIPI_CSI_DT_RGB_RSVD_25,
+       MIPI_CSI_DT_RGB_RSVD_26,
+       MIPI_CSI_DT_RGB_RSVD_27,
+       MIPI_CSI_DT_RAW_6,
+       MIPI_CSI_DT_RAW_7,
+       MIPI_CSI_DT_RAW_8,
+       MIPI_CSI_DT_RAW_10,
+       MIPI_CSI_DT_RAW_12,
+       MIPI_CSI_DT_RAW_14,
+       MIPI_CSI_DT_RAW_RSVD_2E,
+       MIPI_CSI_DT_RAW_RSVD_2F,
+       MIPI_CSI_DT_USER_30,
+       MIPI_CSI_DT_USER_31,
+       MIPI_CSI_DT_USER_32,
+       MIPI_CSI_DT_USER_33,
+       MIPI_CSI_DT_USER_34,
+       MIPI_CSI_DT_USER_35,
+       MIPI_CSI_DT_USER_36,
+       MIPI_CSI_DT_USER_37,
+       MIPI_CSI_DT_RSVD_38,
+       MIPI_CSI_DT_RSVD_39,
+       MIPI_CSI_DT_RSVD_3A,
+       MIPI_CSI_DT_RSVD_3B,
+       MIPI_CSI_DT_RSVD_3C,
+       MIPI_CSI_DT_RSVD_3D,
+       MIPI_CSI_DT_RSVD_3E,
+       MIPI_CSI_DT_RSVD_3F
+};
+
+/**
+ * struct pixel_format - Data Type to string name structure
+ * @pixelformat: MIPI CSI2 Data type
+ * @pixelformatstr: String name of Data Type
+ */
+struct pixel_format {
+       enum csi_datatypes pixelformat;
+       char pixelformatstr[MAX_XIL_CSIDT_STR_LENGTH];
+};
+
+/**
+ * struct xcsi2rxss_event - Event log structure
+ * @mask: Event mask
+ * @name: Name of the event
+ * @counter: Count number of events
+ */
+struct xcsi2rxss_event {
+       u32 mask;
+       const char * const name;
+       unsigned int counter;
+};
+
+/*
+ * struct xcsi2rxss_core - Core configuration CSI2 Rx Subsystem device structure
+ * @dev: Platform structure
+ * @iomem: Base address of subsystem
+ * @irq: requested irq number
+ * @dphy_present: Flag for DPHY register interface presence
+ * @dphy_offset: DPHY registers offset
+ * @enable_active_lanes: If number of active lanes can be modified
+ * @max_num_lanes: Maximum number of lanes present
+ * @vfb: Video Format Bridge enabled or not
+ * @ppc: pixels per clock
+ * @vc: Virtual Channel
+ * @axis_tdata_width: AXI Stream data width
+ * @datatype: Data type filter
+ * @pxlformat: String with CSI pixel format from IP
+ * @num_lanes: Number of lanes requested from application
+ * @events: Structure to maintain event logs
+ */
+struct xcsi2rxss_core {
+       struct device *dev;
+       void __iomem *iomem;
+       int irq;
+       u32 dphy_offset;
+       bool dphy_present;
+       bool enable_active_lanes;
+       u32 max_num_lanes;
+       bool vfb;
+       u32 ppc;
+       u32 vc;
+       u32 axis_tdata_width;
+       u32 datatype;
+       const char *pxlformat;
+       u32 num_lanes;
+       struct xcsi2rxss_event *events;
+};
+
+/**
+ * struct xcsi2rxss_state - CSI2 Rx Subsystem device structure
+ * @core: Core structure for MIPI CSI2 Rx Subsystem
+ * @subdev: The v4l2 subdev structure
+ * @ctrl_handler: control handler
+ * @formats: Active V4L2 formats on each pad
+ * @default_format: default V4L2 media bus format
+ * @vip_format: format information corresponding to the active format
+ * @event: Holds the short packet event
+ * @lock: mutex for serializing operations
+ * @pads: media pads
+ * @npads: number of pads
+ * @streaming: Flag for storing streaming state
+ * @suspended: Flag for storing suspended state
+ *
+ * This structure contains the device driver related parameters
+ */
+struct xcsi2rxss_state {
+       struct xcsi2rxss_core core;
+       struct v4l2_subdev subdev;
+       struct v4l2_ctrl_handler ctrl_handler;
+       struct v4l2_mbus_framefmt formats[2];
+       struct v4l2_mbus_framefmt default_format;
+       const struct xvip_video_format *vip_format;
+       struct v4l2_event event;
+       /*
+        * Used to serialize access.
+        */
+       struct mutex lock;
+       struct media_pad pads[XILINX_CSI_MEDIA_PADS];
+       unsigned int npads;
+       bool streaming;
+       bool suspended;
+};
+
+static inline struct xcsi2rxss_state *
+to_xcsi2rxssstate(struct v4l2_subdev *subdev)
+{
+       return container_of(subdev, struct xcsi2rxss_state, subdev);
+}
+
+/*
+ * Register related operations
+ */
+static inline u32 xcsi2rxss_read(struct xcsi2rxss_core *xcsi2rxss,
+                                u32 addr)
+{
+       return ioread32(xcsi2rxss->iomem + addr);
+}
+
+static inline void xcsi2rxss_write(struct xcsi2rxss_core *xcsi2rxss,
+                                  u32 addr, u32 value)
+{
+       iowrite32(value, xcsi2rxss->iomem + addr);
+}
+
+static inline void xcsi2rxss_clr(struct xcsi2rxss_core *xcsi2rxss,
+                                u32 addr, u32 clr)
+{
+       xcsi2rxss_write(xcsi2rxss,
+                       addr,
+                       xcsi2rxss_read(xcsi2rxss, addr) & ~clr);
+}
+
+static inline void xcsi2rxss_set(struct xcsi2rxss_core *xcsi2rxss,
+                                u32 addr, u32 set)
+{
+       xcsi2rxss_write(xcsi2rxss,
+                       addr,
+                       xcsi2rxss_read(xcsi2rxss, addr) | set);
+}
+
+static const struct pixel_format pixel_formats[] = {
+       { MIPI_CSI_DT_YUV_420_8B, "YUV420_8bit" },
+       { MIPI_CSI_DT_YUV_420_10B, "YUV420_10bit" },
+       { MIPI_CSI_DT_YUV_420_8B_LEGACY, "Legacy_YUV420_8bit" },
+       { MIPI_CSI_DT_YUV_420_8B_CSPS, "YUV420_8bit_CSPS" },
+       { MIPI_CSI_DT_YUV_420_10B_CSPS, "YUV420_10bit_CSPS" },
+       { MIPI_CSI_DT_YUV_422_8B, "YUV422_8bit" },
+       { MIPI_CSI_DT_YUV_422_10B, "YUV422_10bit" },
+       { MIPI_CSI_DT_RGB_444, "RGB444" },
+       { MIPI_CSI_DT_RGB_555, "RGB555" },
+       { MIPI_CSI_DT_RGB_565, "RGB565" },
+       { MIPI_CSI_DT_RGB_666, "RGB666" },
+       { MIPI_CSI_DT_RGB_888, "RGB888" },
+       { MIPI_CSI_DT_RAW_6, "RAW6" },
+       { MIPI_CSI_DT_RAW_7, "RAW7" },
+       { MIPI_CSI_DT_RAW_8, "RAW8" },
+       { MIPI_CSI_DT_RAW_10, "RAW10" },
+       { MIPI_CSI_DT_RAW_12, "RAW12" },
+       { MIPI_CSI_DT_RAW_14, "RAW14 "}
+};
+
+static struct xcsi2rxss_event xcsi2rxss_events[] = {
+       { XCSI_ISR_FR_MASK, "Frame Received", 0 },
+       { XCSI_ISR_ILC_MASK, "Invalid Lane Count Error", 0 },
+       { XCSI_ISR_SPFIFOF_MASK, "Short Packet FIFO OverFlow Error", 0 },
+       { XCSI_ISR_SPFIFONE_MASK, "Short Packet FIFO Not Empty", 0 },
+       { XCSI_ISR_SLBF_MASK, "Streamline Buffer Full Error", 0 },
+       { XCSI_ISR_STOP_MASK, "Lane Stop State", 0 },
+       { XCSI_ISR_SOTERR_MASK, "SOT Error", 0 },
+       { XCSI_ISR_SOTSYNCERR_MASK, "SOT Sync Error", 0 },
+       { XCSI_ISR_ECC2BERR_MASK, "2 Bit ECC Unrecoverable Error", 0 },
+       { XCSI_ISR_ECC1BERR_MASK, "1 Bit ECC Recoverable Error", 0 },
+       { XCSI_ISR_CRCERR_MASK, "CRC Error", 0 },
+       { XCSI_ISR_DATAIDERR_MASK, "Data Id Error", 0 },
+       { XCSI_ISR_VC3FSYNCERR_MASK, "Virtual Channel 3 Frame Sync Error", 0 },
+       { XCSI_ISR_VC3FLVLERR_MASK, "Virtual Channel 3 Frame Level Error", 0 },
+       { XCSI_ISR_VC2FSYNCERR_MASK, "Virtual Channel 2 Frame Sync Error", 0 },
+       { XCSI_ISR_VC2FLVLERR_MASK, "Virtual Channel 2 Frame Level Error", 0 },
+       { XCSI_ISR_VC1FSYNCERR_MASK, "Virtual Channel 1 Frame Sync Error", 0 },
+       { XCSI_ISR_VC1FLVLERR_MASK, "Virtual Channel 1 Frame Level Error", 0 },
+       { XCSI_ISR_VC0FSYNCERR_MASK, "Virtual Channel 0 Frame Sync Error", 0 },
+       { XCSI_ISR_VC0FLVLERR_MASK, "Virtual Channel 0 Frame Level Error", 0 }
+};
+
+#define XMIPICSISS_NUM_EVENTS ARRAY_SIZE(xcsi2rxss_events)
+
+/**
+ * xcsi2rxss_clr_and_set - Clear and set the register with a bitmask
+ * @xcsi2rxss: Xilinx MIPI CSI2 Rx Subsystem subdev core struct
+ * @addr: address of register
+ * @clr: bitmask to be cleared
+ * @set: bitmask to be set
+ *
+ * Clear a bit(s) of mask @clr in the register at address @addr, then set
+ * a bit(s) of mask @set in the register after.
+ */
+static void xcsi2rxss_clr_and_set(struct xcsi2rxss_core *xcsi2rxss,
+                                 u32 addr, u32 clr, u32 set)
+{
+       u32 reg;
+
+       reg = xcsi2rxss_read(xcsi2rxss, addr);
+       reg &= ~clr;
+       reg |= set;
+       xcsi2rxss_write(xcsi2rxss, addr, reg);
+}
+
+/**
+ * xcsi2rxss_pxlfmtstrtodt - Convert pixel format string got from dts
+ * to data type.
+ * @pxlfmtstr: String obtained while parsing device node
+ *
+ * This function takes a CSI pixel format string obtained while parsing
+ * device tree node and converts it to data type.
+ *
+ * Eg. "RAW8" string is converted to 0x2A.
+ * Refer to MIPI CSI2 specification for details.
+ *
+ * Return: Equivalent pixel format value from table
+ */
+static u32 xcsi2rxss_pxlfmtstrtodt(const char *pxlfmtstr)
+{
+       u32 i;
+       u32 maxentries = ARRAY_SIZE(pixel_formats);
+
+       for (i = 0; i < maxentries; i++) {
+               if (!strncmp(pixel_formats[i].pixelformatstr,
+                            pxlfmtstr, MAX_XIL_CSIDT_STR_LENGTH))
+                       return pixel_formats[i].pixelformat;
+       }
+
+       return -EINVAL;
+}
+
+/**
+ * xcsi2rxss_pxlfmtdttostr - Convert pixel format data type to string.
+ * @datatype: MIPI CSI-2 Data Type
+ *
+ * This function takes a CSI pixel format data type and returns a
+ * pointer to the string name.
+ *
+ * Eg. 0x2A returns "RAW8" string.
+ * Refer to MIPI CSI2 specification for details.
+ *
+ * Return: Equivalent pixel format string from table
+ */
+static const char *xcsi2rxss_pxlfmtdttostr(u32 datatype)
+{
+       u32 i;
+       u32 maxentries = ARRAY_SIZE(pixel_formats);
+
+       for (i = 0; i < maxentries; i++) {
+               if (pixel_formats[i].pixelformat == datatype)
+                       return pixel_formats[i].pixelformatstr;
+       }
+
+       return NULL;
+}
+
+/**
+ * xcsi2rxss_enable - Enable or disable the CSI Core
+ * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
+ * @flag: true for enabling, false for disabling
+ *
+ * This function enables/disables the MIPI CSI2 Rx Subsystem core.
+ * After enabling the CSI2 Rx core, the DPHY is enabled in case the
+ * register interface for it is present.
+ */
+static void xcsi2rxss_enable(struct xcsi2rxss_core *core, bool flag)
+{
+       u32 dphyctrlregoffset = core->dphy_offset + XDPHY_CTRLREG_OFFSET;
+
+       if (flag) {
+               xcsi2rxss_write(core, XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
+               if (core->dphy_present)
+                       xcsi2rxss_write(core, dphyctrlregoffset,
+                                       XDPHY_CTRLREG_DPHYEN_MASK);
+       } else {
+               xcsi2rxss_write(core, XCSI_CCR_OFFSET, 0);
+               if (core->dphy_present)
+                       xcsi2rxss_write(core, dphyctrlregoffset, 0);
+       }
+}
+
+/**
+ * xcsi2rxss_interrupts_enable - Enable or disable CSI interrupts
+ * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
+ * @flag: true for enabling, false for disabling
+ *
+ * This function enables/disables the interrupts for the MIPI CSI2
+ * Rx Subsystem.
+ */
+static void xcsi2rxss_interrupts_enable(struct xcsi2rxss_core *core, bool flag)
+{
+       if (flag) {
+               xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
+               xcsi2rxss_write(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
+               xcsi2rxss_set(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
+       } else {
+               xcsi2rxss_clr(core, XCSI_IER_OFFSET, XCSI_INTR_MASK);
+               xcsi2rxss_clr(core, XCSI_GIER_OFFSET, XCSI_GIER_GIE_MASK);
+       }
+}
+
+/**
+ * xcsi2rxss_reset - Does a soft reset of the MIPI CSI2 Rx Subsystem
+ * @core: Core Xilinx CSI2 Rx Subsystem structure pointer
+ *
+ * Return: 0 - on success OR -ETIME if reset times out
+ */
+static int xcsi2rxss_reset(struct xcsi2rxss_core *core)
+{
+       u32 timeout = XCSI_TIMEOUT_VAL;
+
+       xcsi2rxss_set(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
+
+       while (xcsi2rxss_read(core, XCSI_CSR_OFFSET) & XCSI_CSR_RIPCD_MASK) {
+               if (timeout == 0) {
+                       dev_err(core->dev, "Xilinx CSI2 Rx Subsystem Soft Reset Timeout!\n");
+                       return -ETIME;
+               }
+
+               timeout--;
+               udelay(1);
+       }
+
+       xcsi2rxss_clr(core, XCSI_CCR_OFFSET, XCSI_CCR_SOFTRESET_MASK);
+       return 0;
+}
+
+/**
+ * xcsi2rxss_irq_handler - Interrupt handler for CSI-2
+ * @irq: IRQ number
+ * @dev_id: Pointer to device state
+ *
+ * In the interrupt handler, a list of event counters are updated for
+ * corresponding interrupts. This is useful to get status / debug.
+ * If the short packet FIFO not empty or overflow interrupt is received
+ * capture the short packet and notify of event occurrence
+ *
+ * Return: IRQ_HANDLED after handling interrupts
+ */
+static irqreturn_t xcsi2rxss_irq_handler(int irq, void *dev_id)
+{
+       struct xcsi2rxss_state *state = (struct xcsi2rxss_state *)dev_id;
+       struct xcsi2rxss_core *core = &state->core;
+       u32 status;
+
+       status = xcsi2rxss_read(core, XCSI_ISR_OFFSET) & XCSI_INTR_MASK;
+       dev_dbg(core->dev, "interrupt status = 0x%08x\n", status);
+
+       if (!status)
+               return IRQ_NONE;
+
+       if (status & XCSI_ISR_SPFIFONE_MASK) {
+               memset(&state->event, 0, sizeof(state->event));
+
+               state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT;
+
+               *((u32 *)(&state->event.u.data)) =
+                       xcsi2rxss_read(core, XCSI_SPKTR_OFFSET);
+
+               v4l2_subdev_notify_event(&state->subdev, &state->event);
+       }
+
+       if (status & XCSI_ISR_SPFIFOF_MASK) {
+               dev_alert(core->dev, "Short packet FIFO overflowed\n");
+
+               memset(&state->event, 0, sizeof(state->event));
+
+               state->event.type = V4L2_EVENT_XLNXCSIRX_SPKT_OVF;
+
+               v4l2_subdev_notify_event(&state->subdev, &state->event);
+       }
+
+       if (status & XCSI_ISR_SLBF_MASK) {
+               dev_alert(core->dev, "Stream Line Buffer Full!\n");
+
+               memset(&state->event, 0, sizeof(state->event));
+
+               state->event.type = V4L2_EVENT_XLNXCSIRX_SLBF;
+
+               v4l2_subdev_notify_event(&state->subdev, &state->event);
+       }
+
+       if (status & XCSI_ISR_ALLINTR_MASK) {
+               unsigned int i;
+
+               for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
+                       if (!(status & core->events[i].mask))
+                               continue;
+                       core->events[i].counter++;
+                       dev_dbg(core->dev, "%s: %d\n", core->events[i].name,
+                               core->events[i].counter);
+               }
+       }
+
+       xcsi2rxss_write(core, XCSI_ISR_OFFSET, status);
+
+       return IRQ_HANDLED;
+}
+
+static void xcsi2rxss_reset_event_counters(struct xcsi2rxss_state *state)
+{
+       u32 i;
+
+       for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++)
+               state->core.events[i].counter = 0;
+}
+
+/**
+ * xcsi2rxss_log_counters - Print out the event counters.
+ * @state: Pointer to device state
+ *
+ */
+static void xcsi2rxss_log_counters(struct xcsi2rxss_state *state)
+{
+       u32 i;
+
+       for (i = 0; i < XMIPICSISS_NUM_EVENTS; i++) {
+               if (state->core.events[i].counter > 0)
+                       v4l2_info(&state->subdev, "%s events: %d\n",
+                                 state->core.events[i].name,
+                                 state->core.events[i].counter);
+       }
+}
+
+/**
+ * xcsi2rxss_log_status - Logs the status of the CSI-2 Receiver
+ * @sd: Pointer to V4L2 subdevice structure
+ *
+ * This function prints the current status of Xilinx MIPI CSI-2
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_log_status(struct v4l2_subdev *sd)
+{
+       struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+       struct xcsi2rxss_core *core = &xcsi2rxss->core;
+       u32 reg, data, i;
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       xcsi2rxss_log_counters(xcsi2rxss);
+
+       v4l2_info(sd, "***** Core Status *****\n");
+       data = xcsi2rxss_read(core, XCSI_CSR_OFFSET);
+       v4l2_info(sd, "Short Packet FIFO Full = %s\n",
+                 XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFOFULL_MASK));
+       v4l2_info(sd, "Short Packet FIFO Not Empty = %s\n",
+                 XCSI_GET_BITSET_STR(data, XCSI_CSR_SPFIFONE_MASK));
+       v4l2_info(sd, "Stream line buffer full = %s\n",
+                 XCSI_GET_BITSET_STR(data, XCSI_CSR_SLBF_MASK));
+       v4l2_info(sd, "Soft reset/Core disable in progress = %s\n",
+                 XCSI_GET_BITSET_STR(data, XCSI_CSR_RIPCD_MASK));
+
+       /* Clk & Lane Info  */
+       v4l2_info(sd, "******** Clock Lane Info *********\n");
+       data = xcsi2rxss_read(core, XCSI_CLKINFR_OFFSET);
+       v4l2_info(sd, "Clock Lane in Stop State = %s\n",
+                 XCSI_GET_BITSET_STR(data, XCSI_CLKINFR_STOP_MASK));
+
+       v4l2_info(sd, "******** Data Lane Info *********\n");
+       v4l2_info(sd, "Lane\tSoT Error\tSoT Sync Error\tStop State\n");
+       reg = XCSI_L0INFR_OFFSET;
+       for (i = 0; i < 4; i++) {
+               data = xcsi2rxss_read(core, reg);
+
+               v4l2_info(sd, "%d\t%s\t\t%s\t\t%s\n",
+                         i,
+                         XCSI_GET_BITSET_STR(data, XCSI_LXINFR_SOTERR_MASK),
+                         data & XCSI_LXINFR_SOTSYNCERR_MASK ? "true" : "false",
+                         XCSI_GET_BITSET_STR(data, XCSI_LXINFR_STOP_MASK));
+
+               reg += 4;
+       }
+
+       /* Virtual Channel Image Information */
+       v4l2_info(sd, "********** Virtual Channel Info ************\n");
+       v4l2_info(sd, "VC\tLine Count\tByte Count\tData Type\n");
+       reg = XCSI_VC0INF1R_OFFSET;
+       for (i = 0; i < 4; i++) {
+               u32 line_count, byte_count, data_type;
+               char *datatypestr;
+
+               /* Get line and byte count from VCXINFR1 Register */
+               data = xcsi2rxss_read(core, reg);
+               byte_count = (data & XCSI_VCXINF1R_BYTECOUNT_MASK) >>
+                               XCSI_VCXINF1R_BYTECOUNT_SHIFT;
+               line_count = (data & XCSI_VCXINF1R_LINECOUNT_MASK) >>
+                               XCSI_VCXINF1R_LINECOUNT_SHIFT;
+
+               /* Get data type from VCXINFR2 Register */
+               reg += 4;
+               data = xcsi2rxss_read(core, reg);
+               data_type = (data & XCSI_VCXINF2R_DATATYPE_MASK) >>
+                               XCSI_VCXINF2R_DATATYPE_SHIFT;
+               datatypestr = (char *)xcsi2rxss_pxlfmtdttostr(data_type);
+
+               v4l2_info(sd, "%d\t%d\t\t%d\t\t%s\n",
+                         i, line_count, byte_count, datatypestr);
+
+               /* Move to next pair of VC Info registers */
+               reg += 4;
+       }
+
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return 0;
+}
+
+/*
+ * xcsi2rxss_subscribe_event - Subscribe to the custom short packet
+ * receive event.
+ * @sd: V4L2 Sub device
+ * @fh: V4L2 File Handle
+ * @sub: Subcribe event structure
+ *
+ * There are two types of events to be subscribed.
+ *
+ * First is to register for receiving a short packet.
+ * The short packets received are queued up in a FIFO.
+ * On reception of a short packet, an event will be generated
+ * with the short packet contents copied to its data area.
+ * Application subscribed to this event will poll for POLLPRI.
+ * On getting the event, the app dequeues the event to get the short packet
+ * data.
+ *
+ * Second is to register for Short packet FIFO overflow
+ * In case the rate of receiving short packets is high and
+ * the short packet FIFO overflows, this event will be triggered.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_subscribe_event(struct v4l2_subdev *sd,
+                                    struct v4l2_fh *fh,
+                                    struct v4l2_event_subscription *sub)
+{
+       int ret;
+       struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       switch (sub->type) {
+       case V4L2_EVENT_XLNXCSIRX_SPKT:
+       case V4L2_EVENT_XLNXCSIRX_SPKT_OVF:
+       case V4L2_EVENT_XLNXCSIRX_SLBF:
+               ret = v4l2_event_subscribe(fh, sub, XCSI_MAX_SPKT, NULL);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return ret;
+}
+
+/**
+ * xcsi2rxss_unsubscribe_event - Unsubscribe from all events registered
+ * @sd: V4L2 Sub device
+ * @fh: V4L2 file handle
+ * @sub: pointer to Event unsubscription structure
+ *
+ * Return: zero on success, else a negative error code.
+ */
+static int xcsi2rxss_unsubscribe_event(struct v4l2_subdev *sd,
+                                      struct v4l2_fh *fh,
+                                      struct v4l2_event_subscription *sub)
+{
+       int ret = 0;
+       struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+       mutex_lock(&xcsi2rxss->lock);
+       ret = v4l2_event_unsubscribe(fh, sub);
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return ret;
+}
+
+/**
+ * xcsi2rxss_s_ctrl - This is used to set the Xilinx MIPI CSI-2 V4L2 controls
+ * @ctrl: V4L2 control to be set
+ *
+ * This function is used to set the V4L2 controls for the Xilinx MIPI
+ * CSI-2 Rx Subsystem. It is used to set the active lanes in the system.
+ * The event counters can be reset.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+       int ret = 0;
+       u32 timeout = XCSI_TIMEOUT_VAL;
+       u32 active_lanes = 1;
+
+       struct xcsi2rxss_state *xcsi2rxss =
+               container_of(ctrl->handler,
+                            struct xcsi2rxss_state, ctrl_handler);
+       struct xcsi2rxss_core *core = &xcsi2rxss->core;
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       switch (ctrl->id) {
+       case V4L2_CID_XILINX_MIPICSISS_ACT_LANES:
+               /*
+                * This will be called only when "Enable Active Lanes" parameter
+                * is set in design
+                */
+               xcsi2rxss_clr_and_set(core, XCSI_PCR_OFFSET,
+                                     XCSI_PCR_ACTLANES_MASK, ctrl->val - 1);
+
+               /*
+                * If the core is enabled, wait for active lanes to be
+                * set.
+                *
+                * If core is disabled or there is no clock from DPHY Tx
+                * then the read back won't reflect the updated value
+                * as the PPI clock will not be present.
+                */
+
+               if (core->dphy_present) {
+                       u32 dphyclkstatregoffset = core->dphy_offset +
+                                                       XDPHY_CLKSTATREG_OFFSET;
+
+                       u32 dphyclkstat =
+                               xcsi2rxss_read(core, dphyclkstatregoffset) &
+                                       XDPHY_CLKSTATREG_MODE_MASK;
+
+                       u32 coreenable =
+                               xcsi2rxss_read(core, XCSI_CCR_OFFSET) &
+                                              XCSI_CCR_COREENB_MASK;
+
+                       char lpmstr[] = "Low Power";
+                       char hsmstr[] = "High Speed";
+                       char esmstr[] = "Escape";
+                       char *modestr;
+
+                       switch (dphyclkstat) {
+                       case 0:
+                               modestr = lpmstr;
+                               break;
+                       case 1:
+                               modestr = hsmstr;
+                               break;
+                       case 2:
+                               modestr = esmstr;
+                               break;
+                       default:
+                               modestr = NULL;
+                               break;
+                       }
+
+                       dev_dbg(core->dev, "DPHY Clock Lane in %s mode\n",
+                               modestr);
+
+                       if (dphyclkstat == XDPHY_HI_SPEED_MODE &&
+                           coreenable) {
+                               /* Wait for core to apply new active lanes */
+                               while (timeout--)
+                                       udelay(1);
+
+                               active_lanes =
+                                       xcsi2rxss_read(core, XCSI_PCR_OFFSET);
+                               active_lanes &= XCSI_PCR_ACTLANES_MASK;
+                               active_lanes++;
+
+                               if (active_lanes != (u32)ctrl->val) {
+                                       dev_err(core->dev, "Failed to set active lanes!\n");
+                                       ret = -EAGAIN;
+                               }
+                       }
+               } else {
+                       dev_dbg(core->dev, "No read back as no DPHY present.\n");
+               }
+
+               dev_dbg(core->dev, "Set active lanes: requested = %d, active = %d\n",
+                       ctrl->val, active_lanes);
+               break;
+       case V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS:
+               xcsi2rxss_reset_event_counters(xcsi2rxss);
+               break;
+       default:
+               break;
+       }
+
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return ret;
+}
+
+/**
+ * xcsi2rxss_g_volatile_ctrl - get the Xilinx MIPI CSI-2 Rx controls
+ * @ctrl: Pointer to V4L2 control
+ *
+ * This is used to get the number of frames received by the Xilinx
+ * MIPI CSI-2 Rx.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+       int ret = 0;
+       struct xcsi2rxss_state *xcsi2rxss =
+               container_of(ctrl->handler,
+                            struct xcsi2rxss_state, ctrl_handler);
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       switch (ctrl->id) {
+       case V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER:
+               ctrl->val = xcsi2rxss->core.events[0].counter;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return ret;
+}
+
+static int xcsi2rxss_start_stream(struct xcsi2rxss_state *xcsi2rxss)
+{
+       int ret;
+
+       xcsi2rxss_enable(&xcsi2rxss->core, true);
+
+       ret = xcsi2rxss_reset(&xcsi2rxss->core);
+       if (ret < 0)
+               return ret;
+
+       xcsi2rxss_interrupts_enable(&xcsi2rxss->core, true);
+
+       return 0;
+}
+
+static void xcsi2rxss_stop_stream(struct xcsi2rxss_state *xcsi2rxss)
+{
+       xcsi2rxss_interrupts_enable(&xcsi2rxss->core, false);
+       xcsi2rxss_enable(&xcsi2rxss->core, false);
+}
+
+/**
+ * xcsi2rxss_s_stream - It is used to start/stop the streaming.
+ * @sd: V4L2 Sub device
+ * @enable: Flag (True / False)
+ *
+ * This function controls the start or stop of streaming for the
+ * Xilinx MIPI CSI-2 Rx Subsystem provided the device isn't in
+ * suspended state.
+ *
+ * Return: 0 on success, errors otherwise
+ */
+static int xcsi2rxss_s_stream(struct v4l2_subdev *sd, int enable)
+{
+       int ret = 0;
+       struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       if (xcsi2rxss->suspended) {
+               ret = -EBUSY;
+               goto unlock;
+       }
+
+       if (enable) {
+               if (!xcsi2rxss->streaming) {
+                       /* reset the event counters */
+                       xcsi2rxss_reset_event_counters(xcsi2rxss);
+
+                       ret = xcsi2rxss_start_stream(xcsi2rxss);
+                       if (ret == 0)
+                               xcsi2rxss->streaming = true;
+               }
+       } else {
+               if (xcsi2rxss->streaming) {
+                       xcsi2rxss_stop_stream(xcsi2rxss);
+                       xcsi2rxss->streaming = false;
+               }
+       }
+unlock:
+       mutex_unlock(&xcsi2rxss->lock);
+       return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+__xcsi2rxss_get_pad_format(struct xcsi2rxss_state *xcsi2rxss,
+                          struct v4l2_subdev_pad_config *cfg,
+                          unsigned int pad, u32 which)
+{
+       switch (which) {
+       case V4L2_SUBDEV_FORMAT_TRY:
+               return v4l2_subdev_get_try_format(&xcsi2rxss->subdev, cfg, pad);
+       case V4L2_SUBDEV_FORMAT_ACTIVE:
+               return &xcsi2rxss->formats[pad];
+       default:
+               return NULL;
+       }
+}
+
+/**
+ * xcsi2rxss_get_format - Get the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @fmt: Pointer to pad level media bus format
+ *
+ * This function is used to get the pad format information.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
+                               struct v4l2_subdev_pad_config *cfg,
+                               struct v4l2_subdev_format *fmt)
+{
+       struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+       mutex_lock(&xcsi2rxss->lock);
+       fmt->format = *__xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
+                                                 fmt->pad, fmt->which);
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return 0;
+}
+
+/**
+ * xcsi2rxss_set_format - This is used to set the pad format
+ * @sd: Pointer to V4L2 Sub device structure
+ * @cfg: Pointer to sub device pad information structure
+ * @fmt: Pointer to pad level media bus format
+ *
+ * This function is used to set the pad format.
+ * Since the pad format is fixed in hardware, it can't be
+ * modified on run time. So when a format set is requested by
+ * application, all parameters except the format type is
+ * saved for the pad and the original pad format is sent
+ * back to the application.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
+                               struct v4l2_subdev_pad_config *cfg,
+                               struct v4l2_subdev_format *fmt)
+{
+       struct v4l2_mbus_framefmt *__format;
+       struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+       struct xcsi2rxss_core *core = &xcsi2rxss->core;
+       u32 code;
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       /*
+        * Only the format->code parameter matters for CSI as the
+        * CSI format cannot be changed at runtime.
+        * Ensure that format to set is copied to over to CSI pad format
+        */
+       __format = __xcsi2rxss_get_pad_format(xcsi2rxss, cfg,
+                                             fmt->pad, fmt->which);
+
+       /* Save the pad format code */
+       code = __format->code;
+
+       /* If the bayer pattern to be set is SXXXX8 then only 1x8 type
+        * is supported and core's data type doesn't matter.
+        * In case the bayer pattern being set is SXXX10 then only
+        * 1x10 type are supported and core should be configured for RAW10.
+        * In case the bayer pattern being set is SXXX12 then only
+        * 1x12 type are supported and core should be configured for RAW12.
+        *
+        * Otherwise don't allow change.
+        */
+       if ((fmt->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 ||
+            fmt->format.code == MEDIA_BUS_FMT_SGBRG8_1X8 ||
+            fmt->format.code == MEDIA_BUS_FMT_SGRBG8_1X8 ||
+            fmt->format.code == MEDIA_BUS_FMT_SRGGB8_1X8) ||
+            (core->datatype == MIPI_CSI_DT_RAW_10 &&
+            (fmt->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 ||
+             fmt->format.code == MEDIA_BUS_FMT_SGBRG10_1X10 ||
+             fmt->format.code == MEDIA_BUS_FMT_SGRBG10_1X10 ||
+             fmt->format.code == MEDIA_BUS_FMT_SRGGB10_1X10)) ||
+            (core->datatype == MIPI_CSI_DT_RAW_12 &&
+            (fmt->format.code == MEDIA_BUS_FMT_SBGGR12_1X12 ||
+             fmt->format.code == MEDIA_BUS_FMT_SGBRG12_1X12 ||
+             fmt->format.code == MEDIA_BUS_FMT_SGRBG12_1X12 ||
+             fmt->format.code == MEDIA_BUS_FMT_SRGGB12_1X12))) {
+               /* Copy over the format to be set */
+               *__format = fmt->format;
+       } else {
+               /* Restore the original pad format code */
+               fmt->format.code = code;
+               __format->code = code;
+       }
+
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return 0;
+}
+
+/**
+ * xcsi2rxss_open - Called on v4l2_open()
+ * @sd: Pointer to V4L2 sub device structure
+ * @fh: Pointer to V4L2 File handle
+ *
+ * This function is called on v4l2_open(). It sets the default format
+ * for both pads.
+ *
+ * Return: 0 on success
+ */
+static int xcsi2rxss_open(struct v4l2_subdev *sd,
+                         struct v4l2_subdev_fh *fh)
+{
+       struct v4l2_mbus_framefmt *format;
+       struct xcsi2rxss_state *xcsi2rxss = to_xcsi2rxssstate(sd);
+
+       format = v4l2_subdev_get_try_format(sd, fh->pad, 0);
+       *format = xcsi2rxss->default_format;
+
+       format = v4l2_subdev_get_try_format(sd, fh->pad, 1);
+       *format = xcsi2rxss->default_format;
+
+       return 0;
+}
+
+static int xcsi2rxss_close(struct v4l2_subdev *sd,
+                          struct v4l2_subdev_fh *fh)
+{
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Media Operations
+ */
+
+static const struct media_entity_operations xcsi2rxss_media_ops = {
+       .link_validate = v4l2_subdev_link_validate
+};
+
+static const struct v4l2_ctrl_ops xcsi2rxss_ctrl_ops = {
+       .g_volatile_ctrl = xcsi2rxss_g_volatile_ctrl,
+       .s_ctrl = xcsi2rxss_s_ctrl
+};
+
+static struct v4l2_ctrl_config xcsi2rxss_ctrls[] = {
+       {
+               .ops    = &xcsi2rxss_ctrl_ops,
+               .id     = V4L2_CID_XILINX_MIPICSISS_ACT_LANES,
+               .name   = "MIPI CSI2 Rx Subsystem: Active Lanes",
+               .type   = V4L2_CTRL_TYPE_INTEGER,
+               .min    = 1,
+               .max    = 4,
+               .step   = 1,
+               .def    = 1,
+       }, {
+               .ops    = &xcsi2rxss_ctrl_ops,
+               .id     = V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER,
+               .name   = "MIPI CSI2 Rx Subsystem: Frames Received Counter",
+               .type   = V4L2_CTRL_TYPE_INTEGER,
+               .min    = 0,
+               .max    = 0xFFFFFFFF,
+               .step   = 1,
+               .def    = 0,
+               .flags  = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+       }, {
+               .ops    = &xcsi2rxss_ctrl_ops,
+               .id     = V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS,
+               .name   = "MIPI CSI2 Rx Subsystem: Reset Counters",
+               .type   = V4L2_CTRL_TYPE_BUTTON,
+               .min    = 0,
+               .max    = 1,
+               .step   = 1,
+               .def    = 0,
+               .flags  = V4L2_CTRL_FLAG_WRITE_ONLY,
+       }
+};
+
+static const struct v4l2_subdev_core_ops xcsi2rxss_core_ops = {
+       .log_status = xcsi2rxss_log_status,
+       .subscribe_event = xcsi2rxss_subscribe_event,
+       .unsubscribe_event = xcsi2rxss_unsubscribe_event
+};
+
+static struct v4l2_subdev_video_ops xcsi2rxss_video_ops = {
+       .s_stream = xcsi2rxss_s_stream
+};
+
+static struct v4l2_subdev_pad_ops xcsi2rxss_pad_ops = {
+       .get_fmt = xcsi2rxss_get_format,
+       .set_fmt = xcsi2rxss_set_format,
+};
+
+static struct v4l2_subdev_ops xcsi2rxss_ops = {
+       .core = &xcsi2rxss_core_ops,
+       .video = &xcsi2rxss_video_ops,
+       .pad = &xcsi2rxss_pad_ops
+};
+
+static const struct v4l2_subdev_internal_ops xcsi2rxss_internal_ops = {
+       .open = xcsi2rxss_open,
+       .close = xcsi2rxss_close
+};
+
+/* -----------------------------------------------------------------------------
+ * Power Management
+ */
+
+/*
+ * xcsi2rxss_pm_suspend - Function called on Power Suspend
+ * @dev: Pointer to device structure
+ *
+ * On power suspend the CSI-2 Core is disabled if the device isn't
+ * in suspended state and is streaming.
+ *
+ * Return: 0 on success
+ */
+static int __maybe_unused xcsi2rxss_pm_suspend(struct device *dev)
+{
+       struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       if (!xcsi2rxss->suspended && xcsi2rxss->streaming)
+               xcsi2rxss_clr(&xcsi2rxss->core,
+                             XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
+
+       xcsi2rxss->suspended = true;
+
+       mutex_unlock(&xcsi2rxss->lock);
+
+       return 0;
+}
+
+/*
+ * xcsi2rxss_pm_resume - Function called on Power Resume
+ * @dev: Pointer to device structure
+ *
+ * On power resume the CSI-2 Core is enabled when it is in suspended state
+ * and prior to entering suspended state it was streaming.
+ *
+ * Return: 0 on success
+ */
+static int __maybe_unused xcsi2rxss_pm_resume(struct device *dev)
+{
+       struct xcsi2rxss_state *xcsi2rxss = dev_get_drvdata(dev);
+
+       mutex_lock(&xcsi2rxss->lock);
+
+       if (xcsi2rxss->suspended && xcsi2rxss->streaming)
+               xcsi2rxss_set(&xcsi2rxss->core,
+                             XCSI_CCR_OFFSET, XCSI_CCR_COREENB_MASK);
+
+       xcsi2rxss->suspended = false;
+
+       mutex_unlock(&xcsi2rxss->lock);
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Platform Device Driver
+ */
+
+static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
+{
+       struct device_node *node = xcsi2rxss->core.dev->of_node;
+       struct device_node *ports = NULL;
+       struct device_node *port = NULL;
+       unsigned int nports = 0;
+       struct xcsi2rxss_core *core = &xcsi2rxss->core;
+       int ret;
+       bool iic_present;
+
+       core->dphy_present = of_property_read_bool(node, "xlnx,dphy-present");
+       dev_dbg(core->dev, "DPHY present property = %s\n",
+               core->dphy_present ? "Present" : "Absent");
+
+       iic_present = of_property_read_bool(node, "xlnx,iic-present");
+       dev_dbg(core->dev, "IIC present property = %s\n",
+               iic_present ? "Present" : "Absent");
+
+       if (core->dphy_present) {
+               if (iic_present)
+                       core->dphy_offset = 0x20000;
+               else
+                       core->dphy_offset = 0x10000;
+       }
+
+       ret = of_property_read_u32(node, "xlnx,max-lanes",
+                                  &core->max_num_lanes);
+       if (ret < 0) {
+               dev_err(core->dev, "missing xlnx,max-lanes property\n");
+               return ret;
+       }
+
+       if (core->max_num_lanes > 4 || core->max_num_lanes < 1) {
+               dev_err(core->dev, "%d max lanes : invalid xlnx,max-lanes property\n",
+                       core->max_num_lanes);
+               return -EINVAL;
+       }
+
+       ret = of_property_read_u32(node, "xlnx,vc", &core->vc);
+       if (ret < 0) {
+               dev_err(core->dev, "missing xlnx,vc property\n");
+               return ret;
+       }
+       if (core->vc > 4) {
+               dev_err(core->dev, "invalid virtual channel property value.\n");
+               return -EINVAL;
+       }
+
+       core->enable_active_lanes =
+               of_property_read_bool(node, "xlnx,en-active-lanes");
+       dev_dbg(core->dev, "Enable active lanes property = %s\n",
+               core->enable_active_lanes ? "Present" : "Absent");
+
+       ret = of_property_read_string(node, "xlnx,csi-pxl-format",
+                                     &core->pxlformat);
+       if (ret < 0) {
+               dev_err(core->dev, "missing xlnx,csi-pxl-format property\n");
+               return ret;
+       }
+
+       core->datatype = xcsi2rxss_pxlfmtstrtodt(core->pxlformat);
+       if (core->datatype < MIPI_CSI_DT_YUV_420_8B ||
+           core->datatype > MIPI_CSI_DT_RAW_14) {
+               dev_err(core->dev, "Invalid xlnx,csi-pxl-format string\n");
+               return -EINVAL;
+       }
+
+       core->vfb = of_property_read_bool(node, "xlnx,vfb");
+       dev_dbg(core->dev, "Video Format Bridge property = %s\n",
+               core->vfb ? "Present" : "Absent");
+
+       if (core->vfb) {
+               ret = of_property_read_u32(node, "xlnx,ppc", &core->ppc);
+               if (ret < 0 || !(core->ppc == 1 || core->ppc == 2 ||
+                                core->ppc == 4)) {
+                       dev_err(core->dev, "Invalid xlnx,ppc property ret = %d ppc = %d\n",
+                               ret, core->ppc);
+                       return -EINVAL;
+               }
+       }
+
+       ports = of_get_child_by_name(node, "ports");
+       if (!ports)
+               ports = node;
+
+       for_each_child_of_node(ports, port) {
+               int ret;
+               const struct xvip_video_format *format;
+               struct device_node *endpoint;
+               struct v4l2_fwnode_endpoint v4lendpoint;
+
+               if (!port->name || of_node_cmp(port->name, "port"))
+                       continue;
+
+               /*
+                * Currently only a subset of VFB enabled formats present in
+                * xvip are supported in the  driver.
+                *
+                * If the VFB is disabled, the pixels per clock don't matter.
+                * The data width is either 32 or 64 bit as selected in design.
+                *
+                * For e.g. If Data Type is RGB888, VFB is disabled and
+                * data width is 32 bits.
+                *
+                * Clk Cycle  |  Byte 0  |  Byte 1  |  Byte 2  |  Byte 3
+                * -----------+----------+----------+----------+----------
+                *     1      |     B0   |     G0   |     R0   |     B1
+                *     2      |     G1   |     R1   |     B2   |     G2
+                *     3      |     R2   |     B3   |     G3   |     R3
+                */
+               format = xvip_of_get_format(port);
+               if (IS_ERR(format)) {
+                       dev_err(core->dev, "invalid format in DT");
+                       return PTR_ERR(format);
+               }
+
+               if (core->vfb &&
+                   format->vf_code != XVIP_VF_YUV_422 &&
+                   format->vf_code != XVIP_VF_RBG &&
+                   format->vf_code != XVIP_VF_MONO_SENSOR) {
+                       dev_err(core->dev, "Invalid UG934 video format set.\n");
+                       return -EINVAL;
+               }
+
+               /* Get and check the format description */
+               if (!xcsi2rxss->vip_format) {
+                       xcsi2rxss->vip_format = format;
+               } else if (xcsi2rxss->vip_format != format) {
+                       dev_err(core->dev, "in/out format mismatch in DT");
+                       return -EINVAL;
+               }
+
+               endpoint = of_get_next_child(port, NULL);
+               if (!endpoint) {
+                       dev_err(core->dev, "No port at\n");
+                       return -EINVAL;
+               }
+
+               ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+                                                &v4lendpoint);
+               if (ret) {
+                       of_node_put(endpoint);
+                       return ret;
+               }
+
+               of_node_put(endpoint);
+               dev_dbg(core->dev, "%s : port %d bus type = %d\n",
+                       __func__, nports, v4lendpoint.bus_type);
+
+               if (v4lendpoint.bus_type == V4L2_MBUS_CSI2) {
+                       dev_dbg(core->dev, "%s : base.port = %d base.id = %d\n",
+                               __func__, v4lendpoint.base.port,
+                               v4lendpoint.base.id);
+
+                       dev_dbg(core->dev, "%s : mipi number lanes = %d\n",
+                               __func__,
+                               v4lendpoint.bus.mipi_csi2.num_data_lanes);
+               } else {
+                       dev_dbg(core->dev, "%s : Not a CSI2 bus\n", __func__);
+               }
+
+               /* Count the number of ports. */
+               nports++;
+       }
+
+       if (nports != 2) {
+               dev_err(core->dev, "invalid number of ports %u\n", nports);
+               return -EINVAL;
+       }
+       xcsi2rxss->npads = nports;
+
+       /*Register interrupt handler */
+       core->irq = irq_of_parse_and_map(node, 0);
+
+       ret = devm_request_irq(core->dev, core->irq, xcsi2rxss_irq_handler,
+                              IRQF_SHARED, "xilinx-csi2rxss", xcsi2rxss);
+       if (ret) {
+               dev_err(core->dev, "Err = %d Interrupt handler reg failed!\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int xcsi2rxss_probe(struct platform_device *pdev)
+{
+       struct v4l2_subdev *subdev;
+       struct xcsi2rxss_state *xcsi2rxss;
+       struct resource *res;
+
+       u32 i;
+       int ret;
+       int num_ctrls;
+
+       xcsi2rxss = devm_kzalloc(&pdev->dev, sizeof(*xcsi2rxss), GFP_KERNEL);
+       if (!xcsi2rxss)
+               return -ENOMEM;
+
+       mutex_init(&xcsi2rxss->lock);
+
+       xcsi2rxss->core.dev = &pdev->dev;
+
+       ret = xcsi2rxss_parse_of(xcsi2rxss);
+       if (ret < 0)
+               return ret;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       xcsi2rxss->core.iomem = devm_ioremap_resource(xcsi2rxss->core.dev, res);
+       if (IS_ERR(xcsi2rxss->core.iomem))
+               return PTR_ERR(xcsi2rxss->core.iomem);
+
+       /*
+        * Reset and initialize the core.
+        */
+       xcsi2rxss_reset(&xcsi2rxss->core);
+
+       xcsi2rxss->core.events =  (struct xcsi2rxss_event *)&xcsi2rxss_events;
+
+       /* Initialize V4L2 subdevice and media entity */
+       xcsi2rxss->pads[0].flags = MEDIA_PAD_FL_SOURCE;
+       xcsi2rxss->pads[1].flags = MEDIA_PAD_FL_SINK;
+
+       /* Initialize the default format */
+       memset(&xcsi2rxss->default_format, 0,
+              sizeof(xcsi2rxss->default_format));
+       xcsi2rxss->default_format.code = xcsi2rxss->vip_format->code;
+       xcsi2rxss->default_format.field = V4L2_FIELD_NONE;
+       xcsi2rxss->default_format.colorspace = V4L2_COLORSPACE_SRGB;
+       xcsi2rxss->default_format.width = XCSI_DEFAULT_WIDTH;
+       xcsi2rxss->default_format.height = XCSI_DEFAULT_HEIGHT;
+
+       xcsi2rxss->formats[0] = xcsi2rxss->default_format;
+       xcsi2rxss->formats[1] = xcsi2rxss->default_format;
+
+       /* Initialize V4L2 subdevice and media entity */
+       subdev = &xcsi2rxss->subdev;
+       v4l2_subdev_init(subdev, &xcsi2rxss_ops);
+
+       subdev->dev = &pdev->dev;
+       subdev->internal_ops = &xcsi2rxss_internal_ops;
+       strlcpy(subdev->name, dev_name(&pdev->dev), sizeof(subdev->name));
+
+       subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+       subdev->entity.ops = &xcsi2rxss_media_ops;
+
+       v4l2_set_subdevdata(subdev, xcsi2rxss);
+
+       ret = media_entity_pads_init(&subdev->entity, 2, xcsi2rxss->pads);
+       if (ret < 0)
+               goto error;
+
+       /*
+        * In case the Enable Active Lanes config parameter is not set,
+        * dynamic lane reconfiguration is not allowed.
+        * So V4L2_CID_XILINX_MIPICSISS_ACT_LANES ctrl will not be registered.
+        * Accordingly allocate the number of controls
+        */
+       num_ctrls = ARRAY_SIZE(xcsi2rxss_ctrls);
+
+       if (!xcsi2rxss->core.enable_active_lanes)
+               num_ctrls--;
+
+       dev_dbg(xcsi2rxss->core.dev, "# of ctrls = %d\n", num_ctrls);
+
+       v4l2_ctrl_handler_init(&xcsi2rxss->ctrl_handler, num_ctrls);
+
+       for (i = 0; i < ARRAY_SIZE(xcsi2rxss_ctrls); i++) {
+               struct v4l2_ctrl *ctrl;
+
+               if (xcsi2rxss_ctrls[i].id ==
+                       V4L2_CID_XILINX_MIPICSISS_ACT_LANES) {
+                       if (xcsi2rxss->core.enable_active_lanes) {
+                               xcsi2rxss_ctrls[i].max =
+                                       xcsi2rxss->core.max_num_lanes;
+                       } else {
+                               /* Don't register control */
+                               dev_dbg(xcsi2rxss->core.dev,
+                                       "Skip active lane control\n");
+                               continue;
+                       }
+               }
+
+               dev_dbg(xcsi2rxss->core.dev, "%d ctrl = 0x%x\n",
+                       i, xcsi2rxss_ctrls[i].id);
+               ctrl = v4l2_ctrl_new_custom(&xcsi2rxss->ctrl_handler,
+                                           &xcsi2rxss_ctrls[i], NULL);
+               if (!ctrl) {
+                       dev_err(xcsi2rxss->core.dev, "Failed for %s ctrl\n",
+                               xcsi2rxss_ctrls[i].name);
+                       goto error;
+               }
+       }
+
+       dev_dbg(xcsi2rxss->core.dev, "# v4l2 ctrls registered = %d\n", i - 1);
+
+       if (xcsi2rxss->ctrl_handler.error) {
+               dev_err(&pdev->dev, "failed to add controls\n");
+               ret = xcsi2rxss->ctrl_handler.error;
+               goto error;
+       }
+
+       subdev->ctrl_handler = &xcsi2rxss->ctrl_handler;
+
+       ret = v4l2_ctrl_handler_setup(&xcsi2rxss->ctrl_handler);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to set controls\n");
+               goto error;
+       }
+
+       platform_set_drvdata(pdev, xcsi2rxss);
+
+       dev_info(xcsi2rxss->core.dev, "Xilinx CSI2 Rx Subsystem device found!\n");
+
+       ret = v4l2_async_register_subdev(subdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to register subdev\n");
+               goto error;
+       }
+
+       /* default states for streaming and suspend */
+       xcsi2rxss->streaming = false;
+       xcsi2rxss->suspended = false;
+       return 0;
+
+error:
+       v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
+       media_entity_cleanup(&subdev->entity);
+       mutex_destroy(&xcsi2rxss->lock);
+
+       return ret;
+}
+
+static int xcsi2rxss_remove(struct platform_device *pdev)
+{
+       struct xcsi2rxss_state *xcsi2rxss = platform_get_drvdata(pdev);
+       struct v4l2_subdev *subdev = &xcsi2rxss->subdev;
+
+       v4l2_async_unregister_subdev(subdev);
+       v4l2_ctrl_handler_free(&xcsi2rxss->ctrl_handler);
+       media_entity_cleanup(&subdev->entity);
+       mutex_destroy(&xcsi2rxss->lock);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(xcsi2rxss_pm_ops,
+                        xcsi2rxss_pm_suspend, xcsi2rxss_pm_resume);
+
+static const struct of_device_id xcsi2rxss_of_id_table[] = {
+       { .compatible = "xlnx,mipi-csi2-rx-subsystem-2.0" },
+       { .compatible = "xlnx,mipi-csi2-rx-subsystem-3.0" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, xcsi2rxss_of_id_table);
+
+static struct platform_driver xcsi2rxss_driver = {
+       .driver = {
+               .name           = "xilinx-csi2rxss",
+               .pm             = &xcsi2rxss_pm_ops,
+               .of_match_table = xcsi2rxss_of_id_table,
+       },
+       .probe                  = xcsi2rxss_probe,
+       .remove                 = xcsi2rxss_remove,
+};
+module_platform_driver(xcsi2rxss_driver);
+MODULE_AUTHOR("Vishal Sagar <vishal.sagar@xilinx.com>");
+MODULE_DESCRIPTION("Xilinx MIPI CSI2 Rx Subsystem Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/uapi/linux/xilinx-csi2rxss.h b/include/uapi/linux/xilinx-csi2rxss.h
new file mode 100644
index 0000000..973d5b46
--- /dev/null
+++ b/include/uapi/linux/xilinx-csi2rxss.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Events from Xilinx MIPI CSI2 Rx Subsystem exposed to V4L2 application.
+ *
+ * Copyright (C) 2016 - 2018 Xilinx, Inc.
+ * Author: Vishal Sagar <vishal.sagar@xilinx.com>
+ */
+#ifndef __UAPI_XILINX_CSI2RXSS_H__
+#define __UAPI_XILINX_CSI2RXSS_H__
+
+#include <linux/videodev2.h>
+
+/*
+ * Events
+ *
+ * V4L2_EVENT_XLNXCSIRX_SPKT: Short packet received
+ * V4L2_EVENT_XLNXCSIRX_SPKT_OVF: Short packet FIFO overflow
+ * V4L2_EVENT_XLNXCSIRX_SLBF: Stream line buffer full
+ */
+#define V4L2_EVENT_XLNXCSIRX_CLASS     (V4L2_EVENT_PRIVATE_START | 0x100)
+#define V4L2_EVENT_XLNXCSIRX_SPKT      (V4L2_EVENT_XLNXCSIRX_CLASS | 0x1)
+#define V4L2_EVENT_XLNXCSIRX_SPKT_OVF  (V4L2_EVENT_XLNXCSIRX_CLASS | 0x2)
+#define V4L2_EVENT_XLNXCSIRX_SLBF      (V4L2_EVENT_XLNXCSIRX_CLASS | 0x3)
+
+#endif /* __UAPI_XILINX_CSI2RXSS_H__ */
diff --git a/include/uapi/linux/xilinx-v4l2-controls.h b/include/uapi/linux/xilinx-v4l2-controls.h
index b6441fe..4ca3b44 100644
--- a/include/uapi/linux/xilinx-v4l2-controls.h
+++ b/include/uapi/linux/xilinx-v4l2-controls.h
@@ -71,4 +71,18 @@
 /* Noise level */
 #define V4L2_CID_XILINX_TPG_NOISE_GAIN         (V4L2_CID_XILINX_TPG + 17)

+/*
+ * Xilinx MIPI CSI2 Rx Subsystem
+ */
+
+/* Base ID */
+#define V4L2_CID_XILINX_MIPICSISS              (V4L2_CID_USER_BASE + 0xc080)
+
+/* Active Lanes */
+#define V4L2_CID_XILINX_MIPICSISS_ACT_LANES    (V4L2_CID_XILINX_MIPICSISS + 1)
+/* Frames received since streaming is set */
+#define V4L2_CID_XILINX_MIPICSISS_FRAME_COUNTER        (V4L2_CID_XILINX_MIPICSISS + 2)
+/* Reset all event counters */
+#define V4L2_CID_XILINX_MIPICSISS_RESET_COUNTERS (V4L2_CID_XILINX_MIPICSISS + 3)
+
 #endif /* __UAPI_XILINX_V4L2_CONTROLS_H__ */
--
2.7.4

This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

^ permalink raw reply related

* Re: [PATCH 02/11] PM / devfreq: Fix handling of min/max_freq == 0
From: Matthias Kaehlcke @ 2018-05-29 18:57 UTC (permalink / raw)
  To: Chanwoo Choi
  Cc: MyungJoo Ham, Kyungmin Park, Arnd Bergmann, Greg Kroah-Hartman,
	Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
	Brian Norris, Douglas Anderson, Ørjan Eide,
	John Einar Reitan
In-Reply-To: <5B0BA3BB.8060505@samsung.com>

On Mon, May 28, 2018 at 03:37:47PM +0900, Chanwoo Choi wrote:
> Hi,
> 
> On 2018년 05월 26일 05:30, Matthias Kaehlcke wrote:
> > Commit ab8f58ad72c4 ("PM / devfreq: Set min/max_freq when adding the
> > devfreq device") initializes df->min/max_freq with the min/max OPP when
> > the device is added. Later commit f1d981eaecf8 ("PM / devfreq: Use the
> > available min/max frequency") adds df->scaling_min/max_freq and the
> > following to the frequency adjustment code:
> > 
> >   max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
> > 
> > With the current handling of min/max_freq this is incorrect:
> > 
> > Even though df->max_freq is now initialized to a value != 0 user space
> > can still set it to 0, in this case max_freq would be 0 instead of
> > df->scaling_max_freq as intended. In consequence the frequency adjustment
> > is not performed:
> > 
> >   if (max_freq && freq > max_freq) {
> > 	freq = max_freq;
> > 
> > To fix this set df->min/max freq to the min/max OPP in max/max_freq_store,
> > when the user passes a value of 0. This also prevents df->max_freq from
> > being set below the min OPP when df->min_freq is 0, and similar for
> > min_freq. Since it is now guaranteed that df->min/max_freq can't be 0 the
> > checks for this case can be removed.
> > 
> > Fixes: f1d981eaecf8 ("PM / devfreq: Use the available min/max frequency")
> > Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
> > ---
> >  drivers/devfreq/devfreq.c | 30 ++++++++++++++++++------------
> >  1 file changed, 18 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
> > index 0057ef5b0a98..67da4e7b486b 100644
> > --- a/drivers/devfreq/devfreq.c
> > +++ b/drivers/devfreq/devfreq.c
> > @@ -283,11 +283,11 @@ int update_devfreq(struct devfreq *devfreq)
> >  	max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
> >  	min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq);
> >  
> > -	if (min_freq && freq < min_freq) {
> > +	if (freq < min_freq) {
> >  		freq = min_freq;
> >  		flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
> >  	}
> > -	if (max_freq && freq > max_freq) {
> > +	if (freq > max_freq) {
> >  		freq = max_freq;
> >  		flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
> >  	}
> > @@ -1123,17 +1123,20 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
> >  	struct devfreq *df = to_devfreq(dev);
> >  	unsigned long value;
> >  	int ret;
> > -	unsigned long max;
> >  
> >  	ret = sscanf(buf, "%lu", &value);
> >  	if (ret != 1)
> >  		return -EINVAL;
> >  
> >  	mutex_lock(&df->lock);
> > -	max = df->max_freq;
> > -	if (value && max && value > max) {
> > -		ret = -EINVAL;
> > -		goto unlock;
> > +
> > +	if (value) {
> > +		if (value > df->max_freq) {
> > +			ret = -EINVAL;
> > +			goto unlock;
> > +		}
> > +	} else {
> > +		value = df->profile->freq_table[df->profile->max_state - 1];
> >  	}
> 
> If you want to prevent that df->min_freq is zero, 
> you should reinitialize 'value' as following.
> Because freq_table must be in ascending order.
> 	value = df->profile->freq_table[0];

Thanks for pointing this out!

The devfreq device I tested with (a Mali GPU) uses descending order
for some reason, and I assumed that's the usual order.

https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-4.4/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c#208

It seems the ordering doesn't have any impact beyond this patch. If
the order isn't mandatory for drivers that set up their own freq_table
we should probably support both cases to be safe.

> > @@ -1158,17 +1161,20 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
> >  	struct devfreq *df = to_devfreq(dev);
> >  	unsigned long value;
> >  	int ret;
> > -	unsigned long min;
> >  
> >  	ret = sscanf(buf, "%lu", &value);
> >  	if (ret != 1)
> >  		return -EINVAL;
> >  
> >  	mutex_lock(&df->lock);
> > -	min = df->min_freq;
> > -	if (value && min && value < min) {
> > -		ret = -EINVAL;
> > -		goto unlock;
> > +
> > +	if (!value) {
> > +		value = df->profile->freq_table[0];
> 
> ditto.
> 	value = df->profile->freq_table[df->profile->max_state - 1];
> 
> > +	} else {
> > +		if (value < df->min_freq) {
> > +			ret = -EINVAL;
> > +			goto unlock;
> > +		}
> >  	}
> >  
> >  	df->max_freq = value;
> > 
> 
> Actually, min_freq_store() and max_freq_store() are very similar.
> But, this patch changed the order of conditional statement as following:
> If there is no special reason, you better to keep the same format
> for the readability.
> 
> 
> min_freq_store()
> 	if (value) {
> 		...
> 	} else {
> 		value = df->profile->freq_table[df->profile->max_state - 1];
> 	}
> 
> 
> max_freq_store()
> 	if (!value) {
> 		value = df->profile->freq_table[0];
> 	} else {
> 		...
> 

Agreed, better use the same format, I'll update it in the next revision.

^ permalink raw reply

* Re: [PATCH v2 5/6] soc: qcom: rpmh powerdomain driver
From: David Collins @ 2018-05-29 19:03 UTC (permalink / raw)
  To: Rajendra Nayak, viresh.kumar, sboyd, andy.gross, ulf.hansson
  Cc: devicetree, linux-arm-msm, linux-kernel, Lina Iyer
In-Reply-To: <6f8e5fe2-199d-ba1f-19d7-2faf276075f3@codeaurora.org>

Hello Rajendra,

On 05/29/2018 03:19 AM, Rajendra Nayak wrote:
> On 05/26/2018 06:38 AM, David Collins wrote:
>> On 05/25/2018 03:01 AM, Rajendra Nayak wrote:
>>> The RPMh powerdomain driver aggregates the corner votes from various
...
>>> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
>>> index a7a405178967..1faed239701d 100644
>>> --- a/drivers/soc/qcom/Kconfig
>>> +++ b/drivers/soc/qcom/Kconfig
>>> @@ -74,6 +74,15 @@ config QCOM_RMTFS_MEM
>>>  
>>>  	  Say y here if you intend to boot the modem remoteproc.
>>>  
>>> +config QCOM_RPMHPD
>>> +	tristate "Qualcomm RPMh Powerdomain driver"
>>
>> s/Qualcomm/Qualcomm Technologies, Inc./
> 
> All other config options in qcom/Kconfig use 'Qualcomm XYZ feature'
> for the comment. Maybe I will leave it that way for consistency?

I don't have a strong opinion about it.  I just want the legal folks to be
happy.  I'm fine with whatever achieves that goal.


>>> +
>>> +struct rpmhpd_desc {
>>> +	struct rpmhpd **rpmhpds;
>>> +	size_t num_pds;
>>> +};
>>
>> This struct could be removed and the per-platform arrays could instead be
>> NULL terminated.
> 
> Yes, but I would prefer it this way unless you have strong objections.
> Just makes it easier to do the allocations at probe for genpd_onecell_data structures.

I'm fine if you keep it as-is.  I mentioned the alternative because
Stephen had requested the same modification on my qcom-rpmh-regulator
driver patch [1].  Other reviewers may care about this point.


>> Is there an API to determine the currently operating performance state of
>> a given power domain?  Is this information accessible from userspace?  We
>> will definitely need this for general debugging.
> 
> A quick look shows me its not. I agree its a necessary feature for debug.
> I will add a patch to expose it via debugfs

Thanks


>>> +static int rpmhpd_probe(struct platform_device *pdev)
>>> +{
>>> +	int i, ret;
>>> +	size_t num;
>>> +	struct genpd_onecell_data *data;
>>> +	struct rpmhpd **rpmhpds;
>>> +	const struct rpmhpd_desc *desc;
>>> +
>>> +	desc = of_device_get_match_data(&pdev->dev);
>>> +	if (!desc)
>>> +		return -EINVAL;
>>> +
>>> +	rpmhpds = desc->rpmhpds;
>>> +	num = desc->num_pds;
>>> +
>>> +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
>>> +	if (!data)
>>> +		return -ENOMEM;
>>> +
>>> +	data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains),
>>> +				     GFP_KERNEL);
>>> +	data->num_domains = num;
>>> +
>>> +	ret = cmd_db_ready();
>>> +	if (ret) {
>>> +		if (ret != -EPROBE_DEFER)
>>> +			dev_err(&pdev->dev, "Command DB unavailable, ret=%d\n",
>>> +				ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	for (i = 0; i < num; i++) {
>>> +		if (!rpmhpds[i])
>>> +			continue;
>>
>> Why is this check needed?
> 
> Just to check/ignore if there are any holes.
> maybe I should atleast throw a warning instead of silently ignoring it?

A warning message might be a good idea if this condition should ever be
reached but also doesn't necessarily imply that probing must be ceased.
It looks like of_genpd_add_provider_onecell() ignores the NULL initialized
data->domains[i] values so it should be safe to leave the holes in and not
decrement num_domains accordingly.

Take care,
David

[1]: https://lkml.org/lkml/2018/3/21/681

-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH 2/4 RFCv2] net: dsa: Add bindings for Realtek SMI DSAs
From: Florian Fainelli @ 2018-05-29 19:30 UTC (permalink / raw)
  To: Linus Walleij, Andrew Lunn, Vivien Didelot
  Cc: netdev, openwrt-devel, LEDE Development List,
	Antti Seppälä, Roman Yeryomin, Colin Leitner,
	Gabor Juhos, devicetree
In-Reply-To: <20180528174752.6806-3-linus.walleij@linaro.org>



On 05/28/2018 10:47 AM, Linus Walleij wrote:
> The Realtek SMI family is a set of DSA chips that provide
> switching in routers. This binding just follows the pattern
> set by other switches but with the introduction of an embedded
> irqchip to demux and handle the interrupts fired by the single
> line from the chip.
> 
> This interrupt construction is similar to how we handle
> interrupt controllers inside PCI bridges etc.
> 
> Cc: Antti Seppälä <a.seppala@gmail.com>
> Cc: Roman Yeryomin <roman@advem.lv>
> Cc: Colin Leitner <colin.leitner@googlemail.com>
> Cc: Gabor Juhos <juhosg@openwrt.org>
> Cc: Florian Fainelli <f.fainelli@gmail.com>
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
> ---

> +
> +	mdio {
> +		compatible = "realtek,smi-mdio", "dsa-mdio";

You should drop the "dsa-mdio" compatible string here since it both non
documented and not matched either.

Other than that, this looks great, with that fixed:

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
-- 
Florian

^ permalink raw reply

* Re: [PATCH 05/11] PM / devfreq: governors: Return device frequency limits instead of user limits
From: Matthias Kaehlcke @ 2018-05-29 19:32 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
	Rob Herring, Mark Rutland, linux-pm@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Brian Norris, Douglas Anderson
In-Reply-To: <20180528050444epcms1p8e6552d55231a099ec89304dfb1f819b9@epcms1p8>

On Mon, May 28, 2018 at 02:04:44PM +0900, MyungJoo Ham wrote:
> >The performance, powersave and simpleondemand governors can return
> >df->min/max_freq, which are the user defined frequency limits.
> >update_devfreq() already takes care of adjusting the target frequency
> >with the user limits if necessary, therefore we can return
> >df->scaling_min/max_freq instead, which is the min/max frequency
> >supported by the device at a given time (depending on the
> >enabled/disabled OPPs)
> >
> >Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
> >---
> > drivers/devfreq/governor_performance.c    | 2 +-
> > drivers/devfreq/governor_powersave.c      | 2 +-
> > drivers/devfreq/governor_simpleondemand.c | 6 +++---
> > 3 files changed, 5 insertions(+), 5 deletions(-)
> >
> 
> Actually, even scaling_max_freq and scaling_min_freq are
> covered centerally at devfreq.c:update_devfreq();
> 
> Wouldn't it be sufficient to return UINT_MAX for performance
> and return UINT_MIN (0) for powersave, if the purpose is to
> remove redundancy?
> 
> In the same sense, we may return UINT_MAX for freq-increasing
> case for simpleondemand as well, because they are filtered
> centrally anyway.
> 
> (This commit might be better merged to 4/11 in that case as well.)

I did this in the first variant of the patch (before sending it in a
series), but Chanwoo Choi objected:

https://patchwork.kernel.org/patch/10404893/

I also still think that returning a constant would be the cleanest
solution if we can agree on this. What do you think about
DEVFREQ_MIN/MAX_FREQ (0/UINT_MAX) to make things slightly clearer?

^ permalink raw reply

* Re: [PATCH 2/2] media: v4l: xilinx: Add Xilinx MIPI CSI-2 Rx Subsystem driver
From: Randy Dunlap @ 2018-05-29 19:59 UTC (permalink / raw)
  To: Vishal Sagar, hyun.kwon, laurent.pinchart, michal.simek,
	linux-media, devicetree
  Cc: sakari.ailus, hans.verkuil, mchehab, robh+dt, mark.rutland,
	dineshk, linux-arm-kernel, linux-kernel
In-Reply-To: <1527620084-94864-3-git-send-email-vishal.sagar@xilinx.com>

On 05/29/2018 11:54 AM, Vishal Sagar wrote:
> 
> Signed-off-by: Vishal Sagar <vishal.sagar@xilinx.com>
> ---
>  drivers/media/platform/xilinx/Kconfig           |   12 +
>  drivers/media/platform/xilinx/Makefile          |    1 +
>  drivers/media/platform/xilinx/xilinx-csi2rxss.c | 1751 +++++++++++++++++++++++
>  include/uapi/linux/xilinx-csi2rxss.h            |   25 +
>  include/uapi/linux/xilinx-v4l2-controls.h       |   14 +
>  5 files changed, 1803 insertions(+)
>  create mode 100644 drivers/media/platform/xilinx/xilinx-csi2rxss.c
>  create mode 100644 include/uapi/linux/xilinx-csi2rxss.h
> 
> diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
> index a5d21b7..06d5944 100644
> --- a/drivers/media/platform/xilinx/Kconfig
> +++ b/drivers/media/platform/xilinx/Kconfig
> @@ -8,6 +8,18 @@ config VIDEO_XILINX
> 
>  if VIDEO_XILINX
> 
> +config VIDEO_XILINX_CSI2RXSS
> +       tristate "Xilinx CSI2 Rx Subsystem"
> +       depends on VIDEO_XILINX
> +       help
> +         Driver for Xilinx MIPI CSI2 Rx Subsystem. This is a V4L sub-device
> +         based driver that takes input from CSI2 Tx source and converts
> +         it into an AXI4-Stream. It has a DPHY (whose register interface
> +         can be enabled, an optional I2C controller and an optional Video

	    can be enabled),

> +         Format Bridge which converts the AXI4-Stream data to Xilinx Video
> +         Bus formats based on UG934. The driver is used to set the number
> +         of active lanes and get short packet data.
> +
>  config VIDEO_XILINX_TPG
>         tristate "Xilinx Video Test Pattern Generator"
>         depends on VIDEO_XILINX



> This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.

:(

-- 
~Randy

^ permalink raw reply

* Re: [PATCH 07/11] PM / devfreg: Add support policy notifiers
From: Matthias Kaehlcke @ 2018-05-29 20:02 UTC (permalink / raw)
  To: MyungJoo Ham
  Cc: Kyungmin Park, Chanwoo Choi, Arnd Bergmann, Greg Kroah-Hartman,
	Rob Herring, Mark Rutland, linux-pm@vger.kernel.org,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	Brian Norris, Douglas Anderson
In-Reply-To: <20180528051949epcms1p3bedd6ac3e2f20d0bcd8cb082349d97fa@epcms1p3>

On Mon, May 28, 2018 at 02:19:49PM +0900, MyungJoo Ham wrote:
> >Policy notifiers are called before a frequency change and may narrow
> >the min/max frequency range in devfreq_policy, which is used to adjust
> >the target frequency if it is beyond this range.
> >
> >Also add a few helpers:
> > - devfreq_verify_within_[dev_]limits()
> >    - should be used by the notifiers for policy adjustments.
> > - dev_to_devfreq()
> >    - lookup a devfreq strict from a device pointer
> >
> >Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
> >---
> > drivers/devfreq/devfreq.c | 47 +++++++++++++++++++++-------
> > include/linux/devfreq.h   | 66 +++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 102 insertions(+), 11 deletions(-)
> 
> Hello Matthias,
> 
> 
> Why should we have yet another notifier from an instance of devfreq?
> Wouldn't it better to let the current notifier (transition notifier)
> handle new events as well by adding possible event states to it?

Honestly the main reason is that I sought inspiration from cpufreq,
which uses a dedicated policy notifier. Unfortunately this change
predates the git history so I don't know what was the exact rationale
to do it this way.

Some minor advantages that I see are:

- transition notifiers aren't bothered about adjustments and viceversa
- different data types are passed for transitions and adjustments,
  which makes code of notifiers that handle both a bit more messy.

> Anyway, is this the reason why you've separated some data of devfreq
> into "policy" struct? (I was wondering why while reading commit 6/11).

The DEVFREQ_ADJUST is the reason for the "policy struct". With this
change we are dealing with 3 types of frequency pairs: user
(df->min/max_freq), devinfo (df->scaling_min/max_freq) and the
policy/adjustable ones. I think it is cleaner to group them in a
struct (and sub-structs), rather than having 6 individual
<type>_min/max_freq variables. Also it allows to only pass the policy
object to the notifiers, instead of the entire devfreq device.

I opted to do the introduction of the struct policy in a separate NOP
patch, because I think it is easier to review the 'reorg' churn
and the functional change separately.

Please let me know if you'd prefer to have certain things done
differently.

Thanks

Matthias

^ permalink raw reply

* Re: [patch v22 1/4] drivers: jtag: Add JTAG core driver
From: kbuild test robot @ 2018-05-29 20:08 UTC (permalink / raw)
  Cc: kbuild-all, gregkh, arnd, linux-kernel, linux-arm-kernel,
	devicetree, openbmc, joel, jiri, tklauser, linux-serial, vadimp,
	system-sw-low-level, robh+dt, openocd-devel-owner, linux-api,
	davem, mchehab, Oleksandr Shamray
In-Reply-To: <1527503652-21975-2-git-send-email-oleksandrs@mellanox.com>

[-- Attachment #1: Type: text/plain, Size: 1553 bytes --]

Hi Oleksandr,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v4.17-rc7 next-20180529]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Oleksandr-Shamray/JTAG-driver-introduction/20180529-195609
config: sh-allmodconfig (attached as .config)
compiler: sh4-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=sh 

All errors (new ones prefixed by >>):

>> drivers/jtag/jtag.c:288:13: error: static declaration of 'devm_jtag_unregister' follows non-static declaration
    static void devm_jtag_unregister(struct device *dev, void *res)
                ^~~~~~~~~~~~~~~~~~~~
   In file included from drivers/jtag/jtag.c:9:0:
   include/linux/jtag.h:38:6: note: previous declaration of 'devm_jtag_unregister' was here
    void devm_jtag_unregister(struct device *dev, void *res);
         ^~~~~~~~~~~~~~~~~~~~

vim +/devm_jtag_unregister +288 drivers/jtag/jtag.c

   287	
 > 288	static void devm_jtag_unregister(struct device *dev, void *res)
   289	{
   290		jtag_unregister(*(struct jtag **)res);
   291	}
   292	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 47739 bytes --]

^ permalink raw reply

* Re: [PATCH 2/3] ASoC: simple-card: make sysclk index configurable
From: Daniel Mack @ 2018-05-29 20:23 UTC (permalink / raw)
  To: Mark Brown; +Cc: devicetree, alsa-devel, lgirdwood, kuninori.morimoto.gx
In-Reply-To: <20180529112447.GC23509@sirena.org.uk>

On Tuesday, May 29, 2018 01:24 PM, Mark Brown wrote:
> On Mon, May 28, 2018 at 09:35:02PM +0200, Daniel Mack wrote:
>> The simple-card driver currently hard-codes the clk_id parameter in
>> snd_soc_dai_set_sysclk() to 0. Make this configrable for both CPU and
>> codec dai sub-nodes.
> 
>> This still has the limitation that only one clk_id can be configured, but it
>> should help some more platforms to use simple-card in favor to a more
>> specific machine driver.
> 
> If we want to get more complex usage of clocks in the DT we should be
> moving the CODECs over to using the standard clock bindings for this
> stuff rather than inventing custom ASoC clock bindings for it.  That way
> we don't have to deal with the pain of trying to join things up in the
> future.

This will get rather complex too though, because most codec and cpu dais 
can act as clock source xor clock consumer, depending on how the 
hardware is built. Would you want to represent everything, bit clocks, 
frame clocks, master clocks etc as clock nodes?

If that's the case, could you depict how the DT bindings should look 
like by example?


Thanks,
Daniel

^ permalink raw reply

* Re: [PATCH 3/3] ASoC: simple-card: add support for clock divider setup
From: Daniel Mack @ 2018-05-29 20:29 UTC (permalink / raw)
  To: Mark Brown; +Cc: devicetree, alsa-devel, lgirdwood, kuninori.morimoto.gx
In-Reply-To: <20180529113509.GE23509@sirena.org.uk>

On Tuesday, May 29, 2018 01:35 PM, Mark Brown wrote:
> On Mon, May 28, 2018 at 09:35:03PM +0200, Daniel Mack wrote:
>> Add support to call into snd_soc_dai_set_clkdiv() for both CPU and codec
>> DAIs from simple-card's hw_params().
> 
>> This allows platforms to set hardware-specific values without providing an
>> own machine driver.
> 
> Why do we have anything that actually needs this?  The devices with a
> set_clkdiv() operation are pretty legacy at this point and most of them
> should just be figuring out the dividers for themselves anyway.

The PXA platform CPU DAI still has it, and grepping also reveals some 
Freescale and Samsung platforms.

If that's considered legacy, we shouldn't add bindings for it of course. 
I can look into the PXA driver and see if it can auto-detect the 
dividers itself. It won't be straight forward though as the clocking 
setup also depends on configurations such as TDM slots, external vs. 
internal clocks etc. Meh.


Thanks,
Daniel

^ permalink raw reply

* Re: [PATCH 1/3] ASoC: simple-card: set cpu dai clk in hw_params
From: Daniel Mack @ 2018-05-29 20:31 UTC (permalink / raw)
  To: Mark Brown; +Cc: devicetree, alsa-devel, lgirdwood, kuninori.morimoto.gx
In-Reply-To: <20180529113243.GD23509@sirena.org.uk>

On Tuesday, May 29, 2018 01:32 PM, Mark Brown wrote:
> On Tue, May 29, 2018 at 01:17:45PM +0200, Daniel Mack wrote:
>> On Tuesday, May 29, 2018 01:16 PM, Mark Brown wrote:
>>> On Mon, May 28, 2018 at 09:35:01PM +0200, Daniel Mack wrote:
> 
>>>> +		if (dai_props->cpu_dai.clk)
>>>> +			clk_set_rate(dai_props->cpu_dai.clk, mclk);
> 
>>> We're ignoring the return value here.
> 
>> On purpose actually. Not all clocks might be settable, and in that case,
>> this is a no-op. You think we should bail or warn?
> 
> If we need to set the rate and fail to set it then clearly we shouldn't
> just carry on ignoring the error.  You might want some more involved
> logic there around checking if it's actually a rate change before you
> error out, and possibly some logic to carry on with whatever the rate is
> and a reduced set of resulting sample rates.
> 

Fair enough. I'll add that error checking at least, that makes sense.


Thanks for the feedback!
Daniel

^ permalink raw reply

* Re: [patch v25 0/4] JTAG driver introduction
From: Andy Shevchenko @ 2018-05-29 20:40 UTC (permalink / raw)
  To: Oleksandr Shamray
  Cc: Greg Kroah-Hartman, Arnd Bergmann, Linux Kernel Mailing List,
	linux-arm Mailing List, devicetree, openbmc, Joel Stanley,
	Jiří Pírko, Tobias Klauser,
	open list:SERIAL DRIVERS, Vadim Pasternak, system-sw-low-level,
	Rob Herring, openocd-devel-owner, linux-api, David S. Miller,
	Mauro Carvalho Chehab
In-Reply-To: <1527605618-15705-1-git-send-email-oleksandrs@mellanox.com>

On Tue, May 29, 2018 at 5:53 PM, Oleksandr Shamray
<oleksandrs@mellanox.com> wrote:

Please, STOP spamming mailing lists. Give (potential) reviewers time
to look at this.
In any case you already missed a next cycle, so, calm down and better
collect reports and address comments thoroughly.

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* Re: [PATCH 09/11] misc: throttler: Add core support for non-thermal throttling
From: Matthias Kaehlcke @ 2018-05-29 20:57 UTC (permalink / raw)
  To: Chanwoo Choi
  Cc: MyungJoo Ham, Kyungmin Park, Arnd Bergmann, Greg Kroah-Hartman,
	Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
	Brian Norris, Douglas Anderson
In-Reply-To: <5B0BB095.4040106@samsung.com>

On Mon, May 28, 2018 at 04:32:37PM +0900, Chanwoo Choi wrote:

> IMHO, you better to split out the devfreq patches from
> 'throttler' patch set. Because I'm not sure throttler is either
> necessary or not.
> 
> After finishing the review of 'throttler' patches without devfreq handling,
> it would be better for you to send devfreq patches separately.

I could certainly try to get 'throttler' with only cpufreq support
merged, but that would kind of defeat the purpose.

I first sent a RFC patch for the devfreq policy notifiers
(https://patchwork.kernel.org/patch/10401999/) to get an idea if this
is a reasonable path to pursue. In response you asked about "real code
and patches" and here it is :)

For my use case throttler is not really useful without devfreq
support. In this sense I prefer to know 'early' if there are any
blocking issues, rather then making the effort to get a limited
version of the driver merged, and then learn that I wasted my own and
the reviewers time because it is a dead end.

> On 2018년 05월 26일 05:30, Matthias Kaehlcke wrote:
> > The purpose of the throttler is to provide support for non-thermal
> > throttling. Throttling is triggered by external event, e.g. the
> > detection of a high battery discharge current, close to the OCP limit
> > of the battery. The throttler is only in charge of the throttling, not
> > the monitoring, which is done by another (possibly platform specific)
> > driver.
> > 
> > Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
> > ---
> >  drivers/misc/Kconfig            |   1 +
> >  drivers/misc/Makefile           |   1 +
> >  drivers/misc/throttler/Kconfig  |  13 ++
> >  drivers/misc/throttler/Makefile |   1 +
> >  drivers/misc/throttler/core.c   | 373 ++++++++++++++++++++++++++++++++
> >  include/linux/throttler.h       |  10 +
> >  6 files changed, 399 insertions(+)
> >  create mode 100644 drivers/misc/throttler/Kconfig
> >  create mode 100644 drivers/misc/throttler/Makefile
> >  create mode 100644 drivers/misc/throttler/core.c
> >  create mode 100644 include/linux/throttler.h
> > 
> > diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> > index 5d713008749b..691d9625d83c 100644
> > --- a/drivers/misc/Kconfig
> > +++ b/drivers/misc/Kconfig
> > @@ -513,4 +513,5 @@ source "drivers/misc/echo/Kconfig"
> >  source "drivers/misc/cxl/Kconfig"
> >  source "drivers/misc/ocxl/Kconfig"
> >  source "drivers/misc/cardreader/Kconfig"
> > +source "drivers/misc/throttler/Kconfig"
> >  endmenu
> > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > index 20be70c3f118..01a1714dd2ad 100644
> > --- a/drivers/misc/Makefile
> > +++ b/drivers/misc/Makefile
> > @@ -57,3 +57,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
> >  obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
> >  obj-$(CONFIG_OCXL)		+= ocxl/
> >  obj-$(CONFIG_MISC_RTSX)		+= cardreader/
> > +obj-y				+= throttler/
> > diff --git a/drivers/misc/throttler/Kconfig b/drivers/misc/throttler/Kconfig
> > new file mode 100644
> > index 000000000000..ef8388f6bc0a
> > --- /dev/null
> > +++ b/drivers/misc/throttler/Kconfig
> > @@ -0,0 +1,13 @@
> > +menuconfig THROTTLER
> > +	bool "Throttler support"
> > +	default n
> > +	depends on OF
> > +	select CPU_FREQ
> > +	select PM_DEVFREQ
> > +	help
> > +	  This option enables core support for non-thermal throttling of CPUs
> > +	  and devfreq devices.
> > +
> > +	  Note that you also need a event monitor module usually called
> > +	  *_throttler.
> > +
> > diff --git a/drivers/misc/throttler/Makefile b/drivers/misc/throttler/Makefile
> > new file mode 100644
> > index 000000000000..c8d920cee315
> > --- /dev/null
> > +++ b/drivers/misc/throttler/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_THROTTLER)		+= core.o
> > diff --git a/drivers/misc/throttler/core.c b/drivers/misc/throttler/core.c
> > new file mode 100644
> > index 000000000000..c058d03212b8
> > --- /dev/null
> > +++ b/drivers/misc/throttler/core.c
> > @@ -0,0 +1,373 @@
> > +/*
> > + * Core code for non-thermal throttling
> > + *
> > + * Copyright (C) 2018 Google, Inc.
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + */
> > +
> > +#include <linux/cpufreq.h>
> > +#include <linux/devfreq.h>
> > +#include <linux/kernel.h>
> > +#include <linux/notifier.h>
> > +#include <linux/of.h>
> > +#include <linux/of_platform.h>
> > +#include <linux/platform_device.h>
> > +
> > +/*
> > + * Non-thermal throttling: throttling of system components in response to
> > + * external events (e.g. high battery discharge current).
> > + *
> > + * The throttler supports throttling through cpufreq and devfreq. Multiple
> > + * levels of throttling can be configured. At level 0 no throttling is
> > + * active on behalf of the throttler, for values > 0 throttling is typically
> > + * configured to be increasingly aggressive with each level.
> > + * The number of throttling levels is not limited by the throttler (though
> > + * it is likely limited by the throttling devices). It is not necessary to
> > + * configure the same number of levels for all throttling devices. If the
> > + * requested throttling level for a device is higher than the maximum level
> > + * of the device the throttler will sleect the maximum throttling level of
> > + * the device.
> > + *
> > + * Non-thermal throttling is split in two parts:
> > + *
> > + * - throttler core
> > + *   - parses the thermal policy
> > + *   - applies throttling settings for a requested level of throttling
> > + *
> > + * - event monitor driver
> > + *   - monitors the events that trigger throttling
> > + *   - determines the throttling level (often limited to on/off)
> > + *   - requests throttler core to apply throttling settings
> > + *
> > + * It is possible for a system to have more than one throttler and the
> > + * throttlers may make use of the same throttling devices, in case of
> > + * conflicting settings for a device the more aggressive values will be
> > + * applied.
> > + *
> > + */
> > +
> > +struct thrcfg {
> > +	uint32_t *freqs;
> > +	int num_levels;
> > +};
> > +
> > +struct cpufreq_thrdev {
> > +	uint32_t cpu;
> > +	struct thrcfg cfg;
> > +};
> > +
> > +struct devfreq_thrdev {
> > +	struct devfreq *devfreq;
> > +	struct thrcfg cfg;
> > +	struct throttler *thr;
> > +	struct notifier_block nb;
> > +};
> > +
> > +struct __thr_cpufreq {
> > +	struct cpufreq_thrdev *devs;
> > +	int ndevs;
> > +	struct notifier_block nb;
> > +};
> > +
> > +struct __thr_devfreq {
> > +	struct devfreq_thrdev *devs;
> > +	int ndevs;
> > +};
> > +
> > +struct throttler {
> > +	struct device *dev;
> > +	int level;
> > +	struct __thr_cpufreq cpufreq;
> > +	struct __thr_devfreq devfreq;
> > +};
> > +
> > +static unsigned long thr_get_throttling_freq(struct thrcfg *cfg, int level)
> > +{
> > +	if (level == 0 ) {
> > +		WARN(true, "level == 0");
> > +		return 0;
> > +	}
> > +
> > +	if (level <= cfg->num_levels)
> > +		return cfg->freqs[level - 1];
> > +	else
> > +		return cfg->freqs[cfg->num_levels - 1];
> > +}
> > +
> > +static int thr_cpufreq_event(struct notifier_block *nb,
> > +				    unsigned long event, void *data)
> > +{
> > +	struct throttler *thr =
> > +                container_of(nb, struct throttler, cpufreq.nb);
> > +        struct cpufreq_policy *policy = data;
> > +	struct cpufreq_thrdev *ctd;
> > +	int i;
> > +
> > +	if ((event != CPUFREQ_ADJUST) || (thr->level == 0))
> > +                return NOTIFY_DONE;
> > +
> > +	for (i = 0; i < thr->cpufreq.ndevs; i++) {
> > +		ctd = &thr->cpufreq.devs[i];
> > +
> > +		if (ctd->cpu == policy->cpu) {
> > +			unsigned long clamp_freq =
> > +				thr_get_throttling_freq(&ctd->cfg, thr->level);
> > +			if (clamp_freq < policy->max) {
> > +				cpufreq_verify_within_limits(policy, 0, clamp_freq);
> > +			}
> > +		}
> > +	}
> > +
> > +	return NOTIFY_DONE;
> > +}
> > +
> > +static int thr_devfreq_event(struct notifier_block *nb,
> > +				    unsigned long event, void *data)
> > +{
> > +	struct devfreq_thrdev *dtd =
> > +		container_of(nb, struct devfreq_thrdev, nb);
> > +	struct throttler *thr = dtd->thr;
> > +	struct devfreq_policy *policy = data;
> > +	unsigned long clamp_freq;
> > +
> > +	if ((event != DEVFREQ_ADJUST) || (thr->level == 0))
> > +                return NOTIFY_DONE;
> > +
> > +	clamp_freq = thr_get_throttling_freq(&dtd->cfg, thr->level);
> > +	if (clamp_freq < policy->max)
> > +		devfreq_verify_within_limits(policy, 0, clamp_freq);
> > +
> > +	return NOTIFY_DONE;
> > +}
> > +
> > +static void thr_cpufreq_update_policy(struct throttler *thr)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < thr->cpufreq.ndevs; i++) {
> > +		struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i];
> > +		struct cpufreq_policy *policy = cpufreq_cpu_get(ctd->cpu);
> > +
> > +		if (!policy) {
> > +			dev_warn(thr->dev, "CPU%d does have no cpufreq policy!\n", ctd->cpu);
> > +			continue;
> > +		}
> > +
> > +		cpufreq_update_policy(ctd->cpu);
> > +		cpufreq_cpu_put(policy);
> > +	}
> > +}
> > +
> > +static int thr_parse_thrcfg(struct throttler *thr,
> > +		struct device_node *np, struct thrcfg *cfg) {
> > +	int err;
> > +
> > +	cfg->num_levels =
> > +		of_property_count_u32_elems(np, "throttling-frequencies");
> > +	if (cfg->num_levels < 0) {
> > +		pr_err("%s: failed to determine number of throttling frequencies\n",
> > +		       np->full_name);
> > +		return cfg->num_levels;
> > +	}
> > +
> > +	cfg->freqs = devm_kzalloc(thr->dev,
> > +		cfg->num_levels * sizeof(u32), GFP_KERNEL);
> > +	if (!cfg->freqs)
> > +		return -ENOMEM;
> > +
> > +	err = of_property_read_u32_array(np, "throttling-frequencies",
> > +		 cfg->freqs, cfg->num_levels);
> > +	if (err) {
> > +		pr_err("%s: failed to read throttling frequencies\n", np->full_name);
> > +		return err;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static struct devfreq *thr_find_devfreq_dev(struct throttler *thr,
> > +		struct device_node *np_df) {
> > +	struct device_node *node;
> > +	struct platform_device *pdev;
> > +
> > +	node = of_parse_phandle(np_df, "device", 0);
> > +	if (!node) {
> > +		pr_err("%s: failed to get devfreq parent device\n",
> > +		       np_df->full_name);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	pdev = of_find_device_by_node(node);
> > +	if (!pdev) {
> > +		pr_err("%s: could not find devfreq parent device\n",
> > +		       node->full_name);
> > +		return ERR_PTR(-EINVAL);
> > +	}
> > +
> > +	return dev_to_devfreq(&pdev->dev);
> > +}
> > +
> > +static int thr_parse_dt(struct throttler *thr, struct device_node *np)
> > +{
> > +	struct device_node *node, *child;
> > +	int err, i;
> > +
> > +	node = of_get_child_by_name(np, "cpufreq");
> > +	if (node) {
> > +		thr->cpufreq.ndevs = of_get_child_count(node);
> > +		thr->cpufreq.devs = devm_kzalloc(thr->dev,
> > +			sizeof(*thr->cpufreq.devs) * thr->cpufreq.ndevs,
> > +			GFP_KERNEL);
> > +
> > +		i = 0;
> > +		for_each_child_of_node(node, child) {
> > +			struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i];
> > +
> > +			err = of_property_read_u32(child, "cpu", &ctd->cpu);
> > +			if (err) {
> > +				pr_err("%s: failed to read CPU id\n", child->full_name);
> > +				return err;
> > +			}
> > +
> > +			err = thr_parse_thrcfg(thr, child, &ctd->cfg);
> > +			if (err)
> > +				return err;
> > +
> > +			i++;
> > +		}
> > +	}
> > +
> > +	node = of_get_child_by_name(np, "devfreq");
> > +	if (node) {
> > +		thr->devfreq.ndevs = of_get_child_count(node);
> > +		thr->devfreq.devs = devm_kzalloc(thr->dev,
> > +			sizeof(*thr->devfreq.devs) * thr->devfreq.ndevs,
> > +			GFP_KERNEL);
> > +
> > +		i = 0;
> > +		for_each_child_of_node(node, child) {
> > +			struct devfreq_thrdev *dtd = &thr->devfreq.devs[i];
> > +
> > +			dtd->thr = thr;
> > +
> > +			dtd->devfreq = thr_find_devfreq_dev(thr, child);
> > +			if (IS_ERR(dtd->devfreq))
> > +				return PTR_ERR(dtd->devfreq);
> > +
> > +			err = thr_parse_thrcfg(thr, child, &dtd->cfg);
> > +			if (err)
> > +				return err;
> > +
> > +			i++;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void thr_update_devfreq(struct devfreq *devfreq)
> > +{
> > +	mutex_lock(&devfreq->lock);
> > +	update_devfreq(devfreq);
> > +	mutex_unlock(&devfreq->lock);
> > +}
> > +
> > +void throttler_set_level(struct throttler *thr, int level)
> > +{
> > +	int i;
> > +
> > +	if (level == thr->level)
> > +		return;
> > +
> > +	dev_dbg(thr->dev, "throttling level: %d\n", level);
> > +	thr->level = level;
> > +
> > +	if (thr->cpufreq.ndevs > 0)
> > +		thr_cpufreq_update_policy(thr);
> > +
> > +	if (thr->devfreq.ndevs > 0)
> > +		for (i = 0; i < thr->devfreq.ndevs; i++)
> > +			thr_update_devfreq(thr->devfreq.devs[i].devfreq);
> > +}
> > +EXPORT_SYMBOL_GPL(throttler_set_level);
> > +
> > +struct throttler *throttler_setup(struct device *dev)
> > +{
> > +	struct throttler *thr;
> > +	struct device_node *np = dev->of_node;
> > +	int err, i;
> > +
> > +	if (!np)
> > +		/* should never happen */
> > +		return ERR_PTR(-EINVAL);
> > +
> > +	thr = devm_kzalloc(dev, sizeof(*thr), GFP_KERNEL);
> > +	if (!thr)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	thr->dev = dev;
> > +
> > +	err = thr_parse_dt(thr, np);
> > +	if (err)
> > +		return ERR_PTR(err);
> > +
> > +	if (thr->cpufreq.ndevs > 0) {
> > +		thr->cpufreq.nb.notifier_call = thr_cpufreq_event;
> > +		err = cpufreq_register_notifier(&thr->cpufreq.nb,
> > +						CPUFREQ_POLICY_NOTIFIER);
> > +		if (err < 0) {
> > +			dev_err(dev, "failed to register cpufreq notifier\n");
> > +			return ERR_PTR(err);
> > +		}
> > +	}
> > +
> > +	for (i = 0; i < thr->devfreq.ndevs; i++) {
> > +		struct devfreq_thrdev *dtd = &thr->devfreq.devs[i];
> > +
> > +		dtd->nb.notifier_call = thr_devfreq_event;
> > +		err = devm_devfreq_register_notifier(dev, dtd->devfreq,
> > +						     &dtd->nb, DEVFREQ_POLICY_NOTIFIER);
> > +		if (err < 0) {
> > +			dev_err(dev, "failed to register devfreq notifier\n");
> > +			goto err_cpufreq_unregister;
> > +		}
> > +	}
> > +
> > +	return thr;
> > +
> > +err_cpufreq_unregister:
> > +	if (thr->cpufreq.ndevs > 0)
> > +		cpufreq_unregister_notifier(&thr->cpufreq.nb,
> > +					    CPUFREQ_POLICY_NOTIFIER);
> > +
> > +	return ERR_PTR(err);
> > +}
> > +EXPORT_SYMBOL_GPL(throttler_setup);
> > +
> > +void throttler_teardown(struct throttler *thr)
> > +{
> > +	int i;
> > +
> > +	thr->level = 0;
> > +
> > +	if (thr->cpufreq.ndevs > 0) {
> > +		thr_cpufreq_update_policy(thr);
> > +
> > +		cpufreq_unregister_notifier(&thr->cpufreq.nb,
> > +					    CPUFREQ_POLICY_NOTIFIER);
> > +	}
> > +
> > +	if (thr->devfreq.ndevs > 0)
> > +		for (i = 0; i < thr->devfreq.ndevs; i++)
> > +			thr_update_devfreq(thr->devfreq.devs[i].devfreq);
> > +}
> > +EXPORT_SYMBOL_GPL(throttler_teardown);
> > diff --git a/include/linux/throttler.h b/include/linux/throttler.h
> > new file mode 100644
> > index 000000000000..cab8c466da4b
> > --- /dev/null
> > +++ b/include/linux/throttler.h
> > @@ -0,0 +1,10 @@
> > +#ifndef __LINUX_THROTTLER_H__
> > +#define __LINUX_THROTTLER_H__
> > +
> > +struct throttler;
> > +
> > +extern struct throttler *throttler_setup(struct device *dev);
> > +extern void throttler_teardown(struct throttler *thr);
> > +extern void throttler_set_level(struct throttler *thr, int level);
> > +
> > +#endif /* __LINUX_THROTTLER_H__ */
> > 

^ permalink raw reply

* Re: [PATCH] hwmon: binding: Fix "#cooling-cells" property's name
From: Guenter Roeck @ 2018-05-29 20:59 UTC (permalink / raw)
  To: Viresh Kumar
  Cc: Rob Herring, Mark Rutland, Vincent Guittot, devicetree,
	linux-kernel
In-Reply-To: <9754c25e8459685d4919017dd7c8709cee7b89ce.1527224960.git.viresh.kumar@linaro.org>

On Fri, May 25, 2018 at 10:49:57AM +0530, Viresh Kumar wrote:
> It should be "#cooling-cells" instead of "cooling-cells". Fix it.
> 
> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>

Applied to hwmon-next after updating the subject.

Thanks,
Guenter

> ---
>  Documentation/devicetree/bindings/hwmon/gpio-fan.txt | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/hwmon/gpio-fan.txt b/Documentation/devicetree/bindings/hwmon/gpio-fan.txt
> index 439a7430fc68..2becdcfdc840 100644
> --- a/Documentation/devicetree/bindings/hwmon/gpio-fan.txt
> +++ b/Documentation/devicetree/bindings/hwmon/gpio-fan.txt
> @@ -11,7 +11,7 @@ Bindings for fan connected to GPIO lines
>    must have the RPM values in ascending order.
>  - alarm-gpios: This pin going active indicates something is wrong with
>    the fan, and a udev event will be fired.
> -- cooling-cells: If used as a cooling device, must be <2>
> +- #cooling-cells: If used as a cooling device, must be <2>
>    Also see: Documentation/devicetree/bindings/thermal/thermal.txt
>    min and max states are derived from the speed-map of the fan.
>  
> -- 
> 2.15.0.194.g9af6a3dea062
> 

^ permalink raw reply

* Re: [PATCH 09/11] misc: throttler: Add core support for non-thermal throttling
From: Matthias Kaehlcke @ 2018-05-29 21:30 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Arnd Bergmann,
	Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
	Brian Norris, Douglas Anderson
In-Reply-To: <20180528080826.GA17162@kroah.com>

On Mon, May 28, 2018 at 10:08:26AM +0200, Greg Kroah-Hartman wrote:
> On Fri, May 25, 2018 at 01:30:41PM -0700, Matthias Kaehlcke wrote:
> > diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> > index 20be70c3f118..01a1714dd2ad 100644
> > --- a/drivers/misc/Makefile
> > +++ b/drivers/misc/Makefile
> > @@ -57,3 +57,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP)	+= aspeed-lpc-snoop.o
> >  obj-$(CONFIG_PCI_ENDPOINT_TEST)	+= pci_endpoint_test.o
> >  obj-$(CONFIG_OCXL)		+= ocxl/
> >  obj-$(CONFIG_MISC_RTSX)		+= cardreader/
> > +obj-y				+= throttler/
> 
> Shouldn't you depend on a Kconfig option to traverse into this
> directory?

Ack

> > diff --git a/drivers/misc/throttler/Kconfig b/drivers/misc/throttler/Kconfig
> > new file mode 100644
> > index 000000000000..ef8388f6bc0a
> > --- /dev/null
> > +++ b/drivers/misc/throttler/Kconfig
> > @@ -0,0 +1,13 @@
> > +menuconfig THROTTLER
> > +	bool "Throttler support"
> > +	default n
> 
> the default is always 'n' no need to say it again here :)

Will remove

> > +	depends on OF
> > +	select CPU_FREQ
> > +	select PM_DEVFREQ
> > +	help
> > +	  This option enables core support for non-thermal throttling of CPUs
> > +	  and devfreq devices.
> > +
> > +	  Note that you also need a event monitor module usually called
> > +	  *_throttler.
> > +
> > diff --git a/drivers/misc/throttler/Makefile b/drivers/misc/throttler/Makefile
> > new file mode 100644
> > index 000000000000..c8d920cee315
> > --- /dev/null
> > +++ b/drivers/misc/throttler/Makefile
> > @@ -0,0 +1 @@
> > +obj-$(CONFIG_THROTTLER)		+= core.o
> > diff --git a/drivers/misc/throttler/core.c b/drivers/misc/throttler/core.c
> > new file mode 100644
> > index 000000000000..c058d03212b8
> > --- /dev/null
> > +++ b/drivers/misc/throttler/core.c
> > @@ -0,0 +1,373 @@
> > +/*
> > + * Core code for non-thermal throttling
> > + *
> > + * Copyright (C) 2018 Google, Inc.
> > + *
> > + * This software is licensed under the terms of the GNU General Public
> > + * License version 2, as published by the Free Software Foundation, and
> > + * may be copied, distributed, and modified under those terms.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + *
> > + */
> 
> Please just use a single SPDX line, like checkpatch.pl would have warned
> you about.  No need for the full verbous license boiler-plate text here
> at all.

Ok

^ permalink raw reply

* [PATCH 0/6] PAXB INTx support with proper model
From: Ray Jui @ 2018-05-29 21:58 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Bjorn Helgaas, Rob Herring, Mark Rutland
  Cc: linux-kernel, bcm-kernel-feedback-list, linux-pci, devicetree,
	linux-arm-kernel, Ray Jui

This patch series adds PCIe legacy interrupt (INTx) support to the iProc
PCIe driver by modeling it with its own IRQ domain. All 4 interrupts INTA,
INTB, INTC, INTD share the same interrupt line connected to the GIC
in the system. This is now modeled by using its own IRQ domain.

Also update all relevant devicetree files to adapt to the new model

This patch series is available on GIHUB:
repo: https://github.com/Broadcom/arm64-linux.git
branch: pcie-intx-v1

Ray Jui (6):
  PCI: iproc: Update iProc PCI binding for INTx support
  PCI: iproc: Add INTx support with better modeling
  arm: dts: Change PCIe INTx mapping for Cygnus
  arm: dts: Change PCIe INTx mapping for NSP
  arm: dts: Change PCIe INTx mapping for HR2
  arm64: dts: Change PCIe INTx mapping for NS2

 .../devicetree/bindings/pci/brcm,iproc-pcie.txt    | 31 +++++--
 arch/arm/boot/dts/bcm-cygnus.dtsi                  | 18 +++-
 arch/arm/boot/dts/bcm-hr2.dtsi                     | 18 +++-
 arch/arm/boot/dts/bcm-nsp.dtsi                     | 27 ++++--
 arch/arm64/boot/dts/broadcom/northstar2/ns2.dtsi   | 19 +++--
 drivers/pci/host/pcie-iproc-platform.c             |  2 +
 drivers/pci/host/pcie-iproc.c                      | 95 +++++++++++++++++++++-
 drivers/pci/host/pcie-iproc.h                      |  6 ++
 8 files changed, 188 insertions(+), 28 deletions(-)

-- 
2.1.4

^ permalink raw reply

* [PATCH 1/6] PCI: iproc: Update iProc PCI binding for INTx support
From: Ray Jui @ 2018-05-29 21:58 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Bjorn Helgaas, Rob Herring, Mark Rutland
  Cc: linux-kernel, bcm-kernel-feedback-list, linux-pci, devicetree,
	linux-arm-kernel, Ray Jui
In-Reply-To: <1527631130-20045-1-git-send-email-ray.jui@broadcom.com>

Update the iProc PCIe binding document for better modeling of the legacy
interrupt (INTx) support

Signed-off-by: Ray Jui <ray.jui@broadcom.com>
---
 .../devicetree/bindings/pci/brcm,iproc-pcie.txt    | 31 +++++++++++++++++-----
 1 file changed, 24 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
index b8e48b4..7ea24dc 100644
--- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
@@ -13,9 +13,6 @@ controller, used in Stingray
   PAXB-based root complex is used for external endpoint devices. PAXC-based
 root complex is connected to emulated endpoint devices internal to the ASIC
 - reg: base address and length of the PCIe controller I/O register space
-- #interrupt-cells: set to <1>
-- interrupt-map-mask and interrupt-map, standard PCI properties to define the
-  mapping of the PCIe interface to interrupt numbers
 - linux,pci-domain: PCI domain ID. Should be unique for each host controller
 - bus-range: PCI bus numbers covered
 - #address-cells: set to <3>
@@ -41,6 +38,16 @@ Required:
 - brcm,pcie-ob-axi-offset: The offset from the AXI address to the internal
 address used by the iProc PCIe core (not the PCIe address)
 
+Legacy interrupt (INTx) support (optional):
+
+Note INTx is for PAXB only.
+
+- interrupt-controller: claims itself as an interrupt controller for INTx
+- #interrupt-cells: set to <1>
+- interrupt-map-mask and interrupt-map, standard PCI properties to define
+the mapping of the PCIe interface to interrupt numbers
+- interrupts: interrupt line wired to the generic GIC for INTx support
+
 MSI support (optional):
 
 For older platforms without MSI integrated in the GIC, iProc PCIe core provides
@@ -77,9 +84,14 @@ Example:
 		compatible = "brcm,iproc-pcie";
 		reg = <0x18012000 0x1000>;
 
+		interrupt-controller;
 		#interrupt-cells = <1>;
-		interrupt-map-mask = <0 0 0 0>;
-		interrupt-map = <0 0 0 0 &gic GIC_SPI 100 IRQ_TYPE_NONE>;
+		interrupt-map-mask = <0 0 0 7>;
+		interrupt-map = <0 0 0 1 &pcie0 1>,
+				<0 0 0 2 &pcie0 2>,
+				<0 0 0 3 &pcie0 3>,
+				<0 0 0 4 &pcie0 4>;
+		interrupts = <GIC_SPI 100 IRQ_TYPE_NONE>;
 
 		linux,pci-domain = <0>;
 
@@ -115,9 +127,14 @@ Example:
 		compatible = "brcm,iproc-pcie";
 		reg = <0x18013000 0x1000>;
 
+		interrupt-controller;
 		#interrupt-cells = <1>;
-		interrupt-map-mask = <0 0 0 0>;
-		interrupt-map = <0 0 0 0 &gic GIC_SPI 106 IRQ_TYPE_NONE>;
+		interrupt-map-mask = <0 0 0 7>;
+		interrupt-map = <0 0 0 1 &pcie1 1>,
+				<0 0 0 2 &pcie1 2>,
+				<0 0 0 3 &pcie1 3>,
+				<0 0 0 4 &pcie1 4>;
+		interrupts = <GIC_SPI 106 IRQ_TYPE_NONE>;
 
 		linux,pci-domain = <1>;
 
-- 
2.1.4

^ permalink raw reply related

* [PATCH 2/6] PCI: iproc: Add INTx support with better modeling
From: Ray Jui @ 2018-05-29 21:58 UTC (permalink / raw)
  To: Lorenzo Pieralisi, Bjorn Helgaas, Rob Herring, Mark Rutland
  Cc: linux-kernel, bcm-kernel-feedback-list, linux-pci, devicetree,
	linux-arm-kernel, Ray Jui
In-Reply-To: <1527631130-20045-1-git-send-email-ray.jui@broadcom.com>

Add PCIe legacy interrupt INTx support to the iProc PCIe driver by
modeling it with its own IRQ domain. All 4 interrupts INTA, INTB, INTC,
INTD share the same interrupt line connected to the GIC in the system,
while the status of each INTx can be obtained through the INTX CSR
register

Signed-off-by: Ray Jui <ray.jui@broadcom.com>
---
 drivers/pci/host/pcie-iproc-platform.c |  2 +
 drivers/pci/host/pcie-iproc.c          | 95 +++++++++++++++++++++++++++++++++-
 drivers/pci/host/pcie-iproc.h          |  6 +++
 3 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c
index e764a2a..7a51e6c 100644
--- a/drivers/pci/host/pcie-iproc-platform.c
+++ b/drivers/pci/host/pcie-iproc-platform.c
@@ -70,6 +70,8 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 	}
 	pcie->base_addr = reg.start;
 
+	pcie->irq = platform_get_irq(pdev, 0);
+
 	if (of_property_read_bool(np, "brcm,pcie-ob")) {
 		u32 val;
 
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index 14f374d..0bd2c14 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -14,6 +14,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/irqchip/arm-gic-v3.h>
+#include <linux/irqchip/chained_irq.h>
 #include <linux/platform_device.h>
 #include <linux/of_address.h>
 #include <linux/of_pci.h>
@@ -264,6 +265,7 @@ enum iproc_pcie_reg {
 
 	/* enable INTx */
 	IPROC_PCIE_INTX_EN,
+	IPROC_PCIE_INTX_CSR,
 
 	/* outbound address mapping */
 	IPROC_PCIE_OARR0,
@@ -305,6 +307,7 @@ static const u16 iproc_pcie_reg_paxb_bcma[] = {
 	[IPROC_PCIE_CFG_ADDR]		= 0x1f8,
 	[IPROC_PCIE_CFG_DATA]		= 0x1fc,
 	[IPROC_PCIE_INTX_EN]		= 0x330,
+	[IPROC_PCIE_INTX_CSR]		= 0x334,
 	[IPROC_PCIE_LINK_STATUS]	= 0xf0c,
 };
 
@@ -316,6 +319,7 @@ static const u16 iproc_pcie_reg_paxb[] = {
 	[IPROC_PCIE_CFG_ADDR]		= 0x1f8,
 	[IPROC_PCIE_CFG_DATA]		= 0x1fc,
 	[IPROC_PCIE_INTX_EN]		= 0x330,
+	[IPROC_PCIE_INTX_CSR]		= 0x334,
 	[IPROC_PCIE_OARR0]		= 0xd20,
 	[IPROC_PCIE_OMAP0]		= 0xd40,
 	[IPROC_PCIE_OARR1]		= 0xd28,
@@ -332,6 +336,7 @@ static const u16 iproc_pcie_reg_paxb_v2[] = {
 	[IPROC_PCIE_CFG_ADDR]		= 0x1f8,
 	[IPROC_PCIE_CFG_DATA]		= 0x1fc,
 	[IPROC_PCIE_INTX_EN]		= 0x330,
+	[IPROC_PCIE_INTX_CSR]		= 0x334,
 	[IPROC_PCIE_OARR0]		= 0xd20,
 	[IPROC_PCIE_OMAP0]		= 0xd40,
 	[IPROC_PCIE_OARR1]		= 0xd28,
@@ -782,9 +787,90 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie)
 	return link_is_active ? 0 : -ENODEV;
 }
 
-static void iproc_pcie_enable(struct iproc_pcie *pcie)
+static int iproc_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
+			       irq_hw_number_t hwirq)
 {
+	irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
+	irq_set_chip_data(irq, domain->host_data);
+
+	return 0;
+}
+
+static const struct irq_domain_ops intx_domain_ops = {
+	.map = iproc_pcie_intx_map,
+};
+
+static void iproc_pcie_isr(struct irq_desc *desc)
+{
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	struct iproc_pcie *pcie;
+	struct device *dev;
+	unsigned long status;
+	u32 bit, virq;
+
+	chained_irq_enter(chip, desc);
+	pcie = irq_desc_get_handler_data(desc);
+	dev = pcie->dev;
+
+	/* go through INTx A, B, C, D until all interrupts are handled */
+	while ((status = iproc_pcie_read_reg(pcie, IPROC_PCIE_INTX_CSR) &
+		SYS_RC_INTX_MASK) != 0) {
+		for_each_set_bit(bit, &status, PCI_NUM_INTX) {
+			virq = irq_find_mapping(pcie->irq_domain, bit + 1);
+			if (virq)
+				generic_handle_irq(virq);
+			else
+				dev_err(dev, "unexpected INTx%u\n", bit);
+		}
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static int iproc_pcie_intx_enable(struct iproc_pcie *pcie)
+{
+	struct device *dev = pcie->dev;
+	struct device_node *node = dev->of_node;
+	int ret;
+
 	iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK);
+
+	/*
+	 * BCMA devices do not map INTx the same way as platform devices. All
+	 * BCMA needs is the above code to enable INTx
+	 */
+	if (pcie->irq <= 0)
+		return 0;
+
+	/* set IRQ handler */
+	irq_set_chained_handler_and_data(pcie->irq, iproc_pcie_isr, pcie);
+
+	/* add IRQ domain for INTx */
+	pcie->irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX + 1,
+						 &intx_domain_ops, pcie);
+	if (!pcie->irq_domain) {
+		dev_err(dev, "failed to add INTx IRQ domain\n");
+		ret = -ENOMEM;
+		goto err_rm_handler_data;
+	}
+
+	return 0;
+
+err_rm_handler_data:
+	irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
+
+	return ret;
+}
+
+static void iproc_pcie_intx_disable(struct iproc_pcie *pcie)
+{
+	iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, 0x0);
+
+	if (pcie->irq <= 0)
+		return;
+
+	irq_domain_remove(pcie->irq_domain);
+	irq_set_chained_handler_and_data(pcie->irq, NULL, NULL);
 }
 
 static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie,
@@ -1410,7 +1496,11 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 		goto err_power_off_phy;
 	}
 
-	iproc_pcie_enable(pcie);
+	ret = iproc_pcie_intx_enable(pcie);
+	if (ret) {
+		dev_err(dev, "failed to enable INTx\n");
+		goto err_power_off_phy;
+	}
 
 	if (IS_ENABLED(CONFIG_PCI_MSI))
 		if (iproc_pcie_msi_enable(pcie))
@@ -1455,6 +1545,7 @@ int iproc_pcie_remove(struct iproc_pcie *pcie)
 	pci_remove_root_bus(pcie->root_bus);
 
 	iproc_pcie_msi_disable(pcie);
+	iproc_pcie_intx_disable(pcie);
 
 	phy_power_off(pcie->phy);
 	phy_exit(pcie->phy);
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index 67081cb..cbcaf9d 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -72,6 +72,9 @@ struct iproc_msi;
  * @ib: inbound mapping related parameters
  * @ib_map: outbound mapping region related parameters
  *
+ * @irq: interrupt line wired to the generic GIC for INTx
+ * @irq_domain: IRQ domain for INTx
+ *
  * @need_msi_steer: indicates additional configuration of the iProc PCIe
  * controller is required to steer MSI writes to external interrupt controller
  * @msi: MSI data
@@ -99,6 +102,9 @@ struct iproc_pcie {
 	struct iproc_pcie_ib ib;
 	const struct iproc_pcie_ib_map *ib_map;
 
+	int irq;
+	struct irq_domain *irq_domain;
+
 	bool need_msi_steer;
 	struct iproc_msi *msi;
 };
-- 
2.1.4

^ permalink raw reply related


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