diff --git a/Documentation/admin-guide/media/starfive_camss.rst b/Documentation/admin-guide/media/starfive_camss.rst new file mode 100644 index 0000000000000..12c5ebf794b8a --- /dev/null +++ b/Documentation/admin-guide/media/starfive_camss.rst @@ -0,0 +1,68 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +================================ +Starfive Camera Subsystem driver +================================ + +Introduction +------------ + +This file documents the driver for the Starfive Camera Subsystem found on +Starfive JH7110 SoC. The driver is located under drivers/media/platform/starfive. + +The driver implements V4L2, Media controller and v4l2_subdev interfaces. +Camera sensor using V4L2 subdev interface in the kernel is supported. + +The driver has been successfully used on the Gstreamer 1.18.5 with +v4l2src plugin. + + +Starfive Camera Subsystem hardware +---------------------------------- + +The Starfive Camera Subsystem hardware consists of: + +- MIPI DPHY Receiver: receives mipi data from a MIPI camera sensor. +- MIPI CSIRx Controller: is responsible for handling and decoding CSI2 protocol + based camera sensor data stream. +- ISP: handles the image data streams from the MIPI CSIRx Controller. +- VIN(Video In): a top-level module, is responsible for controlling power + and clocks to other modules, dumps the input data to memory or transfers the + input data to ISP. + + +Topology +-------- + +The media controller pipeline graph is as follows: + +.. _starfive_camss_graph: + +.. kernel-figure:: starfive_camss_graph.dot + :alt: starfive_camss_graph.dot + :align: center + +The driver has 5 video devices: + +- stf_vin0_wr_video0: capture device for images directly from the VIN module. +- stf_vin0_isp0_video1: capture device for images without scaling. +- stf_vin0_isp0_ss0_video2: capture device for images with adjustable + scale-down factor. +- stf_vin0_isp0_ss1_video3: capture device for images with adjustable + scale-down factor. +- stf_vin0_isp0_raw_video4: capture device for RAW images. + +The driver has 6 subdevices: + +- stf_isp0: is responsible for all the isp operations. +- stf_vin0_wr: used to dump RAW images to memory. +- stf_vin0_isp0: used to capture unscaled images for the stf_vin0_isp0_video1 + device. +- stf_vin0_isp0_ss0: used to resize and downsample frames for + the stf_vin0_isp0_ss0_video2 capture device. +- stf_vin0_isp0_ss1: used to resize and downsample frames for + the stf_vin0_isp0_ss1_video3 capture device. +- stf_vin0_isp0_raw: used to configure the camss ISP to get RAW + images for stf_vin0_isp0_raw_video4 capture device. diff --git a/Documentation/admin-guide/media/starfive_camss_graph.dot b/Documentation/admin-guide/media/starfive_camss_graph.dot new file mode 100644 index 0000000000000..c35dec8de2542 --- /dev/null +++ b/Documentation/admin-guide/media/starfive_camss_graph.dot @@ -0,0 +1,28 @@ +digraph board { + rankdir=TB + n00000001 [label="{{ 0} | stf_isp0\n/dev/v4l-subdev0 | { 1 | 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n00000001:port1 -> n00000010:port0 + n00000001:port2 -> n00000019:port0 [style=dashed] + n00000001:port3 -> n00000022:port0 [style=dashed] + n00000001:port4 -> n0000002b:port0 [style=dashed] + n00000007 [label="{{ 0} | stf_vin0_wr\n/dev/v4l-subdev1 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000007:port1 -> n0000000a [style=bold] + n0000000a [label="stf_vin0_wr_video0\n/dev/video0", shape=box, style=filled, fillcolor=yellow] + n00000010 [label="{{ 0} | stf_vin0_isp0\n/dev/v4l-subdev2 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000010:port1 -> n00000013 [style=bold] + n00000013 [label="stf_vin0_isp0_video1\n/dev/video1", shape=box, style=filled, fillcolor=yellow] + n00000019 [label="{{ 0} | stf_vin0_isp0_ss0\n/dev/v4l-subdev3 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000019:port1 -> n0000001c [style=bold] + n0000001c [label="stf_vin0_isp0_ss0_video2\n/dev/video2", shape=box, style=filled, fillcolor=yellow] + n00000022 [label="{{ 0} | stf_vin0_isp0_ss1\n/dev/v4l-subdev4 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n00000022:port1 -> n00000025 [style=bold] + n00000025 [label="stf_vin0_isp0_ss1_video3\n/dev/video3", shape=box, style=filled, fillcolor=yellow] + n0000002b [label="{{ 0} | stf_vin0_isp0_raw\n/dev/v4l-subdev5 | { 1}}", shape=Mrecord, style=filled, fillcolor=green] + n0000002b:port1 -> n0000002e [style=bold] + n0000002e [label="stf_vin0_isp0_raw_video4\n/dev/video4", shape=box, style=filled, fillcolor=yellow] + n0000003c [label="{{ 0} | cdns_csi2rx.19800000.csi-bridge\n/dev/v4l-subdev6 | { 1 | 2 | 3 | 4}}", shape=Mrecord, style=filled, fillcolor=green] + n0000003c:port1 -> n00000007:port0 [style=dashed] + n0000003c:port1 -> n00000001:port0 + n00000054 [label="{{} | imx219 6-0010\n/dev/v4l-subdev7 | { 0}}", shape=Mrecord, style=filled, fillcolor=green] + n00000054:port0 -> n0000003c:port0 [style=bold] +} diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst index 1c41f87c39171..3005d637199a6 100644 --- a/Documentation/admin-guide/media/v4l-drivers.rst +++ b/Documentation/admin-guide/media/v4l-drivers.rst @@ -27,6 +27,7 @@ Video4Linux (V4L) driver-specific documentation si470x si4713 si476x + starfive vimc visl vivid diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-aoncrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-aoncrg.yaml new file mode 100644 index 0000000000000..923680a44aef3 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-aoncrg.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-aoncrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Always-On Clock and Reset Generator + +maintainers: + - Emil Renner Berthing + +properties: + compatible: + const: starfive,jh7110-aoncrg + + reg: + maxItems: 1 + + clocks: + oneOf: + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC0 RMII reference or GMAC0 RGMII RX + - description: STG AXI/AHB + - description: APB Bus + - description: GMAC0 GTX + + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC0 RMII reference or GMAC0 RGMII RX + - description: STG AXI/AHB or GMAC0 RGMII RX + - description: APB Bus or STG AXI/AHB + - description: GMAC0 GTX or APB Bus + - description: RTC Oscillator (32.768 kHz) or GMAC0 GTX + + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC0 RMII reference + - description: GMAC0 RGMII RX + - description: STG AXI/AHB + - description: APB Bus + - description: GMAC0 GTX + - description: RTC Oscillator (32.768 kHz) + + clock-names: + oneOf: + - minItems: 5 + items: + - const: osc + - enum: + - gmac0_rmii_refin + - gmac0_rgmii_rxin + - const: stg_axiahb + - const: apb_bus + - const: gmac0_gtxclk + - const: rtc_osc + + - minItems: 6 + items: + - const: osc + - const: gmac0_rmii_refin + - const: gmac0_rgmii_rxin + - const: stg_axiahb + - const: apb_bus + - const: gmac0_gtxclk + - const: rtc_osc + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + + clock-controller@17000000 { + compatible = "starfive,jh7110-aoncrg"; + reg = <0x17000000 0x10000>; + clocks = <&osc>, <&gmac0_rmii_refin>, + <&gmac0_rgmii_rxin>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_APB_BUS>, + <&syscrg JH7110_SYSCLK_GMAC0_GTXCLK>, + <&rtc_osc>; + clock-names = "osc", "gmac0_rmii_refin", + "gmac0_rgmii_rxin", "stg_axiahb", + "apb_bus", "gmac0_gtxclk", + "rtc_osc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml new file mode 100644 index 0000000000000..3b8b85be5cd05 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-ispcrg.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-ispcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Image-Signal-Process Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-ispcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: ISP Top core + - description: ISP Top Axi + - description: NOC ISP Bus + - description: external DVP + + clock-names: + items: + - const: isp_top_core + - const: isp_top_axi + - const: noc_bus_isp_axi + - const: dvp_clk + + resets: + items: + - description: ISP Top core + - description: ISP Top Axi + - description: NOC ISP Bus + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + + power-domains: + maxItems: 1 + description: + ISP domain power + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - '#clock-cells' + - '#reset-cells' + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x19810000 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, + <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>, + <&syscrg JH7110_SYSCLK_NOC_BUS_ISP_AXI>, + <&dvp_clk>; + clock-names = "isp_top_core", "isp_top_axi", + "noc_bus_isp_axi", "dvp_clk"; + resets = <&syscrg JH7110_SYSRST_ISP_TOP>, + <&syscrg JH7110_SYSRST_ISP_TOP_AXI>, + <&syscrg JH7110_SYSRST_NOC_BUS_ISP_AXI>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_ISP>; + }; diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml new file mode 100644 index 0000000000000..9397516f60ba6 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-pll.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-pll.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PLL Clock Generator + +description: + This PLL are high speed, low jitter frequency synthesizers in JH7110. + Each PLL clocks work in integer mode or fraction mode by some dividers, + and the configuration registers and dividers are set in several syscon + registers. So pll node should be a child of SYS-SYSCON node. + The formula for calculating frequency is that, + Fvco = Fref * (NI + NF) / M / Q1 + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-pll + + clocks: + maxItems: 1 + description: Main Oscillator (24 MHz) + + '#clock-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - clocks + - '#clock-cells' + +additionalProperties: false + +examples: + - | + pllclk: pll-clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml new file mode 100644 index 0000000000000..b64ccd84200ac --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-stgcrg.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-stgcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 System-Top-Group Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-stgcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: Main Oscillator (24 MHz) + - description: HIFI4 core + - description: STG AXI/AHB + - description: USB (125 MHz) + - description: CPU Bus + - description: HIFI4 Axi + - description: NOC STG Bus + - description: APB Bus + + clock-names: + items: + - const: osc + - const: hifi4_core + - const: stg_axiahb + - const: usb_125m + - const: cpu_bus + - const: hifi4_axi + - const: nocstg_bus + - const: apb_bus + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + #include + + stgcrg: clock-controller@10230000 { + compatible = "starfive,jh7110-stgcrg"; + reg = <0x10230000 0x10000>; + clocks = <&osc>, + <&syscrg JH7110_SYSCLK_HIFI4_CORE>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_USB_125M>, + <&syscrg JH7110_SYSCLK_CPU_BUS>, + <&syscrg JH7110_SYSCLK_HIFI4_AXI>, + <&syscrg JH7110_SYSCLK_NOCSTG_BUS>, + <&syscrg JH7110_SYSCLK_APB_BUS>; + clock-names = "osc", "hifi4_core", + "stg_axiahb", "usb_125m", + "cpu_bus", "hifi4_axi", + "nocstg_bus", "apb_bus"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml new file mode 100644 index 0000000000000..55d4e7f09cd5a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-syscrg.yaml @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-syscrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 System Clock and Reset Generator + +maintainers: + - Emil Renner Berthing + +properties: + compatible: + const: starfive,jh7110-syscrg + + reg: + maxItems: 1 + + clocks: + oneOf: + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC1 RMII reference or GMAC1 RGMII RX + - description: External I2S TX bit clock + - description: External I2S TX left/right channel clock + - description: External I2S RX bit clock + - description: External I2S RX left/right channel clock + - description: External TDM clock + - description: External audio master clock + - description: PLL0 + - description: PLL1 + - description: PLL2 + + - items: + - description: Main Oscillator (24 MHz) + - description: GMAC1 RMII reference + - description: GMAC1 RGMII RX + - description: External I2S TX bit clock + - description: External I2S TX left/right channel clock + - description: External I2S RX bit clock + - description: External I2S RX left/right channel clock + - description: External TDM clock + - description: External audio master clock + - description: PLL0 + - description: PLL1 + - description: PLL2 + + clock-names: + oneOf: + - items: + - const: osc + - enum: + - gmac1_rmii_refin + - gmac1_rgmii_rxin + - const: i2stx_bclk_ext + - const: i2stx_lrck_ext + - const: i2srx_bclk_ext + - const: i2srx_lrck_ext + - const: tdm_ext + - const: mclk_ext + - const: pll0_out + - const: pll1_out + - const: pll2_out + + - items: + - const: osc + - const: gmac1_rmii_refin + - const: gmac1_rgmii_rxin + - const: i2stx_bclk_ext + - const: i2stx_lrck_ext + - const: i2srx_bclk_ext + - const: i2srx_lrck_ext + - const: tdm_ext + - const: mclk_ext + - const: pll0_out + - const: pll1_out + - const: pll2_out + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + - '#reset-cells' + +additionalProperties: false + +examples: + - | + clock-controller@13020000 { + compatible = "starfive,jh7110-syscrg"; + reg = <0x13020000 0x10000>; + clocks = <&osc>, <&gmac1_rmii_refin>, + <&gmac1_rgmii_rxin>, + <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, + <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, + <&tdm_ext>, <&mclk_ext>, + <&pllclk JH7110_CLK_PLL0_OUT>, + <&pllclk JH7110_CLK_PLL1_OUT>, + <&pllclk JH7110_CLK_PLL2_OUT>; + clock-names = "osc", "gmac1_rmii_refin", + "gmac1_rgmii_rxin", + "i2stx_bclk_ext", "i2stx_lrck_ext", + "i2srx_bclk_ext", "i2srx_lrck_ext", + "tdm_ext", "mclk_ext", + "pll0_out", "pll1_out", "pll2_out"; + #clock-cells = <1>; + #reset-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml b/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml new file mode 100644 index 0000000000000..af77bd8c86b12 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/starfive,jh7110-voutcrg.yaml @@ -0,0 +1,90 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/starfive,jh7110-voutcrg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Video-Output Clock and Reset Generator + +maintainers: + - Xingyu Wu + +properties: + compatible: + const: starfive,jh7110-voutcrg + + reg: + maxItems: 1 + + clocks: + items: + - description: Vout Top core + - description: Vout Top Ahb + - description: Vout Top Axi + - description: Vout Top HDMI MCLK + - description: I2STX0 BCLK + - description: external HDMI pixel + + clock-names: + items: + - const: vout_src + - const: vout_top_ahb + - const: vout_top_axi + - const: vout_top_hdmitx0_mclk + - const: i2stx0_bclk + - const: hdmitx0_pixelclk + + resets: + maxItems: 1 + description: Vout Top core + + '#clock-cells': + const: 1 + description: + See for valid indices. + + '#reset-cells': + const: 1 + description: + See for valid indices. + + power-domains: + maxItems: 1 + description: + Vout domain power + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - '#clock-cells' + - '#reset-cells' + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + voutcrg: clock-controller@295C0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x295C0000 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>, + <&syscrg JH7110_SYSCLK_I2STX0_BCLK>, + <&hdmitx0_pixelclk>; + clock-names = "vout_src", "vout_top_ahb", + "vout_top_axi", "vout_top_hdmitx0_mclk", + "i2stx0_bclk", "hdmitx0_pixelclk"; + resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; diff --git a/Documentation/devicetree/bindings/crypto/starfive,jh7110-crypto.yaml b/Documentation/devicetree/bindings/crypto/starfive,jh7110-crypto.yaml new file mode 100644 index 0000000000000..71a2876bd6e49 --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/starfive,jh7110-crypto.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/crypto/starfive,jh7110-crypto.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive Cryptographic Module + +maintainers: + - Jia Jie Ho + - William Qiu + +properties: + compatible: + const: starfive,jh7110-crypto + + reg: + maxItems: 1 + + clocks: + items: + - description: Hardware reference clock + - description: AHB reference clock + + clock-names: + items: + - const: hclk + - const: ahb + + interrupts: + maxItems: 1 + + resets: + maxItems: 1 + + dmas: + items: + - description: TX DMA channel + - description: RX DMA channel + + dma-names: + items: + - const: tx + - const: rx + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - dmas + - dma-names + +additionalProperties: false + +examples: + - | + crypto: crypto@16000000 { + compatible = "starfive,jh7110-crypto"; + reg = <0x16000000 0x4000>; + clocks = <&clk 15>, <&clk 16>; + clock-names = "hclk", "ahb"; + interrupts = <28>; + resets = <&reset 3>; + dmas = <&dma 1 2>, + <&dma 0 2>; + dma-names = "tx", "rx"; + }; +... diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 5c81194e2300f..363cf8bd150da 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -20,6 +20,7 @@ properties: enum: - snps,axi-dma-1.01a - intel,kmb-axi-dma + - starfive,jh7110-axi-dma reg: minItems: 1 @@ -58,7 +59,8 @@ properties: maximum: 8 resets: - maxItems: 1 + minItems: 1 + maxItems: 2 snps,dma-masters: description: | @@ -109,6 +111,25 @@ required: - snps,priority - snps,block-size +if: + properties: + compatible: + contains: + enum: + - starfive,jh7110-axi-dma +then: + properties: + resets: + minItems: 2 + items: + - description: AXI reset line + - description: AHB reset line + - description: module reset +else: + properties: + resets: + maxItems: 1 + additionalProperties: false examples: diff --git a/Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml b/Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml new file mode 100644 index 0000000000000..f5b34528928dd --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/starfive,jh71x0-temp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH71x0 Temperature Sensor + +maintainers: + - Emil Renner Berthing + +description: | + StarFive Technology Co. JH71x0 embedded temperature sensor + +properties: + compatible: + enum: + - starfive,jh7100-temp + - starfive,jh7110-temp + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 2 + + clock-names: + items: + - const: "sense" + - const: "bus" + + '#thermal-sensor-cells': + const: 0 + + resets: + minItems: 2 + maxItems: 2 + + reset-names: + items: + - const: "sense" + - const: "bus" + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + #include + #include + + temperature-sensor@124a0000 { + compatible = "starfive,jh7100-temp"; + reg = <0x124a0000 0x10000>; + clocks = <&clkgen JH7100_CLK_TEMP_SENSE>, + <&clkgen JH7100_CLK_TEMP_APB>; + clock-names = "sense", "bus"; + #thermal-sensor-cells = <0>; + resets = <&rstgen JH7100_RSTN_TEMP_SENSE>, + <&rstgen JH7100_RSTN_TEMP_APB>; + reset-names = "sense", "bus"; + }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml index 63bc89e134801..4e98261f29489 100644 --- a/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml +++ b/Documentation/devicetree/bindings/interrupt-controller/sifive,plic-1.0.0.yaml @@ -59,6 +59,7 @@ properties: - enum: - sifive,fu540-c000-plic - starfive,jh7100-plic + - starfive,jh7110-plic - canaan,k210-plic - const: sifive,plic-1.0.0 - items: diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt b/Documentation/devicetree/bindings/media/cdns,csi2rx.txt deleted file mode 100644 index 6b02a0657ad97..0000000000000 --- a/Documentation/devicetree/bindings/media/cdns,csi2rx.txt +++ /dev/null @@ -1,100 +0,0 @@ -Cadence MIPI-CSI2 RX controller -=============================== - -The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI -lanes in input, and 4 different pixel streams in output. - -Required properties: - - compatible: must be set to "cdns,csi2rx" and an SoC-specific compatible - - reg: base address and size of the memory mapped region - - clocks: phandles to the clocks driving the controller - - clock-names: must contain: - * sys_clk: main clock - * p_clk: register bank clock - * pixel_if[0-3]_clk: pixel stream output clock, one for each stream - implemented in hardware, between 0 and 3 - -Optional properties: - - phys: phandle to the external D-PHY, phy-names must be provided - - phy-names: must contain "dphy", if the implementation uses an - external D-PHY - -Required subnodes: - - ports: A ports node with one port child node per device input and output - port, in accordance with the video interface bindings defined in - Documentation/devicetree/bindings/media/video-interfaces.txt. The - port nodes are numbered as follows: - - Port Description - ----------------------------- - 0 CSI-2 input - 1 Stream 0 output - 2 Stream 1 output - 3 Stream 2 output - 4 Stream 3 output - - The stream output port nodes are optional if they are not - connected to anything at the hardware level or implemented - in the design.Since there is only one endpoint per port, - the endpoints are not numbered. - - -Example: - -csi2rx: csi-bridge@0d060000 { - compatible = "cdns,csi2rx"; - reg = <0x0d060000 0x1000>; - clocks = <&byteclock>, <&byteclock> - <&coreclock>, <&coreclock>, - <&coreclock>, <&coreclock>; - clock-names = "sys_clk", "p_clk", - "pixel_if0_clk", "pixel_if1_clk", - "pixel_if2_clk", "pixel_if3_clk"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - - csi2rx_in_sensor: endpoint { - remote-endpoint = <&sensor_out_csi2rx>; - clock-lanes = <0>; - data-lanes = <1 2>; - }; - }; - - port@1 { - reg = <1>; - - csi2rx_out_grabber0: endpoint { - remote-endpoint = <&grabber0_in_csi2rx>; - }; - }; - - port@2 { - reg = <2>; - - csi2rx_out_grabber1: endpoint { - remote-endpoint = <&grabber1_in_csi2rx>; - }; - }; - - port@3 { - reg = <3>; - - csi2rx_out_grabber2: endpoint { - remote-endpoint = <&grabber2_in_csi2rx>; - }; - }; - - port@4 { - reg = <4>; - - csi2rx_out_grabber3: endpoint { - remote-endpoint = <&grabber3_in_csi2rx>; - }; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml new file mode 100644 index 0000000000000..ed573a67f93e7 --- /dev/null +++ b/Documentation/devicetree/bindings/media/cdns,csi2rx.yaml @@ -0,0 +1,163 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/cdns,csi2rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence MIPI-CSI2 RX controller + +maintainers: + - Maxime Ripard + +description: + The Cadence MIPI-CSI2 RX controller is a CSI-2 bridge supporting up to 4 CSI + lanes in input, and 4 different pixel streams in output. + +properties: + compatible: + enum: + - cdns,csi2rx + + reg: + maxItems: 1 + + clocks: + items: + - description: CSI2Rx system clock + - description: Gated Register bank clock for APB interface + - description: pixel Clock for Stream interface 0 + - description: pixel Clock for Stream interface 1 + - description: pixel Clock for Stream interface 2 + - description: pixel Clock for Stream interface 3 + + clock-names: + items: + - const: sys + - const: reg_bank + - const: pixel_if0 + - const: pixel_if1 + - const: pixel_if2 + - const: pixel_if3 + + resets: + items: + - description: CSI2Rx system reset + - description: Gated Register bank reset for APB interface + - description: pixel reset for Stream interface 0 + - description: pixel reset for Stream interface 1 + - description: pixel reset for Stream interface 2 + - description: pixel reset for Stream interface 3 + + reset-names: + items: + - const: sys + - const: reg_bank + - const: pixel_if0 + - const: pixel_if1 + - const: pixel_if2 + - const: pixel_if3 + + phys: + maxItems: 1 + description: MIPI D-PHY + + phy-names: + items: + - const: dphy + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port node, single endpoint describing the CSI-2 transmitter. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + properties: + bus-type: + enum: + - 4 + + clock-lanes: + maximum: 4 + + data-lanes: + minItems: 1 + maxItems: 4 + items: + maximum: 4 + + required: + - clock-lanes + - data-lanes + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: + Output port node + + required: + - port@0 + - port@1 + +required: + - compatible + - reg + - clocks + - clock-names + - ports + +additionalProperties: false + +examples: + - | + csi2rx: csi@0d060000 { + compatible = "cdns,csi2rx"; + reg = <0x0d060000 0x1000>; + clocks = <&byteclock 7>, <&byteclock 6>, + <&coreclock 8>, <&coreclock 9>, + <&coreclock 10>, <&coreclock 11>; + clock-names = "sys", "reg_bank", + "pixel_if0", "pixel_if1", + "pixel_if2", "pixel_if3"; + resets = <&bytereset 9>, <&bytereset 4>, + <&corereset 5>, <&corereset 6>, + <&corereset 7>, <&corereset 8>; + reset-names = "sys", "reg_bank", + "pixel_if0", "pixel_if1", + "pixel_if2", "pixel_if3"; + phys = <&csi_phy>; + phy-names = "dphy"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + csi2rx_in_sensor: endpoint { + remote-endpoint = <&sensor_out_csi2rx>; + clock-lanes = <0>; + data-lanes = <1 2>; + }; + }; + + port@1 { + reg = <1>; + + csi2rx_out_grabber0: endpoint { + remote-endpoint = <&grabber0_in_csi2rx>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml new file mode 100644 index 0000000000000..3f348dd53441f --- /dev/null +++ b/Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml @@ -0,0 +1,144 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/starfive,jh7110-camss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Starfive SoC CAMSS ISP + +maintainers: + - Jack Zhu + - Changhuang Liang + +description: + The Starfive CAMSS ISP is a Camera interface for Starfive JH7110 SoC.It + consists of a VIN controller(Video In Controller, a top-level control until) + and an ISP. + +properties: + compatible: + const: starfive,jh7110-camss + + reg: + maxItems: 2 + + reg-names: + items: + - const: syscon + - const: isp + + clocks: + maxItems: 7 + + clock-names: + items: + - const: apb_func + - const: wrapper_clk_c + - const: dvp_inv + - const: axiwr + - const: mipi_rx0_pxl + - const: ispcore_2x + - const: isp_axi + + resets: + maxItems: 6 + + reset-names: + items: + - const: wrapper_p + - const: wrapper_c + - const: axird + - const: axiwr + - const: isp_top_n + - const: isp_top_axi + + power-domains: + items: + - description: JH7110 ISP Power Domain Switch Controller. + + interrupts: + maxItems: 4 + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: + Input port for receiving CSI data. + + properties: + endpoint: + $ref: video-interfaces.yaml# + unevaluatedProperties: false + + required: + - port@1 + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + - resets + - reset-names + - power-domains + - interrupts + +additionalProperties: false + +examples: + - | + stfcamss: isp@19840000 { + compatible = "starfive,jh7110-camss"; + reg = <0x19840000 0x10000>, + <0x19870000 0x30000>; + reg-names = "syscon", "isp"; + clocks = <&ispcrg 0>, + <&ispcrg 13>, + <&ispcrg 2>, + <&ispcrg 12>, + <&ispcrg 1>, + <&syscrg 51>, + <&syscrg 52>; + clock-names = "apb_func", + "wrapper_clk_c", + "dvp_inv", + "axiwr", + "mipi_rx0_pxl", + "ispcore_2x", + "isp_axi"; + resets = <&ispcrg 0>, + <&ispcrg 1>, + <&ispcrg 10>, + <&ispcrg 11>, + <&syscrg 41>, + <&syscrg 42>; + reset-names = "wrapper_p", + "wrapper_c", + "axird", + "axiwr", + "isp_top_n", + "isp_top_axi"; + power-domains = <&pwrc 5>; + interrupts = <92>, <87>, <88>, <90>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + + vin_from_csi2rx: endpoint { + remote-endpoint = <&csi2rx_to_vin>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/net/snps,dwmac.yaml b/Documentation/devicetree/bindings/net/snps,dwmac.yaml index 16b7d29046964..245f7d713261f 100644 --- a/Documentation/devicetree/bindings/net/snps,dwmac.yaml +++ b/Documentation/devicetree/bindings/net/snps,dwmac.yaml @@ -30,6 +30,7 @@ select: - snps,dwmac-4.10a - snps,dwmac-4.20a - snps,dwmac-5.10a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 @@ -87,8 +88,10 @@ properties: - snps,dwmac-4.10a - snps,dwmac-4.20a - snps,dwmac-5.10a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 + - starfive,jh7110-dwmac reg: minItems: 1 @@ -131,12 +134,16 @@ properties: - ptp_ref resets: - maxItems: 1 - description: - MAC Reset signal. + minItems: 1 + items: + - description: GMAC stmmaceth reset + - description: AHB reset reset-names: - const: stmmaceth + minItems: 1 + items: + - const: stmmaceth + - const: ahb power-domains: maxItems: 1 @@ -575,6 +582,7 @@ allOf: - snps,dwmac-3.50a - snps,dwmac-4.10a - snps,dwmac-4.20a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 - st,spear600-gmac @@ -629,6 +637,7 @@ allOf: - snps,dwmac-4.10a - snps,dwmac-4.20a - snps,dwmac-5.10a + - snps,dwmac-5.20 - snps,dwxgmac - snps,dwxgmac-2.10 - st,spear600-gmac diff --git a/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml new file mode 100644 index 0000000000000..b59e6bd8201f2 --- /dev/null +++ b/Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2022 StarFive Technology Co., Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/starfive,jh7110-dwmac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 DWMAC glue layer + +maintainers: + - Emil Renner Berthing + - Samin Guo + +select: + properties: + compatible: + contains: + enum: + - starfive,jh7110-dwmac + required: + - compatible + +properties: + compatible: + items: + - enum: + - starfive,jh7110-dwmac + - const: snps,dwmac-5.20 + + clocks: + items: + - description: GMAC main clock + - description: GMAC AHB clock + - description: PTP clock + - description: TX clock + - description: GTX clock + + clock-names: + items: + - const: stmmaceth + - const: pclk + - const: ptp_ref + - const: tx + - const: gtx + + resets: + items: + - description: MAC Reset signal. + - description: AHB Reset signal. + + reset-names: + items: + - const: stmmaceth + - const: ahb + + starfive,tx-use-rgmii-clk: + description: + Tx clock is provided by external rgmii clock. + type: boolean + + starfive,syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to syscon that configures phy mode + - description: Offset of phy mode selection + - description: Shift of phy mode selection + description: + A phandle to syscon with two arguments that configure phy mode. + The argument one is the offset of phy mode selection, the + argument two is the shift of phy mode selection. + +allOf: + - $ref: snps,dwmac.yaml# + +unevaluatedProperties: false + +required: + - compatible + - clocks + - clock-names + - resets + - reset-names + +examples: + - | + ethernet@16030000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x16030000 0x10000>; + clocks = <&clk 3>, <&clk 2>, <&clk 109>, + <&clk 6>, <&clk 111>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&rst 1>, <&rst 2>; + reset-names = "stmmaceth", "ahb"; + interrupts = <7>, <6>, <5>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + phy-mode = "rgmii-id"; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <8>; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,tso; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&aon_syscon 0xc 0x12>; + phy-handle = <&phy0>; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy0: ethernet-phy@0 { + reg = <0>; + }; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <4>; + snps,rd_osr_lmt = <4>; + snps,blen = <256 128 64 32 0 0 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml new file mode 100644 index 0000000000000..b72ac44bc29d3 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-dphy-rx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive SoC MIPI D-PHY Rx Controller + +maintainers: + - Jack Zhu + - Changhuang Liang + +description: + The StarFive SoC uses the MIPI CSI D-PHY based on M31 IP to transfer + CSI camera data. + +properties: + compatible: + const: starfive,jh7110-dphy-rx + + reg: + maxItems: 1 + + clocks: + items: + - description: config clock + - description: reference clock + - description: escape mode transmit clock + + clock-names: + items: + - const: cfg + - const: ref + - const: tx + + resets: + items: + - description: DPHY_HW reset + - description: DPHY_B09_ALWAYS_ON reset + + starfive,aon-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle of AON SYSCON + - description: register offset + description: The power of dphy rx is configured by AON SYSCON + in this property. + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + - starfive,aon-syscon + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@19820000 { + compatible = "starfive,jh7110-dphy-rx"; + reg = <0x19820000 0x10000>; + clocks = <&ispcrg 3>, + <&ispcrg 4>, + <&ispcrg 5>; + clock-names = "cfg", "ref", "tx"; + resets = <&ispcrg 2>, + <&ispcrg 3>; + starfive,aon-syscon = <&aon_syscon 0x00>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml new file mode 100644 index 0000000000000..aa1c3fe93100f --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-usb-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive USB 2.0 and PCIe 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + enum: + - starfive,jh7110-usb-phy + - starfive,jh7110-pcie-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: usb 125m clock + - description: app 125m clock + + clock-names: + items: + - const: 125m + - const: app_125 + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x10200000 0x10000>; + clocks = <&syscrg 95>, + <&stgcrg 6>; + clock-names = "125m", "app_125"; + #phy-cells = <0>; + }; + + phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x10210000 0x10000>; + #phy-cells = <0>; + }; + + phy@10220000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x10220000 0x10000>; + #phy-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml b/Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml new file mode 100644 index 0000000000000..082b3779fa614 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/starfive,jh7110-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive PWM controller + +maintainers: + - William Qiu + +description: + StarFive SoCs contain PWM and when operating in PWM mode, the PTC core generates + binary signal with user-programmable low and high periods. Clock source for the + PWM can be either system clockor external clock. Each PWM timer block provides 8 + PWM channels. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: starfive,jh7110-pwm + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + - resets + +additionalProperties: false + +examples: + - | + pwm@120d0000 { + compatible = "starfive,jh7110-pwm"; + reg = <0x120d0000 0x10000>; + clocks = <&syscrg 121>; + resets = <&syscrg 108>; + #pwm-cells = <3>; + }; diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml index 001931d526ec7..14b5b7ea0ce0f 100644 --- a/Documentation/devicetree/bindings/riscv/cpus.yaml +++ b/Documentation/devicetree/bindings/riscv/cpus.yaml @@ -35,6 +35,7 @@ properties: - sifive,e7 - sifive,e71 - sifive,rocket0 + - sifive,s7 - sifive,u5 - sifive,u54 - sifive,u7 diff --git a/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml new file mode 100644 index 0000000000000..b61d8921ef42d --- /dev/null +++ b/Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/soc/starfive/starfive,jh7110-syscon.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 SoC system controller + +maintainers: + - William Qiu + +description: | + The StarFive JH7110 SoC system controller provides register information such + as offset, mask and shift to configure related modules such as MMC and PCIe. + +properties: + compatible: + oneOf: + - items: + - enum: + - starfive,jh7110-aon-syscon + - starfive,jh7110-stg-syscon + - starfive,jh7110-sys-syscon + - const: syscon + - items: + - enum: + - starfive,jh7110-aon-syscon + - starfive,jh7110-stg-syscon + - starfive,jh7110-sys-syscon + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + +patternProperties: + # Optional children + "pll-clock-controller": + type: object + $ref: /schemas/clock/starfive,jh7110-pll.yaml# + description: Clock provider for PLL. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x10240000 0x1000>; + }; + + - | + syscon@13030000 { + compatible = "starfive,jh7110-sys-syscon", "syscon", "simple-mfd"; + reg = <0x13030000 0x1000>; + + pll-clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml index 5c01db128be0f..b310069762dd5 100644 --- a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml +++ b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml @@ -19,6 +19,33 @@ allOf: then: required: - power-domains + - if: + properties: + compatible: + contains: + const: starfive,jh7110-qspi + then: + properties: + resets: + minItems: 2 + maxItems: 3 + + reset-names: + minItems: 2 + maxItems: 3 + items: + enum: [ qspi, qspi-ocp, rstc_ref ] + + else: + properties: + resets: + maxItems: 2 + + reset-names: + minItems: 1 + maxItems: 2 + items: + enum: [ qspi, qspi-ocp ] properties: compatible: @@ -30,6 +57,7 @@ properties: - intel,lgm-qspi - xlnx,versal-ospi-1.0 - intel,socfpga-qspi + - starfive,jh7110-qspi - const: cdns,qspi-nor - const: cdns,qspi-nor @@ -79,13 +107,14 @@ properties: maxItems: 1 resets: - maxItems: 2 + minItems: 2 + maxItems: 3 reset-names: - minItems: 1 - maxItems: 2 + minItems: 2 + maxItems: 3 items: - enum: [ qspi, qspi-ocp ] + enum: [ qspi, qspi-ocp, rstc_ref ] required: - compatible diff --git a/Documentation/devicetree/bindings/timer/sifive,clint.yaml b/Documentation/devicetree/bindings/timer/sifive,clint.yaml index aada6957216c3..94bef9424df1b 100644 --- a/Documentation/devicetree/bindings/timer/sifive,clint.yaml +++ b/Documentation/devicetree/bindings/timer/sifive,clint.yaml @@ -31,6 +31,7 @@ properties: - enum: - sifive,fu540-c000-clint - starfive,jh7100-clint + - starfive,jh7110-clint - canaan,k210-clint - const: sifive,clint0 - items: diff --git a/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml new file mode 100644 index 0000000000000..24b34618f2c8e --- /dev/null +++ b/Documentation/devicetree/bindings/timer/starfive,jh7110-timer.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/timer/starfive,jh7110-timer.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 Timer +maintainers: + - Xingyu Wu + - Samin Guo + +description: + This timer has four free-running 32 bit counters in StarFive JH7110 SoC. + And each channel(counter) triggers an interrupt when timeout. They support + one-shot mode and continuous-run mode. + +properties: + compatible: + const: starfive,jh7110-timer + + reg: + maxItems: 1 + + interrupts: + items: + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + clocks: + items: + - description: timer APB + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + clock-names: + items: + - const: apb + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + + resets: + items: + - description: timer APB + - description: channel 0 + - description: channel 1 + - description: channel 2 + - description: channel 3 + + reset-names: + items: + - const: apb + - const: ch0 + - const: ch1 + - const: ch2 + - const: ch3 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + - reset-names + +additionalProperties: false + +examples: + - | + timer@13050000 { + compatible = "starfive,jh7110-timer"; + reg = <0x13050000 0x10000>; + interrupts = <69>, <70>, <71> ,<72>; + clocks = <&clk 124>, + <&clk 125>, + <&clk 126>, + <&clk 127>, + <&clk 128>; + clock-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + resets = <&rst 117>, + <&rst 118>, + <&rst 119>, + <&rst 120>, + <&rst 121>; + reset-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + }; + diff --git a/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml b/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml new file mode 100644 index 0000000000000..b1a8dc6d7b4b7 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml @@ -0,0 +1,119 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/starfive,jh7110-usb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 wrapper module for the Cadence USBSS-DRD controller + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-usb + + clocks: + items: + - description: lpm clock + - description: stb clock + - description: apb clock + - description: axi clock + - description: utmi apb clock + + clock-names: + items: + - const: lpm + - const: stb + - const: apb + - const: axi + - const: utmi_apb + + resets: + items: + - description: PWRUP reset + - description: APB reset + - description: AXI reset + - description: UTMI_APB reset + + starfive,sys-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to System Register Controller sys_syscon node. + - description: offset of SYS_SYSCONSAIF__SYSCFG register for USB. + description: + The phandle to System Register Controller syscon node and the offset + of SYS_SYSCONSAIF__SYSCFG register for USB. + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to System Register Controller stg_syscon node. + - description: register0 offset of STG_SYSCONSAIF__SYSCFG register for USB. + - description: register1 offset of STG_SYSCONSAIF__SYSCFG register for USB. + - description: register2 offset of STG_SYSCONSAIF__SYSCFG register for USB. + - description: register3 offset of STG_SYSCONSAIF__SYSCFG register for USB. + description: + The phandle to System Register Controller syscon node and the offset + of STG_SYSCONSAIF__SYSCFG register for USB. Total 4 regsisters offset + for USB. + + "#address-cells": + maximum: 2 + + "#size-cells": + maximum: 2 + + ranges: true + +patternProperties: + "^usb@[0-9a-f]+$": + type: object + +required: + - compatible + - clocks + - clock-names + - resets + - starfive,sys-syscon + - starfive,stg-syscon + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + usb@10100000 { + compatible = "starfive,jh7110-usb"; + clocks = <&syscrg 4>, + <&stgcrg 5>, + <&stgcrg 1>, + <&stgcrg 3>, + <&stgcrg 2>; + clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; + resets = <&stgcrg 10>, + <&stgcrg 8>, + <&stgcrg 7>, + <&stgcrg 9>; + starfive,stg-syscon = <&stg_syscon 0x4 0xc4 0x148 0x1f4>; + starfive,sys-syscon = <&sys_syscon 0x18>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x10100000 0x100000>; + + usb@0 { + compatible = "cdns,usb3"; + reg = <0x0 0x10000>, + <0x10000 0x10000>, + <0x20000 0x10000>; + reg-names = "otg", "xhci", "dev"; + interrupts = <100>, <108>, <110>; + interrupt-names = "host", "peripheral", "otg"; + maximum-speed = "super-speed"; + dr_mode = "host"; + }; + }; diff --git a/Documentation/devicetree/bindings/watchdog/starfive,jh7100-wdt.yaml b/Documentation/devicetree/bindings/watchdog/starfive,jh7100-wdt.yaml new file mode 100644 index 0000000000000..68f3f6fd08a62 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/starfive,jh7100-wdt.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/starfive,jh7100-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive Watchdog for JH7100 and JH7110 SoC + +maintainers: + - Xingyu Wu + - Samin Guo + +description: + The JH7100 and JH7110 watchdog both are 32 bit counters. JH7100 watchdog + has only one timeout phase and reboots. And JH7110 watchdog has two + timeout phases. At the first phase, the signal of watchdog interrupt + output(WDOGINT) will rise when counter is 0. The counter will reload + the timeout value. And then, if counter decreases to 0 again and WDOGINT + isn't cleared, the watchdog will reset the system unless the watchdog + reset is disabled. + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + enum: + - starfive,jh7100-wdt + - starfive,jh7110-wdt + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: APB clock + - description: Core clock + + clock-names: + items: + - const: apb + - const: core + + resets: + items: + - description: APB reset + - description: Core reset + +required: + - compatible + - reg + - clocks + - clock-names + - resets + +unevaluatedProperties: false + +examples: + - | + watchdog@12480000 { + compatible = "starfive,jh7100-wdt"; + reg = <0x12480000 0x10000>; + clocks = <&clk 171>, + <&clk 172>; + clock-names = "apb", "core"; + resets = <&rst 99>, + <&rst 100>; + }; diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index f1fe75f596a59..d72bb1df64319 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -184,6 +184,7 @@ Hardware Monitoring Kernel Drivers sch5627 sch5636 scpi-hwmon + sfctemp sht15 sht21 sht3x diff --git a/Documentation/hwmon/sfctemp.rst b/Documentation/hwmon/sfctemp.rst new file mode 100644 index 0000000000000..9fbd5bb1f356d --- /dev/null +++ b/Documentation/hwmon/sfctemp.rst @@ -0,0 +1,33 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver sfctemp +===================== + +Supported chips: + - StarFive JH7100 + - StarFive JH7110 + +Authors: + - Emil Renner Berthing + +Description +----------- + +This driver adds support for reading the built-in temperature sensor on the +JH7100 and JH7110 RISC-V SoCs by StarFive Technology Co. Ltd. + +``sysfs`` interface +------------------- + +The temperature sensor can be enabled, disabled and queried via the standard +hwmon interface in sysfs under ``/sys/class/hwmon/hwmonX`` for some value of +``X``: + +================ ==== ============================================= +Name Perm Description +================ ==== ============================================= +temp1_enable RW Enable or disable temperature sensor. + Automatically enabled by the driver, + but may be disabled to save power. +temp1_input RO Temperature reading in milli-degrees Celsius. +================ ==== ============================================= diff --git a/MAINTAINERS b/MAINTAINERS index d8ebab595b2a9..07439527eb028 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4495,6 +4495,7 @@ M: Maxime Ripard L: linux-media@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/media/cdns,*.txt +F: Documentation/devicetree/bindings/media/cdns,csi2rx.yaml F: drivers/media/platform/cadence/cdns-csi2* CADENCE NAND DRIVER @@ -18911,6 +18912,14 @@ S: Supported F: Documentation/networking/devlink/sfc.rst F: drivers/net/ethernet/sfc/ +SFCTEMP HWMON DRIVER +M: Emil Renner Berthing +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/starfive,jh71x0-temp.yaml +F: Documentation/hwmon/sfctemp.rst +F: drivers/hwmon/sfctemp.c + SFF/SFP/SFP+ MODULE SUPPORT M: Russell King L: netdev@vger.kernel.org @@ -19900,17 +19909,40 @@ M: Ion Badulescu S: Odd Fixes F: drivers/net/ethernet/adaptec/starfire* +STARFIVE CAMERA SUBSYSTEM DRIVER +M: Jack Zhu +M: Changhuang Liang +L: linux-media@vger.kernel.org +S: Maintained +F: Documentation/admin-guide/media/starfive_camss.rst +F: Documentation/devicetree/bindings/media/starfive,jh7110-camss.yaml +F: drivers/media/platform/starfive/ + +STARFIVE CRYPTO DRIVER +M: Jia Jie Ho +M: William Qiu +S: Supported +F: Documentation/devicetree/bindings/crypto/starfive* +F: drivers/crypto/starfive/ + STARFIVE DEVICETREES M: Emil Renner Berthing S: Maintained F: arch/riscv/boot/dts/starfive/ -STARFIVE JH7100 CLOCK DRIVERS +STARFIVE DWMAC GLUE LAYER M: Emil Renner Berthing +M: Samin Guo S: Maintained -F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml -F: drivers/clk/starfive/clk-starfive-jh7100* -F: include/dt-bindings/clock/starfive-jh7100*.h +F: Documentation/devicetree/bindings/net/dwmac-starfive.c +F: Documentation/devicetree/bindings/net/starfive,jh7110-dwmac.yaml + +STARFIVE JH7110 DPHY RX DRIVER +M: Jack Zhu +M: Changhuang Liang +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-dphy-rx.yaml +F: drivers/phy/starfive/phy-starfive-dphy-rx.c STARFIVE JH7110 MMC/SD/SDIO DRIVER M: William Qiu @@ -19918,6 +19950,27 @@ S: Supported F: Documentation/devicetree/bindings/mmc/starfive* F: drivers/mmc/host/dw_mmc-starfive.c +STARFIVE JH7110 SYSCON +M: William Qiu +S: Supported +F: Documentation/devicetree/bindings/soc/starfive/starfive,jh7110-syscon.yaml + +STARFIVE JH71X0 CLOCK DRIVERS +M: Emil Renner Berthing +M: Hal Feng +S: Maintained +F: Documentation/devicetree/bindings/clock/starfive,jh71*.yaml +F: drivers/clk/starfive/clk-starfive-jh71* +F: include/dt-bindings/clock/starfive?jh71*.h + +STARFIVE JH71X0 PCIE AND USB PHY DRIVER +M: Emil Renner Berthing +M: Minda Chen +S: Supported +F: Documentation/devicetree/bindings/phy/starfive,jh7110-usb-pcie-phy.yaml +F: drivers/phy/starfive/phy-jh7110-pcie.c +F: drivers/phy/starfive/phy-jh7110-usb.c + STARFIVE JH71X0 PINCTRL DRIVERS M: Emil Renner Berthing M: Jianlong Huang @@ -19928,12 +19981,27 @@ F: drivers/pinctrl/starfive/pinctrl-starfive-jh71* F: include/dt-bindings/pinctrl/pinctrl-starfive-jh7100.h F: include/dt-bindings/pinctrl/starfive,jh7110-pinctrl.h -STARFIVE JH7100 RESET CONTROLLER DRIVER +STARFIVE JH71X0 PWM DRIVERS +M: William Qiu +M: Hal Feng +S: Supported +F: Documentation/devicetree/bindings/pwm/starfive,jh7110-pwm.yaml +F: drivers/pwm/pwm-starfive-ptc.c + +STARFIVE JH71X0 RESET CONTROLLER DRIVERS M: Emil Renner Berthing +M: Hal Feng S: Maintained F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml -F: drivers/reset/reset-starfive-jh7100.c -F: include/dt-bindings/reset/starfive-jh7100.h +F: drivers/reset/starfive/reset-starfive-jh71* +F: include/dt-bindings/reset/starfive?jh71*.h + +STARFIVE JH71X0 USB DRIVERS +M: Emil Renner Berthing +M: Minda Chen +S: Maintained +F: Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml +F: drivers/usb/cdns3/cdns3-starfive.c STARFIVE JH71XX PMU CONTROLLER DRIVER M: Walker Chen @@ -19948,12 +20016,26 @@ S: Maintained T: git https://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git/ F: drivers/soc/starfive/ +STARFIVE TIMER DRIVER +M: Samin Guo +M: Xingyu Wu +S: Supported +F: Documentation/devicetree/bindings/timer/starfive* +F: drivers/clocksource/timer-starfive* + STARFIVE TRNG DRIVER M: Jia Jie Ho S: Supported F: Documentation/devicetree/bindings/rng/starfive* F: drivers/char/hw_random/jh7110-trng.c +STARFIVE WATCHDOG DRIVER +M: Xingyu Wu +M: Samin Guo +S: Supported +F: Documentation/devicetree/bindings/watchdog/starfive* +F: drivers/watchdog/starfive-wdt.c + STATIC BRANCH/CALL M: Peter Zijlstra M: Josh Poimboeuf diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..75060cf81f297 --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +## JH7110 Upstream Status ## + +To get the latest status of each upstreaming patch series, please visit +our RVspace. + +https://rvspace.org/en/project/JH7110_Upstream_Plan + +## Build Instructions ## + +1. Configure Kconfig options + +```shell +# Use default selections +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- defconfig +``` + +or + +```shell +# Select options manually +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- nconfig +``` + +To boot up the VisionFive 2 board, please make sure **SOC_STARFIVE**, +**CLK_STARFIVE_JH7110_SYS**, **PINCTRL_STARFIVE_JH7110_SYS**, +**SERIAL_8250_DW** are selected. +> If you need MMC and GMAC drivers, you should also select +**MMC_DW_STARFIVE** and **DWMAC_STARFIVE**. + +2. Build +```shell +make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- +``` + +## How to Run on VisionFive 2 Board via Network ## + +1. Power on, enter u-boot and set enviroment parameters +``` +setenv fdt_high 0xffffffffffffffff; setenv initrd_high 0xffffffffffffffff; +setenv scriptaddr 0x88100000; setenv script_offset_f 0x1fff000; setenv script_size_f 0x1000; +setenv kernel_addr_r 0x84000000; setenv kernel_comp_addr_r 0x90000000; setenv kernel_comp_size 0x10000000; +setenv fdt_addr_r 0x88000000; setenv ramdisk_addr_r 0x88300000; +``` +2. Set IP addresses for the board and your tftp server +``` +setenv serverip 192.168.w.x; setenv gatewayip 192.168.w.y; setenv ipaddr 192.168.w.z; setenv hostname starfive; setenv netdev eth0; +``` +3. Upload dtb, image and file system to DDR from your tftp server +``` +tftpboot ${fdt_addr_r} jh7110-starfive-visionfive-2-v1.3b.dtb; tftpboot ${kernel_addr_r} Image.gz; tftpboot ${ramdisk_addr_r} initramfs.cpio.gz; +``` +> If your VisionFive 2 is v1.2A, you should upload jh7110-starfive-visionfive-2-v1.2a.dtb instead. +4. Load and boot the kernel +``` +booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}; +``` +When you see the message "buildroot login:", the launch was successful. +You can just input the following accout and password, then continue. +``` +buildroot login: root +Password: starfive +``` diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index c5e42cc376048..473c2a1a68848 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -707,6 +707,12 @@ menu "Power management options" source "kernel/power/Kconfig" +config ARCH_HIBERNATION_POSSIBLE + def_bool y + +config ARCH_HIBERNATION_HEADER + def_bool HIBERNATION + endmenu # "Power management options" menu "CPU Power Management" diff --git a/arch/riscv/boot/dts/starfive/Makefile b/arch/riscv/boot/dts/starfive/Makefile index 7b00a48580ca0..170956846d494 100644 --- a/arch/riscv/boot/dts/starfive/Makefile +++ b/arch/riscv/boot/dts/starfive/Makefile @@ -1,2 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb jh7100-starfive-visionfive-v1.dtb +dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-beaglev-starlight.dtb +dtb-$(CONFIG_ARCH_STARFIVE) += jh7100-starfive-visionfive-v1.dtb + +dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.2a.dtb +dtb-$(CONFIG_ARCH_STARFIVE) += jh7110-starfive-visionfive-2-v1.3b.dtb diff --git a/arch/riscv/boot/dts/starfive/jh7100.dtsi b/arch/riscv/boot/dts/starfive/jh7100.dtsi index 000447482acaa..4218621ea3b92 100644 --- a/arch/riscv/boot/dts/starfive/jh7100.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7100.dtsi @@ -238,5 +238,15 @@ #size-cells = <0>; status = "disabled"; }; + + watchdog@12480000 { + compatible = "starfive,jh7100-wdt"; + reg = <0x0 0x12480000 0x0 0x10000>; + clocks = <&clkgen JH7100_CLK_WDTIMER_APB>, + <&clkgen JH7100_CLK_WDT_CORE>; + clock-names = "apb", "core"; + resets = <&rstgen JH7100_RSTN_WDTIMER_APB>, + <&rstgen JH7100_RSTN_WDT>; + }; }; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h new file mode 100644 index 0000000000000..fb0139b56723a --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-pinfunc.h @@ -0,0 +1,308 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#ifndef __JH7110_PINFUNC_H__ +#define __JH7110_PINFUNC_H__ + +/* + * mux bits: + * | 31 - 24 | 23 - 16 | 15 - 10 | 9 - 8 | 7 - 0 | + * | din | dout | doen | function | gpio nr | + * + * dout: output signal + * doen: output enable signal + * din: optional input signal, 0xff = none + * function: function selector + * gpio nr: gpio number, 0 - 63 + */ +#define GPIOMUX(n, dout, doen, din) ( \ + (((din) & 0xff) << 24) | \ + (((dout) & 0xff) << 16) | \ + (((doen) & 0x3f) << 10) | \ + ((n) & 0x3f)) + +#define PINMUX(n, func) ((1 << 10) | (((func) & 0x3) << 8) | ((n) & 0xff)) + +/* sys_iomux dout */ +#define GPOUT_LOW 0 +#define GPOUT_HIGH 1 +#define GPOUT_SYS_WAVE511_UART_TX 2 +#define GPOUT_SYS_CAN0_STBY 3 +#define GPOUT_SYS_CAN0_TST_NEXT_BIT 4 +#define GPOUT_SYS_CAN0_TST_SAMPLE_POINT 5 +#define GPOUT_SYS_CAN0_TXD 6 +#define GPOUT_SYS_USB_DRIVE_VBUS 7 +#define GPOUT_SYS_QSPI_CS1 8 +#define GPOUT_SYS_SPDIF 9 +#define GPOUT_SYS_HDMI_CEC_SDA 10 +#define GPOUT_SYS_HDMI_DDC_SCL 11 +#define GPOUT_SYS_HDMI_DDC_SDA 12 +#define GPOUT_SYS_WATCHDOG 13 +#define GPOUT_SYS_I2C0_CLK 14 +#define GPOUT_SYS_I2C0_DATA 15 +#define GPOUT_SYS_SDIO0_BACK_END_POWER 16 +#define GPOUT_SYS_SDIO0_CARD_POWER_EN 17 +#define GPOUT_SYS_SDIO0_CCMD_OD_PULLUP_EN 18 +#define GPOUT_SYS_SDIO0_RST 19 +#define GPOUT_SYS_UART0_TX 20 +#define GPOUT_SYS_HIFI4_JTAG_TDO 21 +#define GPOUT_SYS_JTAG_TDO 22 +#define GPOUT_SYS_PDM_MCLK 23 +#define GPOUT_SYS_PWM_CHANNEL0 24 +#define GPOUT_SYS_PWM_CHANNEL1 25 +#define GPOUT_SYS_PWM_CHANNEL2 26 +#define GPOUT_SYS_PWM_CHANNEL3 27 +#define GPOUT_SYS_PWMDAC_LEFT 28 +#define GPOUT_SYS_PWMDAC_RIGHT 29 +#define GPOUT_SYS_SPI0_CLK 30 +#define GPOUT_SYS_SPI0_FSS 31 +#define GPOUT_SYS_SPI0_TXD 32 +#define GPOUT_SYS_GMAC_PHYCLK 33 +#define GPOUT_SYS_I2SRX_BCLK 34 +#define GPOUT_SYS_I2SRX_LRCK 35 +#define GPOUT_SYS_I2STX0_BCLK 36 +#define GPOUT_SYS_I2STX0_LRCK 37 +#define GPOUT_SYS_MCLK 38 +#define GPOUT_SYS_TDM_CLK 39 +#define GPOUT_SYS_TDM_SYNC 40 +#define GPOUT_SYS_TDM_TXD 41 +#define GPOUT_SYS_TRACE_DATA0 42 +#define GPOUT_SYS_TRACE_DATA1 43 +#define GPOUT_SYS_TRACE_DATA2 44 +#define GPOUT_SYS_TRACE_DATA3 45 +#define GPOUT_SYS_TRACE_REF 46 +#define GPOUT_SYS_CAN1_STBY 47 +#define GPOUT_SYS_CAN1_TST_NEXT_BIT 48 +#define GPOUT_SYS_CAN1_TST_SAMPLE_POINT 49 +#define GPOUT_SYS_CAN1_TXD 50 +#define GPOUT_SYS_I2C1_CLK 51 +#define GPOUT_SYS_I2C1_DATA 52 +#define GPOUT_SYS_SDIO1_BACK_END_POWER 53 +#define GPOUT_SYS_SDIO1_CARD_POWER_EN 54 +#define GPOUT_SYS_SDIO1_CLK 55 +#define GPOUT_SYS_SDIO1_CMD_OD_PULLUP_EN 56 +#define GPOUT_SYS_SDIO1_CMD 57 +#define GPOUT_SYS_SDIO1_DATA0 58 +#define GPOUT_SYS_SDIO1_DATA1 59 +#define GPOUT_SYS_SDIO1_DATA2 60 +#define GPOUT_SYS_SDIO1_DATA3 61 +#define GPOUT_SYS_SDIO1_DATA4 63 +#define GPOUT_SYS_SDIO1_DATA5 63 +#define GPOUT_SYS_SDIO1_DATA6 64 +#define GPOUT_SYS_SDIO1_DATA7 65 +#define GPOUT_SYS_SDIO1_RST 66 +#define GPOUT_SYS_UART1_RTS 67 +#define GPOUT_SYS_UART1_TX 68 +#define GPOUT_SYS_I2STX1_SDO0 69 +#define GPOUT_SYS_I2STX1_SDO1 70 +#define GPOUT_SYS_I2STX1_SDO2 71 +#define GPOUT_SYS_I2STX1_SDO3 72 +#define GPOUT_SYS_SPI1_CLK 73 +#define GPOUT_SYS_SPI1_FSS 74 +#define GPOUT_SYS_SPI1_TXD 75 +#define GPOUT_SYS_I2C2_CLK 76 +#define GPOUT_SYS_I2C2_DATA 77 +#define GPOUT_SYS_UART2_RTS 78 +#define GPOUT_SYS_UART2_TX 79 +#define GPOUT_SYS_SPI2_CLK 80 +#define GPOUT_SYS_SPI2_FSS 81 +#define GPOUT_SYS_SPI2_TXD 82 +#define GPOUT_SYS_I2C3_CLK 83 +#define GPOUT_SYS_I2C3_DATA 84 +#define GPOUT_SYS_UART3_TX 85 +#define GPOUT_SYS_SPI3_CLK 86 +#define GPOUT_SYS_SPI3_FSS 87 +#define GPOUT_SYS_SPI3_TXD 88 +#define GPOUT_SYS_I2C4_CLK 89 +#define GPOUT_SYS_I2C4_DATA 90 +#define GPOUT_SYS_UART4_RTS 91 +#define GPOUT_SYS_UART4_TX 92 +#define GPOUT_SYS_SPI4_CLK 93 +#define GPOUT_SYS_SPI4_FSS 94 +#define GPOUT_SYS_SPI4_TXD 95 +#define GPOUT_SYS_I2C5_CLK 96 +#define GPOUT_SYS_I2C5_DATA 97 +#define GPOUT_SYS_UART5_RTS 98 +#define GPOUT_SYS_UART5_TX 99 +#define GPOUT_SYS_SPI5_CLK 100 +#define GPOUT_SYS_SPI5_FSS 101 +#define GPOUT_SYS_SPI5_TXD 102 +#define GPOUT_SYS_I2C6_CLK 103 +#define GPOUT_SYS_I2C6_DATA 104 +#define GPOUT_SYS_SPI6_CLK 105 +#define GPOUT_SYS_SPI6_FSS 106 +#define GPOUT_SYS_SPI6_TXD 107 + +/* aon_iomux dout */ +#define GPOUT_AON_CLK_32K_OUT 2 +#define GPOUT_AON_PTC0_PWM4 3 +#define GPOUT_AON_PTC0_PWM5 4 +#define GPOUT_AON_PTC0_PWM6 5 +#define GPOUT_AON_PTC0_PWM7 6 +#define GPOUT_AON_CLK_GCLK0 7 +#define GPOUT_AON_CLK_GCLK1 8 +#define GPOUT_AON_CLK_GCLK2 9 + +/* sys_iomux doen */ +#define GPOEN_ENABLE 0 +#define GPOEN_DISABLE 1 +#define GPOEN_SYS_HDMI_CEC_SDA 2 +#define GPOEN_SYS_HDMI_DDC_SCL 3 +#define GPOEN_SYS_HDMI_DDC_SDA 4 +#define GPOEN_SYS_I2C0_CLK 5 +#define GPOEN_SYS_I2C0_DATA 6 +#define GPOEN_SYS_HIFI4_JTAG_TDO 7 +#define GPOEN_SYS_JTAG_TDO 8 +#define GPOEN_SYS_PWM0_CHANNEL0 9 +#define GPOEN_SYS_PWM0_CHANNEL1 10 +#define GPOEN_SYS_PWM0_CHANNEL2 11 +#define GPOEN_SYS_PWM0_CHANNEL3 12 +#define GPOEN_SYS_SPI0_NSSPCTL 13 +#define GPOEN_SYS_SPI0_NSSP 14 +#define GPOEN_SYS_TDM_SYNC 15 +#define GPOEN_SYS_TDM_TXD 16 +#define GPOEN_SYS_I2C1_CLK 17 +#define GPOEN_SYS_I2C1_DATA 18 +#define GPOEN_SYS_SDIO1_CMD 19 +#define GPOEN_SYS_SDIO1_DATA0 20 +#define GPOEN_SYS_SDIO1_DATA1 21 +#define GPOEN_SYS_SDIO1_DATA2 22 +#define GPOEN_SYS_SDIO1_DATA3 23 +#define GPOEN_SYS_SDIO1_DATA4 24 +#define GPOEN_SYS_SDIO1_DATA5 25 +#define GPOEN_SYS_SDIO1_DATA6 26 +#define GPOEN_SYS_SDIO1_DATA7 27 +#define GPOEN_SYS_SPI1_NSSPCTL 28 +#define GPOEN_SYS_SPI1_NSSP 29 +#define GPOEN_SYS_I2C2_CLK 30 +#define GPOEN_SYS_I2C2_DATA 31 +#define GPOEN_SYS_SPI2_NSSPCTL 32 +#define GPOEN_SYS_SPI2_NSSP 33 +#define GPOEN_SYS_I2C3_CLK 34 +#define GPOEN_SYS_I2C3_DATA 35 +#define GPOEN_SYS_SPI3_NSSPCTL 36 +#define GPOEN_SYS_SPI3_NSSP 37 +#define GPOEN_SYS_I2C4_CLK 38 +#define GPOEN_SYS_I2C4_DATA 39 +#define GPOEN_SYS_SPI4_NSSPCTL 40 +#define GPOEN_SYS_SPI4_NSSP 41 +#define GPOEN_SYS_I2C5_CLK 42 +#define GPOEN_SYS_I2C5_DATA 43 +#define GPOEN_SYS_SPI5_NSSPCTL 44 +#define GPOEN_SYS_SPI5_NSSP 45 +#define GPOEN_SYS_I2C6_CLK 46 +#define GPOEN_SYS_I2C6_DATA 47 +#define GPOEN_SYS_SPI6_NSSPCTL 48 +#define GPOEN_SYS_SPI6_NSSP 49 + +/* aon_iomux doen */ +#define GPOEN_AON_PTC0_OE_N_4 2 +#define GPOEN_AON_PTC0_OE_N_5 3 +#define GPOEN_AON_PTC0_OE_N_6 4 +#define GPOEN_AON_PTC0_OE_N_7 5 + +/* sys_iomux gin */ +#define GPI_NONE 255 + +#define GPI_SYS_WAVE511_UART_RX 0 +#define GPI_SYS_CAN0_RXD 1 +#define GPI_SYS_USB_OVERCURRENT 2 +#define GPI_SYS_SPDIF 3 +#define GPI_SYS_JTAG_RST 4 +#define GPI_SYS_HDMI_CEC_SDA 5 +#define GPI_SYS_HDMI_DDC_SCL 6 +#define GPI_SYS_HDMI_DDC_SDA 7 +#define GPI_SYS_HDMI_HPD 8 +#define GPI_SYS_I2C0_CLK 9 +#define GPI_SYS_I2C0_DATA 10 +#define GPI_SYS_SDIO0_CD 11 +#define GPI_SYS_SDIO0_INT 12 +#define GPI_SYS_SDIO0_WP 13 +#define GPI_SYS_UART0_RX 14 +#define GPI_SYS_HIFI4_JTAG_TCK 15 +#define GPI_SYS_HIFI4_JTAG_TDI 16 +#define GPI_SYS_HIFI4_JTAG_TMS 17 +#define GPI_SYS_HIFI4_JTAG_RST 18 +#define GPI_SYS_JTAG_TDI 19 +#define GPI_SYS_JTAG_TMS 20 +#define GPI_SYS_PDM_DMIC0 21 +#define GPI_SYS_PDM_DMIC1 22 +#define GPI_SYS_I2SRX_SDIN0 23 +#define GPI_SYS_I2SRX_SDIN1 24 +#define GPI_SYS_I2SRX_SDIN2 25 +#define GPI_SYS_SPI0_CLK 26 +#define GPI_SYS_SPI0_FSS 27 +#define GPI_SYS_SPI0_RXD 28 +#define GPI_SYS_JTAG_TCK 29 +#define GPI_SYS_MCLK_EXT 30 +#define GPI_SYS_I2SRX_BCLK 31 +#define GPI_SYS_I2SRX_LRCK 32 +#define GPI_SYS_I2STX0_BCLK 33 +#define GPI_SYS_I2STX0_LRCK 34 +#define GPI_SYS_TDM_CLK 35 +#define GPI_SYS_TDM_RXD 36 +#define GPI_SYS_TDM_SYNC 37 +#define GPI_SYS_CAN1_RXD 38 +#define GPI_SYS_I2C1_CLK 39 +#define GPI_SYS_I2C1_DATA 40 +#define GPI_SYS_SDIO1_CD 41 +#define GPI_SYS_SDIO1_INT 42 +#define GPI_SYS_SDIO1_WP 43 +#define GPI_SYS_SDIO1_CMD 44 +#define GPI_SYS_SDIO1_DATA0 45 +#define GPI_SYS_SDIO1_DATA1 46 +#define GPI_SYS_SDIO1_DATA2 47 +#define GPI_SYS_SDIO1_DATA3 48 +#define GPI_SYS_SDIO1_DATA4 49 +#define GPI_SYS_SDIO1_DATA5 50 +#define GPI_SYS_SDIO1_DATA6 51 +#define GPI_SYS_SDIO1_DATA7 52 +#define GPI_SYS_SDIO1_STRB 53 +#define GPI_SYS_UART1_CTS 54 +#define GPI_SYS_UART1_RX 55 +#define GPI_SYS_SPI1_CLK 56 +#define GPI_SYS_SPI1_FSS 57 +#define GPI_SYS_SPI1_RXD 58 +#define GPI_SYS_I2C2_CLK 59 +#define GPI_SYS_I2C2_DATA 60 +#define GPI_SYS_UART2_CTS 61 +#define GPI_SYS_UART2_RX 62 +#define GPI_SYS_SPI2_CLK 63 +#define GPI_SYS_SPI2_FSS 64 +#define GPI_SYS_SPI2_RXD 65 +#define GPI_SYS_I2C3_CLK 66 +#define GPI_SYS_I2C3_DATA 67 +#define GPI_SYS_UART3_RX 68 +#define GPI_SYS_SPI3_CLK 69 +#define GPI_SYS_SPI3_FSS 70 +#define GPI_SYS_SPI3_RXD 71 +#define GPI_SYS_I2C4_CLK 72 +#define GPI_SYS_I2C4_DATA 73 +#define GPI_SYS_UART4_CTS 74 +#define GPI_SYS_UART4_RX 75 +#define GPI_SYS_SPI4_CLK 76 +#define GPI_SYS_SPI4_FSS 77 +#define GPI_SYS_SPI4_RXD 78 +#define GPI_SYS_I2C5_CLK 79 +#define GPI_SYS_I2C5_DATA 80 +#define GPI_SYS_UART5_CTS 81 +#define GPI_SYS_UART5_RX 82 +#define GPI_SYS_SPI5_CLK 83 +#define GPI_SYS_SPI5_FSS 84 +#define GPI_SYS_SPI5_RXD 85 +#define GPI_SYS_I2C6_CLK 86 +#define GPI_SYS_I2C6_DATA 87 +#define GPI_SYS_SPI6_CLK 88 +#define GPI_SYS_SPI6_FSS 89 +#define GPI_SYS_SPI6_RXD 90 + +/* aon_iomux gin */ +#define GPI_AON_PMU_GPIO_WAKEUP_0 0 +#define GPI_AON_PMU_GPIO_WAKEUP_1 1 +#define GPI_AON_PMU_GPIO_WAKEUP_2 2 +#define GPI_AON_PMU_GPIO_WAKEUP_3 3 + +#endif diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts new file mode 100644 index 0000000000000..205a13d8c8b14 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.2a.dts @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include "jh7110-starfive-visionfive-2.dtsi" + +/ { + model = "StarFive VisionFive 2 v1.2A"; + compatible = "starfive,visionfive-2-v1.2a", "starfive,jh7110"; +}; + +&gmac1 { + phy-mode = "rmii"; + assigned-clocks = <&syscrg JH7110_SYSCLK_GMAC1_TX>, + <&syscrg JH7110_SYSCLK_GMAC1_RX>; + assigned-clock-parents = <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>, + <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>; +}; + +&phy0 { + rx-internal-delay-ps = <1900>; + tx-internal-delay-ps = <1350>; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts new file mode 100644 index 0000000000000..32fae0de9a444 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2-v1.3b.dts @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include "jh7110-starfive-visionfive-2.dtsi" + +/ { + model = "StarFive VisionFive 2 v1.3B"; + compatible = "starfive,visionfive-2-v1.3b", "starfive,jh7110"; +}; + +&gmac0 { + starfive,tx-use-rgmii-clk; + assigned-clocks = <&aoncrg JH7110_AONCLK_GMAC0_TX>; + assigned-clock-parents = <&aoncrg JH7110_AONCLK_GMAC0_RMII_RTX>; +}; + +&gmac1 { + starfive,tx-use-rgmii-clk; + assigned-clocks = <&syscrg JH7110_SYSCLK_GMAC1_TX>; + assigned-clock-parents = <&syscrg JH7110_SYSCLK_GMAC1_RMII_RTX>; +}; + +&phy0 { + motorcomm,tx-clk-adj-enabled; + motorcomm,tx-clk-100-inverted; + motorcomm,tx-clk-1000-inverted; + rx-internal-delay-ps = <1900>; + tx-internal-delay-ps = <1500>; +}; + +&phy1 { + motorcomm,tx-clk-adj-enabled; + motorcomm,tx-clk-100-inverted; + rx-internal-delay-ps = <0>; + tx-internal-delay-ps = <0>; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi new file mode 100644 index 0000000000000..752bb0b6fd002 --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include "jh7110.dtsi" +#include "jh7110-pinfunc.h" +#include + +/ { + aliases { + ethernet0 = &gmac0; + ethernet1 = &gmac1; + i2c0 = &i2c0; + i2c2 = &i2c2; + i2c5 = &i2c5; + i2c6 = &i2c6; + serial0 = &uart0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + cpus { + timebase-frequency = <4000000>; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x0 0x40000000 0x1 0x0>; + }; + + thermal-zones { + cpu-thermal { + polling-delay-passive = <250>; + polling-delay = <15000>; + + thermal-sensors = <&sfctemp>; + + cooling-maps { + }; + + trips { + cpu_alert0: cpu_alert0 { + /* milliCelsius */ + temperature = <75000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu_crit: cpu_crit { + /* milliCelsius */ + temperature = <90000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; + }; + + gpio-restart { + compatible = "gpio-restart"; + gpios = <&sysgpio 35 GPIO_ACTIVE_HIGH>; + priority = <224>; + }; +}; + +&dvp_clk { + clock-frequency = <74250000>; +}; + +&gmac0_rgmii_rxin { + clock-frequency = <125000000>; +}; + +&gmac0_rmii_refin { + clock-frequency = <50000000>; +}; + +&gmac1_rgmii_rxin { + clock-frequency = <125000000>; +}; + +&gmac1_rmii_refin { + clock-frequency = <50000000>; +}; + +&hdmitx0_pixelclk { + clock-frequency = <297000000>; +}; + +&i2srx_bclk_ext { + clock-frequency = <12288000>; +}; + +&i2srx_lrck_ext { + clock-frequency = <192000>; +}; + +&i2stx_bclk_ext { + clock-frequency = <12288000>; +}; + +&i2stx_lrck_ext { + clock-frequency = <192000>; +}; + +&mclk_ext { + clock-frequency = <12288000>; +}; + +&osc { + clock-frequency = <24000000>; +}; + +&rtc_osc { + clock-frequency = <32768>; +}; + +&tdm_ext { + clock-frequency = <49152000>; +}; + +&gmac0 { + phy-handle = <&phy0>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy0: ethernet-phy@0 { + reg = <0>; + }; + }; +}; + +&gmac1 { + phy-handle = <&phy1>; + phy-mode = "rgmii-id"; + status = "okay"; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + compatible = "snps,dwmac-mdio"; + + phy1: ethernet-phy@1 { + reg = <0>; + }; + }; +}; + +&i2c0 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c0_pins>; + status = "okay"; +}; + +&i2c2 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c2_pins>; + status = "okay"; +}; + +&i2c5 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c5_pins>; + status = "okay"; +}; + +&i2c6 { + clock-frequency = <100000>; + i2c-sda-hold-time-ns = <300>; + i2c-sda-falling-time-ns = <510>; + i2c-scl-falling-time-ns = <510>; + pinctrl-names = "default"; + pinctrl-0 = <&i2c6_pins>; + status = "okay"; +}; + +&mmc0 { + max-frequency = <100000000>; + bus-width = <8>; + cap-mmc-highspeed; + mmc-ddr-1_8v; + mmc-hs200-1_8v; + non-removable; + cap-mmc-hw-reset; + post-power-on-delay-ms = <200>; + status = "okay"; +}; + +&mmc1 { + max-frequency = <100000000>; + bus-width = <4>; + no-sdio; + no-mmc; + broken-cd; + cap-sd-highspeed; + post-power-on-delay-ms = <200>; + status = "okay"; +}; + +&ptc { + pinctrl-names = "default"; + pinctrl-0 = <&pwm_pins>; + status = "okay"; +}; + +&sysgpio { + i2c0_pins: i2c0-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + i2c2_pins: i2c2-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + i2c5_pins: i2c5-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + i2c6_pins: i2c6-0 { + i2c-pins { + pinmux = , + ; + bias-disable; /* external pull-up */ + input-enable; + input-schmitt-enable; + }; + }; + + pwm_pins: pwm-0 { + pwm-pins { + pinmux = , + ; + bias-disable; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + uart0_pins: uart0-0 { + tx-pins { + pinmux = ; + bias-disable; + drive-strength = <12>; + input-disable; + input-schmitt-disable; + slew-rate = <0>; + }; + + rx-pins { + pinmux = ; + bias-disable; /* external pull-up */ + drive-strength = <2>; + input-enable; + input-schmitt-enable; + slew-rate = <0>; + }; + }; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pins>; + status = "okay"; +}; + +&usb0 { + status = "okay"; + usbdrd_cdns3: usb@0 { + dr_mode = "peripheral"; + }; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi new file mode 100644 index 0000000000000..95abd5f6ca23d --- /dev/null +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -0,0 +1,926 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Emil Renner Berthing + */ + +/dts-v1/; +#include +#include +#include + +/ { + compatible = "starfive,jh7110"; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + S7_0: cpu@0 { + compatible = "sifive,s7", "riscv"; + reg = <0>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <8192>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <16384>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imac_zba_zbb"; + tlb-split; + status = "disabled"; + + cpu0_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_1: cpu@1 { + compatible = "sifive,u74-mc", "riscv"; + reg = <1>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu1_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_2: cpu@2 { + compatible = "sifive,u74-mc", "riscv"; + reg = <2>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu2_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_3: cpu@3 { + compatible = "sifive,u74-mc", "riscv"; + reg = <3>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu3_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + U74_4: cpu@4 { + compatible = "sifive,u74-mc", "riscv"; + reg = <4>; + d-cache-block-size = <64>; + d-cache-sets = <64>; + d-cache-size = <32768>; + d-tlb-sets = <1>; + d-tlb-size = <40>; + device_type = "cpu"; + i-cache-block-size = <64>; + i-cache-sets = <64>; + i-cache-size = <32768>; + i-tlb-sets = <1>; + i-tlb-size = <40>; + mmu-type = "riscv,sv39"; + next-level-cache = <&ccache>; + riscv,isa = "rv64imafdc_zba_zbb"; + tlb-split; + + cpu4_intc: interrupt-controller { + compatible = "riscv,cpu-intc"; + interrupt-controller; + #interrupt-cells = <1>; + }; + }; + + cpu-map { + cluster0 { + core0 { + cpu = <&S7_0>; + }; + + core1 { + cpu = <&U74_1>; + }; + + core2 { + cpu = <&U74_2>; + }; + + core3 { + cpu = <&U74_3>; + }; + + core4 { + cpu = <&U74_4>; + }; + }; + }; + }; + + dvp_clk: dvp-clock { + compatible = "fixed-clock"; + clock-output-names = "dvp_clk"; + #clock-cells = <0>; + }; + + gmac0_rgmii_rxin: gmac0-rgmii-rxin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac0_rgmii_rxin"; + #clock-cells = <0>; + }; + + gmac0_rmii_refin: gmac0-rmii-refin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac0_rmii_refin"; + #clock-cells = <0>; + }; + + gmac1_rgmii_rxin: gmac1-rgmii-rxin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac1_rgmii_rxin"; + #clock-cells = <0>; + }; + + gmac1_rmii_refin: gmac1-rmii-refin-clock { + compatible = "fixed-clock"; + clock-output-names = "gmac1_rmii_refin"; + #clock-cells = <0>; + }; + + hdmitx0_pixelclk: hdmitx0-pixel-clock { + compatible = "fixed-clock"; + clock-output-names = "hdmitx0_pixelclk"; + #clock-cells = <0>; + }; + + i2srx_bclk_ext: i2srx-bclk-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2srx_bclk_ext"; + #clock-cells = <0>; + }; + + i2srx_lrck_ext: i2srx-lrck-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2srx_lrck_ext"; + #clock-cells = <0>; + }; + + i2stx_bclk_ext: i2stx-bclk-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2stx_bclk_ext"; + #clock-cells = <0>; + }; + + i2stx_lrck_ext: i2stx-lrck-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "i2stx_lrck_ext"; + #clock-cells = <0>; + }; + + mclk_ext: mclk-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "mclk_ext"; + #clock-cells = <0>; + }; + + osc: oscillator { + compatible = "fixed-clock"; + clock-output-names = "osc"; + #clock-cells = <0>; + }; + + rtc_osc: rtc-oscillator { + compatible = "fixed-clock"; + clock-output-names = "rtc_osc"; + #clock-cells = <0>; + }; + + stmmac_axi_setup: stmmac-axi-config { + snps,lpi_en; + snps,wr_osr_lmt = <4>; + snps,rd_osr_lmt = <4>; + snps,blen = <256 128 64 32 0 0 0>; + }; + + tdm_ext: tdm-ext-clock { + compatible = "fixed-clock"; + clock-output-names = "tdm_ext"; + #clock-cells = <0>; + }; + + soc { + compatible = "simple-bus"; + interrupt-parent = <&plic>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + clint: timer@2000000 { + compatible = "starfive,jh7110-clint", "sifive,clint0"; + reg = <0x0 0x2000000 0x0 0x10000>; + interrupts-extended = <&cpu0_intc 3>, <&cpu0_intc 7>, + <&cpu1_intc 3>, <&cpu1_intc 7>, + <&cpu2_intc 3>, <&cpu2_intc 7>, + <&cpu3_intc 3>, <&cpu3_intc 7>, + <&cpu4_intc 3>, <&cpu4_intc 7>; + }; + + ccache: cache-controller@2010000 { + compatible = "starfive,jh7110-ccache", "sifive,ccache0", "cache"; + reg = <0x0 0x2010000 0x0 0x4000>; + interrupts = <1>, <3>, <4>, <2>; + cache-block-size = <64>; + cache-level = <2>; + cache-sets = <2048>; + cache-size = <2097152>; + cache-unified; + }; + + plic: interrupt-controller@c000000 { + compatible = "starfive,jh7110-plic", "sifive,plic-1.0.0"; + reg = <0x0 0xc000000 0x0 0x4000000>; + interrupts-extended = <&cpu0_intc 11>, + <&cpu1_intc 11>, <&cpu1_intc 9>, + <&cpu2_intc 11>, <&cpu2_intc 9>, + <&cpu3_intc 11>, <&cpu3_intc 9>, + <&cpu4_intc 11>, <&cpu4_intc 9>; + interrupt-controller; + #interrupt-cells = <1>; + #address-cells = <0>; + riscv,ndev = <136>; + }; + + uart0: serial@10000000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x10000000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART0_CORE>, + <&syscrg JH7110_SYSCLK_UART0_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART0_APB>; + interrupts = <32>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart1: serial@10010000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x10010000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART1_CORE>, + <&syscrg JH7110_SYSCLK_UART1_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART1_APB>; + interrupts = <33>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart2: serial@10020000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x10020000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART2_CORE>, + <&syscrg JH7110_SYSCLK_UART2_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART2_APB>; + interrupts = <34>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + i2c0: i2c@10030000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x10030000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C0_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C0_APB>; + interrupts = <35>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c1: i2c@10040000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x10040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C1_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C1_APB>; + interrupts = <36>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c2: i2c@10050000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x10050000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C2_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C2_APB>; + interrupts = <37>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + usb0: usb@10100000 { + compatible = "starfive,jh7110-usb"; + clocks = <&stgcrg JH7110_STGCLK_USB0_LPM>, + <&stgcrg JH7110_STGCLK_USB0_STB>, + <&stgcrg JH7110_STGCLK_USB0_APB>, + <&stgcrg JH7110_STGCLK_USB0_AXI>, + <&stgcrg JH7110_STGCLK_USB0_UTMI_APB>; + clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; + resets = <&stgcrg JH7110_STGRST_USB0_PWRUP>, + <&stgcrg JH7110_STGRST_USB0_APB>, + <&stgcrg JH7110_STGRST_USB0_AXI>, + <&stgcrg JH7110_STGRST_USB0_UTMI_APB>; + starfive,stg-syscon = <&stg_syscon 0x4 0xc4 0x148 0x1f4>; + starfive,sys-syscon = <&sys_syscon 0x18>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x10100000 0x100000>; + + usbdrd_cdns3: usb@0 { + compatible = "cdns,usb3"; + reg = <0x0 0x10000>, + <0x10000 0x10000>, + <0x20000 0x10000>; + reg-names = "otg", "xhci", "dev"; + interrupts = <100>, <108>, <110>; + interrupt-names = "host", "peripheral", "otg"; + phys = <&usbphy0>; + phy-names = "cdns3,usb2-phy"; + maximum-speed = "super-speed"; + }; + }; + + usbphy0: phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x0 0x10200000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_USB_125M>, + <&stgcrg JH7110_STGCLK_USB0_APP_125>; + clock-names = "125m", "app_125"; + #phy-cells = <0>; + }; + + pciephy0: phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x0 0x10210000 0x0 0x10000>; + #phy-cells = <0>; + }; + + pciephy1: phy@10220000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x0 0x10220000 0x0 0x10000>; + #phy-cells = <0>; + }; + + stgcrg: clock-controller@10230000 { + compatible = "starfive,jh7110-stgcrg"; + reg = <0x0 0x10230000 0x0 0x10000>; + clocks = <&osc>, + <&syscrg JH7110_SYSCLK_HIFI4_CORE>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_USB_125M>, + <&syscrg JH7110_SYSCLK_CPU_BUS>, + <&syscrg JH7110_SYSCLK_HIFI4_AXI>, + <&syscrg JH7110_SYSCLK_NOCSTG_BUS>, + <&syscrg JH7110_SYSCLK_APB_BUS>; + clock-names = "osc", "hifi4_core", + "stg_axiahb", "usb_125m", + "cpu_bus", "hifi4_axi", + "nocstg_bus", "apb_bus"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + stg_syscon: syscon@10240000 { + compatible = "starfive,jh7110-stg-syscon", "syscon"; + reg = <0x0 0x10240000 0x0 0x1000>; + }; + + uart3: serial@12000000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x12000000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART3_CORE>, + <&syscrg JH7110_SYSCLK_UART3_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART3_APB>; + interrupts = <45>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart4: serial@12010000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x12010000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART4_CORE>, + <&syscrg JH7110_SYSCLK_UART4_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART4_APB>; + interrupts = <46>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + uart5: serial@12020000 { + compatible = "snps,dw-apb-uart"; + reg = <0x0 0x12020000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_UART5_CORE>, + <&syscrg JH7110_SYSCLK_UART5_APB>; + clock-names = "baudclk", "apb_pclk"; + resets = <&syscrg JH7110_SYSRST_UART5_APB>; + interrupts = <47>; + reg-io-width = <4>; + reg-shift = <2>; + status = "disabled"; + }; + + i2c3: i2c@12030000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12030000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C3_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C3_APB>; + interrupts = <48>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c4: i2c@12040000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C4_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C4_APB>; + interrupts = <49>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c5: i2c@12050000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12050000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C5_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C5_APB>; + interrupts = <50>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + i2c6: i2c@12060000 { + compatible = "snps,designware-i2c"; + reg = <0x0 0x12060000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_I2C6_APB>; + clock-names = "ref"; + resets = <&syscrg JH7110_SYSRST_I2C6_APB>; + interrupts = <51>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + ptc: pwm@120d0000 { + compatible = "starfive,jh7110-pwm"; + reg = <0x0 0x120d0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_PWM_APB>; + resets = <&syscrg JH7110_SYSRST_PWM_APB>; + #pwm-cells=<3>; + status = "disabled"; + }; + + sfctemp: temperature-sensor@120e0000 { + compatible = "starfive,jh7110-temp"; + reg = <0x0 0x120e0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_TEMP_CORE>, + <&syscrg JH7110_SYSCLK_TEMP_APB>; + clock-names = "sense", "bus"; + resets = <&syscrg JH7110_SYSRST_TEMP_CORE>, + <&syscrg JH7110_SYSRST_TEMP_APB>; + reset-names = "sense", "bus"; + #thermal-sensor-cells = <0>; + }; + + qspi: spi@13010000 { + compatible = "starfive,jh7110-qspi", "cdns,qspi-nor"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x0 0x13010000 0x0 0x10000 + 0x0 0x21000000 0x0 0x400000>; + interrupts = <25>; + clocks = <&syscrg JH7110_SYSCLK_QSPI_REF>; + resets = <&syscrg JH7110_SYSRST_QSPI_APB>, + <&syscrg JH7110_SYSRST_QSPI_AHB>, + <&syscrg JH7110_SYSRST_QSPI_REF>; + reset-names = "qspi", "qspi-ocp", "rstc_ref"; + cdns,fifo-depth = <256>; + cdns,fifo-width = <4>; + cdns,trigger-address = <0x0>; + + nor_flash: nor-flash@0 { + compatible = "jedec,spi-nor"; + reg=<0>; + cdns,read-delay = <5>; + spi-max-frequency = <12000000>; + cdns,tshsl-ns = <1>; + cdns,tsd2d-ns = <1>; + cdns,tchsh-ns = <1>; + cdns,tslch-ns = <1>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + spl@0 { + reg = <0x0 0x20000>; + }; + uboot@100000 { + reg = <0x100000 0x300000>; + }; + data@f00000 { + reg = <0xf00000 0x100000>; + }; + }; + }; + }; + + syscrg: clock-controller@13020000 { + compatible = "starfive,jh7110-syscrg"; + reg = <0x0 0x13020000 0x0 0x10000>; + clocks = <&osc>, <&gmac1_rmii_refin>, + <&gmac1_rgmii_rxin>, + <&i2stx_bclk_ext>, <&i2stx_lrck_ext>, + <&i2srx_bclk_ext>, <&i2srx_lrck_ext>, + <&tdm_ext>, <&mclk_ext>, + <&pllclk JH7110_CLK_PLL0_OUT>, + <&pllclk JH7110_CLK_PLL1_OUT>, + <&pllclk JH7110_CLK_PLL2_OUT>; + clock-names = "osc", "gmac1_rmii_refin", + "gmac1_rgmii_rxin", + "i2stx_bclk_ext", "i2stx_lrck_ext", + "i2srx_bclk_ext", "i2srx_lrck_ext", + "tdm_ext", "mclk_ext", + "pll0_out", "pll1_out", "pll2_out"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + sys_syscon: syscon@13030000 { + compatible = "starfive,jh7110-sys-syscon", "syscon", "simple-mfd"; + reg = <0x0 0x13030000 0x0 0x1000>; + + pllclk: pll-clock-controller { + compatible = "starfive,jh7110-pll"; + clocks = <&osc>; + #clock-cells = <1>; + }; + }; + + sysgpio: pinctrl@13040000 { + compatible = "starfive,jh7110-sys-pinctrl"; + reg = <0x0 0x13040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_IOMUX_APB>; + resets = <&syscrg JH7110_SYSRST_IOMUX_APB>; + interrupts = <86>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + }; + + timer@13050000 { + compatible = "starfive,jh7110-timer"; + reg = <0x0 0x13050000 0x0 0x10000>; + interrupts = <69>, <70>, <71> ,<72>; + clocks = <&syscrg JH7110_SYSCLK_TIMER_APB>, + <&syscrg JH7110_SYSCLK_TIMER0>, + <&syscrg JH7110_SYSCLK_TIMER1>, + <&syscrg JH7110_SYSCLK_TIMER2>, + <&syscrg JH7110_SYSCLK_TIMER3>; + clock-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + resets = <&syscrg JH7110_SYSRST_TIMER_APB>, + <&syscrg JH7110_SYSRST_TIMER0>, + <&syscrg JH7110_SYSRST_TIMER1>, + <&syscrg JH7110_SYSRST_TIMER2>, + <&syscrg JH7110_SYSRST_TIMER3>; + reset-names = "apb", "ch0", "ch1", + "ch2", "ch3"; + }; + + wdog: watchdog@13070000 { + compatible = "starfive,jh7110-wdt"; + reg = <0x0 0x13070000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_WDT_APB>, + <&syscrg JH7110_SYSCLK_WDT_CORE>; + clock-names = "apb", "core"; + resets = <&syscrg JH7110_SYSRST_WDT_APB>, + <&syscrg JH7110_SYSRST_WDT_CORE>; + }; + + crypto: crypto@16000000 { + compatible = "starfive,jh7110-crypto"; + reg = <0x0 0x16000000 0x0 0x4000>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "ahb"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + dmas = <&sdma 1 2>, <&sdma 0 2>; + dma-names = "tx", "rx"; + }; + + sdma: dma@16008000 { + compatible = "arm,pl080", "arm,primecell"; + arm,primecell-periphid = <0x00041080>; + reg = <0x0 0x16008000 0x0 0x4000>; + interrupts = <29>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "apb_pclk"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + lli-bus-interface-ahb1; + mem-bus-interface-ahb1; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; + #dma-cells = <2>; + }; + + rng: rng@1600c000 { + compatible = "starfive,jh7110-trng"; + reg = <0x0 0x1600C000 0x0 0x4000>; + clocks = <&stgcrg JH7110_STGCLK_SEC_AHB>, + <&stgcrg JH7110_STGCLK_SEC_MISC_AHB>; + clock-names = "hclk", "ahb"; + resets = <&stgcrg JH7110_STGRST_SEC_AHB>; + interrupts = <30>; + }; + + mmc0: mmc@16010000 { + compatible = "starfive,jh7110-mmc"; + reg = <0x0 0x16010000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SDIO0_AHB>, + <&syscrg JH7110_SYSCLK_SDIO0_SDCARD>; + clock-names = "biu","ciu"; + resets = <&syscrg JH7110_SYSRST_SDIO0_AHB>; + reset-names = "reset"; + interrupts = <74>; + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; + starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>; + status = "disabled"; + }; + + mmc1: mmc@16020000 { + compatible = "starfive,jh7110-mmc"; + reg = <0x0 0x16020000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_SDIO1_AHB>, + <&syscrg JH7110_SYSCLK_SDIO1_SDCARD>; + clock-names = "biu","ciu"; + resets = <&syscrg JH7110_SYSRST_SDIO1_AHB>; + reset-names = "reset"; + interrupts = <75>; + fifo-depth = <32>; + fifo-watermark-aligned; + data-addr = <0>; + starfive,sysreg = <&sys_syscon 0x9c 0x1 0x3e>; + status = "disabled"; + }; + + gmac0: ethernet@16030000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x0 0x16030000 0x0 0x10000>; + clocks = <&aoncrg JH7110_AONCLK_GMAC0_AXI>, + <&aoncrg JH7110_AONCLK_GMAC0_AHB>, + <&syscrg JH7110_SYSCLK_GMAC0_PTP>, + <&aoncrg JH7110_AONCLK_GMAC0_TX_INV>, + <&syscrg JH7110_SYSCLK_GMAC0_GTXC>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&aoncrg JH7110_AONRST_GMAC0_AXI>, + <&aoncrg JH7110_AONRST_GMAC0_AHB>; + reset-names = "stmmaceth", "ahb"; + interrupts = <7>, <6>, <5>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <8>; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,tso; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&aon_syscon 0xc 0x12>; + status = "disabled"; + }; + + gmac1: ethernet@16040000 { + compatible = "starfive,jh7110-dwmac", "snps,dwmac-5.20"; + reg = <0x0 0x16040000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_GMAC1_AXI>, + <&syscrg JH7110_SYSCLK_GMAC1_AHB>, + <&syscrg JH7110_SYSCLK_GMAC1_PTP>, + <&syscrg JH7110_SYSCLK_GMAC1_TX_INV>, + <&syscrg JH7110_SYSCLK_GMAC1_GTXC>; + clock-names = "stmmaceth", "pclk", "ptp_ref", + "tx", "gtx"; + resets = <&syscrg JH7110_SYSRST_GMAC1_AXI>, + <&syscrg JH7110_SYSRST_GMAC1_AHB>; + reset-names = "stmmaceth", "ahb"; + interrupts = <78>, <77>, <76>; + interrupt-names = "macirq", "eth_wake_irq", "eth_lpi"; + snps,multicast-filter-bins = <64>; + snps,perfect-filter-entries = <8>; + rx-fifo-depth = <2048>; + tx-fifo-depth = <2048>; + snps,fixed-burst; + snps,no-pbl-x8; + snps,force_thresh_dma_mode; + snps,axi-config = <&stmmac_axi_setup>; + snps,tso; + snps,en-tx-lpi-clockgating; + snps,txpbl = <16>; + snps,rxpbl = <16>; + starfive,syscon = <&sys_syscon 0x90 0x2>; + status = "disabled"; + }; + + dma: dma-controller@16050000 { + compatible = "starfive,jh7110-axi-dma"; + reg = <0x0 0x16050000 0x0 0x10000>; + clocks = <&stgcrg JH7110_STGCLK_DMA1P_AXI>, + <&stgcrg JH7110_STGCLK_DMA1P_AHB>; + clock-names = "core-clk", "cfgr-clk"; + resets = <&stgcrg JH7110_STGRST_DMA1P_AXI>, + <&stgcrg JH7110_STGRST_DMA1P_AHB>; + interrupts = <73>; + #dma-cells = <1>; + dma-channels = <4>; + snps,dma-masters = <1>; + snps,data-width = <3>; + snps,block-size = <65536 65536 65536 65536>; + snps,priority = <0 1 2 3>; + snps,axi-max-burst-len = <16>; + }; + + aoncrg: clock-controller@17000000 { + compatible = "starfive,jh7110-aoncrg"; + reg = <0x0 0x17000000 0x0 0x10000>; + clocks = <&osc>, <&gmac0_rmii_refin>, + <&gmac0_rgmii_rxin>, + <&syscrg JH7110_SYSCLK_STG_AXIAHB>, + <&syscrg JH7110_SYSCLK_APB_BUS>, + <&syscrg JH7110_SYSCLK_GMAC0_GTXCLK>, + <&rtc_osc>; + clock-names = "osc", "gmac0_rmii_refin", + "gmac0_rgmii_rxin", "stg_axiahb", + "apb_bus", "gmac0_gtxclk", + "rtc_osc"; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + aon_syscon: syscon@17010000 { + compatible = "starfive,jh7110-aon-syscon", "syscon"; + reg = <0x0 0x17010000 0x0 0x1000>; + }; + + aongpio: pinctrl@17020000 { + compatible = "starfive,jh7110-aon-pinctrl"; + reg = <0x0 0x17020000 0x0 0x10000>; + resets = <&aoncrg JH7110_AONRST_IOMUX>; + interrupts = <85>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + }; + + pwrc: power-controller@17030000 { + compatible = "starfive,jh7110-pmu"; + reg = <0x0 0x17030000 0x0 0x10000>; + interrupts = <111>; + #power-domain-cells = <1>; + }; + + ispcrg: clock-controller@19810000 { + compatible = "starfive,jh7110-ispcrg"; + reg = <0x0 0x19810000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_ISP_TOP_CORE>, + <&syscrg JH7110_SYSCLK_ISP_TOP_AXI>, + <&syscrg JH7110_SYSCLK_NOC_BUS_ISP_AXI>, + <&dvp_clk>; + clock-names = "isp_top_core", "isp_top_axi", + "noc_bus_isp_axi", "dvp_clk"; + resets = <&syscrg JH7110_SYSRST_ISP_TOP>, + <&syscrg JH7110_SYSRST_ISP_TOP_AXI>, + <&syscrg JH7110_SYSRST_NOC_BUS_ISP_AXI>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_ISP>; + }; + + csi_phy: phy@19820000 { + compatible = "starfive,jh7110-dphy-rx"; + reg = <0x0 0x19820000 0x0 0x10000>; + clocks = <&ispcrg JH7110_ISPCLK_M31DPHY_CFG_IN>, + <&ispcrg JH7110_ISPCLK_M31DPHY_REF_IN>, + <&ispcrg JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0>; + clock-names = "cfg", "ref", "tx"; + resets = <&ispcrg JH7110_ISPRST_M31DPHY_HW>, + <&ispcrg JH7110_ISPRST_M31DPHY_B09_AON>; + starfive,aon-syscon = <&aon_syscon 0x00>; + #phy-cells = <0>; + }; + + voutcrg: clock-controller@295c0000 { + compatible = "starfive,jh7110-voutcrg"; + reg = <0x0 0x295c0000 0x0 0x10000>; + clocks = <&syscrg JH7110_SYSCLK_VOUT_SRC>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AHB>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_AXI>, + <&syscrg JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK>, + <&syscrg JH7110_SYSCLK_I2STX0_BCLK>, + <&hdmitx0_pixelclk>; + clock-names = "vout_src", "vout_top_ahb", + "vout_top_axi", "vout_top_hdmitx0_mclk", + "i2stx0_bclk", "hdmitx0_pixelclk"; + resets = <&syscrg JH7110_SYSRST_VOUT_TOP_SRC>; + #clock-cells = <1>; + #reset-cells = <1>; + power-domains = <&pwrc JH7110_PD_VOUT>; + }; + }; +}; diff --git a/arch/riscv/include/asm/assembler.h b/arch/riscv/include/asm/assembler.h new file mode 100644 index 0000000000000..44b1457d3e956 --- /dev/null +++ b/arch/riscv/include/asm/assembler.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Jee Heng Sia + */ + +#ifndef __ASSEMBLY__ +#error "Only include this from assembly code" +#endif + +#ifndef __ASM_ASSEMBLER_H +#define __ASM_ASSEMBLER_H + +#include +#include +#include + +/* + * suspend_restore_csrs - restore CSRs + */ + .macro suspend_restore_csrs + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0) + csrw CSR_EPC, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0) + csrw CSR_STATUS, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0) + csrw CSR_TVAL, t0 + REG_L t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0) + csrw CSR_CAUSE, t0 + .endm + +/* + * suspend_restore_regs - Restore registers (except A0 and T0-T6) + */ + .macro suspend_restore_regs + REG_L ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0) + REG_L sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0) + REG_L gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0) + REG_L tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0) + REG_L s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0) + REG_L s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0) + REG_L a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0) + REG_L a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0) + REG_L a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0) + REG_L a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0) + REG_L a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0) + REG_L a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0) + REG_L a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0) + REG_L s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0) + REG_L s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0) + REG_L s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0) + REG_L s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0) + REG_L s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0) + REG_L s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0) + REG_L s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0) + REG_L s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0) + REG_L s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0) + REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0) + .endm + +/* + * copy_page - copy 1 page (4KB) of data from source to destination + * @a0 - destination + * @a1 - source + */ + .macro copy_page a0, a1 + lui a2, 0x1 + add a2, a2, a0 +1 : + REG_L t0, 0(a1) + REG_L t1, SZREG(a1) + + REG_S t0, 0(a0) + REG_S t1, SZREG(a0) + + addi a0, a0, 2 * SZREG + addi a1, a1, 2 * SZREG + bne a2, a0, 1b + .endm + +#endif /* __ASM_ASSEMBLER_H */ diff --git a/arch/riscv/include/asm/suspend.h b/arch/riscv/include/asm/suspend.h index 8be391c2aecb8..02f87867389a9 100644 --- a/arch/riscv/include/asm/suspend.h +++ b/arch/riscv/include/asm/suspend.h @@ -21,6 +21,11 @@ struct suspend_context { #endif }; +/* + * Used by hibernation core and cleared during resume sequence + */ +extern int in_suspend; + /* Low-level CPU suspend entry function */ int __cpu_suspend_enter(struct suspend_context *context); @@ -33,4 +38,21 @@ int cpu_suspend(unsigned long arg, /* Low-level CPU resume entry function */ int __cpu_resume_enter(unsigned long hartid, unsigned long context); +/* Used to save and restore the CSRs */ +void suspend_save_csrs(struct suspend_context *context); +void suspend_restore_csrs(struct suspend_context *context); + +/* Low-level API to support hibernation */ +int swsusp_arch_suspend(void); +int swsusp_arch_resume(void); +int arch_hibernation_header_save(void *addr, unsigned int max_size); +int arch_hibernation_header_restore(void *addr); +int __hibernate_cpu_resume(void); + +/* Used to resume on the CPU we hibernated on */ +int hibernate_resume_nonboot_cpu_disable(void); + +asmlinkage void hibernate_restore_image(unsigned long resume_satp, unsigned long satp_temp, + unsigned long cpu_resume); +asmlinkage int hibernate_core_restore_code(void); #endif diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index 4cf303a779ab9..daab341d55e43 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o obj-$(CONFIG_CPU_PM) += suspend_entry.o suspend.o +obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index df9444397908d..d6a75aac1d27a 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,10 @@ void asm_offsets(void) OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs); + OFFSET(HIBERN_PBE_ADDR, pbe, address); + OFFSET(HIBERN_PBE_ORIG, pbe, orig_address); + OFFSET(HIBERN_PBE_NEXT, pbe, next); + OFFSET(KVM_ARCH_GUEST_ZERO, kvm_vcpu_arch, guest_context.zero); OFFSET(KVM_ARCH_GUEST_RA, kvm_vcpu_arch, guest_context.ra); OFFSET(KVM_ARCH_GUEST_SP, kvm_vcpu_arch, guest_context.sp); diff --git a/arch/riscv/kernel/hibernate-asm.S b/arch/riscv/kernel/hibernate-asm.S new file mode 100644 index 0000000000000..effaf5ca5da0e --- /dev/null +++ b/arch/riscv/kernel/hibernate-asm.S @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Hibernation low level support for RISCV. + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Jee Heng Sia + */ + +#include +#include +#include +#include + +#include + +/* + * int __hibernate_cpu_resume(void) + * Switch back to the hibernated image's page table prior to restoring the CPU + * context. + * + * Always returns 0 + */ +ENTRY(__hibernate_cpu_resume) + /* switch to hibernated image's page table. */ + csrw CSR_SATP, s0 + sfence.vma + + REG_L a0, hibernate_cpu_context + + suspend_restore_csrs + suspend_restore_regs + + /* Return zero value. */ + mv a0, zero + + ret +END(__hibernate_cpu_resume) + +/* + * Prepare to restore the image. + * a0: satp of saved page tables. + * a1: satp of temporary page tables. + * a2: cpu_resume. + */ +ENTRY(hibernate_restore_image) + mv s0, a0 + mv s1, a1 + mv s2, a2 + REG_L s4, restore_pblist + REG_L a1, relocated_restore_code + + jalr a1 +END(hibernate_restore_image) + +/* + * The below code will be executed from a 'safe' page. + * It first switches to the temporary page table, then starts to copy the pages + * back to the original memory location. Finally, it jumps to __hibernate_cpu_resume() + * to restore the CPU context. + */ +ENTRY(hibernate_core_restore_code) + /* switch to temp page table. */ + csrw satp, s1 + sfence.vma +.Lcopy: + /* The below code will restore the hibernated image. */ + REG_L a1, HIBERN_PBE_ADDR(s4) + REG_L a0, HIBERN_PBE_ORIG(s4) + + copy_page a0, a1 + + REG_L s4, HIBERN_PBE_NEXT(s4) + bnez s4, .Lcopy + + jalr s2 +END(hibernate_core_restore_code) diff --git a/arch/riscv/kernel/hibernate.c b/arch/riscv/kernel/hibernate.c new file mode 100644 index 0000000000000..f11be60b06686 --- /dev/null +++ b/arch/riscv/kernel/hibernate.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Hibernation support for RISCV + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Jee Heng Sia + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* The logical cpu number we should resume on, initialised to a non-cpu number. */ +static int sleep_cpu = -EINVAL; + +/* Pointer to the temporary resume page table. */ +static pgd_t *resume_pg_dir; + +/* CPU context to be saved. */ +struct suspend_context *hibernate_cpu_context; +EXPORT_SYMBOL_GPL(hibernate_cpu_context); + +unsigned long relocated_restore_code; +EXPORT_SYMBOL_GPL(relocated_restore_code); + +/** + * struct arch_hibernate_hdr_invariants - container to store kernel build version. + * @uts_version: to save the build number and date so that the we do not resume with + * a different kernel. + */ +struct arch_hibernate_hdr_invariants { + char uts_version[__NEW_UTS_LEN + 1]; +}; + +/** + * struct arch_hibernate_hdr - helper parameters that help us to restore the image. + * @invariants: container to store kernel build version. + * @hartid: to make sure same boot_cpu executes the hibernate/restore code. + * @saved_satp: original page table used by the hibernated image. + * @restore_cpu_addr: the kernel's image address to restore the CPU context. + */ +static struct arch_hibernate_hdr { + struct arch_hibernate_hdr_invariants invariants; + unsigned long hartid; + unsigned long saved_satp; + unsigned long restore_cpu_addr; +} resume_hdr; + +static void arch_hdr_invariants(struct arch_hibernate_hdr_invariants *i) +{ + memset(i, 0, sizeof(*i)); + memcpy(i->uts_version, init_utsname()->version, sizeof(i->uts_version)); +} + +/* + * Check if the given pfn is in the 'nosave' section. + */ +int pfn_is_nosave(unsigned long pfn) +{ + unsigned long nosave_begin_pfn = sym_to_pfn(&__nosave_begin); + unsigned long nosave_end_pfn = sym_to_pfn(&__nosave_end - 1); + + return ((pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn)); +} + +void notrace save_processor_state(void) +{ + WARN_ON(num_online_cpus() != 1); +} + +void notrace restore_processor_state(void) +{ +} + +/* + * Helper parameters need to be saved to the hibernation image header. + */ +int arch_hibernation_header_save(void *addr, unsigned int max_size) +{ + struct arch_hibernate_hdr *hdr = addr; + + if (max_size < sizeof(*hdr)) + return -EOVERFLOW; + + arch_hdr_invariants(&hdr->invariants); + + hdr->hartid = cpuid_to_hartid_map(sleep_cpu); + hdr->saved_satp = csr_read(CSR_SATP); + hdr->restore_cpu_addr = (unsigned long)__hibernate_cpu_resume; + + return 0; +} +EXPORT_SYMBOL_GPL(arch_hibernation_header_save); + +/* + * Retrieve the helper parameters from the hibernation image header. + */ +int arch_hibernation_header_restore(void *addr) +{ + struct arch_hibernate_hdr_invariants invariants; + struct arch_hibernate_hdr *hdr = addr; + int ret = 0; + + arch_hdr_invariants(&invariants); + + if (memcmp(&hdr->invariants, &invariants, sizeof(invariants))) { + pr_crit("Hibernate image not generated by this kernel!\n"); + return -EINVAL; + } + + sleep_cpu = riscv_hartid_to_cpuid(hdr->hartid); + if (sleep_cpu < 0) { + pr_crit("Hibernated on a CPU not known to this kernel!\n"); + sleep_cpu = -EINVAL; + return -EINVAL; + } + +#ifdef CONFIG_SMP + ret = bringup_hibernate_cpu(sleep_cpu); + if (ret) { + sleep_cpu = -EINVAL; + return ret; + } +#endif + resume_hdr = *hdr; + + return ret; +} +EXPORT_SYMBOL_GPL(arch_hibernation_header_restore); + +int swsusp_arch_suspend(void) +{ + int ret = 0; + + if (__cpu_suspend_enter(hibernate_cpu_context)) { + sleep_cpu = smp_processor_id(); + suspend_save_csrs(hibernate_cpu_context); + ret = swsusp_save(); + } else { + suspend_restore_csrs(hibernate_cpu_context); + flush_tlb_all(); + flush_icache_all(); + + /* + * Tell the hibernation core that we've just restored the memory. + */ + in_suspend = 0; + sleep_cpu = -EINVAL; + } + + return ret; +} + +static int temp_pgtable_map_pte(pmd_t *dst_pmdp, pmd_t *src_pmdp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + pte_t *src_ptep; + pte_t *dst_ptep; + + if (pmd_none(READ_ONCE(*dst_pmdp))) { + dst_ptep = (pte_t *)get_safe_page(GFP_ATOMIC); + if (!dst_ptep) + return -ENOMEM; + + pmd_populate_kernel(NULL, dst_pmdp, dst_ptep); + } + + dst_ptep = pte_offset_kernel(dst_pmdp, start); + src_ptep = pte_offset_kernel(src_pmdp, start); + + do { + pte_t pte = READ_ONCE(*src_ptep); + + if (pte_present(pte)) + set_pte(dst_ptep, __pte(pte_val(pte) | pgprot_val(prot))); + } while (dst_ptep++, src_ptep++, start += PAGE_SIZE, start < end); + + return 0; +} + +static int temp_pgtable_map_pmd(pud_t *dst_pudp, pud_t *src_pudp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + unsigned long next; + unsigned long ret; + pmd_t *src_pmdp; + pmd_t *dst_pmdp; + + if (pud_none(READ_ONCE(*dst_pudp))) { + dst_pmdp = (pmd_t *)get_safe_page(GFP_ATOMIC); + if (!dst_pmdp) + return -ENOMEM; + + pud_populate(NULL, dst_pudp, dst_pmdp); + } + + dst_pmdp = pmd_offset(dst_pudp, start); + src_pmdp = pmd_offset(src_pudp, start); + + do { + pmd_t pmd = READ_ONCE(*src_pmdp); + + next = pmd_addr_end(start, end); + + if (pmd_none(pmd)) + continue; + + if (pmd_leaf(pmd)) { + set_pmd(dst_pmdp, __pmd(pmd_val(pmd) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_pte(dst_pmdp, src_pmdp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_pmdp++, src_pmdp++, start = next, start != end); + + return 0; +} + +static int temp_pgtable_map_pud(p4d_t *dst_p4dp, p4d_t *src_p4dp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + unsigned long next; + unsigned long ret; + pud_t *dst_pudp; + pud_t *src_pudp; + + if (p4d_none(READ_ONCE(*dst_p4dp))) { + dst_pudp = (pud_t *)get_safe_page(GFP_ATOMIC); + if (!dst_pudp) + return -ENOMEM; + + p4d_populate(NULL, dst_p4dp, dst_pudp); + } + + dst_pudp = pud_offset(dst_p4dp, start); + src_pudp = pud_offset(src_p4dp, start); + + do { + pud_t pud = READ_ONCE(*src_pudp); + + next = pud_addr_end(start, end); + + if (pud_none(pud)) + continue; + + if (pud_leaf(pud)) { + set_pud(dst_pudp, __pud(pud_val(pud) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_pmd(dst_pudp, src_pudp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_pudp++, src_pudp++, start = next, start != end); + + return 0; +} + +static int temp_pgtable_map_p4d(pgd_t *dst_pgdp, pgd_t *src_pgdp, unsigned long start, + unsigned long end, pgprot_t prot) +{ + unsigned long next; + unsigned long ret; + p4d_t *dst_p4dp; + p4d_t *src_p4dp; + + if (pgd_none(READ_ONCE(*dst_pgdp))) { + dst_p4dp = (p4d_t *)get_safe_page(GFP_ATOMIC); + if (!dst_p4dp) + return -ENOMEM; + + pgd_populate(NULL, dst_pgdp, dst_p4dp); + } + + dst_p4dp = p4d_offset(dst_pgdp, start); + src_p4dp = p4d_offset(src_pgdp, start); + + do { + p4d_t p4d = READ_ONCE(*src_p4dp); + + next = p4d_addr_end(start, end); + + if (p4d_none(p4d)) + continue; + + if (p4d_leaf(p4d)) { + set_p4d(dst_p4dp, __p4d(p4d_val(p4d) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_pud(dst_p4dp, src_p4dp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_p4dp++, src_p4dp++, start = next, start != end); + + return 0; +} + +static int temp_pgtable_mapping(pgd_t *pgdp, unsigned long start, unsigned long end, pgprot_t prot) +{ + pgd_t *dst_pgdp = pgd_offset_pgd(pgdp, start); + pgd_t *src_pgdp = pgd_offset_k(start); + unsigned long next; + unsigned long ret; + + do { + pgd_t pgd = READ_ONCE(*src_pgdp); + + next = pgd_addr_end(start, end); + + if (pgd_none(pgd)) + continue; + + if (pgd_leaf(pgd)) { + set_pgd(dst_pgdp, __pgd(pgd_val(pgd) | pgprot_val(prot))); + } else { + ret = temp_pgtable_map_p4d(dst_pgdp, src_pgdp, start, next, prot); + if (ret) + return -ENOMEM; + } + } while (dst_pgdp++, src_pgdp++, start = next, start != end); + + return 0; +} + +static unsigned long relocate_restore_code(void) +{ + void *page = (void *)get_safe_page(GFP_ATOMIC); + + if (!page) + return -ENOMEM; + + copy_page(page, hibernate_core_restore_code); + + /* Make the page containing the relocated code executable. */ + set_memory_x((unsigned long)page, 1); + + return (unsigned long)page; +} + +int swsusp_arch_resume(void) +{ + unsigned long end = (unsigned long)pfn_to_virt(max_low_pfn); + unsigned long start = PAGE_OFFSET; + int ret; + + /* + * Memory allocated by get_safe_page() will be dealt with by the hibernation core, + * we don't need to free it here. + */ + resume_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC); + if (!resume_pg_dir) + return -ENOMEM; + + /* + * Create a temporary page table and map the whole linear region as executable and + * writable. + */ + ret = temp_pgtable_mapping(resume_pg_dir, start, end, __pgprot(_PAGE_WRITE | _PAGE_EXEC)); + if (ret) + return ret; + + /* Move the restore code to a new page so that it doesn't get overwritten by itself. */ + relocated_restore_code = relocate_restore_code(); + if (relocated_restore_code == -ENOMEM) + return -ENOMEM; + + /* + * Map the __hibernate_cpu_resume() address to the temporary page table so that the + * restore code can jumps to it after finished restore the image. The next execution + * code doesn't find itself in a different address space after switching over to the + * original page table used by the hibernated image. + * The __hibernate_cpu_resume() mapping is unnecessary for RV32 since the kernel and + * linear addresses are identical, but different for RV64. To ensure consistency, we + * map it for both RV32 and RV64 kernels. + * Additionally, we should ensure that the page is writable before restoring the image. + */ + start = (unsigned long)resume_hdr.restore_cpu_addr; + end = start + PAGE_SIZE; + + ret = temp_pgtable_mapping(resume_pg_dir, start, end, __pgprot(_PAGE_WRITE)); + if (ret) + return ret; + + hibernate_restore_image(resume_hdr.saved_satp, (PFN_DOWN(__pa(resume_pg_dir)) | satp_mode), + resume_hdr.restore_cpu_addr); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP_SMP +int hibernate_resume_nonboot_cpu_disable(void) +{ + if (sleep_cpu < 0) { + pr_err("Failing to resume from hibernate on an unknown CPU\n"); + return -ENODEV; + } + + return freeze_secondary_cpus(sleep_cpu); +} +#endif + +static int __init riscv_hibernate_init(void) +{ + hibernate_cpu_context = kzalloc(sizeof(*hibernate_cpu_context), GFP_KERNEL); + + if (WARN_ON(!hibernate_cpu_context)) + return -ENOMEM; + + return 0; +} + +early_initcall(riscv_hibernate_init); diff --git a/arch/riscv/kernel/suspend.c b/arch/riscv/kernel/suspend.c index 9ba24fb8cc932..3c89b8ec69c49 100644 --- a/arch/riscv/kernel/suspend.c +++ b/arch/riscv/kernel/suspend.c @@ -8,7 +8,7 @@ #include #include -static void suspend_save_csrs(struct suspend_context *context) +void suspend_save_csrs(struct suspend_context *context) { context->scratch = csr_read(CSR_SCRATCH); context->tvec = csr_read(CSR_TVEC); @@ -29,7 +29,7 @@ static void suspend_save_csrs(struct suspend_context *context) #endif } -static void suspend_restore_csrs(struct suspend_context *context) +void suspend_restore_csrs(struct suspend_context *context) { csr_write(CSR_SCRATCH, context->scratch); csr_write(CSR_TVEC, context->tvec); diff --git a/arch/riscv/kernel/suspend_entry.S b/arch/riscv/kernel/suspend_entry.S index aafcca58c19de..12b52afe09a42 100644 --- a/arch/riscv/kernel/suspend_entry.S +++ b/arch/riscv/kernel/suspend_entry.S @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -83,39 +84,10 @@ ENTRY(__cpu_resume_enter) add a0, a1, zero /* Restore CSRs */ - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_EPC)(a0) - csrw CSR_EPC, t0 - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_STATUS)(a0) - csrw CSR_STATUS, t0 - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_BADADDR)(a0) - csrw CSR_TVAL, t0 - REG_L t0, (SUSPEND_CONTEXT_REGS + PT_CAUSE)(a0) - csrw CSR_CAUSE, t0 + suspend_restore_csrs /* Restore registers (except A0 and T0-T6) */ - REG_L ra, (SUSPEND_CONTEXT_REGS + PT_RA)(a0) - REG_L sp, (SUSPEND_CONTEXT_REGS + PT_SP)(a0) - REG_L gp, (SUSPEND_CONTEXT_REGS + PT_GP)(a0) - REG_L tp, (SUSPEND_CONTEXT_REGS + PT_TP)(a0) - REG_L s0, (SUSPEND_CONTEXT_REGS + PT_S0)(a0) - REG_L s1, (SUSPEND_CONTEXT_REGS + PT_S1)(a0) - REG_L a1, (SUSPEND_CONTEXT_REGS + PT_A1)(a0) - REG_L a2, (SUSPEND_CONTEXT_REGS + PT_A2)(a0) - REG_L a3, (SUSPEND_CONTEXT_REGS + PT_A3)(a0) - REG_L a4, (SUSPEND_CONTEXT_REGS + PT_A4)(a0) - REG_L a5, (SUSPEND_CONTEXT_REGS + PT_A5)(a0) - REG_L a6, (SUSPEND_CONTEXT_REGS + PT_A6)(a0) - REG_L a7, (SUSPEND_CONTEXT_REGS + PT_A7)(a0) - REG_L s2, (SUSPEND_CONTEXT_REGS + PT_S2)(a0) - REG_L s3, (SUSPEND_CONTEXT_REGS + PT_S3)(a0) - REG_L s4, (SUSPEND_CONTEXT_REGS + PT_S4)(a0) - REG_L s5, (SUSPEND_CONTEXT_REGS + PT_S5)(a0) - REG_L s6, (SUSPEND_CONTEXT_REGS + PT_S6)(a0) - REG_L s7, (SUSPEND_CONTEXT_REGS + PT_S7)(a0) - REG_L s8, (SUSPEND_CONTEXT_REGS + PT_S8)(a0) - REG_L s9, (SUSPEND_CONTEXT_REGS + PT_S9)(a0) - REG_L s10, (SUSPEND_CONTEXT_REGS + PT_S10)(a0) - REG_L s11, (SUSPEND_CONTEXT_REGS + PT_S11)(a0) + suspend_restore_regs /* Return zero value */ add a0, zero, zero diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index 86c56616e5dea..ea3d61de065b3 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -217,18 +217,26 @@ bool kernel_page_present(struct page *page) pgd = pgd_offset_k(addr); if (!pgd_present(*pgd)) return false; + if (pgd_leaf(*pgd)) + return true; p4d = p4d_offset(pgd, addr); if (!p4d_present(*p4d)) return false; + if (p4d_leaf(*p4d)) + return true; pud = pud_offset(p4d, addr); if (!pud_present(*pud)) return false; + if (pud_leaf(*pud)) + return true; pmd = pmd_offset(pud, addr); if (!pmd_present(*pmd)) return false; + if (pmd_leaf(*pmd)) + return true; pte = pte_offset_kernel(pmd, addr); return pte_present(*pte); diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index e3ca0d058a256..3bed88d67a3a0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -117,7 +117,7 @@ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-y += sprd/ obj-$(CONFIG_ARCH_STI) += st/ obj-$(CONFIG_ARCH_STM32) += stm32/ -obj-$(CONFIG_SOC_STARFIVE) += starfive/ +obj-y += starfive/ obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-y += sunxi-ng/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ diff --git a/drivers/clk/starfive/Kconfig b/drivers/clk/starfive/Kconfig index 003bd2d56ce7e..6818be51c0675 100644 --- a/drivers/clk/starfive/Kconfig +++ b/drivers/clk/starfive/Kconfig @@ -1,9 +1,13 @@ # SPDX-License-Identifier: GPL-2.0 +config CLK_STARFIVE_JH71X0 + bool + config CLK_STARFIVE_JH7100 bool "StarFive JH7100 clock support" - depends on SOC_STARFIVE || COMPILE_TEST - default SOC_STARFIVE + depends on ARCH_STARFIVE || COMPILE_TEST + select CLK_STARFIVE_JH71X0 + default ARCH_STARFIVE help Say yes here to support the clock controller on the StarFive JH7100 SoC. @@ -11,7 +15,72 @@ config CLK_STARFIVE_JH7100 config CLK_STARFIVE_JH7100_AUDIO tristate "StarFive JH7100 audio clock support" depends on CLK_STARFIVE_JH7100 - default m if SOC_STARFIVE + select CLK_STARFIVE_JH71X0 + default m if ARCH_STARFIVE help Say Y or M here to support the audio clocks on the StarFive JH7100 SoC. + +config CLK_STARFIVE_JH7110_PLL + bool "StarFive JH7110 PLL clock support" + depends on ARCH_STARFIVE || COMPILE_TEST + default ARCH_STARFIVE + help + Say yes here to support the PLL clock controller on the + StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_SYS + bool "StarFive JH7110 system clock support" + depends on ARCH_STARFIVE || COMPILE_TEST + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + select CLK_STARFIVE_JH7110_PLL + default ARCH_STARFIVE + help + Say yes here to support the system clock controller on the + StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_AON + tristate "StarFive JH7110 always-on clock support" + depends on CLK_STARFIVE_JH7110_SYS + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the always-on clock controller on the + StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_STG + tristate "StarFive JH7110 System-Top-Group clock support" + depends on CLK_STARFIVE_JH7110_SYS + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the System-Top-Group clock controller + on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_ISP + tristate "StarFive JH7110 Image-Signal-Process clock support" + depends on CLK_STARFIVE_JH7110_SYS && JH71XX_PMU + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the Image-Signal-Process clock controller + on the StarFive JH7110 SoC. + +config CLK_STARFIVE_JH7110_VOUT + tristate "StarFive JH7110 Video-Output clock support" + depends on CLK_STARFIVE_JH7110_SYS && JH71XX_PMU + select AUXILIARY_BUS + select CLK_STARFIVE_JH71X0 + select RESET_STARFIVE_JH7110 + default m if ARCH_STARFIVE + help + Say yes here to support the Video-Output clock controller + on the StarFive JH7110 SoC. diff --git a/drivers/clk/starfive/Makefile b/drivers/clk/starfive/Makefile index 0fa8ecb9ec1c6..199ac0f37a2f6 100644 --- a/drivers/clk/starfive/Makefile +++ b/drivers/clk/starfive/Makefile @@ -1,4 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 -# StarFive Clock +obj-$(CONFIG_CLK_STARFIVE_JH71X0) += clk-starfive-jh71x0.o + obj-$(CONFIG_CLK_STARFIVE_JH7100) += clk-starfive-jh7100.o obj-$(CONFIG_CLK_STARFIVE_JH7100_AUDIO) += clk-starfive-jh7100-audio.o + +obj-$(CONFIG_CLK_STARFIVE_JH7110_PLL) += clk-starfive-jh7110-pll.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_SYS) += clk-starfive-jh7110-sys.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_AON) += clk-starfive-jh7110-aon.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_STG) += clk-starfive-jh7110-stg.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_ISP) += clk-starfive-jh7110-isp.o +obj-$(CONFIG_CLK_STARFIVE_JH7110_VOUT) += clk-starfive-jh7110-vout.o diff --git a/drivers/clk/starfive/clk-starfive-jh7100-audio.c b/drivers/clk/starfive/clk-starfive-jh7100-audio.c index 8473a65e219be..02aefb7264f8f 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100-audio.c +++ b/drivers/clk/starfive/clk-starfive-jh7100-audio.c @@ -16,7 +16,7 @@ #include -#include "clk-starfive-jh7100.h" +#include "clk-starfive-jh71x0.h" /* external clocks */ #define JH7100_AUDCLK_AUDIO_SRC (JH7100_AUDCLK_END + 0) @@ -28,66 +28,66 @@ #define JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD (JH7100_AUDCLK_END + 6) #define JH7100_AUDCLK_VAD_INTMEM (JH7100_AUDCLK_END + 7) -static const struct jh7100_clk_data jh7100_audclk_data[] = { - JH7100__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk", 0, 15, 2, +static const struct jh71x0_clk_data jh7100_audclk_data[] = { + JH71X0__GMD(JH7100_AUDCLK_ADC_MCLK, "adc_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100__GMD(JH7100_AUDCLK_I2S1_MCLK, "i2s1_mclk", 0, 15, 2, + JH71X0__GMD(JH7100_AUDCLK_I2S1_MCLK, "i2s1_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_I2SADC_APB, "i2sadc_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_MDIV(JH7100_AUDCLK_I2SADC_BCLK, "i2sadc_bclk", 31, 2, + JH71X0_GATE(JH7100_AUDCLK_I2SADC_APB, "i2sadc_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_MDIV(JH7100_AUDCLK_I2SADC_BCLK, "i2sadc_bclk", 31, 2, JH7100_AUDCLK_ADC_MCLK, JH7100_AUDCLK_I2SADC_BCLK_IOPAD), - JH7100__INV(JH7100_AUDCLK_I2SADC_BCLK_N, "i2sadc_bclk_n", JH7100_AUDCLK_I2SADC_BCLK), - JH7100_MDIV(JH7100_AUDCLK_I2SADC_LRCLK, "i2sadc_lrclk", 63, 3, + JH71X0__INV(JH7100_AUDCLK_I2SADC_BCLK_N, "i2sadc_bclk_n", JH7100_AUDCLK_I2SADC_BCLK), + JH71X0_MDIV(JH7100_AUDCLK_I2SADC_LRCLK, "i2sadc_lrclk", 63, 3, JH7100_AUDCLK_I2SADC_BCLK_N, JH7100_AUDCLK_I2SADC_LRCLK_IOPAD, JH7100_AUDCLK_I2SADC_BCLK), - JH7100_GATE(JH7100_AUDCLK_PDM_APB, "pdm_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__GMD(JH7100_AUDCLK_PDM_MCLK, "pdm_mclk", 0, 15, 2, + JH71X0_GATE(JH7100_AUDCLK_PDM_APB, "pdm_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__GMD(JH7100_AUDCLK_PDM_MCLK, "pdm_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_I2SVAD_APB, "i2svad_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__GMD(JH7100_AUDCLK_SPDIF, "spdif", 0, 15, 2, + JH71X0_GATE(JH7100_AUDCLK_I2SVAD_APB, "i2svad_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__GMD(JH7100_AUDCLK_SPDIF, "spdif", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_SPDIF_APB, "spdif_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_GATE(JH7100_AUDCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__GMD(JH7100_AUDCLK_DAC_MCLK, "dac_mclk", 0, 15, 2, + JH71X0_GATE(JH7100_AUDCLK_SPDIF_APB, "spdif_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_GATE(JH7100_AUDCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__GMD(JH7100_AUDCLK_DAC_MCLK, "dac_mclk", 0, 15, 2, JH7100_AUDCLK_AUDIO_SRC, JH7100_AUDCLK_AUDIO_12288), - JH7100_GATE(JH7100_AUDCLK_I2SDAC_APB, "i2sdac_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_MDIV(JH7100_AUDCLK_I2SDAC_BCLK, "i2sdac_bclk", 31, 2, + JH71X0_GATE(JH7100_AUDCLK_I2SDAC_APB, "i2sdac_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_MDIV(JH7100_AUDCLK_I2SDAC_BCLK, "i2sdac_bclk", 31, 2, JH7100_AUDCLK_DAC_MCLK, JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), - JH7100__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n", JH7100_AUDCLK_I2SDAC_BCLK), - JH7100_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 31, 2, + JH71X0__INV(JH7100_AUDCLK_I2SDAC_BCLK_N, "i2sdac_bclk_n", JH7100_AUDCLK_I2SDAC_BCLK), + JH71X0_MDIV(JH7100_AUDCLK_I2SDAC_LRCLK, "i2sdac_lrclk", 31, 2, JH7100_AUDCLK_I2S1_MCLK, JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), - JH7100_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk", 31, 2, + JH71X0_GATE(JH7100_AUDCLK_I2S1_APB, "i2s1_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0_MDIV(JH7100_AUDCLK_I2S1_BCLK, "i2s1_bclk", 31, 2, JH7100_AUDCLK_I2S1_MCLK, JH7100_AUDCLK_I2SDAC_BCLK_IOPAD), - JH7100__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n", JH7100_AUDCLK_I2S1_BCLK), - JH7100_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk", 63, 3, + JH71X0__INV(JH7100_AUDCLK_I2S1_BCLK_N, "i2s1_bclk_n", JH7100_AUDCLK_I2S1_BCLK), + JH71X0_MDIV(JH7100_AUDCLK_I2S1_LRCLK, "i2s1_lrclk", 63, 3, JH7100_AUDCLK_I2S1_BCLK_N, JH7100_AUDCLK_I2SDAC_LRCLK_IOPAD), - JH7100_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb", 0, JH7100_AUDCLK_APB0_BUS), - JH7100__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus", 8, JH7100_AUDCLK_DOM7AHB_BUS), - JH7100_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7100_AUDCLK_DOM7AHB_BUS), - JH7100_GATE(JH7100_AUDCLK_USB_APB, "usb_apb", CLK_IGNORE_UNUSED, JH7100_AUDCLK_APB_EN), - JH7100_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB), - JH7100_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB), - JH7100__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS), - JH7100__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2, + JH71X0_GATE(JH7100_AUDCLK_I2SDAC16K_APB, "i2s1dac16k_apb", 0, JH7100_AUDCLK_APB0_BUS), + JH71X0__DIV(JH7100_AUDCLK_APB0_BUS, "apb0_bus", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH71X0_GATE(JH7100_AUDCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7100_AUDCLK_DOM7AHB_BUS), + JH71X0_GATE(JH7100_AUDCLK_USB_APB, "usb_apb", CLK_IGNORE_UNUSED, JH7100_AUDCLK_APB_EN), + JH71X0_GDIV(JH7100_AUDCLK_USB_LPM, "usb_lpm", CLK_IGNORE_UNUSED, 4, JH7100_AUDCLK_USB_APB), + JH71X0_GDIV(JH7100_AUDCLK_USB_STB, "usb_stb", CLK_IGNORE_UNUSED, 3, JH7100_AUDCLK_USB_APB), + JH71X0__DIV(JH7100_AUDCLK_APB_EN, "apb_en", 8, JH7100_AUDCLK_DOM7AHB_BUS), + JH71X0__MUX(JH7100_AUDCLK_VAD_MEM, "vad_mem", 2, JH7100_AUDCLK_VAD_INTMEM, JH7100_AUDCLK_AUDIO_12288), }; static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *data) { - struct jh7100_clk_priv *priv = data; + struct jh71x0_clk_priv *priv = data; unsigned int idx = clkspec->args[0]; if (idx < JH7100_AUDCLK_END) @@ -98,7 +98,7 @@ static struct clk_hw *jh7100_audclk_get(struct of_phandle_args *clkspec, void *d static int jh7100_audclk_probe(struct platform_device *pdev) { - struct jh7100_clk_priv *priv; + struct jh71x0_clk_priv *priv; unsigned int idx; int ret; @@ -117,12 +117,12 @@ static int jh7100_audclk_probe(struct platform_device *pdev) struct clk_parent_data parents[4] = {}; struct clk_init_data init = { .name = jh7100_audclk_data[idx].name, - .ops = starfive_jh7100_clk_ops(max), + .ops = starfive_jh71x0_clk_ops(max), .parent_data = parents, - .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, + .num_parents = ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, .flags = jh7100_audclk_data[idx].flags, }; - struct jh7100_clk *clk = &priv->reg[idx]; + struct jh71x0_clk *clk = &priv->reg[idx]; unsigned int i; for (i = 0; i < init.num_parents; i++) { @@ -140,7 +140,7 @@ static int jh7100_audclk_probe(struct platform_device *pdev) clk->hw.init = &init; clk->idx = idx; - clk->max_div = max & JH7100_CLK_DIV_MASK; + clk->max_div = max & JH71X0_CLK_DIV_MASK; ret = devm_clk_hw_register(priv->dev, &clk->hw); if (ret) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.c b/drivers/clk/starfive/clk-starfive-jh7100.c index 691aeebc70927..69cc11ea7e334 100644 --- a/drivers/clk/starfive/clk-starfive-jh7100.c +++ b/drivers/clk/starfive/clk-starfive-jh7100.c @@ -7,20 +7,15 @@ * Copyright (C) 2021 Emil Renner Berthing */ -#include #include -#include #include #include -#include -#include #include -#include #include #include -#include "clk-starfive-jh7100.h" +#include "clk-starfive-jh71x0.h" /* external clocks */ #define JH7100_CLK_OSC_SYS (JH7100_CLK_END + 0) @@ -28,570 +23,253 @@ #define JH7100_CLK_GMAC_RMII_REF (JH7100_CLK_END + 2) #define JH7100_CLK_GMAC_GR_MII_RX (JH7100_CLK_END + 3) -static const struct jh7100_clk_data jh7100_clk_data[] __initconst = { - JH7100__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4, +static const struct jh71x0_clk_data jh7100_clk_data[] __initconst = { + JH71X0__MUX(JH7100_CLK_CPUNDBUS_ROOT, "cpundbus_root", 4, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 3, + JH71X0__MUX(JH7100_CLK_DLA_ROOT, "dla_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 4, + JH71X0__MUX(JH7100_CLK_DSP_ROOT, "dsp_root", 4, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 3, + JH71X0__MUX(JH7100_CLK_GMACUSB_ROOT, "gmacusb_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 2, + JH71X0__MUX(JH7100_CLK_PERH0_ROOT, "perh0_root", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT), - JH7100__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 2, + JH71X0__MUX(JH7100_CLK_PERH1_ROOT, "perh1_root", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 3, + JH71X0__MUX(JH7100_CLK_VIN_ROOT, "vin_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 3, + JH71X0__MUX(JH7100_CLK_VOUT_ROOT, "vout_root", 3, JH7100_CLK_OSC_AUD, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL2_OUT), - JH7100_GDIV(JH7100_CLK_AUDIO_ROOT, "audio_root", 0, 8, JH7100_CLK_PLL0_OUT), - JH7100__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 3, + JH71X0_GDIV(JH7100_CLK_AUDIO_ROOT, "audio_root", 0, 8, JH7100_CLK_PLL0_OUT), + JH71X0__MUX(JH7100_CLK_CDECHIFI4_ROOT, "cdechifi4_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL1_OUT, JH7100_CLK_PLL2_OUT), - JH7100__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 3, + JH71X0__MUX(JH7100_CLK_CDEC_ROOT, "cdec_root", 3, JH7100_CLK_OSC_SYS, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL1_OUT), - JH7100__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 3, + JH71X0__MUX(JH7100_CLK_VOUTBUS_ROOT, "voutbus_root", 3, JH7100_CLK_OSC_AUD, JH7100_CLK_PLL0_OUT, JH7100_CLK_PLL2_OUT), - JH7100__DIV(JH7100_CLK_CPUNBUS_ROOT_DIV, "cpunbus_root_div", 2, JH7100_CLK_CPUNDBUS_ROOT), - JH7100__DIV(JH7100_CLK_DSP_ROOT_DIV, "dsp_root_div", 4, JH7100_CLK_DSP_ROOT), - JH7100__DIV(JH7100_CLK_PERH0_SRC, "perh0_src", 4, JH7100_CLK_PERH0_ROOT), - JH7100__DIV(JH7100_CLK_PERH1_SRC, "perh1_src", 4, JH7100_CLK_PERH1_ROOT), - JH7100_GDIV(JH7100_CLK_PLL0_TESTOUT, "pll0_testout", 0, 31, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_PLL1_TESTOUT, "pll1_testout", 0, 31, JH7100_CLK_DLA_ROOT), - JH7100_GDIV(JH7100_CLK_PLL2_TESTOUT, "pll2_testout", 0, 31, JH7100_CLK_PERH1_SRC), - JH7100__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 2, + JH71X0__DIV(JH7100_CLK_CPUNBUS_ROOT_DIV, "cpunbus_root_div", 2, JH7100_CLK_CPUNDBUS_ROOT), + JH71X0__DIV(JH7100_CLK_DSP_ROOT_DIV, "dsp_root_div", 4, JH7100_CLK_DSP_ROOT), + JH71X0__DIV(JH7100_CLK_PERH0_SRC, "perh0_src", 4, JH7100_CLK_PERH0_ROOT), + JH71X0__DIV(JH7100_CLK_PERH1_SRC, "perh1_src", 4, JH7100_CLK_PERH1_ROOT), + JH71X0_GDIV(JH7100_CLK_PLL0_TESTOUT, "pll0_testout", 0, 31, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_PLL1_TESTOUT, "pll1_testout", 0, 31, JH7100_CLK_DLA_ROOT), + JH71X0_GDIV(JH7100_CLK_PLL2_TESTOUT, "pll2_testout", 0, 31, JH7100_CLK_PERH1_SRC), + JH71X0__MUX(JH7100_CLK_PLL2_REF, "pll2_refclk", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_OSC_AUD), - JH7100__DIV(JH7100_CLK_CPU_CORE, "cpu_core", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_CPU_AXI, "cpu_axi", 8, JH7100_CLK_CPU_CORE), - JH7100__DIV(JH7100_CLK_AHB_BUS, "ahb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_APB1_BUS, "apb1_bus", 8, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_APB2_BUS, "apb2_bus", 8, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_DOM3AHB_BUS, "dom3ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_DOM7AHB_BUS, "dom7ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_U74_CORE0, "u74_core0", CLK_IS_CRITICAL, JH7100_CLK_CPU_CORE), - JH7100_GDIV(JH7100_CLK_U74_CORE1, "u74_core1", CLK_IS_CRITICAL, 8, JH7100_CLK_CPU_CORE), - JH7100_GATE(JH7100_CLK_U74_AXI, "u74_axi", CLK_IS_CRITICAL, JH7100_CLK_CPU_AXI), - JH7100_GATE(JH7100_CLK_U74RTC_TOGGLE, "u74rtc_toggle", CLK_IS_CRITICAL, JH7100_CLK_OSC_SYS), - JH7100_GATE(JH7100_CLK_SGDMA2P_AXI, "sgdma2p_axi", 0, JH7100_CLK_CPU_AXI), - JH7100_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI), - JH7100_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT), - JH7100_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS), - JH7100_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS), - JH7100_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV), - JH7100__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT), - JH7100_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC), - JH7100__DIV(JH7100_CLK_VCDECBUS_SRC, "vcdecbus_src", 4, JH7100_CLK_CDECHIFI4_ROOT), - JH7100__DIV(JH7100_CLK_VDEC_BUS, "vdec_bus", 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GATE(JH7100_CLK_VDEC_AXI, "vdec_axi", 0, JH7100_CLK_VDEC_BUS), - JH7100_GATE(JH7100_CLK_VDECBRG_MAIN, "vdecbrg_mainclk", 0, JH7100_CLK_VDEC_BUS), - JH7100_GDIV(JH7100_CLK_VDEC_BCLK, "vdec_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GDIV(JH7100_CLK_VDEC_CCLK, "vdec_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), - JH7100_GATE(JH7100_CLK_VDEC_APB, "vdec_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_JPEG_AXI, "jpeg_axi", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_JPEG_CCLK, "jpeg_cclk", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GATE(JH7100_CLK_JPEG_APB, "jpeg_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_GC300_2X, "gc300_2x", 0, 8, JH7100_CLK_CDECHIFI4_ROOT), - JH7100_GATE(JH7100_CLK_GC300_AHB, "gc300_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_JPCGC300_AXIBUS, "jpcgc300_axibus", 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GATE(JH7100_CLK_GC300_AXI, "gc300_axi", 0, JH7100_CLK_JPCGC300_AXIBUS), - JH7100_GATE(JH7100_CLK_JPCGC300_MAIN, "jpcgc300_mainclk", 0, JH7100_CLK_JPCGC300_AXIBUS), - JH7100__DIV(JH7100_CLK_VENC_BUS, "venc_bus", 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GATE(JH7100_CLK_VENC_AXI, "venc_axi", 0, JH7100_CLK_VENC_BUS), - JH7100_GATE(JH7100_CLK_VENCBRG_MAIN, "vencbrg_mainclk", 0, JH7100_CLK_VENC_BUS), - JH7100_GDIV(JH7100_CLK_VENC_BCLK, "venc_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), - JH7100_GDIV(JH7100_CLK_VENC_CCLK, "venc_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), - JH7100_GATE(JH7100_CLK_VENC_APB, "venc_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_DDRPLL_DIV2, "ddrpll_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_PLL1_OUT), - JH7100_GDIV(JH7100_CLK_DDRPLL_DIV4, "ddrpll_div4", CLK_IS_CRITICAL, 2, JH7100_CLK_DDRPLL_DIV2), - JH7100_GDIV(JH7100_CLK_DDRPLL_DIV8, "ddrpll_div8", CLK_IS_CRITICAL, 2, JH7100_CLK_DDRPLL_DIV4), - JH7100_GDIV(JH7100_CLK_DDROSC_DIV2, "ddrosc_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_OSC_SYS), - JH7100_GMUX(JH7100_CLK_DDRC0, "ddrc0", CLK_IS_CRITICAL, 4, + JH71X0__DIV(JH7100_CLK_CPU_CORE, "cpu_core", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_CPU_AXI, "cpu_axi", 8, JH7100_CLK_CPU_CORE), + JH71X0__DIV(JH7100_CLK_AHB_BUS, "ahb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_APB1_BUS, "apb1_bus", 8, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_APB2_BUS, "apb2_bus", 8, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_DOM3AHB_BUS, "dom3ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_DOM7AHB_BUS, "dom7ahb_bus", CLK_IS_CRITICAL, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_U74_CORE0, "u74_core0", CLK_IS_CRITICAL, JH7100_CLK_CPU_CORE), + JH71X0_GDIV(JH7100_CLK_U74_CORE1, "u74_core1", CLK_IS_CRITICAL, 8, JH7100_CLK_CPU_CORE), + JH71X0_GATE(JH7100_CLK_U74_AXI, "u74_axi", CLK_IS_CRITICAL, JH7100_CLK_CPU_AXI), + JH71X0_GATE(JH7100_CLK_U74RTC_TOGGLE, "u74rtc_toggle", CLK_IS_CRITICAL, JH7100_CLK_OSC_SYS), + JH71X0_GATE(JH7100_CLK_SGDMA2P_AXI, "sgdma2p_axi", 0, JH7100_CLK_CPU_AXI), + JH71X0_GATE(JH7100_CLK_DMA2PNOC_AXI, "dma2pnoc_axi", 0, JH7100_CLK_CPU_AXI), + JH71X0_GATE(JH7100_CLK_SGDMA2P_AHB, "sgdma2p_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_DLA_BUS, "dla_bus", 4, JH7100_CLK_DLA_ROOT), + JH71X0_GATE(JH7100_CLK_DLA_AXI, "dla_axi", 0, JH7100_CLK_DLA_BUS), + JH71X0_GATE(JH7100_CLK_DLANOC_AXI, "dlanoc_axi", 0, JH7100_CLK_DLA_BUS), + JH71X0_GATE(JH7100_CLK_DLA_APB, "dla_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_VP6_CORE, "vp6_core", 0, 4, JH7100_CLK_DSP_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_VP6BUS_SRC, "vp6bus_src", 4, JH7100_CLK_DSP_ROOT), + JH71X0_GDIV(JH7100_CLK_VP6_AXI, "vp6_axi", 0, 4, JH7100_CLK_VP6BUS_SRC), + JH71X0__DIV(JH7100_CLK_VCDECBUS_SRC, "vcdecbus_src", 4, JH7100_CLK_CDECHIFI4_ROOT), + JH71X0__DIV(JH7100_CLK_VDEC_BUS, "vdec_bus", 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GATE(JH7100_CLK_VDEC_AXI, "vdec_axi", 0, JH7100_CLK_VDEC_BUS), + JH71X0_GATE(JH7100_CLK_VDECBRG_MAIN, "vdecbrg_mainclk", 0, JH7100_CLK_VDEC_BUS), + JH71X0_GDIV(JH7100_CLK_VDEC_BCLK, "vdec_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GDIV(JH7100_CLK_VDEC_CCLK, "vdec_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), + JH71X0_GATE(JH7100_CLK_VDEC_APB, "vdec_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_JPEG_AXI, "jpeg_axi", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GDIV(JH7100_CLK_JPEG_CCLK, "jpeg_cclk", 0, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_JPEG_APB, "jpeg_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_GC300_2X, "gc300_2x", 0, 8, JH7100_CLK_CDECHIFI4_ROOT), + JH71X0_GATE(JH7100_CLK_GC300_AHB, "gc300_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_JPCGC300_AXIBUS, "jpcgc300_axibus", 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GATE(JH7100_CLK_GC300_AXI, "gc300_axi", 0, JH7100_CLK_JPCGC300_AXIBUS), + JH71X0_GATE(JH7100_CLK_JPCGC300_MAIN, "jpcgc300_mainclk", 0, JH7100_CLK_JPCGC300_AXIBUS), + JH71X0__DIV(JH7100_CLK_VENC_BUS, "venc_bus", 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GATE(JH7100_CLK_VENC_AXI, "venc_axi", 0, JH7100_CLK_VENC_BUS), + JH71X0_GATE(JH7100_CLK_VENCBRG_MAIN, "vencbrg_mainclk", 0, JH7100_CLK_VENC_BUS), + JH71X0_GDIV(JH7100_CLK_VENC_BCLK, "venc_bclk", 0, 8, JH7100_CLK_VCDECBUS_SRC), + JH71X0_GDIV(JH7100_CLK_VENC_CCLK, "venc_cclk", 0, 8, JH7100_CLK_CDEC_ROOT), + JH71X0_GATE(JH7100_CLK_VENC_APB, "venc_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_DDRPLL_DIV2, "ddrpll_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_PLL1_OUT), + JH71X0_GDIV(JH7100_CLK_DDRPLL_DIV4, "ddrpll_div4", CLK_IS_CRITICAL, 2, + JH7100_CLK_DDRPLL_DIV2), + JH71X0_GDIV(JH7100_CLK_DDRPLL_DIV8, "ddrpll_div8", CLK_IS_CRITICAL, 2, + JH7100_CLK_DDRPLL_DIV4), + JH71X0_GDIV(JH7100_CLK_DDROSC_DIV2, "ddrosc_div2", CLK_IS_CRITICAL, 2, JH7100_CLK_OSC_SYS), + JH71X0_GMUX(JH7100_CLK_DDRC0, "ddrc0", CLK_IS_CRITICAL, 4, JH7100_CLK_DDROSC_DIV2, JH7100_CLK_DDRPLL_DIV2, JH7100_CLK_DDRPLL_DIV4, JH7100_CLK_DDRPLL_DIV8), - JH7100_GMUX(JH7100_CLK_DDRC1, "ddrc1", CLK_IS_CRITICAL, 4, + JH71X0_GMUX(JH7100_CLK_DDRC1, "ddrc1", CLK_IS_CRITICAL, 4, JH7100_CLK_DDROSC_DIV2, JH7100_CLK_DDRPLL_DIV2, JH7100_CLK_DDRPLL_DIV4, JH7100_CLK_DDRPLL_DIV8), - JH7100_GATE(JH7100_CLK_DDRPHY_APB, "ddrphy_apb", 0, JH7100_CLK_APB1_BUS), - JH7100__DIV(JH7100_CLK_NOC_ROB, "noc_rob", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_NOC_COG, "noc_cog", 8, JH7100_CLK_DLA_ROOT), - JH7100_GATE(JH7100_CLK_NNE_AHB, "nne_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_NNEBUS_SRC1, "nnebus_src1", 4, JH7100_CLK_DSP_ROOT), - JH7100__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 2, + JH71X0_GATE(JH7100_CLK_DDRPHY_APB, "ddrphy_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0__DIV(JH7100_CLK_NOC_ROB, "noc_rob", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_NOC_COG, "noc_cog", 8, JH7100_CLK_DLA_ROOT), + JH71X0_GATE(JH7100_CLK_NNE_AHB, "nne_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_NNEBUS_SRC1, "nnebus_src1", 4, JH7100_CLK_DSP_ROOT), + JH71X0__MUX(JH7100_CLK_NNE_BUS, "nne_bus", 2, JH7100_CLK_CPU_AXI, JH7100_CLK_NNEBUS_SRC1), - JH7100_GATE(JH7100_CLK_NNE_AXI, "nne_axi", 0, JH7100_CLK_NNE_BUS), - JH7100_GATE(JH7100_CLK_NNENOC_AXI, "nnenoc_axi", 0, JH7100_CLK_NNE_BUS), - JH7100_GATE(JH7100_CLK_DLASLV_AXI, "dlaslv_axi", 0, JH7100_CLK_NNE_BUS), - JH7100_GATE(JH7100_CLK_DSPX2C_AXI, "dspx2c_axi", CLK_IS_CRITICAL, JH7100_CLK_NNE_BUS), - JH7100__DIV(JH7100_CLK_HIFI4_SRC, "hifi4_src", 4, JH7100_CLK_CDECHIFI4_ROOT), - JH7100__DIV(JH7100_CLK_HIFI4_COREFREE, "hifi4_corefree", 8, JH7100_CLK_HIFI4_SRC), - JH7100_GATE(JH7100_CLK_HIFI4_CORE, "hifi4_core", 0, JH7100_CLK_HIFI4_COREFREE), - JH7100__DIV(JH7100_CLK_HIFI4_BUS, "hifi4_bus", 8, JH7100_CLK_HIFI4_COREFREE), - JH7100_GATE(JH7100_CLK_HIFI4_AXI, "hifi4_axi", 0, JH7100_CLK_HIFI4_BUS), - JH7100_GATE(JH7100_CLK_HIFI4NOC_AXI, "hifi4noc_axi", 0, JH7100_CLK_HIFI4_BUS), - JH7100__DIV(JH7100_CLK_SGDMA1P_BUS, "sgdma1p_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GATE(JH7100_CLK_SGDMA1P_AXI, "sgdma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), - JH7100_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), - JH7100_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), - JH7100_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS), - JH7100_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS), - JH7100__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT), - JH7100_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV), - JH7100_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32, JH7100_CLK_USBPHY_ROOTDIV), - JH7100__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, + JH71X0_GATE(JH7100_CLK_NNE_AXI, "nne_axi", 0, JH7100_CLK_NNE_BUS), + JH71X0_GATE(JH7100_CLK_NNENOC_AXI, "nnenoc_axi", 0, JH7100_CLK_NNE_BUS), + JH71X0_GATE(JH7100_CLK_DLASLV_AXI, "dlaslv_axi", 0, JH7100_CLK_NNE_BUS), + JH71X0_GATE(JH7100_CLK_DSPX2C_AXI, "dspx2c_axi", CLK_IS_CRITICAL, JH7100_CLK_NNE_BUS), + JH71X0__DIV(JH7100_CLK_HIFI4_SRC, "hifi4_src", 4, JH7100_CLK_CDECHIFI4_ROOT), + JH71X0__DIV(JH7100_CLK_HIFI4_COREFREE, "hifi4_corefree", 8, JH7100_CLK_HIFI4_SRC), + JH71X0_GATE(JH7100_CLK_HIFI4_CORE, "hifi4_core", 0, JH7100_CLK_HIFI4_COREFREE), + JH71X0__DIV(JH7100_CLK_HIFI4_BUS, "hifi4_bus", 8, JH7100_CLK_HIFI4_COREFREE), + JH71X0_GATE(JH7100_CLK_HIFI4_AXI, "hifi4_axi", 0, JH7100_CLK_HIFI4_BUS), + JH71X0_GATE(JH7100_CLK_HIFI4NOC_AXI, "hifi4noc_axi", 0, JH7100_CLK_HIFI4_BUS), + JH71X0__DIV(JH7100_CLK_SGDMA1P_BUS, "sgdma1p_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_SGDMA1P_AXI, "sgdma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), + JH71X0_GATE(JH7100_CLK_DMA1P_AXI, "dma1p_axi", 0, JH7100_CLK_SGDMA1P_BUS), + JH71X0_GDIV(JH7100_CLK_X2C_AXI, "x2c_axi", CLK_IS_CRITICAL, 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0__DIV(JH7100_CLK_USB_BUS, "usb_bus", 8, JH7100_CLK_CPUNBUS_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_USB_AXI, "usb_axi", 0, JH7100_CLK_USB_BUS), + JH71X0_GATE(JH7100_CLK_USBNOC_AXI, "usbnoc_axi", 0, JH7100_CLK_USB_BUS), + JH71X0__DIV(JH7100_CLK_USBPHY_ROOTDIV, "usbphy_rootdiv", 4, JH7100_CLK_GMACUSB_ROOT), + JH71X0_GDIV(JH7100_CLK_USBPHY_125M, "usbphy_125m", 0, 8, JH7100_CLK_USBPHY_ROOTDIV), + JH71X0_GDIV(JH7100_CLK_USBPHY_PLLDIV25M, "usbphy_plldiv25m", 0, 32, + JH7100_CLK_USBPHY_ROOTDIV), + JH71X0__MUX(JH7100_CLK_USBPHY_25M, "usbphy_25m", 2, JH7100_CLK_OSC_SYS, JH7100_CLK_USBPHY_PLLDIV25M), - JH7100_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT), - JH7100_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), - JH7100_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), - JH7100_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), - JH7100__DIV(JH7100_CLK_ISP0_BUS, "isp0_bus", 8, JH7100_CLK_VIN_SRC), - JH7100_GATE(JH7100_CLK_ISP0_AXI, "isp0_axi", 0, JH7100_CLK_ISP0_BUS), - JH7100_GATE(JH7100_CLK_ISP0NOC_AXI, "isp0noc_axi", 0, JH7100_CLK_ISP0_BUS), - JH7100_GATE(JH7100_CLK_ISPSLV_AXI, "ispslv_axi", 0, JH7100_CLK_ISP0_BUS), - JH7100__DIV(JH7100_CLK_ISP1_BUS, "isp1_bus", 8, JH7100_CLK_VIN_SRC), - JH7100_GATE(JH7100_CLK_ISP1_AXI, "isp1_axi", 0, JH7100_CLK_ISP1_BUS), - JH7100_GATE(JH7100_CLK_ISP1NOC_AXI, "isp1noc_axi", 0, JH7100_CLK_ISP1_BUS), - JH7100__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC), - JH7100_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS), - JH7100_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS), - JH7100_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT), - JH7100__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT), - JH7100__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC), - JH7100_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS), - JH7100_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS), - JH7100_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC), - JH7100__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT), - JH7100_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC), - JH7100__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT), - JH7100_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT), - JH7100_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), - JH7100_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), - JH7100__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3, + JH71X0_FDIV(JH7100_CLK_AUDIO_DIV, "audio_div", JH7100_CLK_AUDIO_ROOT), + JH71X0_GATE(JH7100_CLK_AUDIO_SRC, "audio_src", 0, JH7100_CLK_AUDIO_DIV), + JH71X0_GATE(JH7100_CLK_AUDIO_12288, "audio_12288", 0, JH7100_CLK_OSC_AUD), + JH71X0_GDIV(JH7100_CLK_VIN_SRC, "vin_src", 0, 4, JH7100_CLK_VIN_ROOT), + JH71X0__DIV(JH7100_CLK_ISP0_BUS, "isp0_bus", 8, JH7100_CLK_VIN_SRC), + JH71X0_GATE(JH7100_CLK_ISP0_AXI, "isp0_axi", 0, JH7100_CLK_ISP0_BUS), + JH71X0_GATE(JH7100_CLK_ISP0NOC_AXI, "isp0noc_axi", 0, JH7100_CLK_ISP0_BUS), + JH71X0_GATE(JH7100_CLK_ISPSLV_AXI, "ispslv_axi", 0, JH7100_CLK_ISP0_BUS), + JH71X0__DIV(JH7100_CLK_ISP1_BUS, "isp1_bus", 8, JH7100_CLK_VIN_SRC), + JH71X0_GATE(JH7100_CLK_ISP1_AXI, "isp1_axi", 0, JH7100_CLK_ISP1_BUS), + JH71X0_GATE(JH7100_CLK_ISP1NOC_AXI, "isp1noc_axi", 0, JH7100_CLK_ISP1_BUS), + JH71X0__DIV(JH7100_CLK_VIN_BUS, "vin_bus", 8, JH7100_CLK_VIN_SRC), + JH71X0_GATE(JH7100_CLK_VIN_AXI, "vin_axi", 0, JH7100_CLK_VIN_BUS), + JH71X0_GATE(JH7100_CLK_VINNOC_AXI, "vinnoc_axi", 0, JH7100_CLK_VIN_BUS), + JH71X0_GDIV(JH7100_CLK_VOUT_SRC, "vout_src", 0, 4, JH7100_CLK_VOUT_ROOT), + JH71X0__DIV(JH7100_CLK_DISPBUS_SRC, "dispbus_src", 4, JH7100_CLK_VOUTBUS_ROOT), + JH71X0__DIV(JH7100_CLK_DISP_BUS, "disp_bus", 4, JH7100_CLK_DISPBUS_SRC), + JH71X0_GATE(JH7100_CLK_DISP_AXI, "disp_axi", 0, JH7100_CLK_DISP_BUS), + JH71X0_GATE(JH7100_CLK_DISPNOC_AXI, "dispnoc_axi", 0, JH7100_CLK_DISP_BUS), + JH71X0_GATE(JH7100_CLK_SDIO0_AHB, "sdio0_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GDIV(JH7100_CLK_SDIO0_CCLKINT, "sdio0_cclkint", 0, 24, JH7100_CLK_PERH0_SRC), + JH71X0__INV(JH7100_CLK_SDIO0_CCLKINT_INV, "sdio0_cclkint_inv", JH7100_CLK_SDIO0_CCLKINT), + JH71X0_GATE(JH7100_CLK_SDIO1_AHB, "sdio1_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GDIV(JH7100_CLK_SDIO1_CCLKINT, "sdio1_cclkint", 0, 24, JH7100_CLK_PERH1_SRC), + JH71X0__INV(JH7100_CLK_SDIO1_CCLKINT_INV, "sdio1_cclkint_inv", JH7100_CLK_SDIO1_CCLKINT), + JH71X0_GATE(JH7100_CLK_GMAC_AHB, "gmac_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0__DIV(JH7100_CLK_GMAC_ROOT_DIV, "gmac_root_div", 8, JH7100_CLK_GMACUSB_ROOT), + JH71X0_GDIV(JH7100_CLK_GMAC_PTP_REF, "gmac_ptp_refclk", 0, 31, JH7100_CLK_GMAC_ROOT_DIV), + JH71X0_GDIV(JH7100_CLK_GMAC_GTX, "gmac_gtxclk", 0, 255, JH7100_CLK_GMAC_ROOT_DIV), + JH71X0_GDIV(JH7100_CLK_GMAC_RMII_TX, "gmac_rmii_txclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), + JH71X0_GDIV(JH7100_CLK_GMAC_RMII_RX, "gmac_rmii_rxclk", 0, 8, JH7100_CLK_GMAC_RMII_REF), + JH71X0__MUX(JH7100_CLK_GMAC_TX, "gmac_tx", 3, JH7100_CLK_GMAC_GTX, JH7100_CLK_GMAC_TX_INV, JH7100_CLK_GMAC_RMII_TX), - JH7100__INV(JH7100_CLK_GMAC_TX_INV, "gmac_tx_inv", JH7100_CLK_GMAC_TX), - JH7100__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 2, + JH71X0__INV(JH7100_CLK_GMAC_TX_INV, "gmac_tx_inv", JH7100_CLK_GMAC_TX), + JH71X0__MUX(JH7100_CLK_GMAC_RX_PRE, "gmac_rx_pre", 2, JH7100_CLK_GMAC_GR_MII_RX, JH7100_CLK_GMAC_RMII_RX), - JH7100__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE), - JH7100_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF), - JH7100_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV), - JH7100_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_E24_AHB, "e24_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_E24RTC_TOGGLE, "e24rtc_toggle", 0, JH7100_CLK_OSC_SYS), - JH7100_GATE(JH7100_CLK_QSPI_AHB, "qspi_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_QSPI_APB, "qspi_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_QSPI_REF, "qspi_refclk", 0, 31, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_SEC_AHB, "sec_ahb", 0, JH7100_CLK_AHB_BUS), - JH7100_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB), - JH7100_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB), - JH7100_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB), - JH7100_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_UART1_APB, "uart1_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_UART1_CORE, "uart1_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_SPI0_APB, "spi0_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_SPI0_CORE, "spi0_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_SPI1_APB, "spi1_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_SPI1_CORE, "spi1_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_I2C0_APB, "i2c0_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_I2C0_CORE, "i2c0_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_I2C1_APB, "i2c1_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GDIV(JH7100_CLK_I2C1_CORE, "i2c1_core", 0, 63, JH7100_CLK_PERH1_SRC), - JH7100_GATE(JH7100_CLK_GPIO_APB, "gpio_apb", 0, JH7100_CLK_APB1_BUS), - JH7100_GATE(JH7100_CLK_UART2_APB, "uart2_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_UART2_CORE, "uart2_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_UART3_APB, "uart3_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_UART3_CORE, "uart3_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_SPI2_APB, "spi2_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_SPI2_CORE, "spi2_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_SPI3_APB, "spi3_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_SPI3_CORE, "spi3_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_I2C2_APB, "i2c2_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_I2C2_CORE, "i2c2_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_I2C3_APB, "i2c3_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_I2C3_CORE, "i2c3_core", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_WDTIMER_APB, "wdtimer_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_WDT_CORE, "wdt_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER0_CORE, "timer0_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER1_CORE, "timer1_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER2_CORE, "timer2_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER3_CORE, "timer3_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER4_CORE, "timer4_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER5_CORE, "timer5_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GDIV(JH7100_CLK_TIMER6_CORE, "timer6_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), - JH7100_GATE(JH7100_CLK_VP6INTC_APB, "vp6intc_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GATE(JH7100_CLK_PWM_APB, "pwm_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GATE(JH7100_CLK_MSI_APB, "msi_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GATE(JH7100_CLK_TEMP_APB, "temp_apb", 0, JH7100_CLK_APB2_BUS), - JH7100_GDIV(JH7100_CLK_TEMP_SENSE, "temp_sense", 0, 31, JH7100_CLK_OSC_SYS), - JH7100_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0__INV(JH7100_CLK_GMAC_RX_INV, "gmac_rx_inv", JH7100_CLK_GMAC_RX_PRE), + JH71X0_GATE(JH7100_CLK_GMAC_RMII, "gmac_rmii", 0, JH7100_CLK_GMAC_RMII_REF), + JH71X0_GDIV(JH7100_CLK_GMAC_TOPHYREF, "gmac_tophyref", 0, 127, JH7100_CLK_GMAC_ROOT_DIV), + JH71X0_GATE(JH7100_CLK_SPI2AHB_AHB, "spi2ahb_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GDIV(JH7100_CLK_SPI2AHB_CORE, "spi2ahb_core", 0, 31, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_EZMASTER_AHB, "ezmaster_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_E24_AHB, "e24_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_E24RTC_TOGGLE, "e24rtc_toggle", 0, JH7100_CLK_OSC_SYS), + JH71X0_GATE(JH7100_CLK_QSPI_AHB, "qspi_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_QSPI_APB, "qspi_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_QSPI_REF, "qspi_refclk", 0, 31, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_SEC_AHB, "sec_ahb", 0, JH7100_CLK_AHB_BUS), + JH71X0_GATE(JH7100_CLK_AES, "aes_clk", 0, JH7100_CLK_SEC_AHB), + JH71X0_GATE(JH7100_CLK_SHA, "sha_clk", 0, JH7100_CLK_SEC_AHB), + JH71X0_GATE(JH7100_CLK_PKA, "pka_clk", 0, JH7100_CLK_SEC_AHB), + JH71X0_GATE(JH7100_CLK_TRNG_APB, "trng_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GATE(JH7100_CLK_OTP_APB, "otp_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GATE(JH7100_CLK_UART0_APB, "uart0_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_UART0_CORE, "uart0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_UART1_APB, "uart1_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_UART1_CORE, "uart1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_SPI0_APB, "spi0_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_SPI0_CORE, "spi0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_SPI1_APB, "spi1_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_SPI1_CORE, "spi1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_I2C0_APB, "i2c0_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_I2C0_CORE, "i2c0_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_I2C1_APB, "i2c1_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GDIV(JH7100_CLK_I2C1_CORE, "i2c1_core", 0, 63, JH7100_CLK_PERH1_SRC), + JH71X0_GATE(JH7100_CLK_GPIO_APB, "gpio_apb", 0, JH7100_CLK_APB1_BUS), + JH71X0_GATE(JH7100_CLK_UART2_APB, "uart2_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_UART2_CORE, "uart2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_UART3_APB, "uart3_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_UART3_CORE, "uart3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_SPI2_APB, "spi2_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_SPI2_CORE, "spi2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_SPI3_APB, "spi3_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_SPI3_CORE, "spi3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_I2C2_APB, "i2c2_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_I2C2_CORE, "i2c2_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_I2C3_APB, "i2c3_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_I2C3_CORE, "i2c3_core", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_WDTIMER_APB, "wdtimer_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_WDT_CORE, "wdt_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER0_CORE, "timer0_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER1_CORE, "timer1_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER2_CORE, "timer2_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER3_CORE, "timer3_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER4_CORE, "timer4_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER5_CORE, "timer5_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GDIV(JH7100_CLK_TIMER6_CORE, "timer6_coreclk", 0, 63, JH7100_CLK_PERH0_SRC), + JH71X0_GATE(JH7100_CLK_VP6INTC_APB, "vp6intc_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GATE(JH7100_CLK_PWM_APB, "pwm_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GATE(JH7100_CLK_MSI_APB, "msi_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GATE(JH7100_CLK_TEMP_APB, "temp_apb", 0, JH7100_CLK_APB2_BUS), + JH71X0_GDIV(JH7100_CLK_TEMP_SENSE, "temp_sense", 0, 31, JH7100_CLK_OSC_SYS), + JH71X0_GATE(JH7100_CLK_SYSERR_APB, "syserr_apb", 0, JH7100_CLK_APB2_BUS), }; -static struct jh7100_clk *jh7100_clk_from(struct clk_hw *hw) -{ - return container_of(hw, struct jh7100_clk, hw); -} - -static struct jh7100_clk_priv *jh7100_priv_from(struct jh7100_clk *clk) -{ - return container_of(clk, struct jh7100_clk_priv, reg[clk->idx]); -} - -static u32 jh7100_clk_reg_get(struct jh7100_clk *clk) -{ - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); - void __iomem *reg = priv->base + 4 * clk->idx; - - return readl_relaxed(reg); -} - -static void jh7100_clk_reg_rmw(struct jh7100_clk *clk, u32 mask, u32 value) -{ - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); - void __iomem *reg = priv->base + 4 * clk->idx; - unsigned long flags; - - spin_lock_irqsave(&priv->rmw_lock, flags); - value |= readl_relaxed(reg) & ~mask; - writel_relaxed(value, reg); - spin_unlock_irqrestore(&priv->rmw_lock, flags); -} - -static int jh7100_clk_enable(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, JH7100_CLK_ENABLE); - return 0; -} - -static void jh7100_clk_disable(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_ENABLE, 0); -} - -static int jh7100_clk_is_enabled(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - - return !!(jh7100_clk_reg_get(clk) & JH7100_CLK_ENABLE); -} - -static unsigned long jh7100_clk_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 div = jh7100_clk_reg_get(clk) & JH7100_CLK_DIV_MASK; - - return div ? parent_rate / div : 0; -} - -static int jh7100_clk_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long parent = req->best_parent_rate; - unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); - unsigned long div = min_t(unsigned long, DIV_ROUND_UP(parent, rate), clk->max_div); - unsigned long result = parent / div; - - /* - * we want the result clamped by min_rate and max_rate if possible: - * case 1: div hits the max divider value, which means it's less than - * parent / rate, so the result is greater than rate and min_rate in - * particular. we can't do anything about result > max_rate because the - * divider doesn't go any further. - * case 2: div = DIV_ROUND_UP(parent, rate) which means the result is - * always lower or equal to rate and max_rate. however the result may - * turn out lower than min_rate, but then the next higher rate is fine: - * div - 1 = ceil(parent / rate) - 1 < parent / rate - * and thus - * min_rate <= rate < parent / (div - 1) - */ - if (result < req->min_rate && div > 1) - result = parent / (div - 1); - - req->rate = result; - return 0; -} - -static int jh7100_clk_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate), - 1UL, (unsigned long)clk->max_div); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, div); - return 0; -} - -static unsigned long jh7100_clk_frac_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 reg = jh7100_clk_reg_get(clk); - unsigned long div100 = 100 * (reg & JH7100_CLK_INT_MASK) + - ((reg & JH7100_CLK_FRAC_MASK) >> JH7100_CLK_FRAC_SHIFT); - - return (div100 >= JH7100_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; -} - -static int jh7100_clk_frac_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - unsigned long parent100 = 100 * req->best_parent_rate; - unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); - unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), - JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); - unsigned long result = parent100 / div100; - - /* clamp the result as in jh7100_clk_determine_rate() above */ - if (result > req->max_rate && div100 < JH7100_CLK_FRAC_MAX) - result = parent100 / (div100 + 1); - if (result < req->min_rate && div100 > JH7100_CLK_FRAC_MIN) - result = parent100 / (div100 - 1); - - req->rate = result; - return 0; -} - -static int jh7100_clk_frac_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), - JH7100_CLK_FRAC_MIN, JH7100_CLK_FRAC_MAX); - u32 value = ((div100 % 100) << JH7100_CLK_FRAC_SHIFT) | (div100 / 100); - - jh7100_clk_reg_rmw(clk, JH7100_CLK_DIV_MASK, value); - return 0; -} - -static u8 jh7100_clk_get_parent(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = jh7100_clk_reg_get(clk); - - return (value & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT; -} - -static int jh7100_clk_set_parent(struct clk_hw *hw, u8 index) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = (u32)index << JH7100_CLK_MUX_SHIFT; - - jh7100_clk_reg_rmw(clk, JH7100_CLK_MUX_MASK, value); - return 0; -} - -static int jh7100_clk_mux_determine_rate(struct clk_hw *hw, - struct clk_rate_request *req) -{ - return clk_mux_determine_rate_flags(hw, req, 0); -} - -static int jh7100_clk_get_phase(struct clk_hw *hw) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value = jh7100_clk_reg_get(clk); - - return (value & JH7100_CLK_INVERT) ? 180 : 0; -} - -static int jh7100_clk_set_phase(struct clk_hw *hw, int degrees) -{ - struct jh7100_clk *clk = jh7100_clk_from(hw); - u32 value; - - if (degrees == 0) - value = 0; - else if (degrees == 180) - value = JH7100_CLK_INVERT; - else - return -EINVAL; - - jh7100_clk_reg_rmw(clk, JH7100_CLK_INVERT, value); - return 0; -} - -#ifdef CONFIG_DEBUG_FS -static void jh7100_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) -{ - static const struct debugfs_reg32 jh7100_clk_reg = { - .name = "CTRL", - .offset = 0, - }; - struct jh7100_clk *clk = jh7100_clk_from(hw); - struct jh7100_clk_priv *priv = jh7100_priv_from(clk); - struct debugfs_regset32 *regset; - - regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); - if (!regset) - return; - - regset->regs = &jh7100_clk_reg; - regset->nregs = 1; - regset->base = priv->base + 4 * clk->idx; - - debugfs_create_regset32("registers", 0400, dentry, regset); -} -#else -#define jh7100_clk_debug_init NULL -#endif - -static const struct clk_ops jh7100_clk_gate_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_div_ops = { - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_fdiv_ops = { - .recalc_rate = jh7100_clk_frac_recalc_rate, - .determine_rate = jh7100_clk_frac_determine_rate, - .set_rate = jh7100_clk_frac_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_gdiv_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_mux_ops = { - .determine_rate = jh7100_clk_mux_determine_rate, - .set_parent = jh7100_clk_set_parent, - .get_parent = jh7100_clk_get_parent, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_gmux_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .determine_rate = jh7100_clk_mux_determine_rate, - .set_parent = jh7100_clk_set_parent, - .get_parent = jh7100_clk_get_parent, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_mdiv_ops = { - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .get_parent = jh7100_clk_get_parent, - .set_parent = jh7100_clk_set_parent, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_gmd_ops = { - .enable = jh7100_clk_enable, - .disable = jh7100_clk_disable, - .is_enabled = jh7100_clk_is_enabled, - .recalc_rate = jh7100_clk_recalc_rate, - .determine_rate = jh7100_clk_determine_rate, - .get_parent = jh7100_clk_get_parent, - .set_parent = jh7100_clk_set_parent, - .set_rate = jh7100_clk_set_rate, - .debug_init = jh7100_clk_debug_init, -}; - -static const struct clk_ops jh7100_clk_inv_ops = { - .get_phase = jh7100_clk_get_phase, - .set_phase = jh7100_clk_set_phase, - .debug_init = jh7100_clk_debug_init, -}; - -const struct clk_ops *starfive_jh7100_clk_ops(u32 max) -{ - if (max & JH7100_CLK_DIV_MASK) { - if (max & JH7100_CLK_MUX_MASK) { - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gmd_ops; - return &jh7100_clk_mdiv_ops; - } - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gdiv_ops; - if (max == JH7100_CLK_FRAC_MAX) - return &jh7100_clk_fdiv_ops; - return &jh7100_clk_div_ops; - } - - if (max & JH7100_CLK_MUX_MASK) { - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gmux_ops; - return &jh7100_clk_mux_ops; - } - - if (max & JH7100_CLK_ENABLE) - return &jh7100_clk_gate_ops; - - return &jh7100_clk_inv_ops; -} -EXPORT_SYMBOL_GPL(starfive_jh7100_clk_ops); - static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data) { - struct jh7100_clk_priv *priv = data; + struct jh71x0_clk_priv *priv = data; unsigned int idx = clkspec->args[0]; if (idx < JH7100_CLK_PLL0_OUT) @@ -605,7 +283,7 @@ static struct clk_hw *jh7100_clk_get(struct of_phandle_args *clkspec, void *data static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) { - struct jh7100_clk_priv *priv; + struct jh71x0_clk_priv *priv; unsigned int idx; int ret; @@ -639,12 +317,12 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) struct clk_parent_data parents[4] = {}; struct clk_init_data init = { .name = jh7100_clk_data[idx].name, - .ops = starfive_jh7100_clk_ops(max), + .ops = starfive_jh71x0_clk_ops(max), .parent_data = parents, - .num_parents = ((max & JH7100_CLK_MUX_MASK) >> JH7100_CLK_MUX_SHIFT) + 1, + .num_parents = ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, .flags = jh7100_clk_data[idx].flags, }; - struct jh7100_clk *clk = &priv->reg[idx]; + struct jh71x0_clk *clk = &priv->reg[idx]; unsigned int i; for (i = 0; i < init.num_parents; i++) { @@ -666,7 +344,7 @@ static int __init clk_starfive_jh7100_probe(struct platform_device *pdev) clk->hw.init = &init; clk->idx = idx; - clk->max_div = max & JH7100_CLK_DIV_MASK; + clk->max_div = max & JH71X0_CLK_DIV_MASK; ret = devm_clk_hw_register(priv->dev, &clk->hw); if (ret) diff --git a/drivers/clk/starfive/clk-starfive-jh7100.h b/drivers/clk/starfive/clk-starfive-jh7100.h deleted file mode 100644 index f116be5740a5f..0000000000000 --- a/drivers/clk/starfive/clk-starfive-jh7100.h +++ /dev/null @@ -1,112 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __CLK_STARFIVE_JH7100_H -#define __CLK_STARFIVE_JH7100_H - -#include -#include - -/* register fields */ -#define JH7100_CLK_ENABLE BIT(31) -#define JH7100_CLK_INVERT BIT(30) -#define JH7100_CLK_MUX_MASK GENMASK(27, 24) -#define JH7100_CLK_MUX_SHIFT 24 -#define JH7100_CLK_DIV_MASK GENMASK(23, 0) -#define JH7100_CLK_FRAC_MASK GENMASK(15, 8) -#define JH7100_CLK_FRAC_SHIFT 8 -#define JH7100_CLK_INT_MASK GENMASK(7, 0) - -/* fractional divider min/max */ -#define JH7100_CLK_FRAC_MIN 100UL -#define JH7100_CLK_FRAC_MAX 25599UL - -/* clock data */ -struct jh7100_clk_data { - const char *name; - unsigned long flags; - u32 max; - u8 parents[4]; -}; - -#define JH7100_GATE(_idx, _name, _flags, _parent) [_idx] = { \ - .name = _name, \ - .flags = CLK_SET_RATE_PARENT | (_flags), \ - .max = JH7100_CLK_ENABLE, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100__DIV(_idx, _name, _max, _parent) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = _max, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100_GDIV(_idx, _name, _flags, _max, _parent) [_idx] = { \ - .name = _name, \ - .flags = _flags, \ - .max = JH7100_CLK_ENABLE | (_max), \ - .parents = { [0] = _parent }, \ -} - -#define JH7100_FDIV(_idx, _name, _parent) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = JH7100_CLK_FRAC_MAX, \ - .parents = { [0] = _parent }, \ -} - -#define JH7100__MUX(_idx, _name, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = ((_nparents) - 1) << JH7100_CLK_MUX_SHIFT, \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100_GMUX(_idx, _name, _flags, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = _flags, \ - .max = JH7100_CLK_ENABLE | \ - (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT), \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100_MDIV(_idx, _name, _max, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = 0, \ - .max = (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100__GMD(_idx, _name, _flags, _max, _nparents, ...) [_idx] = { \ - .name = _name, \ - .flags = _flags, \ - .max = JH7100_CLK_ENABLE | \ - (((_nparents) - 1) << JH7100_CLK_MUX_SHIFT) | (_max), \ - .parents = { __VA_ARGS__ }, \ -} - -#define JH7100__INV(_idx, _name, _parent) [_idx] = { \ - .name = _name, \ - .flags = CLK_SET_RATE_PARENT, \ - .max = JH7100_CLK_INVERT, \ - .parents = { [0] = _parent }, \ -} - -struct jh7100_clk { - struct clk_hw hw; - unsigned int idx; - unsigned int max_div; -}; - -struct jh7100_clk_priv { - /* protect clk enable and set rate/parent from happening at the same time */ - spinlock_t rmw_lock; - struct device *dev; - void __iomem *base; - struct clk_hw *pll[3]; - struct jh7100_clk reg[]; -}; - -const struct clk_ops *starfive_jh7100_clk_ops(u32 max); - -#endif diff --git a/drivers/clk/starfive/clk-starfive-jh7110-aon.c b/drivers/clk/starfive/clk-starfive-jh7110-aon.c new file mode 100644 index 0000000000000..941f1189fe5da --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-aon.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Always-On Clock Driver + * + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_AONCLK_OSC (JH7110_AONCLK_END + 0) +#define JH7110_AONCLK_GMAC0_RMII_REFIN (JH7110_AONCLK_END + 1) +#define JH7110_AONCLK_GMAC0_RGMII_RXIN (JH7110_AONCLK_END + 2) +#define JH7110_AONCLK_STG_AXIAHB (JH7110_AONCLK_END + 3) +#define JH7110_AONCLK_APB_BUS (JH7110_AONCLK_END + 4) +#define JH7110_AONCLK_GMAC0_GTXCLK (JH7110_AONCLK_END + 5) +#define JH7110_AONCLK_RTC_OSC (JH7110_AONCLK_END + 6) + +static const struct jh71x0_clk_data jh7110_aonclk_data[] = { + /* source */ + JH71X0__DIV(JH7110_AONCLK_OSC_DIV4, "osc_div4", 4, JH7110_AONCLK_OSC), + JH71X0__MUX(JH7110_AONCLK_APB_FUNC, "apb_func", 2, + JH7110_AONCLK_OSC_DIV4, + JH7110_AONCLK_OSC), + /* gmac0 */ + JH71X0_GATE(JH7110_AONCLK_GMAC0_AHB, "gmac0_ahb", 0, JH7110_AONCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_AONCLK_GMAC0_AXI, "gmac0_axi", 0, JH7110_AONCLK_STG_AXIAHB), + JH71X0__DIV(JH7110_AONCLK_GMAC0_RMII_RTX, "gmac0_rmii_rtx", 30, + JH7110_AONCLK_GMAC0_RMII_REFIN), + JH71X0_GMUX(JH7110_AONCLK_GMAC0_TX, "gmac0_tx", CLK_SET_RATE_PARENT, 2, + JH7110_AONCLK_GMAC0_GTXCLK, + JH7110_AONCLK_GMAC0_RMII_RTX), + JH71X0__INV(JH7110_AONCLK_GMAC0_TX_INV, "gmac0_tx_inv", JH7110_AONCLK_GMAC0_TX), + JH71X0__MUX(JH7110_AONCLK_GMAC0_RX, "gmac0_rx", 2, + JH7110_AONCLK_GMAC0_RGMII_RXIN, + JH7110_AONCLK_GMAC0_RMII_RTX), + JH71X0__INV(JH7110_AONCLK_GMAC0_RX_INV, "gmac0_rx_inv", JH7110_AONCLK_GMAC0_RX), + /* otpc */ + JH71X0_GATE(JH7110_AONCLK_OTPC_APB, "otpc_apb", 0, JH7110_AONCLK_APB_BUS), + /* rtc */ + JH71X0_GATE(JH7110_AONCLK_RTC_APB, "rtc_apb", 0, JH7110_AONCLK_APB_BUS), + JH71X0__DIV(JH7110_AONCLK_RTC_INTERNAL, "rtc_internal", 1022, JH7110_AONCLK_OSC), + JH71X0__MUX(JH7110_AONCLK_RTC_32K, "rtc_32k", 2, + JH7110_AONCLK_RTC_OSC, + JH7110_AONCLK_RTC_INTERNAL), + JH71X0_GATE(JH7110_AONCLK_RTC_CAL, "rtc_cal", 0, JH7110_AONCLK_OSC), +}; + +static struct clk_hw *jh7110_aonclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_AONCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_aoncrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_AONCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + dev_set_drvdata(priv->dev, (void *)(&priv->base)); + + for (idx = 0; idx < JH7110_AONCLK_END; idx++) { + u32 max = jh7110_aonclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_aonclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_aonclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_aonclk_data[idx].parents[i]; + + if (pidx < JH7110_AONCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx == JH7110_AONCLK_OSC) + parents[i].fw_name = "osc"; + else if (pidx == JH7110_AONCLK_GMAC0_RMII_REFIN) + parents[i].fw_name = "gmac0_rmii_refin"; + else if (pidx == JH7110_AONCLK_GMAC0_RGMII_RXIN) + parents[i].fw_name = "gmac0_rgmii_rxin"; + else if (pidx == JH7110_AONCLK_STG_AXIAHB) + parents[i].fw_name = "stg_axiahb"; + else if (pidx == JH7110_AONCLK_APB_BUS) + parents[i].fw_name = "apb_bus"; + else if (pidx == JH7110_AONCLK_GMAC0_GTXCLK) + parents[i].fw_name = "gmac0_gtxclk"; + else if (pidx == JH7110_AONCLK_RTC_OSC) + parents[i].fw_name = "rtc_osc"; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_aonclk_get, priv); + if (ret) + return ret; + + return jh7110_reset_controller_register(priv, "reset-aon", 1); +} + +static const struct of_device_id jh7110_aoncrg_match[] = { + { .compatible = "starfive,jh7110-aoncrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_aoncrg_match); + +static struct platform_driver jh7110_aoncrg_driver = { + .probe = jh7110_aoncrg_probe, + .driver = { + .name = "clk-starfive-jh7110-aon", + .of_match_table = jh7110_aoncrg_match, + }, +}; +module_platform_driver(jh7110_aoncrg_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH7110 always-on clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/starfive/clk-starfive-jh7110-isp.c b/drivers/clk/starfive/clk-starfive-jh7110-isp.c new file mode 100644 index 0000000000000..8e372dc423d8b --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-isp.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Image-Signal-Process Clock Driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_ISPCLK_ISP_TOP_CORE (JH7110_ISPCLK_END + 0) +#define JH7110_ISPCLK_ISP_TOP_AXI (JH7110_ISPCLK_END + 1) +#define JH7110_ISPCLK_NOC_BUS_ISP_AXI (JH7110_ISPCLK_END + 2) +#define JH7110_ISPCLK_DVP_CLK (JH7110_ISPCLK_END + 3) +#define JH7110_ISPCLK_EXT_END (JH7110_ISPCLK_END + 4) + +/* ISP domian clocks */ +struct isp_top_crg { + struct clk_bulk_data *top_clks; + int top_clks_num; + void __iomem *base; +}; + +static struct clk_bulk_data jh7110_isp_top_clks[] = { + { .id = "isp_top_core" }, + { .id = "isp_top_axi" } +}; + +static const struct jh71x0_clk_data jh7110_ispclk_data[] = { + /* syscon */ + JH71X0__DIV(JH7110_ISPCLK_DOM4_APB_FUNC, "dom4_apb_func", 15, + JH7110_ISPCLK_ISP_TOP_AXI), + JH71X0__DIV(JH7110_ISPCLK_MIPI_RX0_PXL, "mipi_rx0_pxl", 8, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__INV(JH7110_ISPCLK_DVP_INV, "dvp_inv", JH7110_ISPCLK_DVP_CLK), + /* vin */ + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_CFG_IN, "m31dphy_cfg_in", 16, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_REF_IN, "m31dphy_ref_in", 16, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0__DIV(JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0, "m31dphy_tx_esc_lan0", 60, + JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0_GATE(JH7110_ISPCLK_VIN_ABP, "vin_abp", 0, + JH7110_ISPCLK_DOM4_APB_FUNC), + JH71X0__DIV(JH7110_ISPCLK_VIN_SYS, "vin_sys", 8, JH7110_ISPCLK_ISP_TOP_CORE), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF0, "vin_pixel_if0", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF1, "vin_pixel_if1", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF2, "vin_pixel_if2", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0_GATE(JH7110_ISPCLK_VIN_PIXEL_IF3, "vin_pixel_if3", 0, + JH7110_ISPCLK_MIPI_RX0_PXL), + JH71X0__MUX(JH7110_ISPCLK_VIN_P_AXI_WR, "vin_p_axi_wr", 2, + JH7110_ISPCLK_MIPI_RX0_PXL, + JH7110_ISPCLK_DVP_INV), + /* ispv2_top_wrapper */ + JH71X0_GMUX(JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C, "ispv2_top_wrapper_c", 0, 2, + JH7110_ISPCLK_MIPI_RX0_PXL, + JH7110_ISPCLK_DVP_INV), +}; + +static struct isp_top_crg *top_crg_from(void __iomem **base) +{ + return container_of(base, struct isp_top_crg, base); +} + +static inline int jh7110_isp_top_crg_init(struct jh71x0_clk_priv *priv, struct isp_top_crg *top) +{ + struct reset_control *top_rsts; + int ret; + + top->top_clks = jh7110_isp_top_clks; + top->top_clks_num = ARRAY_SIZE(jh7110_isp_top_clks); + ret = devm_clk_bulk_get(priv->dev, top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to get top clocks\n"); + + /* The resets should be shared and other ISP modules will use its. */ + top_rsts = devm_reset_control_array_get_shared(priv->dev); + if (IS_ERR(top_rsts)) + return dev_err_probe(priv->dev, PTR_ERR(top_rsts), + "failed to get top resets\n"); + + ret = clk_bulk_prepare_enable(top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to enable top clocks\n"); + + return reset_control_deassert(top_rsts); +} + +static struct clk_hw *jh7110_ispclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_ISPCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_ispcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + struct isp_top_crg *top; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_ISPCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + top = devm_kzalloc(&pdev->dev, sizeof(*top), GFP_KERNEL); + if (!top) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + pm_runtime_enable(priv->dev); + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "failed to turn on power\n"); + + ret = jh7110_isp_top_crg_init(priv, top); + if (ret) + goto err_clk; + + top->base = priv->base; + dev_set_drvdata(priv->dev, (void *)(&top->base)); + + for (idx = 0; idx < JH7110_ISPCLK_END; idx++) { + u32 max = jh7110_ispclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_ispclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_ispclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + const char *fw_name[JH7110_ISPCLK_EXT_END - JH7110_ISPCLK_END] = { + "isp_top_core", + "isp_top_axi", + "noc_bus_isp_axi", + "dvp_clk" + }; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_ispclk_data[idx].parents[i]; + + if (pidx < JH7110_ISPCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else + parents[i].fw_name = fw_name[pidx - JH7110_ISPCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + goto err_exit; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_ispclk_get, priv); + if (ret) + goto err_exit; + + ret = jh7110_reset_controller_register(priv, "reset-isp", 3); + if (ret) + goto err_exit; + + return 0; + +err_exit: + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); +err_clk: + pm_runtime_put_sync(priv->dev); + pm_runtime_disable(priv->dev); + return ret; +} + +static int jh7110_ispcrg_remove(struct platform_device *pdev) +{ + void __iomem **base = dev_get_drvdata(&pdev->dev); + struct isp_top_crg *top = top_crg_from(base); + + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id jh7110_ispcrg_match[] = { + { .compatible = "starfive,jh7110-ispcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_ispcrg_match); + +static struct platform_driver jh7110_ispcrg_driver = { + .probe = jh7110_ispcrg_probe, + .remove = jh7110_ispcrg_remove, + .driver = { + .name = "clk-starfive-jh7110-isp", + .of_match_table = jh7110_ispcrg_match, + }, +}; +module_platform_driver(jh7110_ispcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive JH7110 Image-Signal-Process clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/starfive/clk-starfive-jh7110-pll.c b/drivers/clk/starfive/clk-starfive-jh7110-pll.c new file mode 100644 index 0000000000000..b947861065db5 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-pll.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 PLL Clock Generator Driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * Copyright (C) 2022 Xingyu Wu + * + * This driver is about to register JH7110 PLL clock generator and support ops. + * The JH7110 have three PLL clock, PLL0, PLL1 and PLL2. + * Each PLL clocks work in integer mode or fraction mode by some dividers, + * and the configuration registers and dividers are set in several syscon registers. + * The formula for calculating frequency is: + * Fvco = Fref * (NI + NF) / M / Q1 + * Fref: OSC source clock rate + * NI: integer frequency dividing ratio of feedback divider, set by fbdiv[11:0]. + * NF: fractional frequency dividing ratio, set by frac[23:0]. NF = frac[23:0] / 2^24 = 0 ~ 0.999. + * M: frequency dividing ratio of pre-divider, set by prediv[5:0]. + * Q1: frequency dividing ratio of post divider, set by postdiv1[1:0], Q1= 1,2,4,8. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh7110-pll.h" + +static struct jh7110_clk_pll_data *jh7110_pll_data_from(struct clk_hw *hw) +{ + return container_of(hw, struct jh7110_clk_pll_data, hw); +} + +static struct jh7110_clk_pll_priv *jh7110_pll_priv_from(struct jh7110_clk_pll_data *data) +{ + return container_of(data, struct jh7110_clk_pll_priv, data[data->idx]); +} + +/* Read register value from syscon and calculate PLL(x) frequency */ +static unsigned long jh7110_pll_get_freq(struct jh7110_clk_pll_data *data, + unsigned long parent_rate) +{ + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + struct jh7110_pll_syscon_offset *offset = &data->offset; + struct jh7110_pll_syscon_mask *mask = &data->mask; + struct jh7110_pll_syscon_shift *shift = &data->shift; + unsigned long freq = 0; + unsigned long frac_cal; + u32 dacpd; + u32 dsmpd; + u32 fbdiv; + u32 prediv; + u32 postdiv1; + u32 frac; + u32 reg_value; + + if (regmap_read(priv->syscon_regmap, offset->dacpd, ®_value)) + goto read_register_error; + dacpd = (reg_value & mask->dacpd) >> shift->dacpd; + + if (regmap_read(priv->syscon_regmap, offset->dsmpd, ®_value)) + goto read_register_error; + dsmpd = (reg_value & mask->dsmpd) >> shift->dsmpd; + + if (regmap_read(priv->syscon_regmap, offset->fbdiv, ®_value)) + goto read_register_error; + fbdiv = (reg_value & mask->fbdiv) >> shift->fbdiv; + /* fbdiv value should be 8 to 4095 */ + if (fbdiv < 8) + goto read_register_error; + + if (regmap_read(priv->syscon_regmap, offset->prediv, ®_value)) + goto read_register_error; + prediv = (reg_value & mask->prediv) >> shift->prediv; + + if (regmap_read(priv->syscon_regmap, offset->postdiv1, ®_value)) + goto read_register_error; + /* postdiv1 = 2 ^ reg_value */ + postdiv1 = 1 << ((reg_value & mask->postdiv1) >> shift->postdiv1); + + if (regmap_read(priv->syscon_regmap, offset->frac, ®_value)) + goto read_register_error; + frac = (reg_value & mask->frac) >> shift->frac; + + /* + * Integer Mode (Both 1) or Fraction Mode (Both 0). + * And the decimal places are counted by expanding them by + * a factor of STARFIVE_PLL_FRAC_PATR_SIZE. + */ + if (dacpd == 1 && dsmpd == 1) + frac_cal = 0; + else if (dacpd == 0 && dsmpd == 0) + frac_cal = (unsigned long)frac * STARFIVE_PLL_FRAC_PATR_SIZE / (1 << 24); + else + goto read_register_error; + + if (frac_cal) + /* fraction mode: Fvco = Fref * (NI + NF) / M / Q1 */ + freq = parent_rate / STARFIVE_PLL_FRAC_PATR_SIZE * + (fbdiv * STARFIVE_PLL_FRAC_PATR_SIZE + frac_cal) / prediv / postdiv1; + else + /* integer mode: Fvco = Fref * NI / M / Q1 */ + freq = parent_rate * fbdiv / prediv / postdiv1; + +read_register_error: + return freq; +} + +/* Select the appropriate frequency from the already configured registers value */ +static int jh7110_pll_select_freq_syscon(struct jh7110_clk_pll_data *data, + unsigned long target_rate) +{ + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + const struct starfive_pll_syscon_value *syscon_value; + unsigned int id; + unsigned int pll_arry_size; + + if (data->idx == JH7110_CLK_PLL0_OUT) + pll_arry_size = ARRAY_SIZE(jh7110_pll0_syscon_freq); + else if (data->idx == JH7110_CLK_PLL1_OUT) + pll_arry_size = ARRAY_SIZE(jh7110_pll1_syscon_freq); + else + pll_arry_size = ARRAY_SIZE(jh7110_pll2_syscon_freq); + + for (id = 0; id < pll_arry_size; id++) { + if (data->idx == JH7110_CLK_PLL0_OUT) + syscon_value = &jh7110_pll0_syscon_freq[id]; + else if (data->idx == JH7110_CLK_PLL1_OUT) + syscon_value = &jh7110_pll1_syscon_freq[id]; + else + syscon_value = &jh7110_pll2_syscon_freq[id]; + + if (target_rate == syscon_value->freq) + goto select_succeed; + } + + dev_err(priv->dev, "pll%d frequency:%ld do not match, please check it.\n", + data->idx, target_rate); + return -EINVAL; + +select_succeed: + data->freq_select_idx = id; + return 0; +} + +static int jh7110_pll_set_freq_syscon(struct jh7110_clk_pll_data *data) +{ + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + struct jh7110_pll_syscon_offset *offset = &data->offset; + struct jh7110_pll_syscon_mask *mask = &data->mask; + struct jh7110_pll_syscon_shift *shift = &data->shift; + unsigned int freq_idx = data->freq_select_idx; + const struct starfive_pll_syscon_value *syscon_value; + int ret; + + if (data->idx == JH7110_CLK_PLL0_OUT) + syscon_value = &jh7110_pll0_syscon_freq[freq_idx]; + else if (data->idx == JH7110_CLK_PLL1_OUT) + syscon_value = &jh7110_pll1_syscon_freq[freq_idx]; + else + syscon_value = &jh7110_pll2_syscon_freq[freq_idx]; + + ret = regmap_update_bits(priv->syscon_regmap, offset->dacpd, mask->dacpd, + (syscon_value->dacpd << shift->dacpd)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->dsmpd, mask->dsmpd, + (syscon_value->dsmpd << shift->dsmpd)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->prediv, mask->prediv, + (syscon_value->prediv << shift->prediv)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->fbdiv, mask->fbdiv, + (syscon_value->fbdiv << shift->fbdiv)); + if (ret) + goto set_failed; + + ret = regmap_update_bits(priv->syscon_regmap, offset->postdiv1, mask->postdiv1, + ((syscon_value->postdiv1 >> 1) << shift->postdiv1)); + if (ret) + goto set_failed; + + /* frac: Integer Mode (Both 1) or Fraction Mode (Both 0) */ + if (syscon_value->dacpd == 0 && syscon_value->dsmpd == 0) + ret = regmap_update_bits(priv->syscon_regmap, offset->frac, mask->frac, + (syscon_value->frac << shift->frac)); + else if (syscon_value->dacpd != syscon_value->dsmpd) + ret = -EINVAL; + +set_failed: + return ret; +} + +static unsigned long jh7110_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + + return jh7110_pll_get_freq(data, parent_rate); +} + +static int jh7110_pll_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) +{ + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + int ret = jh7110_pll_select_freq_syscon(data, req->rate); + + if (ret) + return ret; + + if (data->idx == JH7110_CLK_PLL0_OUT) + req->rate = jh7110_pll0_syscon_freq[data->freq_select_idx].freq; + else if (data->idx == JH7110_CLK_PLL1_OUT) + req->rate = jh7110_pll1_syscon_freq[data->freq_select_idx].freq; + else + req->rate = jh7110_pll2_syscon_freq[data->freq_select_idx].freq; + + return 0; +} + +static int jh7110_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + + return jh7110_pll_set_freq_syscon(data); +} + +#ifdef CONFIG_DEBUG_FS +static void jh7110_pll_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + static const struct debugfs_reg32 jh7110_clk_pll_reg = { + .name = "CTRL", + .offset = 0, + }; + struct jh7110_clk_pll_data *data = jh7110_pll_data_from(hw); + struct jh7110_clk_pll_priv *priv = jh7110_pll_priv_from(data); + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = &jh7110_clk_pll_reg; + regset->nregs = 1; + + debugfs_create_regset32("registers", 0400, dentry, regset); +} +#else +#define jh7110_pll_debug_init NULL +#endif + +static const struct clk_ops jh7110_pll_ops = { + .recalc_rate = jh7110_pll_recalc_rate, + .determine_rate = jh7110_pll_determine_rate, + .set_rate = jh7110_pll_set_rate, + .debug_init = jh7110_pll_debug_init, +}; + +/* get offset, mask and shift of PLL(x) syscon */ +static int jh7110_pll_data_get(struct jh7110_clk_pll_data *data, int index) +{ + struct jh7110_pll_syscon_offset *offset = &data->offset; + struct jh7110_pll_syscon_mask *mask = &data->mask; + struct jh7110_pll_syscon_shift *shift = &data->shift; + + if (index == JH7110_CLK_PLL0_OUT) { + offset->dacpd = STARFIVE_JH7110_PLL0_DACPD_OFFSET; + offset->dsmpd = STARFIVE_JH7110_PLL0_DSMPD_OFFSET; + offset->fbdiv = STARFIVE_JH7110_PLL0_FBDIV_OFFSET; + offset->frac = STARFIVE_JH7110_PLL0_FRAC_OFFSET; + offset->prediv = STARFIVE_JH7110_PLL0_PREDIV_OFFSET; + offset->postdiv1 = STARFIVE_JH7110_PLL0_POSTDIV1_OFFSET; + + mask->dacpd = STARFIVE_JH7110_PLL0_DACPD_MASK; + mask->dsmpd = STARFIVE_JH7110_PLL0_DSMPD_MASK; + mask->fbdiv = STARFIVE_JH7110_PLL0_FBDIV_MASK; + mask->frac = STARFIVE_JH7110_PLL0_FRAC_MASK; + mask->prediv = STARFIVE_JH7110_PLL0_PREDIV_MASK; + mask->postdiv1 = STARFIVE_JH7110_PLL0_POSTDIV1_MASK; + + shift->dacpd = STARFIVE_JH7110_PLL0_DACPD_SHIFT; + shift->dsmpd = STARFIVE_JH7110_PLL0_DSMPD_SHIFT; + shift->fbdiv = STARFIVE_JH7110_PLL0_FBDIV_SHIFT; + shift->frac = STARFIVE_JH7110_PLL0_FRAC_SHIFT; + shift->prediv = STARFIVE_JH7110_PLL0_PREDIV_SHIFT; + shift->postdiv1 = STARFIVE_JH7110_PLL0_POSTDIV1_SHIFT; + + } else if (index == JH7110_CLK_PLL1_OUT) { + offset->dacpd = STARFIVE_JH7110_PLL1_DACPD_OFFSET; + offset->dsmpd = STARFIVE_JH7110_PLL1_DSMPD_OFFSET; + offset->fbdiv = STARFIVE_JH7110_PLL1_FBDIV_OFFSET; + offset->frac = STARFIVE_JH7110_PLL1_FRAC_OFFSET; + offset->prediv = STARFIVE_JH7110_PLL1_PREDIV_OFFSET; + offset->postdiv1 = STARFIVE_JH7110_PLL1_POSTDIV1_OFFSET; + + mask->dacpd = STARFIVE_JH7110_PLL1_DACPD_MASK; + mask->dsmpd = STARFIVE_JH7110_PLL1_DSMPD_MASK; + mask->fbdiv = STARFIVE_JH7110_PLL1_FBDIV_MASK; + mask->frac = STARFIVE_JH7110_PLL1_FRAC_MASK; + mask->prediv = STARFIVE_JH7110_PLL1_PREDIV_MASK; + mask->postdiv1 = STARFIVE_JH7110_PLL1_POSTDIV1_MASK; + + shift->dacpd = STARFIVE_JH7110_PLL1_DACPD_SHIFT; + shift->dsmpd = STARFIVE_JH7110_PLL1_DSMPD_SHIFT; + shift->fbdiv = STARFIVE_JH7110_PLL1_FBDIV_SHIFT; + shift->frac = STARFIVE_JH7110_PLL1_FRAC_SHIFT; + shift->prediv = STARFIVE_JH7110_PLL1_PREDIV_SHIFT; + shift->postdiv1 = STARFIVE_JH7110_PLL1_POSTDIV1_SHIFT; + + } else if (index == JH7110_CLK_PLL2_OUT) { + offset->dacpd = STARFIVE_JH7110_PLL2_DACPD_OFFSET; + offset->dsmpd = STARFIVE_JH7110_PLL2_DSMPD_OFFSET; + offset->fbdiv = STARFIVE_JH7110_PLL2_FBDIV_OFFSET; + offset->frac = STARFIVE_JH7110_PLL2_FRAC_OFFSET; + offset->prediv = STARFIVE_JH7110_PLL2_PREDIV_OFFSET; + offset->postdiv1 = STARFIVE_JH7110_PLL2_POSTDIV1_OFFSET; + + mask->dacpd = STARFIVE_JH7110_PLL2_DACPD_MASK; + mask->dsmpd = STARFIVE_JH7110_PLL2_DSMPD_MASK; + mask->fbdiv = STARFIVE_JH7110_PLL2_FBDIV_MASK; + mask->frac = STARFIVE_JH7110_PLL2_FRAC_MASK; + mask->prediv = STARFIVE_JH7110_PLL2_PREDIV_MASK; + mask->postdiv1 = STARFIVE_JH7110_PLL2_POSTDIV1_MASK; + + shift->dacpd = STARFIVE_JH7110_PLL2_DACPD_SHIFT; + shift->dsmpd = STARFIVE_JH7110_PLL2_DSMPD_SHIFT; + shift->fbdiv = STARFIVE_JH7110_PLL2_FBDIV_SHIFT; + shift->frac = STARFIVE_JH7110_PLL2_FRAC_SHIFT; + shift->prediv = STARFIVE_JH7110_PLL2_PREDIV_SHIFT; + shift->postdiv1 = STARFIVE_JH7110_PLL2_POSTDIV1_SHIFT; + + } else { + return -ENOENT; + } + + return 0; +} + +static struct clk_hw *jh7110_pll_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh7110_clk_pll_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_PLLCLK_END) + return &priv->data[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_pll_probe(struct platform_device *pdev) +{ + const char *pll_name[JH7110_PLLCLK_END] = { + "pll0_out", + "pll1_out", + "pll2_out" + }; + struct jh7110_clk_pll_priv *priv; + struct jh7110_clk_pll_data *data; + int ret; + unsigned int idx; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, data, JH7110_PLLCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + priv->syscon_regmap = syscon_node_to_regmap(priv->dev->of_node->parent); + if (IS_ERR(priv->syscon_regmap)) + return PTR_ERR(priv->syscon_regmap); + + for (idx = 0; idx < JH7110_PLLCLK_END; idx++) { + struct clk_parent_data parents = { + .index = 0, + }; + struct clk_init_data init = { + .name = pll_name[idx], + .ops = &jh7110_pll_ops, + .parent_data = &parents, + .num_parents = 1, + .flags = 0, + }; + + data = &priv->data[idx]; + + ret = jh7110_pll_data_get(data, idx); + if (ret) + return ret; + + data->hw.init = &init; + data->idx = idx; + + ret = devm_clk_hw_register(&pdev->dev, &data->hw); + if (ret) + return ret; + } + + return devm_of_clk_add_hw_provider(&pdev->dev, jh7110_pll_get, priv); +} + +static const struct of_device_id jh7110_pll_match[] = { + { .compatible = "starfive,jh7110-pll" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_pll_match); + +static struct platform_driver jh7110_pll_driver = { + .driver = { + .name = "clk-starfive-jh7110-pll", + .of_match_table = jh7110_pll_match, + }, +}; +builtin_platform_driver_probe(jh7110_pll_driver, jh7110_pll_probe); diff --git a/drivers/clk/starfive/clk-starfive-jh7110-pll.h b/drivers/clk/starfive/clk-starfive-jh7110-pll.h new file mode 100644 index 0000000000000..3deb35f144dc1 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-pll.h @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * StarFive JH7110 PLL Clock Generator Driver + * + * Copyright (C) 2022 Xingyu Wu + */ + +#ifndef _CLK_STARFIVE_JH7110_PLL_H_ +#define _CLK_STARFIVE_JH7110_PLL_H_ + +#include + +/* The decimal places are counted by expanding them by a factor of STARFIVE_PLL_FRAC_PATR_SIZE. */ +#define STARFIVE_PLL_FRAC_PATR_SIZE 1000 + +#define STARFIVE_JH7110_PLL0_DACPD_OFFSET 0x18 +#define STARFIVE_JH7110_PLL0_DACPD_SHIFT 24 +#define STARFIVE_JH7110_PLL0_DACPD_MASK BIT(24) +#define STARFIVE_JH7110_PLL0_DSMPD_OFFSET 0x18 +#define STARFIVE_JH7110_PLL0_DSMPD_SHIFT 25 +#define STARFIVE_JH7110_PLL0_DSMPD_MASK BIT(25) +#define STARFIVE_JH7110_PLL0_FBDIV_OFFSET 0x1c +#define STARFIVE_JH7110_PLL0_FBDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL0_FBDIV_MASK GENMASK(11, 0) +#define STARFIVE_JH7110_PLL0_FRAC_OFFSET 0x20 +#define STARFIVE_JH7110_PLL0_FRAC_SHIFT 0 +#define STARFIVE_JH7110_PLL0_FRAC_MASK GENMASK(23, 0) +#define STARFIVE_JH7110_PLL0_POSTDIV1_OFFSET 0x20 +#define STARFIVE_JH7110_PLL0_POSTDIV1_SHIFT 28 +#define STARFIVE_JH7110_PLL0_POSTDIV1_MASK GENMASK(29, 28) +#define STARFIVE_JH7110_PLL0_PREDIV_OFFSET 0x24 +#define STARFIVE_JH7110_PLL0_PREDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL0_PREDIV_MASK GENMASK(5, 0) + +#define STARFIVE_JH7110_PLL1_DACPD_OFFSET 0x24 +#define STARFIVE_JH7110_PLL1_DACPD_SHIFT 15 +#define STARFIVE_JH7110_PLL1_DACPD_MASK BIT(15) +#define STARFIVE_JH7110_PLL1_DSMPD_OFFSET 0x24 +#define STARFIVE_JH7110_PLL1_DSMPD_SHIFT 16 +#define STARFIVE_JH7110_PLL1_DSMPD_MASK BIT(16) +#define STARFIVE_JH7110_PLL1_FBDIV_OFFSET 0x24 +#define STARFIVE_JH7110_PLL1_FBDIV_SHIFT 17 +#define STARFIVE_JH7110_PLL1_FBDIV_MASK GENMASK(28, 17) +#define STARFIVE_JH7110_PLL1_FRAC_OFFSET 0x28 +#define STARFIVE_JH7110_PLL1_FRAC_SHIFT 0 +#define STARFIVE_JH7110_PLL1_FRAC_MASK GENMASK(23, 0) +#define STARFIVE_JH7110_PLL1_POSTDIV1_OFFSET 0x28 +#define STARFIVE_JH7110_PLL1_POSTDIV1_SHIFT 28 +#define STARFIVE_JH7110_PLL1_POSTDIV1_MASK GENMASK(29, 28) +#define STARFIVE_JH7110_PLL1_PREDIV_OFFSET 0x2c +#define STARFIVE_JH7110_PLL1_PREDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL1_PREDIV_MASK GENMASK(5, 0) + +#define STARFIVE_JH7110_PLL2_DACPD_OFFSET 0x2c +#define STARFIVE_JH7110_PLL2_DACPD_SHIFT 15 +#define STARFIVE_JH7110_PLL2_DACPD_MASK BIT(15) +#define STARFIVE_JH7110_PLL2_DSMPD_OFFSET 0x2c +#define STARFIVE_JH7110_PLL2_DSMPD_SHIFT 16 +#define STARFIVE_JH7110_PLL2_DSMPD_MASK BIT(16) +#define STARFIVE_JH7110_PLL2_FBDIV_OFFSET 0x2c +#define STARFIVE_JH7110_PLL2_FBDIV_SHIFT 17 +#define STARFIVE_JH7110_PLL2_FBDIV_MASK GENMASK(28, 17) +#define STARFIVE_JH7110_PLL2_FRAC_OFFSET 0x30 +#define STARFIVE_JH7110_PLL2_FRAC_SHIFT 0 +#define STARFIVE_JH7110_PLL2_FRAC_MASK GENMASK(23, 0) +#define STARFIVE_JH7110_PLL2_POSTDIV1_OFFSET 0x30 +#define STARFIVE_JH7110_PLL2_POSTDIV1_SHIFT 28 +#define STARFIVE_JH7110_PLL2_POSTDIV1_MASK GENMASK(29, 28) +#define STARFIVE_JH7110_PLL2_PREDIV_OFFSET 0x34 +#define STARFIVE_JH7110_PLL2_PREDIV_SHIFT 0 +#define STARFIVE_JH7110_PLL2_PREDIV_MASK GENMASK(5, 0) + +struct jh7110_pll_syscon_offset { + unsigned int dacpd; + unsigned int dsmpd; + unsigned int fbdiv; + unsigned int frac; + unsigned int prediv; + unsigned int postdiv1; +}; + +struct jh7110_pll_syscon_mask { + u32 dacpd; + u32 dsmpd; + u32 fbdiv; + u32 frac; + u32 prediv; + u32 postdiv1; +}; + +struct jh7110_pll_syscon_shift { + char dacpd; + char dsmpd; + char fbdiv; + char frac; + char prediv; + char postdiv1; +}; + +struct jh7110_clk_pll_data { + struct clk_hw hw; + unsigned int idx; + unsigned int freq_select_idx; + + struct jh7110_pll_syscon_offset offset; + struct jh7110_pll_syscon_mask mask; + struct jh7110_pll_syscon_shift shift; +}; + +struct jh7110_clk_pll_priv { + struct device *dev; + struct regmap *syscon_regmap; + struct jh7110_clk_pll_data data[]; +}; + +struct starfive_pll_syscon_value { + unsigned long freq; + u32 prediv; + u32 fbdiv; + u32 postdiv1; +/* Both daxpd and dsmpd set 1 while integer multiple mode */ +/* Both daxpd and dsmpd set 0 while fraction multiple mode */ + u32 dacpd; + u32 dsmpd; +/* frac value should be decimals multiplied by 2^24 */ + u32 frac; +}; + +enum starfive_pll0_freq_index { + PLL0_FREQ_375 = 0, + PLL0_FREQ_500, + PLL0_FREQ_625, + PLL0_FREQ_750, + PLL0_FREQ_875, + PLL0_FREQ_1000, + PLL0_FREQ_1250, + PLL0_FREQ_1375, + PLL0_FREQ_1500, + PLL0_FREQ_MAX +}; + +enum starfive_pll1_freq_index { + PLL1_FREQ_1066 = 0, + PLL1_FREQ_1200, + PLL1_FREQ_1400, + PLL1_FREQ_1600, + PLL1_FREQ_MAX +}; + +enum starfive_pll2_freq_index { + PLL2_FREQ_1188 = 0, + PLL2_FREQ_12288, + PLL2_FREQ_MAX +}; + +/* + * Because the pll frequency is relatively fixed, + * it cannot be set arbitrarily, so it needs a specific configuration. + * PLL0 frequency should be multiple of 125MHz (USB frequency). + */ +static const struct starfive_pll_syscon_value + jh7110_pll0_syscon_freq[] = { + [PLL0_FREQ_375] = { + .freq = 375000000, + .prediv = 8, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_500] = { + .freq = 500000000, + .prediv = 6, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_625] = { + .freq = 625000000, + .prediv = 24, + .fbdiv = 625, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_750] = { + .freq = 750000000, + .prediv = 4, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_875] = { + .freq = 875000000, + .prediv = 24, + .fbdiv = 875, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1000] = { + .freq = 1000000000, + .prediv = 3, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1250] = { + .freq = 1250000000, + .prediv = 12, + .fbdiv = 625, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1375] = { + .freq = 1375000000, + .prediv = 24, + .fbdiv = 1375, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL0_FREQ_1500] = { + .freq = 1500000000, + .prediv = 2, + .fbdiv = 125, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, +}; + +static const struct starfive_pll_syscon_value + jh7110_pll1_syscon_freq[] = { + [PLL1_FREQ_1066] = { + .freq = 1066000000, + .prediv = 12, + .fbdiv = 533, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL1_FREQ_1200] = { + .freq = 1200000000, + .prediv = 1, + .fbdiv = 50, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL1_FREQ_1400] = { + .freq = 1400000000, + .prediv = 6, + .fbdiv = 350, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL1_FREQ_1600] = { + .freq = 1600000000, + .prediv = 3, + .fbdiv = 200, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, +}; + +static const struct starfive_pll_syscon_value + jh7110_pll2_syscon_freq[] = { + [PLL2_FREQ_1188] = { + .freq = 1188000000, + .prediv = 2, + .fbdiv = 99, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, + [PLL2_FREQ_12288] = { + .freq = 1228800000, + .prediv = 5, + .fbdiv = 256, + .postdiv1 = 1, + .dacpd = 1, + .dsmpd = 1, + }, +}; + +#endif diff --git a/drivers/clk/starfive/clk-starfive-jh7110-stg.c b/drivers/clk/starfive/clk-starfive-jh7110-stg.c new file mode 100644 index 0000000000000..547f213a22b00 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-stg.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 System-Top-Group Clock Driver + * + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_STGCLK_OSC (JH7110_STGCLK_END + 0) +#define JH7110_STGCLK_HIFI4_CORE (JH7110_STGCLK_END + 1) +#define JH7110_STGCLK_STG_AXIAHB (JH7110_STGCLK_END + 2) +#define JH7110_STGCLK_USB_125M (JH7110_STGCLK_END + 3) +#define JH7110_STGCLK_CPU_BUS (JH7110_STGCLK_END + 4) +#define JH7110_STGCLK_HIFI4_AXI (JH7110_STGCLK_END + 5) +#define JH7110_STGCLK_NOCSTG_BUS (JH7110_STGCLK_END + 6) +#define JH7110_STGCLK_APB_BUS (JH7110_STGCLK_END + 7) +#define JH7110_STGCLK_EXT_END (JH7110_STGCLK_END + 8) + +static const struct jh71x0_clk_data jh7110_stgclk_data[] = { + /* hifi4 */ + JH71X0_GATE(JH7110_STGCLK_HIFI4_CLK_CORE, "hifi4_clk_core", 0, + JH7110_STGCLK_HIFI4_CORE), + /* usb */ + JH71X0_GATE(JH7110_STGCLK_USB0_APB, "usb0_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_USB0_UTMI_APB, "usb0_utmi_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_USB0_AXI, "usb0_axi", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GDIV(JH7110_STGCLK_USB0_LPM, "usb0_lpm", 0, 2, JH7110_STGCLK_OSC), + JH71X0_GDIV(JH7110_STGCLK_USB0_STB, "usb0_stb", 0, 4, JH7110_STGCLK_OSC), + JH71X0_GATE(JH7110_STGCLK_USB0_APP_125, "usb0_app_125", 0, JH7110_STGCLK_USB_125M), + JH71X0__DIV(JH7110_STGCLK_USB0_REFCLK, "usb0_refclk", 2, JH7110_STGCLK_OSC), + /* pci-e */ + JH71X0_GATE(JH7110_STGCLK_PCIE0_AXI_MST0, "pcie0_axi_mst0", 0, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE0_APB, "pcie0_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_PCIE0_TL, "pcie0_tl", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE1_AXI_MST0, "pcie1_axi_mst0", 0, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE1_APB, "pcie1_apb", 0, JH7110_STGCLK_APB_BUS), + JH71X0_GATE(JH7110_STGCLK_PCIE1_TL, "pcie1_tl", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_PCIE_SLV_MAIN, "pcie_slv_main", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + /* security */ + JH71X0_GATE(JH7110_STGCLK_SEC_AHB, "sec_ahb", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_SEC_MISC_AHB, "sec_misc_ahb", 0, JH7110_STGCLK_STG_AXIAHB), + /* stg mtrx */ + JH71X0_GATE(JH7110_STGCLK_GRP0_MAIN, "mtrx_grp0_main", CLK_IS_CRITICAL, + JH7110_STGCLK_CPU_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP0_BUS, "mtrx_grp0_bus", CLK_IS_CRITICAL, + JH7110_STGCLK_NOCSTG_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP0_STG, "mtrx_grp0_stg", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_GRP1_MAIN, "mtrx_grp1_main", CLK_IS_CRITICAL, + JH7110_STGCLK_CPU_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP1_BUS, "mtrx_grp1_bus", CLK_IS_CRITICAL, + JH7110_STGCLK_NOCSTG_BUS), + JH71X0_GATE(JH7110_STGCLK_GRP1_STG, "mtrx_grp1_stg", CLK_IS_CRITICAL, + JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_GRP1_HIFI, "mtrx_grp1_hifi", CLK_IS_CRITICAL, + JH7110_STGCLK_HIFI4_AXI), + /* e24_rvpi */ + JH71X0_GDIV(JH7110_STGCLK_E2_RTC, "e2_rtc", 0, 24, JH7110_STGCLK_OSC), + JH71X0_GATE(JH7110_STGCLK_E2_CORE, "e2_core", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_E2_DBG, "e2_dbg", 0, JH7110_STGCLK_STG_AXIAHB), + /* dw_sgdma1p */ + JH71X0_GATE(JH7110_STGCLK_DMA1P_AXI, "dma1p_axi", 0, JH7110_STGCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_STGCLK_DMA1P_AHB, "dma1p_ahb", 0, JH7110_STGCLK_STG_AXIAHB), +}; + +static struct clk_hw *jh7110_stgclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_STGCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_stgcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg, JH7110_STGCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + dev_set_drvdata(priv->dev, (void *)(&priv->base)); + + for (idx = 0; idx < JH7110_STGCLK_END; idx++) { + u32 max = jh7110_stgclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_stgclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_stgclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + const char *fw_name[JH7110_STGCLK_EXT_END - JH7110_STGCLK_END] = { + "osc", + "hifi4_core", + "stg_axiahb", + "usb_125m", + "cpu_bus", + "hifi4_axi", + "nocstg_bus", + "apb_bus" + }; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_stgclk_data[idx].parents[i]; + + if (pidx < JH7110_STGCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7110_STGCLK_EXT_END) + parents[i].fw_name = fw_name[pidx - JH7110_STGCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_stgclk_get, priv); + if (ret) + return ret; + + return jh7110_reset_controller_register(priv, "reset-stg", 2); +} + +static const struct of_device_id jh7110_stgcrg_match[] = { + { .compatible = "starfive,jh7110-stgcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_stgcrg_match); + +static struct platform_driver jh7110_stgcrg_driver = { + .probe = jh7110_stgcrg_probe, + .driver = { + .name = "clk-starfive-jh7110-stg", + .of_match_table = jh7110_stgcrg_match, + }, +}; +module_platform_driver(jh7110_stgcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_DESCRIPTION("StarFive JH7110 System-Top-Group clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/starfive/clk-starfive-jh7110-sys.c b/drivers/clk/starfive/clk-starfive-jh7110-sys.c new file mode 100644 index 0000000000000..ab82a49179086 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-sys.c @@ -0,0 +1,427 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 System Clock Driver + * + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_SYSCLK_OSC (JH7110_SYSCLK_END + 0) +#define JH7110_SYSCLK_GMAC1_RMII_REFIN (JH7110_SYSCLK_END + 1) +#define JH7110_SYSCLK_GMAC1_RGMII_RXIN (JH7110_SYSCLK_END + 2) +#define JH7110_SYSCLK_I2STX_BCLK_EXT (JH7110_SYSCLK_END + 3) +#define JH7110_SYSCLK_I2STX_LRCK_EXT (JH7110_SYSCLK_END + 4) +#define JH7110_SYSCLK_I2SRX_BCLK_EXT (JH7110_SYSCLK_END + 5) +#define JH7110_SYSCLK_I2SRX_LRCK_EXT (JH7110_SYSCLK_END + 6) +#define JH7110_SYSCLK_TDM_EXT (JH7110_SYSCLK_END + 7) +#define JH7110_SYSCLK_MCLK_EXT (JH7110_SYSCLK_END + 8) +#define JH7110_SYSCLK_PLL0_OUT (JH7110_SYSCLK_END + 9) +#define JH7110_SYSCLK_PLL1_OUT (JH7110_SYSCLK_END + 10) +#define JH7110_SYSCLK_PLL2_OUT (JH7110_SYSCLK_END + 11) + +static const struct jh71x0_clk_data jh7110_sysclk_data[] __initconst = { + /* root */ + JH71X0__MUX(JH7110_SYSCLK_CPU_ROOT, "cpu_root", 2, + JH7110_SYSCLK_OSC, + JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_CPU_CORE, "cpu_core", 7, JH7110_SYSCLK_CPU_ROOT), + JH71X0__DIV(JH7110_SYSCLK_CPU_BUS, "cpu_bus", 2, JH7110_SYSCLK_CPU_CORE), + JH71X0__MUX(JH7110_SYSCLK_GPU_ROOT, "gpu_root", 2, + JH7110_SYSCLK_PLL2_OUT, + JH7110_SYSCLK_PLL1_OUT), + JH71X0_MDIV(JH7110_SYSCLK_PERH_ROOT, "perh_root", 2, 2, + JH7110_SYSCLK_PLL0_OUT, + JH7110_SYSCLK_PLL2_OUT), + JH71X0__MUX(JH7110_SYSCLK_BUS_ROOT, "bus_root", 2, + JH7110_SYSCLK_OSC, + JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_NOCSTG_BUS, "nocstg_bus", 3, JH7110_SYSCLK_BUS_ROOT), + JH71X0__DIV(JH7110_SYSCLK_AXI_CFG0, "axi_cfg0", 3, JH7110_SYSCLK_BUS_ROOT), + JH71X0__DIV(JH7110_SYSCLK_STG_AXIAHB, "stg_axiahb", 2, JH7110_SYSCLK_AXI_CFG0), + JH71X0_GATE(JH7110_SYSCLK_AHB0, "ahb0", CLK_IS_CRITICAL, JH7110_SYSCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_SYSCLK_AHB1, "ahb1", CLK_IS_CRITICAL, JH7110_SYSCLK_STG_AXIAHB), + JH71X0__DIV(JH7110_SYSCLK_APB_BUS, "apb_bus", 8, JH7110_SYSCLK_STG_AXIAHB), + JH71X0_GATE(JH7110_SYSCLK_APB0, "apb0", CLK_IS_CRITICAL, JH7110_SYSCLK_APB_BUS), + JH71X0__DIV(JH7110_SYSCLK_PLL0_DIV2, "pll0_div2", 2, JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV2, "pll1_div2", 2, JH7110_SYSCLK_PLL1_OUT), + JH71X0__DIV(JH7110_SYSCLK_PLL2_DIV2, "pll2_div2", 2, JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_AUDIO_ROOT, "audio_root", 8, JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_MCLK_INNER, "mclk_inner", 64, JH7110_SYSCLK_AUDIO_ROOT), + JH71X0__MUX(JH7110_SYSCLK_MCLK, "mclk", 2, + JH7110_SYSCLK_MCLK_INNER, + JH7110_SYSCLK_MCLK_EXT), + JH71X0_GATE(JH7110_SYSCLK_MCLK_OUT, "mclk_out", 0, JH7110_SYSCLK_MCLK_INNER), + JH71X0_MDIV(JH7110_SYSCLK_ISP_2X, "isp_2x", 8, 2, + JH7110_SYSCLK_PLL2_OUT, + JH7110_SYSCLK_PLL1_OUT), + JH71X0__DIV(JH7110_SYSCLK_ISP_AXI, "isp_axi", 4, JH7110_SYSCLK_ISP_2X), + JH71X0_GDIV(JH7110_SYSCLK_GCLK0, "gclk0", 0, 62, JH7110_SYSCLK_PLL0_DIV2), + JH71X0_GDIV(JH7110_SYSCLK_GCLK1, "gclk1", 0, 62, JH7110_SYSCLK_PLL1_DIV2), + JH71X0_GDIV(JH7110_SYSCLK_GCLK2, "gclk2", 0, 62, JH7110_SYSCLK_PLL2_DIV2), + /* cores */ + JH71X0_GATE(JH7110_SYSCLK_CORE, "core", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE1, "core1", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE2, "core2", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE3, "core3", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_CORE4, "core4", CLK_IS_CRITICAL, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_DEBUG, "debug", 0, JH7110_SYSCLK_CPU_BUS), + JH71X0__DIV(JH7110_SYSCLK_RTC_TOGGLE, "rtc_toggle", 6, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TRACE0, "trace0", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE1, "trace1", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE2, "trace2", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE3, "trace3", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE4, "trace4", 0, JH7110_SYSCLK_CPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_TRACE_COM, "trace_com", 0, JH7110_SYSCLK_CPU_BUS), + /* noc */ + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_CPU_AXI, "noc_bus_cpu_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_CPU_BUS), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_AXICFG0_AXI, "noc_bus_axicfg0_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_AXI_CFG0), + /* ddr */ + JH71X0__DIV(JH7110_SYSCLK_OSC_DIV2, "osc_div2", 2, JH7110_SYSCLK_OSC), + JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV4, "pll1_div4", 2, JH7110_SYSCLK_PLL1_DIV2), + JH71X0__DIV(JH7110_SYSCLK_PLL1_DIV8, "pll1_div8", 2, JH7110_SYSCLK_PLL1_DIV4), + JH71X0__MUX(JH7110_SYSCLK_DDR_BUS, "ddr_bus", 4, + JH7110_SYSCLK_OSC_DIV2, + JH7110_SYSCLK_PLL1_DIV2, + JH7110_SYSCLK_PLL1_DIV4, + JH7110_SYSCLK_PLL1_DIV8), + JH71X0_GATE(JH7110_SYSCLK_DDR_AXI, "ddr_axi", CLK_IS_CRITICAL, JH7110_SYSCLK_DDR_BUS), + /* gpu */ + JH71X0__DIV(JH7110_SYSCLK_GPU_CORE, "gpu_core", 7, JH7110_SYSCLK_GPU_ROOT), + JH71X0_GATE(JH7110_SYSCLK_GPU_CORE_CLK, "gpu_core_clk", 0, JH7110_SYSCLK_GPU_CORE), + JH71X0_GATE(JH7110_SYSCLK_GPU_SYS_CLK, "gpu_sys_clk", 0, JH7110_SYSCLK_ISP_AXI), + JH71X0_GATE(JH7110_SYSCLK_GPU_APB, "gpu_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_GPU_RTC_TOGGLE, "gpu_rtc_toggle", 0, 12, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_GPU_AXI, "noc_bus_gpu_axi", 0, JH7110_SYSCLK_GPU_CORE), + /* isp */ + JH71X0_GATE(JH7110_SYSCLK_ISP_TOP_CORE, "isp_top_core", 0, JH7110_SYSCLK_ISP_2X), + JH71X0_GATE(JH7110_SYSCLK_ISP_TOP_AXI, "isp_top_axi", 0, JH7110_SYSCLK_ISP_AXI), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_ISP_AXI, "noc_bus_isp_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_ISP_AXI), + /* hifi4 */ + JH71X0__DIV(JH7110_SYSCLK_HIFI4_CORE, "hifi4_core", 15, JH7110_SYSCLK_BUS_ROOT), + JH71X0__DIV(JH7110_SYSCLK_HIFI4_AXI, "hifi4_axi", 2, JH7110_SYSCLK_HIFI4_CORE), + /* axi_cfg1 */ + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG1_MAIN, "axi_cfg1_main", CLK_IS_CRITICAL, + JH7110_SYSCLK_ISP_AXI), + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG1_AHB, "axi_cfg1_ahb", CLK_IS_CRITICAL, + JH7110_SYSCLK_AHB0), + /* vout */ + JH71X0_GATE(JH7110_SYSCLK_VOUT_SRC, "vout_src", 0, JH7110_SYSCLK_PLL2_OUT), + JH71X0__DIV(JH7110_SYSCLK_VOUT_AXI, "vout_axi", 7, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_DISP_AXI, "noc_bus_disp_axi", 0, JH7110_SYSCLK_VOUT_AXI), + JH71X0_GATE(JH7110_SYSCLK_VOUT_TOP_AHB, "vout_top_ahb", 0, JH7110_SYSCLK_AHB1), + JH71X0_GATE(JH7110_SYSCLK_VOUT_TOP_AXI, "vout_top_axi", 0, JH7110_SYSCLK_VOUT_AXI), + JH71X0_GATE(JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK, "vout_top_hdmitx0_mclk", 0, + JH7110_SYSCLK_MCLK), + JH71X0__DIV(JH7110_SYSCLK_VOUT_TOP_MIPIPHY_REF, "vout_top_mipiphy_ref", 2, + JH7110_SYSCLK_OSC), + /* jpegc */ + JH71X0__DIV(JH7110_SYSCLK_JPEGC_AXI, "jpegc_axi", 16, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_CODAJ12_AXI, "codaj12_axi", 0, JH7110_SYSCLK_JPEGC_AXI), + JH71X0_GDIV(JH7110_SYSCLK_CODAJ12_CORE, "codaj12_core", 0, 16, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_CODAJ12_APB, "codaj12_apb", 0, JH7110_SYSCLK_APB_BUS), + /* vdec */ + JH71X0__DIV(JH7110_SYSCLK_VDEC_AXI, "vdec_axi", 7, JH7110_SYSCLK_BUS_ROOT), + JH71X0_GATE(JH7110_SYSCLK_WAVE511_AXI, "wave511_axi", 0, JH7110_SYSCLK_VDEC_AXI), + JH71X0_GDIV(JH7110_SYSCLK_WAVE511_BPU, "wave511_bpu", 0, 7, JH7110_SYSCLK_BUS_ROOT), + JH71X0_GDIV(JH7110_SYSCLK_WAVE511_VCE, "wave511_vce", 0, 7, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GATE(JH7110_SYSCLK_WAVE511_APB, "wave511_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_VDEC_JPG, "vdec_jpg", 0, JH7110_SYSCLK_JPEGC_AXI), + JH71X0_GATE(JH7110_SYSCLK_VDEC_MAIN, "vdec_main", 0, JH7110_SYSCLK_VDEC_AXI), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_VDEC_AXI, "noc_bus_vdec_axi", 0, JH7110_SYSCLK_VDEC_AXI), + /* venc */ + JH71X0__DIV(JH7110_SYSCLK_VENC_AXI, "venc_axi", 15, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_WAVE420L_AXI, "wave420l_axi", 0, JH7110_SYSCLK_VENC_AXI), + JH71X0_GDIV(JH7110_SYSCLK_WAVE420L_BPU, "wave420l_bpu", 0, 15, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GDIV(JH7110_SYSCLK_WAVE420L_VCE, "wave420l_vce", 0, 15, JH7110_SYSCLK_PLL2_OUT), + JH71X0_GATE(JH7110_SYSCLK_WAVE420L_APB, "wave420l_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_VENC_AXI, "noc_bus_venc_axi", 0, JH7110_SYSCLK_VENC_AXI), + /* axi_cfg0 */ + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG0_MAIN_DIV, "axi_cfg0_main_div", CLK_IS_CRITICAL, + JH7110_SYSCLK_AHB1), + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG0_MAIN, "axi_cfg0_main", CLK_IS_CRITICAL, + JH7110_SYSCLK_AXI_CFG0), + JH71X0_GATE(JH7110_SYSCLK_AXI_CFG0_HIFI4, "axi_cfg0_hifi4", CLK_IS_CRITICAL, + JH7110_SYSCLK_HIFI4_AXI), + /* intmem */ + JH71X0_GATE(JH7110_SYSCLK_AXIMEM2_AXI, "aximem2_axi", 0, JH7110_SYSCLK_AXI_CFG0), + /* qspi */ + JH71X0_GATE(JH7110_SYSCLK_QSPI_AHB, "qspi_ahb", CLK_IGNORE_UNUSED, JH7110_SYSCLK_AHB1), + JH71X0_GATE(JH7110_SYSCLK_QSPI_APB, "qspi_apb", CLK_IGNORE_UNUSED, JH7110_SYSCLK_APB_BUS), + JH71X0__DIV(JH7110_SYSCLK_QSPI_REF_SRC, "qspi_ref_src", 16, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GMUX(JH7110_SYSCLK_QSPI_REF, "qspi_ref", 0, 2, + JH7110_SYSCLK_OSC, + JH7110_SYSCLK_QSPI_REF_SRC), + /* sdio */ + JH71X0_GATE(JH7110_SYSCLK_SDIO0_AHB, "sdio0_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GATE(JH7110_SYSCLK_SDIO1_AHB, "sdio1_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GDIV(JH7110_SYSCLK_SDIO0_SDCARD, "sdio0_sdcard", 0, 15, JH7110_SYSCLK_AXI_CFG0), + JH71X0_GDIV(JH7110_SYSCLK_SDIO1_SDCARD, "sdio1_sdcard", 0, 15, JH7110_SYSCLK_AXI_CFG0), + /* stg */ + JH71X0__DIV(JH7110_SYSCLK_USB_125M, "usb_125m", 15, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GATE(JH7110_SYSCLK_NOC_BUS_STG_AXI, "noc_bus_stg_axi", CLK_IS_CRITICAL, + JH7110_SYSCLK_NOCSTG_BUS), + /* gmac1 */ + JH71X0_GATE(JH7110_SYSCLK_GMAC1_AHB, "gmac1_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GATE(JH7110_SYSCLK_GMAC1_AXI, "gmac1_axi", 0, JH7110_SYSCLK_STG_AXIAHB), + JH71X0__DIV(JH7110_SYSCLK_GMAC_SRC, "gmac_src", 7, JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_GMAC1_GTXCLK, "gmac1_gtxclk", 15, JH7110_SYSCLK_PLL0_OUT), + JH71X0__DIV(JH7110_SYSCLK_GMAC1_RMII_RTX, "gmac1_rmii_rtx", 30, + JH7110_SYSCLK_GMAC1_RMII_REFIN), + JH71X0_GDIV(JH7110_SYSCLK_GMAC1_PTP, "gmac1_ptp", 0, 31, JH7110_SYSCLK_GMAC_SRC), + JH71X0__MUX(JH7110_SYSCLK_GMAC1_RX, "gmac1_rx", 2, + JH7110_SYSCLK_GMAC1_RGMII_RXIN, + JH7110_SYSCLK_GMAC1_RMII_RTX), + JH71X0__INV(JH7110_SYSCLK_GMAC1_RX_INV, "gmac1_rx_inv", JH7110_SYSCLK_GMAC1_RX), + JH71X0_GMUX(JH7110_SYSCLK_GMAC1_TX, "gmac1_tx", CLK_SET_RATE_PARENT, 2, + JH7110_SYSCLK_GMAC1_GTXCLK, + JH7110_SYSCLK_GMAC1_RMII_RTX), + JH71X0__INV(JH7110_SYSCLK_GMAC1_TX_INV, "gmac1_tx_inv", JH7110_SYSCLK_GMAC1_TX), + JH71X0_GATE(JH7110_SYSCLK_GMAC1_GTXC, "gmac1_gtxc", CLK_SET_RATE_PARENT, + JH7110_SYSCLK_GMAC1_GTXCLK), + /* gmac0 */ + JH71X0_GDIV(JH7110_SYSCLK_GMAC0_GTXCLK, "gmac0_gtxclk", 0, 15, JH7110_SYSCLK_PLL0_OUT), + JH71X0_GDIV(JH7110_SYSCLK_GMAC0_PTP, "gmac0_ptp", 0, 31, JH7110_SYSCLK_GMAC_SRC), + JH71X0_GDIV(JH7110_SYSCLK_GMAC_PHY, "gmac_phy", 0, 31, JH7110_SYSCLK_GMAC_SRC), + JH71X0_GATE(JH7110_SYSCLK_GMAC0_GTXC, "gmac0_gtxc", CLK_SET_RATE_PARENT, + JH7110_SYSCLK_GMAC0_GTXCLK), + /* apb misc */ + JH71X0_GATE(JH7110_SYSCLK_IOMUX_APB, "iomux_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_MAILBOX_APB, "mailbox_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_INT_CTRL_APB, "int_ctrl_apb", 0, JH7110_SYSCLK_APB_BUS), + /* can0 */ + JH71X0_GATE(JH7110_SYSCLK_CAN0_APB, "can0_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_CAN0_TIMER, "can0_timer", 0, 24, JH7110_SYSCLK_OSC), + JH71X0_GDIV(JH7110_SYSCLK_CAN0_CAN, "can0_can", 0, 63, JH7110_SYSCLK_PERH_ROOT), + /* can1 */ + JH71X0_GATE(JH7110_SYSCLK_CAN1_APB, "can1_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_CAN1_TIMER, "can1_timer", 0, 24, JH7110_SYSCLK_OSC), + JH71X0_GDIV(JH7110_SYSCLK_CAN1_CAN, "can1_can", 0, 63, JH7110_SYSCLK_PERH_ROOT), + /* pwm */ + JH71X0_GATE(JH7110_SYSCLK_PWM_APB, "pwm_apb", 0, JH7110_SYSCLK_APB_BUS), + /* wdt */ + JH71X0_GATE(JH7110_SYSCLK_WDT_APB, "wdt_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_WDT_CORE, "wdt_core", 0, JH7110_SYSCLK_OSC), + /* timer */ + JH71X0_GATE(JH7110_SYSCLK_TIMER_APB, "timer_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_TIMER0, "timer0", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TIMER1, "timer1", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TIMER2, "timer2", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_TIMER3, "timer3", 0, JH7110_SYSCLK_OSC), + /* temp sensor */ + JH71X0_GATE(JH7110_SYSCLK_TEMP_APB, "temp_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GDIV(JH7110_SYSCLK_TEMP_CORE, "temp_core", 0, 24, JH7110_SYSCLK_OSC), + /* spi */ + JH71X0_GATE(JH7110_SYSCLK_SPI0_APB, "spi0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPI1_APB, "spi1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPI2_APB, "spi2_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPI3_APB, "spi3_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_SPI4_APB, "spi4_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_SPI5_APB, "spi5_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_SPI6_APB, "spi6_apb", 0, JH7110_SYSCLK_APB_BUS), + /* i2c */ + JH71X0_GATE(JH7110_SYSCLK_I2C0_APB, "i2c0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_I2C1_APB, "i2c1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_I2C2_APB, "i2c2_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_I2C3_APB, "i2c3_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_I2C4_APB, "i2c4_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_I2C5_APB, "i2c5_apb", 0, JH7110_SYSCLK_APB_BUS), + JH71X0_GATE(JH7110_SYSCLK_I2C6_APB, "i2c6_apb", 0, JH7110_SYSCLK_APB_BUS), + /* uart */ + JH71X0_GATE(JH7110_SYSCLK_UART0_APB, "uart0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_UART0_CORE, "uart0_core", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_UART1_APB, "uart1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_UART1_CORE, "uart1_core", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_UART2_APB, "uart2_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_UART2_CORE, "uart2_core", 0, JH7110_SYSCLK_OSC), + JH71X0_GATE(JH7110_SYSCLK_UART3_APB, "uart3_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_UART3_CORE, "uart3_core", 0, 10, JH7110_SYSCLK_PERH_ROOT), + JH71X0_GATE(JH7110_SYSCLK_UART4_APB, "uart4_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_UART4_CORE, "uart4_core", 0, 10, JH7110_SYSCLK_PERH_ROOT), + JH71X0_GATE(JH7110_SYSCLK_UART5_APB, "uart5_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_UART5_CORE, "uart5_core", 0, 10, JH7110_SYSCLK_PERH_ROOT), + /* pwmdac */ + JH71X0_GATE(JH7110_SYSCLK_PWMDAC_APB, "pwmdac_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_PWMDAC_CORE, "pwmdac_core", 0, 256, JH7110_SYSCLK_AUDIO_ROOT), + /* spdif */ + JH71X0_GATE(JH7110_SYSCLK_SPDIF_APB, "spdif_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GATE(JH7110_SYSCLK_SPDIF_CORE, "spdif_core", 0, JH7110_SYSCLK_MCLK), + /* i2stx0 */ + JH71X0_GATE(JH7110_SYSCLK_I2STX0_APB, "i2stx0_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_I2STX0_BCLK_MST, "i2stx0_bclk_mst", 0, 32, JH7110_SYSCLK_MCLK), + JH71X0__INV(JH7110_SYSCLK_I2STX0_BCLK_MST_INV, "i2stx0_bclk_mst_inv", + JH7110_SYSCLK_I2STX0_BCLK_MST), + JH71X0_MDIV(JH7110_SYSCLK_I2STX0_LRCK_MST, "i2stx0_lrck_mst", 64, 2, + JH7110_SYSCLK_I2STX0_BCLK_MST_INV, + JH7110_SYSCLK_I2STX0_BCLK_MST), + JH71X0__MUX(JH7110_SYSCLK_I2STX0_BCLK, "i2stx0_bclk", 2, + JH7110_SYSCLK_I2STX0_BCLK_MST, + JH7110_SYSCLK_I2STX_BCLK_EXT), + JH71X0__INV(JH7110_SYSCLK_I2STX0_BCLK_INV, "i2stx0_bclk_inv", JH7110_SYSCLK_I2STX0_BCLK), + JH71X0__MUX(JH7110_SYSCLK_I2STX0_LRCK, "i2stx0_lrck", 2, + JH7110_SYSCLK_I2STX0_LRCK_MST, + JH7110_SYSCLK_I2STX_LRCK_EXT), + /* i2stx1 */ + JH71X0_GATE(JH7110_SYSCLK_I2STX1_APB, "i2stx1_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_I2STX1_BCLK_MST, "i2stx1_bclk_mst", 0, 32, JH7110_SYSCLK_MCLK), + JH71X0__INV(JH7110_SYSCLK_I2STX1_BCLK_MST_INV, "i2stx1_bclk_mst_inv", + JH7110_SYSCLK_I2STX1_BCLK_MST), + JH71X0_MDIV(JH7110_SYSCLK_I2STX1_LRCK_MST, "i2stx1_lrck_mst", 64, 2, + JH7110_SYSCLK_I2STX1_BCLK_MST_INV, + JH7110_SYSCLK_I2STX1_BCLK_MST), + JH71X0__MUX(JH7110_SYSCLK_I2STX1_BCLK, "i2stx1_bclk", 2, + JH7110_SYSCLK_I2STX1_BCLK_MST, + JH7110_SYSCLK_I2STX_BCLK_EXT), + JH71X0__INV(JH7110_SYSCLK_I2STX1_BCLK_INV, "i2stx1_bclk_inv", JH7110_SYSCLK_I2STX1_BCLK), + JH71X0__MUX(JH7110_SYSCLK_I2STX1_LRCK, "i2stx1_lrck", 2, + JH7110_SYSCLK_I2STX1_LRCK_MST, + JH7110_SYSCLK_I2STX_LRCK_EXT), + /* i2srx */ + JH71X0_GATE(JH7110_SYSCLK_I2SRX_APB, "i2srx_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_I2SRX_BCLK_MST, "i2srx_bclk_mst", 0, 32, JH7110_SYSCLK_MCLK), + JH71X0__INV(JH7110_SYSCLK_I2SRX_BCLK_MST_INV, "i2srx_bclk_mst_inv", + JH7110_SYSCLK_I2SRX_BCLK_MST), + JH71X0_MDIV(JH7110_SYSCLK_I2SRX_LRCK_MST, "i2srx_lrck_mst", 64, 2, + JH7110_SYSCLK_I2SRX_BCLK_MST_INV, + JH7110_SYSCLK_I2SRX_BCLK_MST), + JH71X0__MUX(JH7110_SYSCLK_I2SRX_BCLK, "i2srx_bclk", 2, + JH7110_SYSCLK_I2SRX_BCLK_MST, + JH7110_SYSCLK_I2SRX_BCLK_EXT), + JH71X0__INV(JH7110_SYSCLK_I2SRX_BCLK_INV, "i2srx_bclk_inv", JH7110_SYSCLK_I2SRX_BCLK), + JH71X0__MUX(JH7110_SYSCLK_I2SRX_LRCK, "i2srx_lrck", 2, + JH7110_SYSCLK_I2SRX_LRCK_MST, + JH7110_SYSCLK_I2SRX_LRCK_EXT), + /* pdm */ + JH71X0_GDIV(JH7110_SYSCLK_PDM_DMIC, "pdm_dmic", 0, 64, JH7110_SYSCLK_MCLK), + JH71X0_GATE(JH7110_SYSCLK_PDM_APB, "pdm_apb", 0, JH7110_SYSCLK_APB0), + /* tdm */ + JH71X0_GATE(JH7110_SYSCLK_TDM_AHB, "tdm_ahb", 0, JH7110_SYSCLK_AHB0), + JH71X0_GATE(JH7110_SYSCLK_TDM_APB, "tdm_apb", 0, JH7110_SYSCLK_APB0), + JH71X0_GDIV(JH7110_SYSCLK_TDM_INTERNAL, "tdm_internal", 0, 64, JH7110_SYSCLK_MCLK), + JH71X0__MUX(JH7110_SYSCLK_TDM_TDM, "tdm_tdm", 2, + JH7110_SYSCLK_TDM_INTERNAL, + JH7110_SYSCLK_TDM_EXT), + JH71X0__INV(JH7110_SYSCLK_TDM_TDM_INV, "tdm_tdm_inv", JH7110_SYSCLK_TDM_TDM), + /* jtag */ + JH71X0__DIV(JH7110_SYSCLK_JTAG_CERTIFICATION_TRNG, "jtag_certification_trng", 4, + JH7110_SYSCLK_OSC), +}; + +static struct clk_hw *jh7110_sysclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_SYSCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int __init jh7110_syscrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_SYSCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + dev_set_drvdata(priv->dev, (void *)(&priv->base)); + + for (idx = 0; idx < JH7110_SYSCLK_END; idx++) { + u32 max = jh7110_sysclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_sysclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_sysclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_sysclk_data[idx].parents[i]; + + if (pidx < JH7110_SYSCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx == JH7110_SYSCLK_OSC) + parents[i].fw_name = "osc"; + else if (pidx == JH7110_SYSCLK_GMAC1_RMII_REFIN) + parents[i].fw_name = "gmac1_rmii_refin"; + else if (pidx == JH7110_SYSCLK_GMAC1_RGMII_RXIN) + parents[i].fw_name = "gmac1_rgmii_rxin"; + else if (pidx == JH7110_SYSCLK_I2STX_BCLK_EXT) + parents[i].fw_name = "i2stx_bclk_ext"; + else if (pidx == JH7110_SYSCLK_I2STX_LRCK_EXT) + parents[i].fw_name = "i2stx_lrck_ext"; + else if (pidx == JH7110_SYSCLK_I2SRX_BCLK_EXT) + parents[i].fw_name = "i2srx_bclk_ext"; + else if (pidx == JH7110_SYSCLK_I2SRX_LRCK_EXT) + parents[i].fw_name = "i2srx_lrck_ext"; + else if (pidx == JH7110_SYSCLK_TDM_EXT) + parents[i].fw_name = "tdm_ext"; + else if (pidx == JH7110_SYSCLK_MCLK_EXT) + parents[i].fw_name = "mclk_ext"; + else if (pidx == JH7110_SYSCLK_PLL0_OUT) + parents[i].fw_name = "pll0_out"; + else if (pidx == JH7110_SYSCLK_PLL1_OUT) + parents[i].fw_name = "pll1_out"; + else if (pidx == JH7110_SYSCLK_PLL2_OUT) + parents[i].fw_name = "pll2_out"; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + return ret; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_sysclk_get, priv); + if (ret) + return ret; + + return jh7110_reset_controller_register(priv, "reset-sys", 0); +} + +static const struct of_device_id jh7110_syscrg_match[] = { + { .compatible = "starfive,jh7110-syscrg" }, + { /* sentinel */ } +}; + +static struct platform_driver jh7110_syscrg_driver = { + .driver = { + .name = "clk-starfive-jh7110-sys", + .of_match_table = jh7110_syscrg_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(jh7110_syscrg_driver, jh7110_syscrg_probe); diff --git a/drivers/clk/starfive/clk-starfive-jh7110-vout.c b/drivers/clk/starfive/clk-starfive-jh7110-vout.c new file mode 100644 index 0000000000000..02a6c98b1bcda --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh7110-vout.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH7110 Video-Output Clock Driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-starfive-jh71x0.h" + +/* external clocks */ +#define JH7110_VOUTCLK_VOUT_SRC (JH7110_VOUTCLK_END + 0) +#define JH7110_VOUTCLK_VOUT_TOP_AHB (JH7110_VOUTCLK_END + 1) +#define JH7110_VOUTCLK_VOUT_TOP_AXI (JH7110_VOUTCLK_END + 2) +#define JH7110_VOUTCLK_VOUT_TOP_HDMITX0_MCLK (JH7110_VOUTCLK_END + 3) +#define JH7110_VOUTCLK_I2STX0_BCLK (JH7110_VOUTCLK_END + 4) +#define JH7110_VOUTCLK_HDMITX0_PIXELCLK (JH7110_VOUTCLK_END + 5) +#define JH7110_VOUTCLK_EXT_END (JH7110_VOUTCLK_END + 6) + +/* VOUT domian clocks */ +struct vout_top_crg { + struct clk_bulk_data *top_clks; + int top_clks_num; + void __iomem *base; +}; + +static struct clk_bulk_data jh7110_vout_top_clks[] = { + { .id = "vout_src" }, + { .id = "vout_top_ahb" } +}; + +static const struct jh71x0_clk_data jh7110_voutclk_data[] = { + /* divider */ + JH71X0__DIV(JH7110_VOUTCLK_APB, "apb", 8, JH7110_VOUTCLK_VOUT_TOP_AHB), + JH71X0__DIV(JH7110_VOUTCLK_DC8200_PIX, "dc8200_pix", 63, JH7110_VOUTCLK_VOUT_SRC), + JH71X0__DIV(JH7110_VOUTCLK_DSI_SYS, "dsi_sys", 31, JH7110_VOUTCLK_VOUT_SRC), + JH71X0__DIV(JH7110_VOUTCLK_TX_ESC, "tx_esc", 31, JH7110_VOUTCLK_VOUT_TOP_AHB), + /* dc8200 */ + JH71X0_GATE(JH7110_VOUTCLK_DC8200_AXI, "dc8200_axi", 0, JH7110_VOUTCLK_VOUT_TOP_AXI), + JH71X0_GATE(JH7110_VOUTCLK_DC8200_CORE, "dc8200_core", 0, JH7110_VOUTCLK_VOUT_TOP_AXI), + JH71X0_GATE(JH7110_VOUTCLK_DC8200_AHB, "dc8200_ahb", 0, JH7110_VOUTCLK_VOUT_TOP_AHB), + JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX0, "dc8200_pix0", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + JH71X0_GMUX(JH7110_VOUTCLK_DC8200_PIX1, "dc8200_pix1", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + /* LCD */ + JH71X0_GMUX(JH7110_VOUTCLK_DOM_VOUT_TOP_LCD, "dom_vout_top_lcd", 0, 2, + JH7110_VOUTCLK_DC8200_PIX0, + JH7110_VOUTCLK_DC8200_PIX1), + /* dsiTx */ + JH71X0_GATE(JH7110_VOUTCLK_DSITX_APB, "dsiTx_apb", 0, JH7110_VOUTCLK_DSI_SYS), + JH71X0_GATE(JH7110_VOUTCLK_DSITX_SYS, "dsiTx_sys", 0, JH7110_VOUTCLK_DSI_SYS), + JH71X0_GMUX(JH7110_VOUTCLK_DSITX_DPI, "dsiTx_dpi", 0, 2, + JH7110_VOUTCLK_DC8200_PIX, + JH7110_VOUTCLK_HDMITX0_PIXELCLK), + JH71X0_GATE(JH7110_VOUTCLK_DSITX_TXESC, "dsiTx_txesc", 0, JH7110_VOUTCLK_TX_ESC), + /* mipitx DPHY */ + JH71X0_GATE(JH7110_VOUTCLK_MIPITX_DPHY_TXESC, "mipitx_dphy_txesc", 0, + JH7110_VOUTCLK_TX_ESC), + /* hdmi */ + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_MCLK, "hdmi_tx_mclk", 0, + JH7110_VOUTCLK_VOUT_TOP_HDMITX0_MCLK), + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_BCLK, "hdmi_tx_bclk", 0, + JH7110_VOUTCLK_I2STX0_BCLK), + JH71X0_GATE(JH7110_VOUTCLK_HDMI_TX_SYS, "hdmi_tx_sys", 0, JH7110_VOUTCLK_APB), +}; + +static struct vout_top_crg *top_crg_from(void __iomem **base) +{ + return container_of(base, struct vout_top_crg, base); +} + +static int jh7110_vout_top_crg_init(struct jh71x0_clk_priv *priv, struct vout_top_crg *top) +{ + struct reset_control *top_rst; + int ret; + + top->top_clks = jh7110_vout_top_clks; + top->top_clks_num = ARRAY_SIZE(jh7110_vout_top_clks); + ret = devm_clk_bulk_get(priv->dev, top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to get top clocks\n"); + + /* The reset should be shared and other Vout modules will use its. */ + top_rst = devm_reset_control_get_shared(priv->dev, NULL); + if (IS_ERR(top_rst)) + return dev_err_probe(priv->dev, PTR_ERR(top_rst), "failed to get top reset\n"); + + ret = clk_bulk_prepare_enable(top->top_clks_num, top->top_clks); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to enable top clocks\n"); + + return reset_control_deassert(top_rst); +} + +static struct clk_hw *jh7110_voutclk_get(struct of_phandle_args *clkspec, void *data) +{ + struct jh71x0_clk_priv *priv = data; + unsigned int idx = clkspec->args[0]; + + if (idx < JH7110_VOUTCLK_END) + return &priv->reg[idx].hw; + + return ERR_PTR(-EINVAL); +} + +static int jh7110_voutcrg_probe(struct platform_device *pdev) +{ + struct jh71x0_clk_priv *priv; + struct vout_top_crg *top; + unsigned int idx; + int ret; + + priv = devm_kzalloc(&pdev->dev, + struct_size(priv, reg, JH7110_VOUTCLK_END), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + top = devm_kzalloc(&pdev->dev, sizeof(*top), GFP_KERNEL); + if (!top) + return -ENOMEM; + + spin_lock_init(&priv->rmw_lock); + priv->dev = &pdev->dev; + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + pm_runtime_enable(priv->dev); + ret = pm_runtime_get_sync(priv->dev); + if (ret < 0) + return dev_err_probe(priv->dev, ret, "failed to turn on power\n"); + + ret = jh7110_vout_top_crg_init(priv, top); + if (ret) + goto err_clk; + + top->base = priv->base; + dev_set_drvdata(priv->dev, (void *)(&top->base)); + + for (idx = 0; idx < JH7110_VOUTCLK_END; idx++) { + u32 max = jh7110_voutclk_data[idx].max; + struct clk_parent_data parents[4] = {}; + struct clk_init_data init = { + .name = jh7110_voutclk_data[idx].name, + .ops = starfive_jh71x0_clk_ops(max), + .parent_data = parents, + .num_parents = + ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, + .flags = jh7110_voutclk_data[idx].flags, + }; + struct jh71x0_clk *clk = &priv->reg[idx]; + unsigned int i; + const char *fw_name[JH7110_VOUTCLK_EXT_END - JH7110_VOUTCLK_END] = { + "vout_src", + "vout_top_ahb", + "vout_top_axi", + "vout_top_hdmitx0_mclk", + "i2stx0_bclk", + "hdmitx0_pixelclk" + }; + + for (i = 0; i < init.num_parents; i++) { + unsigned int pidx = jh7110_voutclk_data[idx].parents[i]; + + if (pidx < JH7110_VOUTCLK_END) + parents[i].hw = &priv->reg[pidx].hw; + else if (pidx < JH7110_VOUTCLK_EXT_END) + parents[i].fw_name = fw_name[pidx - JH7110_VOUTCLK_END]; + } + + clk->hw.init = &init; + clk->idx = idx; + clk->max_div = max & JH71X0_CLK_DIV_MASK; + + ret = devm_clk_hw_register(&pdev->dev, &clk->hw); + if (ret) + goto err_exit; + } + + ret = devm_of_clk_add_hw_provider(&pdev->dev, jh7110_voutclk_get, priv); + if (ret) + goto err_exit; + + ret = jh7110_reset_controller_register(priv, "reset-vout", 4); + if (ret) + goto err_exit; + + return 0; + +err_exit: + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); +err_clk: + pm_runtime_put_sync(priv->dev); + pm_runtime_disable(priv->dev); + return ret; +} + +static int jh7110_voutcrg_remove(struct platform_device *pdev) +{ + void __iomem **base = dev_get_drvdata(&pdev->dev); + struct vout_top_crg *top = top_crg_from(base); + + clk_bulk_disable_unprepare(top->top_clks_num, top->top_clks); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id jh7110_voutcrg_match[] = { + { .compatible = "starfive,jh7110-voutcrg" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, jh7110_voutcrg_match); + +static struct platform_driver jh7110_voutcrg_driver = { + .probe = jh7110_voutcrg_probe, + .remove = jh7110_voutcrg_remove, + .driver = { + .name = "clk-starfive-jh7110-vout", + .of_match_table = jh7110_voutcrg_match, + }, +}; +module_platform_driver(jh7110_voutcrg_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive JH7110 Video-Output clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.c b/drivers/clk/starfive/clk-starfive-jh71x0.c new file mode 100644 index 0000000000000..a8c60630ae731 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh71x0.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive JH71X0 Clock Generator Driver + * + * Copyright (C) 2021-2022 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include + +#include "clk-starfive-jh71x0.h" + +static struct jh71x0_clk *jh71x0_clk_from(struct clk_hw *hw) +{ + return container_of(hw, struct jh71x0_clk, hw); +} + +static struct jh71x0_clk_priv *jh71x0_priv_from(struct jh71x0_clk *clk) +{ + return container_of(clk, struct jh71x0_clk_priv, reg[clk->idx]); +} + +static u32 jh71x0_clk_reg_get(struct jh71x0_clk *clk) +{ + struct jh71x0_clk_priv *priv = jh71x0_priv_from(clk); + void __iomem *reg = priv->base + 4 * clk->idx; + + return readl_relaxed(reg); +} + +static void jh71x0_clk_reg_rmw(struct jh71x0_clk *clk, u32 mask, u32 value) +{ + struct jh71x0_clk_priv *priv = jh71x0_priv_from(clk); + void __iomem *reg = priv->base + 4 * clk->idx; + unsigned long flags; + + spin_lock_irqsave(&priv->rmw_lock, flags); + value |= readl_relaxed(reg) & ~mask; + writel_relaxed(value, reg); + spin_unlock_irqrestore(&priv->rmw_lock, flags); +} + +static int jh71x0_clk_enable(struct clk_hw *hw) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_ENABLE, JH71X0_CLK_ENABLE); + return 0; +} + +static void jh71x0_clk_disable(struct clk_hw *hw) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_ENABLE, 0); +} + +static int jh71x0_clk_is_enabled(struct clk_hw *hw) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + + return !!(jh71x0_clk_reg_get(clk) & JH71X0_CLK_ENABLE); +} + +static unsigned long jh71x0_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 div = jh71x0_clk_reg_get(clk) & JH71X0_CLK_DIV_MASK; + + return div ? parent_rate / div : 0; +} + +static int jh71x0_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + unsigned long parent = req->best_parent_rate; + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); + unsigned long div = min_t(unsigned long, DIV_ROUND_UP(parent, rate), clk->max_div); + unsigned long result = parent / div; + + /* + * we want the result clamped by min_rate and max_rate if possible: + * case 1: div hits the max divider value, which means it's less than + * parent / rate, so the result is greater than rate and min_rate in + * particular. we can't do anything about result > max_rate because the + * divider doesn't go any further. + * case 2: div = DIV_ROUND_UP(parent, rate) which means the result is + * always lower or equal to rate and max_rate. however the result may + * turn out lower than min_rate, but then the next higher rate is fine: + * div - 1 = ceil(parent / rate) - 1 < parent / rate + * and thus + * min_rate <= rate < parent / (div - 1) + */ + if (result < req->min_rate && div > 1) + result = parent / (div - 1); + + req->rate = result; + return 0; +} + +static int jh71x0_clk_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + unsigned long div = clamp(DIV_ROUND_CLOSEST(parent_rate, rate), + 1UL, (unsigned long)clk->max_div); + + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_DIV_MASK, div); + return 0; +} + +static unsigned long jh71x0_clk_frac_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 reg = jh71x0_clk_reg_get(clk); + unsigned long div100 = 100 * (reg & JH71X0_CLK_INT_MASK) + + ((reg & JH71X0_CLK_FRAC_MASK) >> JH71X0_CLK_FRAC_SHIFT); + + return (div100 >= JH71X0_CLK_FRAC_MIN) ? 100 * parent_rate / div100 : 0; +} + +static int jh71x0_clk_frac_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + unsigned long parent100 = 100 * req->best_parent_rate; + unsigned long rate = clamp(req->rate, req->min_rate, req->max_rate); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(parent100, rate), + JH71X0_CLK_FRAC_MIN, JH71X0_CLK_FRAC_MAX); + unsigned long result = parent100 / div100; + + /* clamp the result as in jh71x0_clk_determine_rate() above */ + if (result > req->max_rate && div100 < JH71X0_CLK_FRAC_MAX) + result = parent100 / (div100 + 1); + if (result < req->min_rate && div100 > JH71X0_CLK_FRAC_MIN) + result = parent100 / (div100 - 1); + + req->rate = result; + return 0; +} + +static int jh71x0_clk_frac_set_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + unsigned long div100 = clamp(DIV_ROUND_CLOSEST(100 * parent_rate, rate), + JH71X0_CLK_FRAC_MIN, JH71X0_CLK_FRAC_MAX); + u32 value = ((div100 % 100) << JH71X0_CLK_FRAC_SHIFT) | (div100 / 100); + + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_DIV_MASK, value); + return 0; +} + +static u8 jh71x0_clk_get_parent(struct clk_hw *hw) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 value = jh71x0_clk_reg_get(clk); + + return (value & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT; +} + +static int jh71x0_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 value = (u32)index << JH71X0_CLK_MUX_SHIFT; + + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_MUX_MASK, value); + return 0; +} + +static int jh71x0_clk_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + return clk_mux_determine_rate_flags(hw, req, 0); +} + +static int jh71x0_clk_get_phase(struct clk_hw *hw) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 value = jh71x0_clk_reg_get(clk); + + return (value & JH71X0_CLK_INVERT) ? 180 : 0; +} + +static int jh71x0_clk_set_phase(struct clk_hw *hw, int degrees) +{ + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + u32 value; + + if (degrees == 0) + value = 0; + else if (degrees == 180) + value = JH71X0_CLK_INVERT; + else + return -EINVAL; + + jh71x0_clk_reg_rmw(clk, JH71X0_CLK_INVERT, value); + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void jh71x0_clk_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + static const struct debugfs_reg32 jh71x0_clk_reg = { + .name = "CTRL", + .offset = 0, + }; + struct jh71x0_clk *clk = jh71x0_clk_from(hw); + struct jh71x0_clk_priv *priv = jh71x0_priv_from(clk); + struct debugfs_regset32 *regset; + + regset = devm_kzalloc(priv->dev, sizeof(*regset), GFP_KERNEL); + if (!regset) + return; + + regset->regs = &jh71x0_clk_reg; + regset->nregs = 1; + regset->base = priv->base + 4 * clk->idx; + + debugfs_create_regset32("registers", 0400, dentry, regset); +} +#else +#define jh71x0_clk_debug_init NULL +#endif + +static const struct clk_ops jh71x0_clk_gate_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_div_ops = { + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_fdiv_ops = { + .recalc_rate = jh71x0_clk_frac_recalc_rate, + .determine_rate = jh71x0_clk_frac_determine_rate, + .set_rate = jh71x0_clk_frac_set_rate, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_gdiv_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_mux_ops = { + .determine_rate = jh71x0_clk_mux_determine_rate, + .set_parent = jh71x0_clk_set_parent, + .get_parent = jh71x0_clk_get_parent, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_gmux_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .determine_rate = jh71x0_clk_mux_determine_rate, + .set_parent = jh71x0_clk_set_parent, + .get_parent = jh71x0_clk_get_parent, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_mdiv_ops = { + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .get_parent = jh71x0_clk_get_parent, + .set_parent = jh71x0_clk_set_parent, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_gmd_ops = { + .enable = jh71x0_clk_enable, + .disable = jh71x0_clk_disable, + .is_enabled = jh71x0_clk_is_enabled, + .recalc_rate = jh71x0_clk_recalc_rate, + .determine_rate = jh71x0_clk_determine_rate, + .get_parent = jh71x0_clk_get_parent, + .set_parent = jh71x0_clk_set_parent, + .set_rate = jh71x0_clk_set_rate, + .debug_init = jh71x0_clk_debug_init, +}; + +static const struct clk_ops jh71x0_clk_inv_ops = { + .get_phase = jh71x0_clk_get_phase, + .set_phase = jh71x0_clk_set_phase, + .debug_init = jh71x0_clk_debug_init, +}; + +const struct clk_ops *starfive_jh71x0_clk_ops(u32 max) +{ + if (max & JH71X0_CLK_DIV_MASK) { + if (max & JH71X0_CLK_MUX_MASK) { + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gmd_ops; + return &jh71x0_clk_mdiv_ops; + } + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gdiv_ops; + if (max == JH71X0_CLK_FRAC_MAX) + return &jh71x0_clk_fdiv_ops; + return &jh71x0_clk_div_ops; + } + + if (max & JH71X0_CLK_MUX_MASK) { + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gmux_ops; + return &jh71x0_clk_mux_ops; + } + + if (max & JH71X0_CLK_ENABLE) + return &jh71x0_clk_gate_ops; + + return &jh71x0_clk_inv_ops; +} +EXPORT_SYMBOL_GPL(starfive_jh71x0_clk_ops); + +#if IS_ENABLED(CONFIG_CLK_STARFIVE_JH7110_SYS) + +static void jh7110_reset_unregister_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); +} + +static void jh7110_reset_adev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + auxiliary_device_uninit(adev); +} + +int jh7110_reset_controller_register(struct jh71x0_clk_priv *priv, + const char *adev_name, + u32 adev_id) +{ + struct auxiliary_device *adev; + int ret; + + adev = devm_kzalloc(priv->dev, sizeof(*adev), GFP_KERNEL); + if (!adev) + return -ENOMEM; + + adev->name = adev_name; + adev->dev.parent = priv->dev; + adev->dev.release = jh7110_reset_adev_release; + adev->id = adev_id; + + ret = auxiliary_device_init(adev); + if (ret) + return ret; + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + return devm_add_action_or_reset(priv->dev, + jh7110_reset_unregister_adev, adev); +} +EXPORT_SYMBOL_GPL(jh7110_reset_controller_register); + +#endif diff --git a/drivers/clk/starfive/clk-starfive-jh71x0.h b/drivers/clk/starfive/clk-starfive-jh71x0.h new file mode 100644 index 0000000000000..cfe69aabd65a3 --- /dev/null +++ b/drivers/clk/starfive/clk-starfive-jh71x0.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __CLK_STARFIVE_JH71X0_H +#define __CLK_STARFIVE_JH71X0_H + +#include +#include +#include +#include + +/* register fields */ +#define JH71X0_CLK_ENABLE BIT(31) +#define JH71X0_CLK_INVERT BIT(30) +#define JH71X0_CLK_MUX_MASK GENMASK(27, 24) +#define JH71X0_CLK_MUX_SHIFT 24 +#define JH71X0_CLK_DIV_MASK GENMASK(23, 0) +#define JH71X0_CLK_FRAC_MASK GENMASK(15, 8) +#define JH71X0_CLK_FRAC_SHIFT 8 +#define JH71X0_CLK_INT_MASK GENMASK(7, 0) + +/* fractional divider min/max */ +#define JH71X0_CLK_FRAC_MIN 100UL +#define JH71X0_CLK_FRAC_MAX 25599UL + +/* clock data */ +struct jh71x0_clk_data { + const char *name; + unsigned long flags; + u32 max; + u8 parents[4]; +}; + +#define JH71X0_GATE(_idx, _name, _flags, _parent) \ +[_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT | (_flags), \ + .max = JH71X0_CLK_ENABLE, \ + .parents = { [0] = _parent }, \ +} + +#define JH71X0__DIV(_idx, _name, _max, _parent) \ +[_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = _max, \ + .parents = { [0] = _parent }, \ +} + +#define JH71X0_GDIV(_idx, _name, _flags, _max, _parent) \ +[_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH71X0_CLK_ENABLE | (_max), \ + .parents = { [0] = _parent }, \ +} + +#define JH71X0_FDIV(_idx, _name, _parent) \ +[_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = JH71X0_CLK_FRAC_MAX, \ + .parents = { [0] = _parent }, \ +} + +#define JH71X0__MUX(_idx, _name, _nparents, ...) \ +[_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = ((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT, \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH71X0_GMUX(_idx, _name, _flags, _nparents, ...) \ +[_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH71X0_CLK_ENABLE | \ + (((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH71X0_MDIV(_idx, _name, _max, _nparents, ...) \ +[_idx] = { \ + .name = _name, \ + .flags = 0, \ + .max = (((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT) | (_max), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH71X0__GMD(_idx, _name, _flags, _max, _nparents, ...) \ +[_idx] = { \ + .name = _name, \ + .flags = _flags, \ + .max = JH71X0_CLK_ENABLE | \ + (((_nparents) - 1) << JH71X0_CLK_MUX_SHIFT) | (_max), \ + .parents = { __VA_ARGS__ }, \ +} + +#define JH71X0__INV(_idx, _name, _parent) \ +[_idx] = { \ + .name = _name, \ + .flags = CLK_SET_RATE_PARENT, \ + .max = JH71X0_CLK_INVERT, \ + .parents = { [0] = _parent }, \ +} + +struct jh71x0_clk { + struct clk_hw hw; + unsigned int idx; + unsigned int max_div; +}; + +struct jh71x0_clk_priv { + /* protect clk enable and set rate/parent from happening at the same time */ + spinlock_t rmw_lock; + struct device *dev; + void __iomem *base; + struct clk_hw *pll[3]; + struct jh71x0_clk reg[]; +}; + +const struct clk_ops *starfive_jh71x0_clk_ops(u32 max); + +#if IS_ENABLED(CONFIG_CLK_STARFIVE_JH7110_SYS) + +int jh7110_reset_controller_register(struct jh71x0_clk_priv *priv, + const char *adev_name, + u32 adev_id); + +#endif + +#endif diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 5fc8f0e7fb382..35daf07d4db7b 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -630,6 +630,18 @@ config RISCV_TIMER is accessed via both the SBI and the rdcycle instruction. This is required for all RISC-V systems. +config STARFIVE_TIMER + bool "Timer for the STARFIVE SoCs" + depends on ARCH_STARFIVE || COMPILE_TEST + select TIMER_OF + select CLKSRC_MMIO + default ARCH_STARFIVE + help + This enables the timer for StarFive SoCs. On RISC-V platform, + the system has started RISCV_TIMER. But you can also use this timer + to do a lot more on StarFive SoCs. This timer can provide high + precision and four channels to use in JH7110 SoC. + config CLINT_TIMER bool "CLINT Timer for the RISC-V platform" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && RISCV diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 64ab547de97b9..276695d95cdc3 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o +obj-$(CONFIG_STARFIVE_TIMER) += timer-starfive.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o diff --git a/drivers/clocksource/timer-starfive.c b/drivers/clocksource/timer-starfive.c new file mode 100644 index 0000000000000..53163948ed868 --- /dev/null +++ b/drivers/clocksource/timer-starfive.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Starfive Timer driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * + * Author: + * Xingyu Wu + * Samin Guo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "timer-starfive.h" + +static const struct starfive_timer_chan_base starfive_timer_jh7110_base = { + .ctrl = STARFIVE_TIMER_JH7110_CTL, + .load = STARFIVE_TIMER_JH7110_LOAD, + .enable = STARFIVE_TIMER_JH7110_ENABLE, + .reload = STARFIVE_TIMER_JH7110_RELOAD, + .value = STARFIVE_TIMER_JH7110_VALUE, + .intclr = STARFIVE_TIMER_JH7110_INT_CLR, + .intmask = STARFIVE_TIMER_JH7110_INT_MASK, + .channel_num = STARFIVE_TIMER_CH_4, + .channel_base = {STARFIVE_TIMER_CH_BASE(0), STARFIVE_TIMER_CH_BASE(1), + STARFIVE_TIMER_CH_BASE(2), STARFIVE_TIMER_CH_BASE(3)}, +}; + +static inline struct starfive_clkevt *to_starfive_clkevt(struct clock_event_device *evt) +{ + return container_of(evt, struct starfive_clkevt, evt); +} + +/* 0:continuous-run mode, 1:single-run mode */ +static inline void starfive_timer_set_mod(struct starfive_clkevt *clkevt, int mod) +{ + writel(mod, clkevt->ctrl); +} + +/* Interrupt Mask Register, 0:Unmask, 1:Mask */ +static inline void starfive_timer_int_enable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_INTMASK_DIS, clkevt->intmask); +} + +static inline void starfive_timer_int_disable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_INTMASK_ENA, clkevt->intmask); +} + +/* + * BIT(0): Read value represent channel intr status. + * Write 1 to this bit to clear interrupt. Write 0 has no effects. + * BIT(1): "1" means that it is clearing interrupt. BIT(0) can not be written. + */ +static inline int starfive_timer_int_clear(struct starfive_clkevt *clkevt) +{ + u32 value; + int ret; + + /* waiting interrupt can be to clearing */ + ret = readl_poll_timeout_atomic(clkevt->intclr, value, + !(value & STARFIVE_TIMER_JH7110_INT_CLR_AVA_MASK), + STARFIVE_DELAY_US, STARFIVE_TIMEOUT_US); + if (!ret) + writel(0x1, clkevt->intclr); + + return ret; +} + +/* + * The initial value to be loaded into the + * counter and is also used as the reload value. + * val = clock rate --> 1s + */ +static inline void starfive_timer_set_load(struct starfive_clkevt *clkevt, u32 val) +{ + writel(val, clkevt->load); +} + +static inline u32 starfive_timer_get_val(struct starfive_clkevt *clkevt) +{ + return readl(clkevt->value); +} + +/* + * Write RELOAD register to reload preset value to counter. + * (Write 0 and write 1 are both ok) + */ +static inline void starfive_timer_set_reload(struct starfive_clkevt *clkevt) +{ + writel(0, clkevt->reload); +} + +static inline void starfive_timer_enable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_ENA, clkevt->enable); +} + +static inline void starfive_timer_disable(struct starfive_clkevt *clkevt) +{ + writel(STARFIVE_TIMER_DIS, clkevt->enable); +} + +static int starfive_timer_int_init_enable(struct starfive_clkevt *clkevt) +{ + int ret; + + starfive_timer_int_disable(clkevt); + ret = starfive_timer_int_clear(clkevt); + if (ret) + return ret; + + starfive_timer_int_enable(clkevt); + starfive_timer_enable(clkevt); + + return 0; +} + +static int starfive_timer_shutdown(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + return starfive_timer_int_clear(clkevt); +} + +static void starfive_timer_suspend(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + clkevt->reload_val = starfive_timer_get_val(clkevt); + starfive_timer_shutdown(evt); +} + +static void starfive_timer_resume(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_set_load(clkevt, clkevt->reload_val); + starfive_timer_set_reload(clkevt); + starfive_timer_int_enable(clkevt); + starfive_timer_enable(clkevt); +} + +static int starfive_timer_tick_resume(struct clock_event_device *evt) +{ + starfive_timer_resume(evt); + + return 0; +} + +static int starfive_clocksource_init(struct starfive_clkevt *clkevt) +{ + int ret; + + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_CONTIN); + starfive_timer_set_load(clkevt, STARFIVE_TIMER_MAX_TICKS); + ret = starfive_timer_int_init_enable(clkevt); + if (ret) + return ret; + + return clocksource_mmio_init(clkevt->value, clkevt->name, clkevt->rate, + STARFIVE_CLOCK_SOURCE_RATING, STARFIVE_VALID_BITS, + clocksource_mmio_readl_down); +} + +/* IRQ handler for the timer */ +static irqreturn_t starfive_timer_interrupt(int irq, void *priv) +{ + struct clock_event_device *evt = (struct clock_event_device *)priv; + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + if (starfive_timer_int_clear(clkevt)) + return IRQ_NONE; + + if (evt->event_handler) + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static int starfive_timer_set_periodic(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_CONTIN); + starfive_timer_set_load(clkevt, clkevt->periodic); + + return starfive_timer_int_init_enable(clkevt); +} + +static int starfive_timer_set_oneshot(struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_SINGLE); + starfive_timer_set_load(clkevt, STARFIVE_TIMER_MAX_TICKS); + + return starfive_timer_int_init_enable(clkevt); +} + +static int starfive_timer_set_next_event(unsigned long next, + struct clock_event_device *evt) +{ + struct starfive_clkevt *clkevt = to_starfive_clkevt(evt); + + starfive_timer_disable(clkevt); + starfive_timer_set_mod(clkevt, STARFIVE_TIMER_MOD_SINGLE); + starfive_timer_set_load(clkevt, next); + starfive_timer_enable(clkevt); + + return 0; +} + +static void starfive_set_clockevent(struct clock_event_device *evt) +{ + evt->features = CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_DYNIRQ; + evt->set_state_shutdown = starfive_timer_shutdown; + evt->set_state_periodic = starfive_timer_set_periodic; + evt->set_state_oneshot = starfive_timer_set_oneshot; + evt->set_state_oneshot_stopped = starfive_timer_shutdown; + evt->tick_resume = starfive_timer_tick_resume; + evt->set_next_event = starfive_timer_set_next_event; + evt->suspend = starfive_timer_suspend; + evt->resume = starfive_timer_resume; + evt->rating = STARFIVE_CLOCKEVENT_RATING; +} + +static void starfive_clockevents_register(struct starfive_clkevt *clkevt) +{ + clkevt->rate = clk_get_rate(clkevt->clk); + clkevt->periodic = DIV_ROUND_CLOSEST(clkevt->rate, HZ); + + starfive_set_clockevent(&clkevt->evt); + clkevt->evt.name = clkevt->name; + clkevt->evt.irq = clkevt->irq; + clkevt->evt.cpumask = cpu_possible_mask; + + clockevents_config_and_register(&clkevt->evt, clkevt->rate, + STARFIVE_TIMER_MIN_TICKS, STARFIVE_TIMER_MAX_TICKS); +} + +static void __init starfive_clkevt_base_init(const struct starfive_timer_chan_base *timer, + struct starfive_clkevt *clkevt, + void __iomem *base, int ch) +{ + void __iomem *channel_base; + + channel_base = base + timer->channel_base[ch]; + clkevt->base = channel_base; + clkevt->ctrl = channel_base + timer->ctrl; + clkevt->load = channel_base + timer->load; + clkevt->enable = channel_base + timer->enable; + clkevt->reload = channel_base + timer->reload; + clkevt->value = channel_base + timer->value; + clkevt->intclr = channel_base + timer->intclr; + clkevt->intmask = channel_base + timer->intmask; +} + +static int __init starfive_timer_probe(struct platform_device *pdev) +{ + const struct starfive_timer_chan_base *timer_base = of_device_get_match_data(&pdev->dev); + char name[10]; + struct starfive_timer_priv *priv; + struct starfive_clkevt *clkevt; + struct clk *pclk; + struct reset_control *rst; + int ch; + int ret; + + priv = devm_kzalloc(&pdev->dev, struct_size(priv, clkevt, timer_base->channel_num), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->base), + "failed to map registers\n"); + + rst = devm_reset_control_get_exclusive(&pdev->dev, "apb"); + if (IS_ERR(rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(rst), "failed to get apb reset\n"); + + pclk = devm_clk_get_enabled(&pdev->dev, "apb"); + if (IS_ERR(pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pclk), + "failed to get & enable apb clock\n"); + + ret = reset_control_deassert(rst); + if (ret) + goto err; + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + for (ch = 0; ch < timer_base->channel_num; ch++) { + clkevt = &priv->clkevt[ch]; + snprintf(name, sizeof(name), "ch%d", ch); + + starfive_clkevt_base_init(timer_base, clkevt, priv->base, ch); + /* Ensure timers are disabled */ + starfive_timer_disable(clkevt); + + rst = devm_reset_control_get_exclusive(&pdev->dev, name); + if (IS_ERR(rst)) { + ret = PTR_ERR(rst); + goto err; + } + + clkevt->clk = devm_clk_get_enabled(&pdev->dev, name); + if (IS_ERR(clkevt->clk)) { + ret = PTR_ERR(clkevt->clk); + goto err; + } + + ret = reset_control_deassert(rst); + if (ret) + goto ch_err; + + clkevt->irq = platform_get_irq(pdev, ch); + if (clkevt->irq < 0) { + ret = clkevt->irq; + goto ch_err; + } + + snprintf(clkevt->name, sizeof(clkevt->name), "%s.ch%d", pdev->name, ch); + starfive_clockevents_register(clkevt); + + ret = devm_request_irq(&pdev->dev, clkevt->irq, starfive_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, + clkevt->name, &clkevt->evt); + if (ret) + goto ch_err; + + ret = starfive_clocksource_init(clkevt); + if (ret) + goto ch_err; + } + + return 0; + +ch_err: + /* Only unregister the failed channel and the rest timer channels continue to work. */ + clk_disable_unprepare(clkevt->clk); +err: + /* If no other channel successfully registers, pclk should be disabled. */ + if (!ch) + clk_disable_unprepare(pclk); + + return ret; +} + +static const struct of_device_id starfive_timer_match[] = { + { .compatible = "starfive,jh7110-timer", .data = &starfive_timer_jh7110_base }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_timer_match); + +static struct platform_driver starfive_timer_driver = { + .probe = starfive_timer_probe, + .driver = { + .name = "starfive-timer", + .of_match_table = starfive_timer_match, + }, +}; +module_platform_driver(starfive_timer_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_DESCRIPTION("StarFive timer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clocksource/timer-starfive.h b/drivers/clocksource/timer-starfive.h new file mode 100644 index 0000000000000..62eb630cc98d6 --- /dev/null +++ b/drivers/clocksource/timer-starfive.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#ifndef __STARFIVE_TIMER_H__ +#define __STARFIVE_TIMER_H__ + +/* Bias: Ch0-0x0, Ch1-0x40, Ch2-0x80, and so on. */ +#define STARFIVE_TIMER_CH_LEN 0x40 +#define STARFIVE_TIMER_CH_BASE(x) ((STARFIVE_TIMER_CH_##x) * STARFIVE_TIMER_CH_LEN) + +#define STARFIVE_CLOCK_SOURCE_RATING 200 +#define STARFIVE_VALID_BITS 32 +#define STARFIVE_DELAY_US 0 +#define STARFIVE_TIMEOUT_US 10000 +#define STARFIVE_CLOCKEVENT_RATING 300 +#define STARFIVE_TIMER_MAX_TICKS 0xffffffff +#define STARFIVE_TIMER_MIN_TICKS 0xf + +#define STARFIVE_TIMER_JH7110_INT_STATUS 0x00 /* RO[0:4]: Interrupt Status for channel0~4 */ +#define STARFIVE_TIMER_JH7110_CTL 0x04 /* RW[0]: 0-continuous run, 1-single run */ +#define STARFIVE_TIMER_JH7110_LOAD 0x08 /* RW: load value to counter */ +#define STARFIVE_TIMER_JH7110_ENABLE 0x10 /* RW[0]: timer enable register */ +#define STARFIVE_TIMER_JH7110_RELOAD 0x14 /* RW: write 1 or 0 both reload counter */ +#define STARFIVE_TIMER_JH7110_VALUE 0x18 /* RO: timer value register */ +#define STARFIVE_TIMER_JH7110_INT_CLR 0x20 /* RW: timer interrupt clear register */ +#define STARFIVE_TIMER_JH7110_INT_MASK 0x24 /* RW[0]: timer interrupt mask register */ +#define STARFIVE_TIMER_JH7110_INT_CLR_AVA_MASK BIT(1) + +enum STARFIVE_TIMER_CH { + STARFIVE_TIMER_CH_0 = 0, + STARFIVE_TIMER_CH_1, + STARFIVE_TIMER_CH_2, + STARFIVE_TIMER_CH_3, + STARFIVE_TIMER_CH_4, + STARFIVE_TIMER_CH_5, + STARFIVE_TIMER_CH_6, + STARFIVE_TIMER_CH_7, + STARFIVE_TIMER_CH_MAX +}; + +enum STARFIVE_TIMER_INTMASK { + STARFIVE_TIMER_INTMASK_DIS = 0, + STARFIVE_TIMER_INTMASK_ENA = 1 +}; + +enum STARFIVE_TIMER_MOD { + STARFIVE_TIMER_MOD_CONTIN = 0, + STARFIVE_TIMER_MOD_SINGLE = 1 +}; + +enum STARFIVE_TIMER_CTL_EN { + STARFIVE_TIMER_DIS = 0, + STARFIVE_TIMER_ENA = 1 +}; + +struct starfive_timer_chan_base { + /* Resgister */ + unsigned int ctrl; + unsigned int load; + unsigned int enable; + unsigned int reload; + unsigned int value; + unsigned int intclr; + unsigned int intmask; + + unsigned int channel_num; /* timer channel numbers */ + unsigned int channel_base[]; +}; + +struct starfive_clkevt { + struct clock_event_device evt; + struct clk *clk; + char name[20]; + int irq; + u32 periodic; + u32 rate; + u32 reload_val; + void __iomem *base; + void __iomem *ctrl; + void __iomem *load; + void __iomem *enable; + void __iomem *reload; + void __iomem *value; + void __iomem *intclr; + void __iomem *intmask; +}; + +struct starfive_timer_priv { + struct device *dev; + void __iomem *base; + struct starfive_clkevt clkevt[]; +}; + +#endif /* __STARFIVE_TIMER_H__ */ diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 3b2516d1433f7..c9b6a8747fa12 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -822,5 +822,6 @@ config CRYPTO_DEV_SA2UL source "drivers/crypto/keembay/Kconfig" source "drivers/crypto/aspeed/Kconfig" +source "drivers/crypto/starfive/Kconfig" endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 476f1a25ca326..5ff5242a18244 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -52,3 +52,4 @@ obj-y += xilinx/ obj-y += hisilicon/ obj-$(CONFIG_CRYPTO_DEV_AMLOGIC_GXL) += amlogic/ obj-y += keembay/ +obj-y += starfive/ diff --git a/drivers/crypto/starfive/Kconfig b/drivers/crypto/starfive/Kconfig new file mode 100644 index 0000000000000..cde485910f884 --- /dev/null +++ b/drivers/crypto/starfive/Kconfig @@ -0,0 +1,21 @@ +# +# StarFive crypto drivers configuration +# + +config CRYPTO_DEV_JH7110 + tristate "StarFive JH7110 cryptographic engine driver" + depends on SOC_STARFIVE + select CRYPTO_ENGINE + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_SM3_GENERIC + select ARM_AMBA + select DMADEVICES + select AMBA_PL08X + help + Support for StarFive JH7110 crypto hardware acceleration engine. + This module provides acceleration for public key algo, + skciphers, AEAD and hash functions. + + If you choose 'M' here, this module will be called starfive-crypto. diff --git a/drivers/crypto/starfive/Makefile b/drivers/crypto/starfive/Makefile new file mode 100644 index 0000000000000..2af49062e36d9 --- /dev/null +++ b/drivers/crypto/starfive/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_CRYPTO_DEV_JH7110) += jh7110-crypto.o +jh7110-crypto-objs := jh7110-cryp.o jh7110-hash.o diff --git a/drivers/crypto/starfive/jh7110-cryp.c b/drivers/crypto/starfive/jh7110-cryp.c new file mode 100644 index 0000000000000..94c99aab308f7 --- /dev/null +++ b/drivers/crypto/starfive/jh7110-cryp.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cryptographic API. + * + * Support for StarFive hardware cryptographic engine. + * Copyright (c) 2022 StarFive Technology + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jh7110-cryp.h" + +#define DRIVER_NAME "starfive-crypto" + +struct starfive_dev_list { + struct list_head dev_list; + spinlock_t lock; /* protect dev_list */ +}; + +static struct starfive_dev_list dev_list = { + .dev_list = LIST_HEAD_INIT(dev_list.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(dev_list.lock), +}; + +struct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = NULL, *tmp; + + spin_lock_bh(&dev_list.lock); + if (!ctx->cryp) { + list_for_each_entry(tmp, &dev_list.dev_list, list) { + cryp = tmp; + break; + } + ctx->cryp = cryp; + } else { + cryp = ctx->cryp; + } + + spin_unlock_bh(&dev_list.lock); + + return cryp; +} + +static int starfive_dma_init(struct starfive_cryp_dev *cryp) +{ + dma_cap_mask_t mask; + + cryp->tx = NULL; + cryp->rx = NULL; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + cryp->tx = dma_request_chan(cryp->dev, "tx"); + if (IS_ERR(cryp->tx)) + return dev_err_probe(cryp->dev, PTR_ERR(cryp->tx), + "Error requesting tx dma channel.\n"); + + cryp->rx = dma_request_chan(cryp->dev, "rx"); + if (IS_ERR(cryp->rx)) { + dma_release_channel(cryp->tx); + return dev_err_probe(cryp->dev, PTR_ERR(cryp->rx), + "Error requesting rx dma channel.\n"); + } + + init_completion(&cryp->tx_comp); + init_completion(&cryp->rx_comp); + + return 0; +} + +static void starfive_dma_cleanup(struct starfive_cryp_dev *cryp) +{ + dma_release_channel(cryp->tx); + dma_release_channel(cryp->rx); +} + +static irqreturn_t starfive_cryp_irq(int irq, void *priv) +{ + u32 status; + struct starfive_cryp_dev *cryp = (struct starfive_cryp_dev *)priv; + + status = readl(cryp->base + STARFIVE_IE_FLAG_OFFSET); + if (status & STARFIVE_IE_FLAG_HASH_DONE) { + writel(STARFIVE_IE_MASK_HASH_DONE, cryp->base + STARFIVE_IE_MASK_OFFSET); + complete(&cryp->hash_done); + } + + return IRQ_HANDLED; +} + +static int starfive_cryp_probe(struct platform_device *pdev) +{ + struct starfive_cryp_dev *cryp; + struct resource *res; + int irq; + int ret; + + cryp = devm_kzalloc(&pdev->dev, sizeof(*cryp), GFP_KERNEL); + if (!cryp) + return -ENOMEM; + + platform_set_drvdata(pdev, cryp); + cryp->dev = &pdev->dev; + + cryp->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(cryp->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->base), + "Error remapping memory for platform device\n"); + + cryp->phys_base = res->start; + cryp->dma_maxburst = 32; + + cryp->hclk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(cryp->hclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->hclk), + "Error getting hardware reference clock\n"); + + cryp->ahb = devm_clk_get(&pdev->dev, "ahb"); + if (IS_ERR(cryp->ahb)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->ahb), + "Error getting ahb reference clock\n"); + + cryp->rst = devm_reset_control_get_shared(cryp->dev, NULL); + if (IS_ERR(cryp->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(cryp->rst), + "Error getting hardware reset line\n"); + + init_completion(&cryp->hash_done); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, starfive_cryp_irq, 0, pdev->name, + (void *)cryp); + if (ret) + return dev_err_probe(&pdev->dev, irq, + "Failed to register interrupt handler\n"); + + clk_prepare_enable(cryp->hclk); + clk_prepare_enable(cryp->ahb); + reset_control_deassert(cryp->rst); + + spin_lock(&dev_list.lock); + list_add(&cryp->list, &dev_list.dev_list); + spin_unlock(&dev_list.lock); + + ret = starfive_dma_init(cryp); + if (ret) + goto err_dma_init; + + /* Initialize crypto engine */ + cryp->engine = crypto_engine_alloc_init(&pdev->dev, 1); + if (!cryp->engine) { + ret = -ENOMEM; + goto err_engine; + } + + ret = crypto_engine_start(cryp->engine); + if (ret) + goto err_engine_start; + + ret = starfive_hash_register_algs(); + if (ret) + goto err_algs_hash; + + return 0; + +err_algs_hash: + crypto_engine_stop(cryp->engine); +err_engine_start: + crypto_engine_exit(cryp->engine); +err_engine: + starfive_dma_cleanup(cryp); +err_dma_init: + spin_lock(&dev_list.lock); + list_del(&cryp->list); + spin_unlock(&dev_list.lock); + + return ret; +} + +static int starfive_cryp_remove(struct platform_device *pdev) +{ + struct starfive_cryp_dev *cryp = platform_get_drvdata(pdev); + + if (!cryp) + return -ENODEV; + + starfive_hash_unregister_algs(); + + crypto_engine_stop(cryp->engine); + crypto_engine_exit(cryp->engine); + + starfive_dma_cleanup(cryp); + + spin_lock(&dev_list.lock); + list_del(&cryp->list); + spin_unlock(&dev_list.lock); + + clk_disable_unprepare(cryp->hclk); + clk_disable_unprepare(cryp->ahb); + reset_control_assert(cryp->rst); + + return 0; +} + +static const struct of_device_id starfive_dt_ids[] __maybe_unused = { + { .compatible = "starfive,jh7110-crypto", .data = NULL}, + {}, +}; +MODULE_DEVICE_TABLE(of, starfive_dt_ids); + +static struct platform_driver starfive_cryp_driver = { + .probe = starfive_cryp_probe, + .remove = starfive_cryp_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = starfive_dt_ids, + }, +}; + +module_platform_driver(starfive_cryp_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("StarFive Cryptographic Module"); diff --git a/drivers/crypto/starfive/jh7110-cryp.h b/drivers/crypto/starfive/jh7110-cryp.h new file mode 100644 index 0000000000000..28cc73308031d --- /dev/null +++ b/drivers/crypto/starfive/jh7110-cryp.h @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __STARFIVE_STR_H__ +#define __STARFIVE_STR_H__ + +#include +#include +#include + +#include +#include +#include + +#define STARFIVE_ALG_CR_OFFSET 0x0 +#define STARFIVE_ALG_FIFO_OFFSET 0x4 +#define STARFIVE_IE_MASK_OFFSET 0x8 +#define STARFIVE_IE_FLAG_OFFSET 0xc +#define STARFIVE_DMA_IN_LEN_OFFSET 0x10 +#define STARFIVE_DMA_OUT_LEN_OFFSET 0x14 + +#define STARFIVE_IE_MASK_HASH_DONE BIT(2) +#define STARFIVE_IE_FLAG_HASH_DONE BIT(2) + +#define STARFIVE_MSG_BUFFER_SIZE SZ_16K +#define MAX_KEY_SIZE SHA512_BLOCK_SIZE + +union starfive_hash_csr { + u32 v; + struct { + u32 start :1; + u32 reset :1; + u32 ie :1; + u32 firstb :1; +#define STARFIVE_HASH_SM3 0x0 +#define STARFIVE_HASH_SHA224 0x3 +#define STARFIVE_HASH_SHA256 0x4 +#define STARFIVE_HASH_SHA384 0x5 +#define STARFIVE_HASH_SHA512 0x6 +#define STARFIVE_HASH_MODE_MASK 0x7 + u32 mode :3; + u32 rsvd_1 :1; + u32 final :1; + u32 rsvd_2 :2; +#define STARFIVE_HASH_HMAC_FLAGS 0x800 + u32 hmac :1; + u32 rsvd_3 :1; +#define STARFIVE_HASH_KEY_DONE BIT(13) + u32 key_done :1; + u32 key_flag :1; +#define STARFIVE_HASH_HMAC_DONE BIT(15) + u32 hmac_done :1; +#define STARFIVE_HASH_BUSY BIT(16) + u32 busy :1; + u32 hashdone :1; + u32 rsvd_4 :14; + }; +}; + + +union starfive_alg_cr { + u32 v; + struct { + u32 start :1; + u32 aes_dma_en :1; + u32 rsvd_0 :1; + u32 hash_dma_en :1; + u32 alg_done :1; + u32 rsvd_1 :3; + u32 clear :1; + u32 rsvd_2 :23; + }; +}; + +struct starfive_cryp_ctx { + struct crypto_engine_ctx enginectx; + struct starfive_cryp_dev *cryp; + struct starfive_cryp_request_ctx *rctx; + + unsigned int hash_mode; + u8 key[MAX_KEY_SIZE]; + int keylen; + struct crypto_ahash *ahash_fbk; +}; + +struct starfive_cryp_dev { + struct list_head list; + struct device *dev; + struct clk *hclk; + struct clk *ahb; + struct reset_control *rst; + + void __iomem *base; + phys_addr_t phys_base; + + u32 dma_maxburst; + struct dma_chan *tx; + struct dma_chan *rx; + struct dma_slave_config cfg_in; + struct dma_slave_config cfg_out; + struct completion tx_comp; + struct completion rx_comp; + struct completion hash_done; + struct crypto_engine *engine; + + union starfive_alg_cr alg_cr; +}; + +struct starfive_cryp_request_ctx { + union { + struct ahash_request *hreq; + } req; + union { + union starfive_hash_csr hash; + } csr; + + struct scatterlist *in_sg; + struct scatterlist in_sgl; + struct ahash_request ahash_fbk_req; + size_t total; + unsigned int blksize; + unsigned int digsize; + unsigned long in_sg_len; + bool sgs_copied; +}; + +struct starfive_cryp_dev *starfive_cryp_find_dev(struct starfive_cryp_ctx *ctx); + +int starfive_hash_register_algs(void); +void starfive_hash_unregister_algs(void); + +#endif diff --git a/drivers/crypto/starfive/jh7110-hash.c b/drivers/crypto/starfive/jh7110-hash.c new file mode 100644 index 0000000000000..39570f517ccca --- /dev/null +++ b/drivers/crypto/starfive/jh7110-hash.c @@ -0,0 +1,1041 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hash function and HMAC support for StarFive driver + * + * Copyright (c) 2022 StarFive Technology + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "jh7110-cryp.h" + +#define STARFIVE_HASH_REGS_OFFSET 0x300 +#define STARFIVE_HASH_SHACSR (STARFIVE_HASH_REGS_OFFSET + 0x0) +#define STARFIVE_HASH_SHAWDR (STARFIVE_HASH_REGS_OFFSET + 0x4) +#define STARFIVE_HASH_SHARDR (STARFIVE_HASH_REGS_OFFSET + 0x8) +#define STARFIVE_HASH_SHAWSR (STARFIVE_HASH_REGS_OFFSET + 0xC) +#define STARFIVE_HASH_SHAWLEN3 (STARFIVE_HASH_REGS_OFFSET + 0x10) +#define STARFIVE_HASH_SHAWLEN2 (STARFIVE_HASH_REGS_OFFSET + 0x14) +#define STARFIVE_HASH_SHAWLEN1 (STARFIVE_HASH_REGS_OFFSET + 0x18) +#define STARFIVE_HASH_SHAWLEN0 (STARFIVE_HASH_REGS_OFFSET + 0x1C) +#define STARFIVE_HASH_SHAWKR (STARFIVE_HASH_REGS_OFFSET + 0x20) +#define STARFIVE_HASH_SHAWKLEN (STARFIVE_HASH_REGS_OFFSET + 0x24) + +#define STARFIVE_HASH_BUFLEN SHA512_BLOCK_SIZE + +static inline int starfive_hash_wait_busy(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + u32 status; + + return readl_relaxed_poll_timeout(cryp->base + STARFIVE_HASH_SHACSR, status, + !(status & STARFIVE_HASH_BUSY), 10, 100000); +} + +static inline int starfive_hash_wait_key_done(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_dev *cryp = ctx->cryp; + u32 status; + + return readl_relaxed_poll_timeout(cryp->base + STARFIVE_HASH_SHACSR, status, + (status & STARFIVE_HASH_KEY_DONE), 10, 100000); +} + +static void starfive_hash_start(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + + rctx->csr.hash.v = readl(cryp->base + STARFIVE_HASH_SHACSR); + rctx->csr.hash.firstb = 0; + rctx->csr.hash.final = 1; + + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); +} + +static int starfive_hash_hmac_key(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + int klen = ctx->keylen, loop; + unsigned int *key = (unsigned int *)ctx->key; + unsigned char *cl; + + writel(ctx->keylen, cryp->base + STARFIVE_HASH_SHAWKLEN); + + rctx->csr.hash.hmac = !!(ctx->hash_mode & STARFIVE_HASH_HMAC_FLAGS); + rctx->csr.hash.key_flag = 1; + + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); + + for (loop = 0; loop < klen / sizeof(unsigned int); loop++, key++) + writel(*key, cryp->base + STARFIVE_HASH_SHAWKR); + + if (klen & 0x3) { + cl = (unsigned char *)key; + for (loop = 0; loop < (klen & 0x3); loop++, cl++) + writeb(*cl, cryp->base + STARFIVE_HASH_SHAWKR); + } + + if (starfive_hash_wait_key_done(ctx)) + return dev_err_probe(cryp->dev, -ETIMEDOUT, "starfive_hash_wait_key_done error\n"); + + return 0; +} + +static void starfive_hash_dma_callback(void *param) +{ + struct starfive_cryp_dev *cryp = param; + + complete(&cryp->tx_comp); +} + +static int starfive_hash_xmit_dma(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + struct dma_async_tx_descriptor *in_desc; + dma_cookie_t cookie; + union starfive_alg_cr alg_cr; + int total_len; + int ret; + + if (!rctx->total) + return 0; + + writel(rctx->total, cryp->base + STARFIVE_DMA_IN_LEN_OFFSET); + + total_len = rctx->total; + total_len = (total_len & 0x3) ? (((total_len >> 2) + 1) << 2) : total_len; + sg_dma_len(rctx->in_sg) = total_len; + + alg_cr.v = 0; + alg_cr.start = 1; + alg_cr.hash_dma_en = 1; + + writel(alg_cr.v, cryp->base + STARFIVE_ALG_CR_OFFSET); + + ret = dma_map_sg(cryp->dev, rctx->in_sg, rctx->in_sg_len, DMA_TO_DEVICE); + if (!ret) + return dev_err_probe(cryp->dev, -EINVAL, "dma_map_sg() error\n"); + + cryp->cfg_in.direction = DMA_MEM_TO_DEV; + cryp->cfg_in.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cryp->cfg_in.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cryp->cfg_in.src_maxburst = cryp->dma_maxburst; + cryp->cfg_in.dst_maxburst = cryp->dma_maxburst; + cryp->cfg_in.dst_addr = cryp->phys_base + STARFIVE_ALG_FIFO_OFFSET; + + dmaengine_slave_config(cryp->tx, &cryp->cfg_in); + + in_desc = dmaengine_prep_slave_sg(cryp->tx, rctx->in_sg, + rctx->in_sg_len, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!in_desc) + return -EINVAL; + + reinit_completion(&cryp->tx_comp); + + in_desc->callback = starfive_hash_dma_callback; + in_desc->callback_param = cryp; + + cookie = dmaengine_submit(in_desc); + dma_async_issue_pending(cryp->tx); + + if (!wait_for_completion_timeout(&cryp->tx_comp, + msecs_to_jiffies(10000))) { + dev_err(cryp->dev, "wait_for_completion_timeout error, cookie = %x\n", + dma_async_is_tx_complete(cryp->rx, cookie, + NULL, NULL)); + } + + dma_unmap_sg(cryp->dev, rctx->in_sg, rctx->in_sg_len, DMA_TO_DEVICE); + + alg_cr.v = 0; + alg_cr.clear = 1; + + writel(alg_cr.v, cryp->base + STARFIVE_ALG_CR_OFFSET); + + return 0; +} + +static int starfive_hash_xmit(struct starfive_cryp_ctx *ctx) +{ + struct starfive_cryp_request_ctx *rctx = ctx->rctx; + struct starfive_cryp_dev *cryp = ctx->cryp; + int ret; + + rctx->csr.hash.v = 0; + rctx->csr.hash.reset = 1; + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); + + if (starfive_hash_wait_busy(ctx)) + return dev_err_probe(cryp->dev, -ETIMEDOUT, "Error resetting engine.\n"); + + rctx->csr.hash.v = 0; + rctx->csr.hash.mode = ctx->hash_mode & STARFIVE_HASH_MODE_MASK; + rctx->csr.hash.ie = 1; + + if (ctx->hash_mode & STARFIVE_HASH_HMAC_FLAGS) { + ret = starfive_hash_hmac_key(ctx); + if (ret) + return ret; + } else { + rctx->csr.hash.start = 1; + rctx->csr.hash.firstb = 1; + writel(rctx->csr.hash.v, cryp->base + STARFIVE_HASH_SHACSR); + } + + ret = starfive_hash_xmit_dma(ctx); + if (ret) + return ret; + + reinit_completion(&cryp->hash_done); + writel(~STARFIVE_IE_MASK_HASH_DONE, cryp->base + STARFIVE_IE_MASK_OFFSET); + + starfive_hash_start(ctx); + + if (!wait_for_completion_timeout(&cryp->hash_done, usecs_to_jiffies(1000))) + return dev_err_probe(cryp->dev, -ETIMEDOUT, "Timeout waiting for hash done\n"); + + return 0; +} + +static int starfive_hash_copy_hash(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + int count, *data; + int mlen; + + if (!req->result) + return 0; + + mlen = rctx->digsize / sizeof(u32); + data = (u32 *)req->result; + + for (count = 0; count < mlen; count++) + data[count] = readl(ctx->cryp->base + STARFIVE_HASH_SHARDR); + + return 0; +} + +static void starfive_hash_finish_req(struct ahash_request *req, int err) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + + if (!err) + err = starfive_hash_copy_hash(req); + + if (rctx->sgs_copied) { + void *buf_in; + int pages, len; + + buf_in = sg_virt(&rctx->in_sgl); + len = ALIGN(rctx->total, rctx->blksize); + pages = len ? get_order(len) : 1; + free_pages((unsigned long)buf_in, pages); + } + + crypto_finalize_hash_request(cryp->engine, req, err); +} + +static int starfive_hash_check_aligned(struct scatterlist *sg, size_t total, size_t align) +{ + int len = 0; + + if (!total) + return 0; + + if (!IS_ALIGNED(total, align)) + return -EINVAL; + + while (sg) { + if (!IS_ALIGNED(sg->offset, sizeof(u32))) + return -EINVAL; + + if (!IS_ALIGNED(sg->length, align)) + return -EINVAL; + + len += sg->length; + sg = sg_next(sg); + } + + if (len != total) + return -EINVAL; + + return 0; +} + +static int starfive_hash_check_io_aligned(struct starfive_cryp_request_ctx *rctx) +{ + return starfive_hash_check_aligned(rctx->in_sg, rctx->total, rctx->blksize); +} + +static void sg_copy_buf(void *buf, struct scatterlist *sg, + unsigned int start, unsigned int nbytes, int out) +{ + struct scatter_walk walk; + + if (!nbytes) + return; + + scatterwalk_start(&walk, sg); + scatterwalk_advance(&walk, start); + scatterwalk_copychunks(buf, &walk, nbytes, out); + scatterwalk_done(&walk, out, 0); +} + +static int starfive_hash_copy_sgs(struct starfive_cryp_request_ctx *rctx) +{ + void *buf_in; + int pages, total_in; + + if (!starfive_hash_check_io_aligned(rctx)) { + rctx->sgs_copied = 0; + return 0; + } + + total_in = ALIGN(rctx->total, rctx->blksize); + pages = total_in ? get_order(total_in) : 1; + buf_in = (void *)__get_free_pages(GFP_ATOMIC, pages); + if (!buf_in) { + rctx->sgs_copied = 0; + return -EFAULT; + } + + sg_copy_buf(buf_in, rctx->in_sg, 0, rctx->total, 0); + sg_init_one(&rctx->in_sgl, buf_in, total_in); + + rctx->in_sg = &rctx->in_sgl; + rctx->in_sg_len = 1; + rctx->sgs_copied = 1; + + return 0; +} + +static int starfive_hash_prepare_req(struct crypto_engine *engine, void *areq) +{ + struct ahash_request *req = container_of(areq, struct ahash_request, + base); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + + if (!cryp) + return -ENODEV; + + rctx->req.hreq = req; + + return starfive_hash_copy_sgs(rctx); +} + +static int starfive_hash_one_request(struct crypto_engine *engine, void *areq) +{ + struct ahash_request *req = container_of(areq, struct ahash_request, + base); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(crypto_ahash_reqtfm(req)); + struct starfive_cryp_dev *cryp = ctx->cryp; + int err; + + if (!cryp) + return -ENODEV; + + err = starfive_hash_xmit(ctx); + if (err) + return err; + + starfive_hash_finish_req(req, err); + + return 0; +} + +static int starfive_hash_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_ahash_init(&rctx->ahash_fbk_req); +} + +static int starfive_hash_update(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->ahash_fbk_req.nbytes = req->nbytes; + rctx->ahash_fbk_req.src = req->src; + + return crypto_ahash_update(&rctx->ahash_fbk_req); +} + +static int starfive_hash_final(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->ahash_fbk_req.result = req->result; + + return crypto_ahash_final(&rctx->ahash_fbk_req); +} + +static int starfive_hash_finup(struct ahash_request *req) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->ahash_fbk_req.nbytes = req->nbytes; + rctx->ahash_fbk_req.src = req->src; + rctx->ahash_fbk_req.result = req->result; + + return crypto_ahash_finup(&rctx->ahash_fbk_req); +} + +static int starfive_hash_digest(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct starfive_cryp_dev *cryp = ctx->cryp; + + memset(rctx, 0, sizeof(struct starfive_cryp_request_ctx)); + + rctx->req.hreq = req; + rctx->total = req->nbytes; + rctx->in_sg = req->src; + rctx->blksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + rctx->digsize = crypto_ahash_digestsize(tfm); + rctx->in_sg_len = sg_nents_for_len(rctx->in_sg, rctx->total); + ctx->rctx = rctx; + + return crypto_transfer_hash_request_to_engine(cryp->engine, req); +} + +static int starfive_hash_export(struct ahash_request *req, void *out) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_ahash_export(&rctx->ahash_fbk_req, out); +} + +static int starfive_hash_import(struct ahash_request *req, const void *in) +{ + struct starfive_cryp_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->ahash_fbk_req, ctx->ahash_fbk); + rctx->ahash_fbk_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_ahash_import(&rctx->ahash_fbk_req, in); +} + +static int starfive_hash_cra_init_algs(struct crypto_tfm *tfm, + const char *algs_hmac_name, + unsigned int mode, + const char *alg_name) +{ + struct starfive_cryp_ctx *ctx = crypto_tfm_ctx(tfm); + struct crypto_ahash *ahash = __crypto_ahash_cast(tfm); + + ctx->cryp = starfive_cryp_find_dev(ctx); + + if (!ctx->cryp) + return -ENODEV; + + ctx->ahash_fbk = crypto_alloc_ahash(alg_name, 0, + CRYPTO_ALG_NEED_FALLBACK); + + if (IS_ERR(ctx->ahash_fbk)) + return dev_err_probe(ctx->cryp->dev, PTR_ERR(ctx->ahash_fbk), + "starfive_hash: Could not load fallback driver.\n"); + + crypto_hash_alg_common(ahash)->statesize = crypto_ahash_statesize(ctx->ahash_fbk); + crypto_ahash_set_reqsize(ahash, sizeof(struct starfive_cryp_request_ctx) + + crypto_ahash_reqsize(ctx->ahash_fbk)); + + ctx->keylen = 0; + ctx->hash_mode = mode; + + if (algs_hmac_name) + ctx->hash_mode |= STARFIVE_HASH_HMAC_FLAGS; + + ctx->enginectx.op.do_one_request = starfive_hash_one_request; + ctx->enginectx.op.prepare_request = starfive_hash_prepare_req; + ctx->enginectx.op.unprepare_request = NULL; + + return 0; +} + +static void starfive_hash_cra_exit(struct crypto_tfm *tfm) +{ + struct starfive_cryp_ctx *ctx = crypto_tfm_ctx(tfm); + + crypto_free_ahash(ctx->ahash_fbk); + + ctx->ahash_fbk = NULL; + ctx->enginectx.op.do_one_request = NULL; + ctx->enginectx.op.prepare_request = NULL; + ctx->enginectx.op.unprepare_request = NULL; +} + +static int starfive_hash_long_setkey(struct starfive_cryp_ctx *ctx, + const u8 *key, unsigned int keylen, + const char *alg_name) +{ + struct crypto_wait wait; + struct ahash_request *req; + struct scatterlist sg; + struct crypto_ahash *ahash_tfm; + u8 *buf; + int ret; + + ahash_tfm = crypto_alloc_ahash(alg_name, 0, 0); + if (IS_ERR(ahash_tfm)) + return PTR_ERR(ahash_tfm); + + req = ahash_request_alloc(ahash_tfm, GFP_KERNEL); + if (!req) { + ret = -ENOMEM; + goto err_free_ahash; + } + + crypto_init_wait(&wait); + ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); + crypto_ahash_clear_flags(ahash_tfm, ~0); + + buf = kzalloc(keylen + STARFIVE_HASH_BUFLEN, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err_free_req; + } + + memcpy(buf, key, keylen); + sg_init_one(&sg, buf, keylen); + ahash_request_set_crypt(req, &sg, ctx->key, keylen); + + ret = crypto_wait_req(crypto_ahash_digest(req), &wait); + + kfree(buf); +err_free_req: + ahash_request_free(req); +err_free_ahash: + crypto_free_ahash(ahash_tfm); + return ret; +} + +static int starfive_hash224_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha224"); + } + + return ret; +} + +static int starfive_hash256_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha256"); + } + + return ret; +} + +static int starfive_hash384_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha384"); + } + + return ret; +} + +static int starfive_hash512_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sha512"); + } + + return ret; +} + +static int starfive_sm3_setkey(struct crypto_ahash *tfm, + const u8 *key, unsigned int keylen) +{ + struct starfive_cryp_ctx *ctx = crypto_ahash_ctx(tfm); + unsigned int digestsize = crypto_ahash_digestsize(tfm); + unsigned int blocksize; + int ret = 0; + + blocksize = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm)); + crypto_ahash_setkey(ctx->ahash_fbk, key, keylen); + + if (keylen <= blocksize) { + memcpy(ctx->key, key, keylen); + ctx->keylen = keylen; + } else { + ctx->keylen = digestsize; + ret = starfive_hash_long_setkey(ctx, key, keylen, "starfive-sm3"); + } + + return ret; +} + +static int starfive_hash_cra_sha224_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA224, + "sha224-generic"); +} + +static int starfive_hash_cra_sha256_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA256, + "sha256-generic"); +} + +static int starfive_hash_cra_sha384_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA384, + "sha384-generic"); +} + +static int starfive_hash_cra_sha512_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SHA512, + "sha512-generic"); +} + +static int starfive_hash_cra_sm3_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, NULL, + STARFIVE_HASH_SM3, + "sm3-generic"); +} + +static int starfive_hash_cra_hmac_sha224_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha224", + STARFIVE_HASH_SHA224, + "hmac(sha224-generic)"); +} + +static int starfive_hash_cra_hmac_sha256_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha256", + STARFIVE_HASH_SHA256, + "hmac(sha256-generic)"); +} + +static int starfive_hash_cra_hmac_sha384_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha384", + STARFIVE_HASH_SHA384, + "hmac(sha384-generic)"); +} + +static int starfive_hash_cra_hmac_sha512_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sha512", + STARFIVE_HASH_SHA512, + "hmac(sha512-generic)"); +} + +static int starfive_hash_cra_hmac_sm3_init(struct crypto_tfm *tfm) +{ + return starfive_hash_cra_init_algs(tfm, "sm3", + STARFIVE_HASH_SM3, + "hmac(sm3-generic)"); +} + +static struct ahash_alg algs_sha2_sm3[] = { +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA224_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name = "starfive-sha224", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha224_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .setkey = starfive_hash224_setkey, + .halg = { + .digestsize = SHA224_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "hmac(sha224)", + .cra_driver_name = "starfive-hmac-sha224", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha224_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA256_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name = "starfive-sha256", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha256_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .setkey = starfive_hash256_setkey, + .halg = { + .digestsize = SHA256_DIGEST_SIZE, + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "hmac(sha256)", + .cra_driver_name = "starfive-hmac-sha256", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha256_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA384_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha384", + .cra_driver_name = "starfive-sha384", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha384_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .setkey = starfive_hash384_setkey, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA384_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "hmac(sha384)", + .cra_driver_name = "starfive-hmac-sha384", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha384_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA512_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha512", + .cra_driver_name = "starfive-sha512", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sha512_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .setkey = starfive_hash512_setkey, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SHA512_DIGEST_SIZE, + .statesize = sizeof(struct sha512_state), + .base = { + .cra_name = "hmac(sha512)", + .cra_driver_name = "starfive-hmac-sha512", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sha512_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SM3_DIGEST_SIZE, + .statesize = sizeof(struct sm3_state), + .base = { + .cra_name = "sm3", + .cra_driver_name = "starfive-sm3", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_sm3_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +{ + .init = starfive_hash_init, + .update = starfive_hash_update, + .final = starfive_hash_final, + .finup = starfive_hash_finup, + .digest = starfive_hash_digest, + .setkey = starfive_sm3_setkey, + .export = starfive_hash_export, + .import = starfive_hash_import, + .halg = { + .digestsize = SM3_DIGEST_SIZE, + .statesize = sizeof(struct sm3_state), + .base = { + .cra_name = "hmac(sm3)", + .cra_driver_name = "starfive-hmac-sm3", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_ASYNC | + CRYPTO_ALG_TYPE_AHASH | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct starfive_cryp_ctx), + .cra_alignmask = 3, + .cra_init = starfive_hash_cra_hmac_sm3_init, + .cra_exit = starfive_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } +}, +}; + +int starfive_hash_register_algs(void) +{ + int ret = 0; + + ret = crypto_register_ahashes(algs_sha2_sm3, ARRAY_SIZE(algs_sha2_sm3)); + + return ret; +} + +void starfive_hash_unregister_algs(void) +{ + crypto_unregister_ahashes(algs_sha2_sm3, ARRAY_SIZE(algs_sha2_sm3)); +} diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 4169e1d7d5ca5..4390c91af522f 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -21,10 +21,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -46,6 +48,10 @@ DMA_SLAVE_BUSWIDTH_32_BYTES | \ DMA_SLAVE_BUSWIDTH_64_BYTES) +#define AXI_DMA_FLAG_HAS_APB_REGS BIT(0) +#define AXI_DMA_FLAG_HAS_RESETS BIT(1) +#define AXI_DMA_FLAG_USE_CFG2 BIT(2) + static inline void axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val) { @@ -86,7 +92,8 @@ static inline void axi_chan_config_write(struct axi_dma_chan *chan, cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS | config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS); - if (chan->chip->dw->hdata->reg_map_8_channels) { + if (chan->chip->dw->hdata->reg_map_8_channels && + !chan->chip->dw->hdata->use_cfg2) { cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS | config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS | config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS | @@ -1140,7 +1147,7 @@ static int dma_chan_terminate_all(struct dma_chan *dchan) axi_chan_disable(chan); ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val, - !(val & chan_active), 1000, 10000); + !(val & chan_active), 1000, 200000); if (ret == -ETIMEDOUT) dev_warn(dchan2dev(dchan), "%s failed to stop\n", axi_chan_name(chan)); @@ -1367,10 +1374,11 @@ static int parse_device_properties(struct axi_dma_chip *chip) static int dw_probe(struct platform_device *pdev) { - struct device_node *node = pdev->dev.of_node; struct axi_dma_chip *chip; struct dw_axi_dma *dw; struct dw_axi_dma_hcfg *hdata; + struct reset_control *resets; + unsigned int flags; u32 i; int ret; @@ -1398,12 +1406,25 @@ static int dw_probe(struct platform_device *pdev) if (IS_ERR(chip->regs)) return PTR_ERR(chip->regs); - if (of_device_is_compatible(node, "intel,kmb-axi-dma")) { + flags = (uintptr_t)of_device_get_match_data(&pdev->dev); + if (flags & AXI_DMA_FLAG_HAS_APB_REGS) { chip->apb_regs = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(chip->apb_regs)) return PTR_ERR(chip->apb_regs); } + if (flags & AXI_DMA_FLAG_HAS_RESETS) { + resets = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(resets)) + return PTR_ERR(resets); + + ret = reset_control_deassert(resets); + if (ret) + return ret; + } + + chip->dw->hdata->use_cfg2 = !!(flags & AXI_DMA_FLAG_USE_CFG2); + chip->core_clk = devm_clk_get(chip->dev, "core-clk"); if (IS_ERR(chip->core_clk)) return PTR_ERR(chip->core_clk); @@ -1554,8 +1575,15 @@ static const struct dev_pm_ops dw_axi_dma_pm_ops = { }; static const struct of_device_id dw_dma_of_id_table[] = { - { .compatible = "snps,axi-dma-1.01a" }, - { .compatible = "intel,kmb-axi-dma" }, + { + .compatible = "snps,axi-dma-1.01a" + }, { + .compatible = "intel,kmb-axi-dma", + .data = (void *)AXI_DMA_FLAG_HAS_APB_REGS, + }, { + .compatible = "starfive,jh7110-axi-dma", + .data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2), + }, {} }; MODULE_DEVICE_TABLE(of, dw_dma_of_id_table); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h index e9d5eb0fd5948..eb267cb24f670 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h @@ -33,6 +33,7 @@ struct dw_axi_dma_hcfg { /* Register map for DMAX_NUM_CHANNELS <= 8 */ bool reg_map_8_channels; bool restrict_axi_burst_len; + bool use_cfg2; }; struct axi_dma_chan { diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5b3b76477b0e3..abb82a73dbafe 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1929,6 +1929,16 @@ config SENSORS_STTS751 This driver can also be built as a module. If so, the module will be called stts751. +config SENSORS_SFCTEMP + tristate "Starfive JH71x0 temperature sensor" + depends on ARCH_STARFIVE || COMPILE_TEST + help + If you say yes here you get support for temperature sensor + on the Starfive JH71x0 SoCs. + + This driver can also be built as a module. If so, the module + will be called sfctemp. + config SENSORS_SMM665 tristate "Summit Microelectronics SMM665" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 88712b5031c87..386f4debe68ba 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -181,6 +181,7 @@ obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o +obj-$(CONFIG_SENSORS_SFCTEMP) += sfctemp.o obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o diff --git a/drivers/hwmon/sfctemp.c b/drivers/hwmon/sfctemp.c new file mode 100644 index 0000000000000..d7484e2b81005 --- /dev/null +++ b/drivers/hwmon/sfctemp.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Emil Renner Berthing + * Copyright (C) 2021 Samin Guo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * TempSensor reset. The RSTN can be de-asserted once the analog core has + * powered up. Trst(min 100ns) + * 0:reset 1:de-assert + */ +#define SFCTEMP_RSTN BIT(0) + +/* + * TempSensor analog core power down. The analog core will be powered up + * Tpu(min 50us) after PD is de-asserted. RSTN should be held low until the + * analog core is powered up. + * 0:power up 1:power down + */ +#define SFCTEMP_PD BIT(1) + +/* + * TempSensor start conversion enable. + * 0:disable 1:enable + */ +#define SFCTEMP_RUN BIT(2) + +/* + * TempSensor conversion value output. + * Temp(C)=DOUT*Y/4094 - K + */ +#define SFCTEMP_DOUT_POS 16 +#define SFCTEMP_DOUT_MSK GENMASK(27, 16) + +/* DOUT to Celcius conversion constants */ +#define SFCTEMP_Y1000 237500L +#define SFCTEMP_Z 4094L +#define SFCTEMP_K1000 81100L + +struct sfctemp { + /* serialize access to hardware register and enabled below */ + struct mutex lock; + void __iomem *regs; + struct clk *clk_sense; + struct clk *clk_bus; + struct reset_control *rst_sense; + struct reset_control *rst_bus; + bool enabled; +}; + +static void sfctemp_power_up(struct sfctemp *sfctemp) +{ + /* make sure we're powered down first */ + writel(SFCTEMP_PD, sfctemp->regs); + udelay(1); + + writel(0, sfctemp->regs); + /* wait t_pu(50us) + t_rst(100ns) */ + usleep_range(60, 200); + + /* de-assert reset */ + writel(SFCTEMP_RSTN, sfctemp->regs); + udelay(1); /* wait t_su(500ps) */ +} + +static void sfctemp_power_down(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_PD, sfctemp->regs); +} + +static void sfctemp_run(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_RSTN | SFCTEMP_RUN, sfctemp->regs); + udelay(1); +} + +static void sfctemp_stop(struct sfctemp *sfctemp) +{ + writel(SFCTEMP_RSTN, sfctemp->regs); +} + +static int sfctemp_enable(struct sfctemp *sfctemp) +{ + int ret = 0; + + mutex_lock(&sfctemp->lock); + if (sfctemp->enabled) + goto done; + + ret = clk_prepare_enable(sfctemp->clk_bus); + if (ret) + goto err; + ret = reset_control_deassert(sfctemp->rst_bus); + if (ret) + goto err_disable_bus; + + ret = clk_prepare_enable(sfctemp->clk_sense); + if (ret) + goto err_assert_bus; + ret = reset_control_deassert(sfctemp->rst_sense); + if (ret) + goto err_disable_sense; + + sfctemp_power_up(sfctemp); + sfctemp_run(sfctemp); + sfctemp->enabled = true; +done: + mutex_unlock(&sfctemp->lock); + return ret; + +err_disable_sense: + clk_disable_unprepare(sfctemp->clk_sense); +err_assert_bus: + reset_control_assert(sfctemp->rst_bus); +err_disable_bus: + clk_disable_unprepare(sfctemp->clk_bus); +err: + mutex_unlock(&sfctemp->lock); + return ret; +} + +static int sfctemp_disable(struct sfctemp *sfctemp) +{ + mutex_lock(&sfctemp->lock); + if (!sfctemp->enabled) + goto done; + + sfctemp_stop(sfctemp); + sfctemp_power_down(sfctemp); + reset_control_assert(sfctemp->rst_sense); + clk_disable_unprepare(sfctemp->clk_sense); + reset_control_assert(sfctemp->rst_bus); + clk_disable_unprepare(sfctemp->clk_bus); + sfctemp->enabled = false; +done: + mutex_unlock(&sfctemp->lock); + return 0; +} + +static void sfctemp_disable_action(void *data) +{ + sfctemp_disable(data); +} + +static int sfctemp_convert(struct sfctemp *sfctemp, long *val) +{ + int ret; + + mutex_lock(&sfctemp->lock); + if (!sfctemp->enabled) { + ret = -ENODATA; + goto out; + } + + /* calculate temperature in milli Celcius */ + *val = (long)((readl(sfctemp->regs) & SFCTEMP_DOUT_MSK) >> SFCTEMP_DOUT_POS) + * SFCTEMP_Y1000 / SFCTEMP_Z - SFCTEMP_K1000; + + ret = 0; +out: + mutex_unlock(&sfctemp->lock); + return ret; +} + +static umode_t sfctemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + return 0644; + case hwmon_temp_input: + return 0444; + default: + return 0; + } + default: + return 0; + } +} + +static int sfctemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sfctemp *sfctemp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + *val = sfctemp->enabled; + return 0; + case hwmon_temp_input: + return sfctemp_convert(sfctemp, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int sfctemp_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sfctemp *sfctemp = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_enable: + if (val == 0) + return sfctemp_disable(sfctemp); + if (val == 1) + return sfctemp_enable(sfctemp); + return -EINVAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct hwmon_channel_info *sfctemp_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_ENABLE | HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops sfctemp_hwmon_ops = { + .is_visible = sfctemp_is_visible, + .read = sfctemp_read, + .write = sfctemp_write, +}; + +static const struct hwmon_chip_info sfctemp_chip_info = { + .ops = &sfctemp_hwmon_ops, + .info = sfctemp_info, +}; + +static int sfctemp_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *hwmon_dev; + struct sfctemp *sfctemp; + int ret; + + sfctemp = devm_kzalloc(dev, sizeof(*sfctemp), GFP_KERNEL); + if (!sfctemp) + return -ENOMEM; + + dev_set_drvdata(dev, sfctemp); + mutex_init(&sfctemp->lock); + + sfctemp->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(sfctemp->regs)) + return PTR_ERR(sfctemp->regs); + + sfctemp->clk_sense = devm_clk_get(dev, "sense"); + if (IS_ERR(sfctemp->clk_sense)) + return dev_err_probe(dev, PTR_ERR(sfctemp->clk_sense), + "error getting sense clock\n"); + + sfctemp->clk_bus = devm_clk_get(dev, "bus"); + if (IS_ERR(sfctemp->clk_bus)) + return dev_err_probe(dev, PTR_ERR(sfctemp->clk_bus), + "error getting bus clock\n"); + + sfctemp->rst_sense = devm_reset_control_get_exclusive(dev, "sense"); + if (IS_ERR(sfctemp->rst_sense)) + return dev_err_probe(dev, PTR_ERR(sfctemp->rst_sense), + "error getting sense reset\n"); + + sfctemp->rst_bus = devm_reset_control_get_exclusive(dev, "bus"); + if (IS_ERR(sfctemp->rst_bus)) + return dev_err_probe(dev, PTR_ERR(sfctemp->rst_bus), + "error getting busreset\n"); + + ret = reset_control_assert(sfctemp->rst_sense); + if (ret) + return dev_err_probe(dev, ret, "error asserting sense reset\n"); + + ret = reset_control_assert(sfctemp->rst_bus); + if (ret) + return dev_err_probe(dev, ret, "error asserting bus reset\n"); + + ret = devm_add_action(dev, sfctemp_disable_action, sfctemp); + if (ret) + return ret; + + ret = sfctemp_enable(sfctemp); + if (ret) + return dev_err_probe(dev, ret, "error enabling temperature sensor: %d\n", ret); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "sfctemp", sfctemp, + &sfctemp_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id sfctemp_of_match[] = { + { .compatible = "starfive,jh7100-temp" }, + { .compatible = "starfive,jh7110-temp" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sfctemp_of_match); + +static struct platform_driver sfctemp_driver = { + .probe = sfctemp_probe, + .driver = { + .name = "sfctemp", + .of_match_table = sfctemp_of_match, + }, +}; +module_platform_driver(sfctemp_driver); + +MODULE_AUTHOR("Emil Renner Berthing"); +MODULE_DESCRIPTION("StarFive JH71x0 temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index ee579916f8744..627eaa0ab3eef 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -80,6 +80,7 @@ source "drivers/media/platform/renesas/Kconfig" source "drivers/media/platform/rockchip/Kconfig" source "drivers/media/platform/samsung/Kconfig" source "drivers/media/platform/st/Kconfig" +source "drivers/media/platform/starfive/Kconfig" source "drivers/media/platform/sunxi/Kconfig" source "drivers/media/platform/ti/Kconfig" source "drivers/media/platform/verisilicon/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 5453bb868e679..5a038498a370d 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -23,6 +23,7 @@ obj-y += renesas/ obj-y += rockchip/ obj-y += samsung/ obj-y += st/ +obj-y += starfive/ obj-y += sunxi/ obj-y += ti/ obj-y += verisilicon/ diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c index cc3ebb0d96f66..7e7b096869fcf 100644 --- a/drivers/media/platform/cadence/cdns-csi2rx.c +++ b/drivers/media/platform/cadence/cdns-csi2rx.c @@ -10,9 +10,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -30,6 +32,12 @@ #define CSI2RX_STATIC_CFG_DLANE_MAP(llane, plane) ((plane) << (16 + (llane) * 4)) #define CSI2RX_STATIC_CFG_LANES_MASK GENMASK(11, 8) +#define CSI2RX_DPHY_LANE_CTRL_REG 0x40 +#define CSI2RX_DPHY_CL_RST BIT(16) +#define CSI2RX_DPHY_DL_RST(i) BIT((i) + 12) +#define CSI2RX_DPHY_CL_EN BIT(4) +#define CSI2RX_DPHY_DL_EN(i) BIT(i) + #define CSI2RX_STREAM_BASE(n) (((n) + 1) * 0x100) #define CSI2RX_STREAM_CTRL_REG(n) (CSI2RX_STREAM_BASE(n) + 0x000) @@ -37,6 +45,7 @@ #define CSI2RX_STREAM_DATA_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x008) #define CSI2RX_STREAM_DATA_CFG_EN_VC_SELECT BIT(31) +#define CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 BIT(7) #define CSI2RX_STREAM_DATA_CFG_VC_SELECT(n) BIT((n) + 16) #define CSI2RX_STREAM_CFG_REG(n) (CSI2RX_STREAM_BASE(n) + 0x00c) @@ -54,8 +63,19 @@ enum csi2rx_pads { CSI2RX_PAD_MAX, }; +struct csi2rx_fmt { + u32 code; + u8 bpp; + u32 dt; +}; + +struct csi2rx_platform_info { + unsigned long sys_clk_rate; +}; + struct csi2rx_priv { struct device *dev; + unsigned int power_count; unsigned int count; /* @@ -68,6 +88,9 @@ struct csi2rx_priv { struct clk *sys_clk; struct clk *p_clk; struct clk *pixel_clk[CSI2RX_STREAMS_MAX]; + struct reset_control *sys_rst; + struct reset_control *p_rst; + struct reset_control *pixel_rst[CSI2RX_STREAMS_MAX]; struct phy *dphy; u8 lanes[CSI2RX_LANES_MAX]; @@ -83,14 +106,100 @@ struct csi2rx_priv { /* Remote source */ struct v4l2_subdev *source_subdev; int source_pad; + + const struct csi2rx_platform_info *platform_info; +}; + +static const struct csi2rx_fmt formats[] = { + { + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .bpp = 10, + .dt = 0x2b, + }, + { + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .bpp = 10, + .dt = 0x2b, + }, + { + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .bpp = 10, + .dt = 0x2b, + }, + { + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .bpp = 10, + .dt = 0x2b, + }, }; +static u8 csi2rx_get_bpp(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return formats[i].bpp; + } + + return 0; +} + +static u32 csi2rx_get_dt(u32 code) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return formats[i].dt; + } + + return 0; +} + +static s64 csi2rx_get_pixel_rate(struct csi2rx_priv *csi2rx) +{ + struct v4l2_ctrl *ctrl; + + ctrl = v4l2_ctrl_find(csi2rx->source_subdev->ctrl_handler, + V4L2_CID_PIXEL_RATE); + if (!ctrl) { + dev_err(csi2rx->dev, "no pixel rate control in subdev: %s\n", + csi2rx->source_subdev->name); + return -EINVAL; + } + + return v4l2_ctrl_g_ctrl_int64(ctrl); +} + static inline struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev) { return container_of(subdev, struct csi2rx_priv, subdev); } +static int csi2rx_s_power(struct v4l2_subdev *subdev, int on) +{ + struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + + mutex_lock(&csi2rx->lock); + + if (on) { + if (!csi2rx->power_count) + phy_init(csi2rx->dphy); + + csi2rx->power_count++; + } else { + csi2rx->power_count--; + + if (!csi2rx->power_count) + phy_exit(csi2rx->dphy); + } + + mutex_unlock(&csi2rx->lock); + return 0; +} + static void csi2rx_reset(struct csi2rx_priv *csi2rx) { writel(CSI2RX_SOFT_RESET_PROTOCOL | CSI2RX_SOFT_RESET_FRONT, @@ -101,17 +210,70 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx) writel(0, csi2rx->base + CSI2RX_SOFT_RESET_REG); } +static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx) +{ + union phy_configure_opts opts = { }; + struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; + struct v4l2_subdev_format sd_fmt; + s64 pixel_rate; + int ret; + u8 bpp; + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(csi2rx->source_subdev, pad, get_fmt, NULL, + &sd_fmt); + if (ret) + return ret; + + bpp = csi2rx_get_bpp(sd_fmt.format.code); + if (!bpp) + return -EINVAL; + + pixel_rate = csi2rx_get_pixel_rate(csi2rx); + if (pixel_rate < 0) + return pixel_rate; + + ret = phy_mipi_dphy_get_default_config(pixel_rate, bpp, + csi2rx->num_lanes, cfg); + if (ret) + return ret; + + phy_pm_runtime_get_sync(csi2rx->dphy); + + ret = phy_power_on(csi2rx->dphy); + if (ret) + goto out; + + ret = phy_configure(csi2rx->dphy, &opts); + if (ret) { + /* Can't do anything if it fails. Ignore the return value. */ + phy_power_off(csi2rx->dphy); + goto out; + } + +out: + phy_pm_runtime_put_sync(csi2rx->dphy); + + return ret; +} + static int csi2rx_start(struct csi2rx_priv *csi2rx) { + struct v4l2_subdev_format sd_fmt; unsigned int i; unsigned long lanes_used = 0; u32 reg; + u32 dt = 0; int ret; ret = clk_prepare_enable(csi2rx->p_clk); if (ret) return ret; + reset_control_deassert(csi2rx->p_rst); + csi2rx_reset(csi2rx); reg = csi2rx->num_lanes << 8; @@ -139,6 +301,29 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pclk; + /* Enable DPHY clk and data lanes. */ + if (csi2rx->dphy) { + reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST; + for (i = 0; i < csi2rx->num_lanes; i++) { + reg |= CSI2RX_DPHY_DL_EN(csi2rx->lanes[i] - 1); + reg |= CSI2RX_DPHY_DL_RST(csi2rx->lanes[i] - 1); + } + + writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + } + + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + sd_fmt.pad = 0; + + ret = v4l2_subdev_call(csi2rx->source_subdev, pad, get_fmt, NULL, + &sd_fmt); + if (ret) + dev_warn(csi2rx->dev, "Couldn't get format\n"); + + dt = csi2rx_get_dt(sd_fmt.format.code); + if (!dt) + dev_warn(csi2rx->dev, "Couldn't get dt\n"); + /* * Create a static mapping between the CSI virtual channels * and the output stream. @@ -154,6 +339,8 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + reset_control_deassert(csi2rx->pixel_rst[i]); + writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF, csi2rx->base + CSI2RX_STREAM_CFG_REG(i)); @@ -161,6 +348,11 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) CSI2RX_STREAM_DATA_CFG_VC_SELECT(i), csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); + if (dt) + writel(readl(csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)) | + CSI2RX_STREAM_DATA_CFG_EN_DATA_TYPE_0 | dt, + csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i)); + writel(CSI2RX_STREAM_CTRL_START, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); } @@ -169,10 +361,27 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx) if (ret) goto err_disable_pixclk; + if (csi2rx->platform_info && csi2rx->platform_info->sys_clk_rate > 0) + clk_set_rate(csi2rx->sys_clk, + csi2rx->platform_info->sys_clk_rate); + + reset_control_deassert(csi2rx->sys_rst); + + if (csi2rx->dphy) { + ret = csi2rx_configure_ext_dphy(csi2rx); + if (ret) { + dev_err(csi2rx->dev, + "Failed to configure external DPHY: %d\n", ret); + goto err_disable_sysclk; + } + } + clk_disable_unprepare(csi2rx->p_clk); return 0; +err_disable_sysclk: + clk_disable_unprepare(csi2rx->sys_clk); err_disable_pixclk: for (; i > 0; i--) clk_disable_unprepare(csi2rx->pixel_clk[i - 1]); @@ -188,18 +397,28 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx) unsigned int i; clk_prepare_enable(csi2rx->p_clk); + reset_control_assert(csi2rx->sys_rst); clk_disable_unprepare(csi2rx->sys_clk); for (i = 0; i < csi2rx->max_streams; i++) { writel(0, csi2rx->base + CSI2RX_STREAM_CTRL_REG(i)); + reset_control_assert(csi2rx->pixel_rst[i]); clk_disable_unprepare(csi2rx->pixel_clk[i]); } + reset_control_assert(csi2rx->p_rst); clk_disable_unprepare(csi2rx->p_clk); if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false)) dev_warn(csi2rx->dev, "Couldn't disable our subdev\n"); + + if (csi2rx->dphy) { + writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG); + + if (phy_power_off(csi2rx->dphy)) + dev_warn(csi2rx->dev, "Couldn't power off DPHY\n"); + } } static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) @@ -236,11 +455,16 @@ static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable) return ret; } +static const struct v4l2_subdev_core_ops csi2rx_core_ops = { + .s_power = csi2rx_s_power, +}; + static const struct v4l2_subdev_video_ops csi2rx_video_ops = { .s_stream = csi2rx_s_stream, }; static const struct v4l2_subdev_ops csi2rx_subdev_ops = { + .core = &csi2rx_core_ops, .video = &csi2rx_video_ops, }; @@ -250,6 +474,8 @@ static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, { struct v4l2_subdev *subdev = notifier->sd; struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev); + struct v4l2_device *v4l2_dev; + int ret; csi2rx->source_pad = media_entity_get_fwnode_pad(&s_subdev->entity, s_subdev->fwnode, @@ -265,6 +491,15 @@ static int csi2rx_async_bound(struct v4l2_async_notifier *notifier, dev_dbg(csi2rx->dev, "Bound %s pad: %d\n", s_subdev->name, csi2rx->source_pad); + /* ensure source subdev register subdev node */ + v4l2_dev = notifier->v4l2_dev ? notifier->v4l2_dev : + notifier->parent->v4l2_dev; + if (v4l2_dev) { + ret = v4l2_device_register_subdev_nodes(v4l2_dev); + if (ret < 0) + return ret; + } + return media_create_pad_link(&csi2rx->source_subdev->entity, csi2rx->source_pad, &csi2rx->subdev.entity, 0, @@ -299,21 +534,23 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, return PTR_ERR(csi2rx->p_clk); } + csi2rx->sys_rst = + devm_reset_control_get_optional_exclusive(&pdev->dev, + "sys_rst"); + if (IS_ERR(csi2rx->sys_rst)) + return PTR_ERR(csi2rx->sys_rst); + + csi2rx->p_rst = + devm_reset_control_get_optional_exclusive(&pdev->dev, "p_rst"); + if (IS_ERR(csi2rx->p_rst)) + return PTR_ERR(csi2rx->p_rst); + csi2rx->dphy = devm_phy_optional_get(&pdev->dev, "dphy"); if (IS_ERR(csi2rx->dphy)) { dev_err(&pdev->dev, "Couldn't get external D-PHY\n"); return PTR_ERR(csi2rx->dphy); } - /* - * FIXME: Once we'll have external D-PHY support, the check - * will need to be removed. - */ - if (csi2rx->dphy) { - dev_err(&pdev->dev, "External D-PHY not supported yet\n"); - return -EINVAL; - } - ret = clk_prepare_enable(csi2rx->p_clk); if (ret) { dev_err(&pdev->dev, "Couldn't prepare and enable P clock\n"); @@ -343,13 +580,14 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, * FIXME: Once we'll have internal D-PHY support, the check * will need to be removed. */ - if (csi2rx->has_internal_dphy) { + if (!csi2rx->dphy && csi2rx->has_internal_dphy) { dev_err(&pdev->dev, "Internal D-PHY not supported yet\n"); return -EINVAL; } for (i = 0; i < csi2rx->max_streams; i++) { char clk_name[16]; + char rst_name[16]; snprintf(clk_name, sizeof(clk_name), "pixel_if%u_clk", i); csi2rx->pixel_clk[i] = devm_clk_get(&pdev->dev, clk_name); @@ -357,6 +595,13 @@ static int csi2rx_get_resources(struct csi2rx_priv *csi2rx, dev_err(&pdev->dev, "Couldn't get clock %s\n", clk_name); return PTR_ERR(csi2rx->pixel_clk[i]); } + + snprintf(rst_name, sizeof(rst_name), "pixel_if%u_rst", i); + csi2rx->pixel_rst[i] = + devm_reset_control_get_optional_exclusive(&pdev->dev, + rst_name); + if (IS_ERR(csi2rx->pixel_rst[i])) + return PTR_ERR(csi2rx->pixel_rst[i]); } return 0; @@ -425,6 +670,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx = kzalloc(sizeof(*csi2rx), GFP_KERNEL); if (!csi2rx) return -ENOMEM; + csi2rx->platform_info = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, csi2rx); csi2rx->dev = &pdev->dev; mutex_init(&csi2rx->lock); @@ -441,6 +687,7 @@ static int csi2rx_probe(struct platform_device *pdev) csi2rx->subdev.dev = &pdev->dev; v4l2_subdev_init(&csi2rx->subdev, &csi2rx_subdev_ops); v4l2_set_subdevdata(&csi2rx->subdev, &pdev->dev); + csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; snprintf(csi2rx->subdev.name, V4L2_SUBDEV_NAME_SIZE, "%s.%s", KBUILD_MODNAME, dev_name(&pdev->dev)); @@ -462,6 +709,7 @@ static int csi2rx_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Probed CSI2RX with %u/%u lanes, %u streams, %s D-PHY\n", csi2rx->num_lanes, csi2rx->max_lanes, csi2rx->max_streams, + csi2rx->dphy ? "external" : csi2rx->has_internal_dphy ? "internal" : "no"); return 0; @@ -483,8 +731,13 @@ static int csi2rx_remove(struct platform_device *pdev) return 0; } +static const struct csi2rx_platform_info stf_jh7110_info = { + .sys_clk_rate = 297000000, +}; + static const struct of_device_id csi2rx_of_table[] = { { .compatible = "cdns,csi2rx" }, + { .compatible = "starfive,jh7110-csi2rx", .data = &stf_jh7110_info }, { }, }; MODULE_DEVICE_TABLE(of, csi2rx_of_table); diff --git a/drivers/media/platform/starfive/Kconfig b/drivers/media/platform/starfive/Kconfig new file mode 100644 index 0000000000000..4bb1e25b66ca6 --- /dev/null +++ b/drivers/media/platform/starfive/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +comment "Starfive media platform drivers" + +config VIDEO_STARFIVE_CAMSS + tristate "Starfive Camera Subsystem driver" + depends on V4L_PLATFORM_DRIVERS + depends on VIDEO_DEV && OF + select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Enable this to support for the Starfive Camera subsystem + found on Starfive JH7110 SoC. + + To compile this driver as a module, choose M here: the + module will be called stf-camss. diff --git a/drivers/media/platform/starfive/Makefile b/drivers/media/platform/starfive/Makefile new file mode 100644 index 0000000000000..bf497dd24bbbc --- /dev/null +++ b/drivers/media/platform/starfive/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for StarFive camera subsystem driver. +# + +starfive-camss-objs += \ + stf_camss.o \ + stf_isp.o \ + stf_isp_hw_ops.o \ + stf_video.o \ + stf_vin.o \ + stf_vin_hw_ops.o + +obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive-camss.o \ diff --git a/drivers/media/platform/starfive/stf_camss.c b/drivers/media/platform/starfive/stf_camss.c new file mode 100644 index 0000000000000..525f2d80c5eb7 --- /dev/null +++ b/drivers/media/platform/starfive/stf_camss.c @@ -0,0 +1,728 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_camss.c + * + * Starfive Camera Subsystem driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stf_camss.h" + +static struct clk_bulk_data stfcamss_clocks[] = { + { .id = "clk_apb_func" }, + { .id = "clk_wrapper_clk_c" }, + { .id = "clk_dvp_inv" }, + { .id = "clk_axiwr" }, + { .id = "clk_mipi_rx0_pxl" }, + { .id = "clk_ispcore_2x" }, + { .id = "clk_isp_axi" }, +}; + +static struct reset_control_bulk_data stfcamss_resets[] = { + { .id = "rst_wrapper_p" }, + { .id = "rst_wrapper_c" }, + { .id = "rst_axird" }, + { .id = "rst_axiwr" }, + { .id = "rst_isp_top_n" }, + { .id = "rst_isp_top_axi" }, +}; + +int stfcamss_get_mem_res(struct platform_device *pdev, + struct stfcamss *stfcamss) +{ + stfcamss->syscon_base = + devm_platform_ioremap_resource_byname(pdev, "syscon"); + if (IS_ERR(stfcamss->syscon_base)) + return PTR_ERR(stfcamss->syscon_base); + + stfcamss->isp_base = + devm_platform_ioremap_resource_byname(pdev, "isp"); + if (IS_ERR(stfcamss->isp_base)) + return PTR_ERR(stfcamss->isp_base); + + return 0; +} + +/* + * stfcamss_find_sensor - Find a linked media entity which represents a sensor + * @entity: Media entity to start searching from + * + * Return a pointer to sensor media entity or NULL if not found + */ +struct media_entity *stfcamss_find_sensor(struct media_entity *entity) +{ + struct media_pad *pad; + + while (1) { + if (!entity->pads) + return NULL; + + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return NULL; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return NULL; + + entity = pad->entity; + + if (entity->function == MEDIA_ENT_F_CAM_SENSOR) + return entity; + } +} + +/* + * stfcamss_of_parse_endpoint_node - Parse port endpoint node + * @dev: Device + * @node: Device node to be parsed + * @csd: Parsed data from port endpoint node + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_of_parse_endpoint_node(struct device *dev, + struct device_node *node, + struct stfcamss_async_subdev *csd) +{ + struct v4l2_fwnode_endpoint vep = { { 0 } }; + + v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep); + st_debug(ST_CAMSS, "%s: vep.base.port = 0x%x, id = 0x%x\n", + __func__, vep.base.port, vep.base.id); + + csd->port = vep.base.port; + switch (csd->port) { + case PORT_NUMBER_DVP_SENSOR: + break; + case PORT_NUMBER_CSI2RX: + break; + default: + break; + }; + + return 0; +} + +/* + * stfcamss_of_parse_ports - Parse ports node + * @stfcamss: STFCAMSS device + * + * Return number of "port" nodes found in "ports" node + */ +static int stfcamss_of_parse_ports(struct stfcamss *stfcamss) +{ + struct device *dev = stfcamss->dev; + struct device_node *node = NULL; + struct device_node *remote = NULL; + int ret, num_subdevs = 0; + + for_each_endpoint_of_node(dev->of_node, node) { + struct stfcamss_async_subdev *csd; + + if (!of_device_is_available(node)) + continue; + + remote = of_graph_get_remote_port_parent(node); + if (!remote) { + st_err(ST_CAMSS, "Cannot get remote parent\n"); + ret = -EINVAL; + goto err_cleanup; + } + + csd = v4l2_async_nf_add_fwnode(&stfcamss->notifier, + of_fwnode_handle(remote), + struct stfcamss_async_subdev); + of_node_put(remote); + if (IS_ERR(csd)) { + ret = PTR_ERR(csd); + goto err_cleanup; + } + + ret = stfcamss_of_parse_endpoint_node(dev, node, csd); + if (ret < 0) + goto err_cleanup; + + num_subdevs++; + } + + return num_subdevs; + +err_cleanup: + of_node_put(node); + return ret; +} + +/* + * stfcamss_init_subdevices - Initialize subdev structures and resources + * @stfcamss: STFCAMSS device + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_init_subdevices(struct stfcamss *stfcamss) +{ + int ret; + + ret = stf_isp_subdev_init(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to init stf_isp sub-device: %d\n", ret); + return ret; + } + + ret = stf_vin_subdev_init(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to init stf_vin sub-device: %d\n", ret); + return ret; + } + return ret; +} + +static int stfcamss_register_subdevices(struct stfcamss *stfcamss) +{ + int ret; + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct stf_isp_dev *isp_dev = stfcamss->isp_dev; + + ret = stf_isp_register(isp_dev, &stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register stf isp%d entity: %d\n", 0, ret); + goto err_reg_isp; + } + + ret = stf_vin_register(vin_dev, &stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register vin entity: %d\n", ret); + goto err_reg_vin; + } + + ret = media_create_pad_link(&isp_dev->subdev.entity, + STF_ISP_PAD_SRC, + &vin_dev->line[VIN_LINE_ISP].subdev.entity, + STF_VIN_PAD_SINK, + 0); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP].subdev.entity.name, + ret); + goto err_link; + } + + ret = media_create_pad_link( + &isp_dev->subdev.entity, + STF_ISP_PAD_SRC_SS0, + &vin_dev->line[VIN_LINE_ISP_SS0].subdev.entity, + STF_VIN_PAD_SINK, + 0); + + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP_SS0].subdev.entity.name, + ret); + goto err_link; + } + + ret = media_create_pad_link( + &isp_dev->subdev.entity, + STF_ISP_PAD_SRC_SS1, + &vin_dev->line[VIN_LINE_ISP_SS1].subdev.entity, + STF_VIN_PAD_SINK, + 0); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP_SS1].subdev.entity.name, + ret); + goto err_link; + } + + ret = media_create_pad_link( + &isp_dev->subdev.entity, + STF_ISP_PAD_SRC_RAW, + &vin_dev->line[VIN_LINE_ISP_RAW].subdev.entity, + STF_VIN_PAD_SINK, + 0); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to link %s->%s entities: %d\n", + isp_dev->subdev.entity.name, + vin_dev->line[VIN_LINE_ISP_RAW].subdev.entity.name, + ret); + goto err_link; + } + + return ret; + +err_link: + stf_vin_unregister(stfcamss->vin_dev); +err_reg_vin: + stf_isp_unregister(stfcamss->isp_dev); +err_reg_isp: + return ret; +} + +static void stfcamss_unregister_subdevices(struct stfcamss *stfcamss) +{ + stf_isp_unregister(stfcamss->isp_dev); + stf_vin_unregister(stfcamss->vin_dev); +} + +static int stfcamss_register_media_subdev_nod(struct v4l2_async_notifier *async, + struct v4l2_subdev *sd) +{ + struct stfcamss *stfcamss = + container_of(async, struct stfcamss, notifier); + struct host_data *host_data = v4l2_get_subdev_hostdata(sd); + struct media_entity *sensor; + struct media_entity *input; + int ret; + int i, j; + + for (i = 0; host_data->host_entity[i] && (i < HOST_ENTITY_MAX); i++) { + sensor = &sd->entity; + input = host_data->host_entity[i]; + + for (j = 0; j < sensor->num_pads; j++) { + if (sensor->pads[j].flags & MEDIA_PAD_FL_SOURCE) + break; + } + + if (j == sensor->num_pads) { + st_err(ST_CAMSS, "No source pad in external entity\n"); + return -EINVAL; + } + + ret = media_create_pad_link( + sensor, + j, + input, + STF_PAD_SINK, + sensor->function == MEDIA_ENT_F_CAM_SENSOR ? + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED : + 0); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to link %s->%s entities: %d\n", + sensor->name, input->name, ret); + return ret; + } + } + + ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev); + if (ret < 0) + return ret; + + if (stfcamss->media_dev.devnode) + return ret; + + st_debug(ST_CAMSS, "stfcamss register media device\n"); + return media_device_register(&stfcamss->media_dev); +} + +static int stfcamss_subdev_notifier_bound(struct v4l2_async_notifier *async, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct stfcamss *stfcamss = + container_of(async, struct stfcamss, notifier); + struct stfcamss_async_subdev *csd = + container_of(asd, struct stfcamss_async_subdev, asd); + enum port_num port = csd->port; + struct stf_isp_dev *isp_dev = stfcamss->isp_dev; + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct host_data *host_data = &stfcamss->host_data; + + switch (port) { + case PORT_NUMBER_DVP_SENSOR: + host_data->host_entity[0] = NULL; + host_data->host_entity[1] = NULL; + /* not support DVP sensor */ + break; + case PORT_NUMBER_CSI2RX: + host_data->host_entity[0] = + &vin_dev->line[VIN_LINE_WR].subdev.entity; + host_data->host_entity[1] = &isp_dev->subdev.entity; + break; + default: + break; + }; + + v4l2_set_subdev_hostdata(subdev, host_data); + stfcamss_register_media_subdev_nod(async, subdev); + + return 0; +} + +static const struct v4l2_async_notifier_operations +stfcamss_subdev_notifier_ops = { + .bound = stfcamss_subdev_notifier_bound, +}; + +static const struct media_device_ops stfcamss_media_ops = { + .link_notify = v4l2_pipeline_link_notify, +}; + +/* + * stfcamss_probe - Probe STFCAMSS platform device + * @pdev: Pointer to STFCAMSS platform device + * + * Return 0 on success or a negative error code on failure + */ +static int stfcamss_probe(struct platform_device *pdev) +{ + struct stfcamss *stfcamss; + struct device *dev = &pdev->dev; + int ret = 0, num_subdevs; + + stfcamss = devm_kzalloc(dev, sizeof(struct stfcamss), GFP_KERNEL); + if (!stfcamss) + return -ENOMEM; + + stfcamss->isp_dev = devm_kzalloc(dev, sizeof(*stfcamss->isp_dev), + GFP_KERNEL); + if (!stfcamss->isp_dev) { + ret = -ENOMEM; + goto err_cam; + } + + stfcamss->vin_dev = devm_kzalloc(dev, sizeof(*stfcamss->vin_dev), + GFP_KERNEL); + if (!stfcamss->vin_dev) { + ret = -ENOMEM; + goto err_cam; + } + + stfcamss->irq = platform_get_irq(pdev, 0); + if (stfcamss->irq <= 0) { + st_err(ST_CAMSS, "Could not get irq\n"); + goto err_cam; + } + + stfcamss->isp_irq = platform_get_irq(pdev, 1); + if (stfcamss->isp_irq <= 0) { + st_err(ST_CAMSS, "Could not get isp irq\n"); + goto err_cam; + } + + stfcamss->isp_irq_csi = platform_get_irq(pdev, 2); + if (stfcamss->isp_irq_csi <= 0) { + st_err(ST_CAMSS, "Could not get isp csi irq\n"); + goto err_cam; + } + + stfcamss->isp_irq_csiline = platform_get_irq(pdev, 3); + if (stfcamss->isp_irq_csiline <= 0) { + st_err(ST_CAMSS, "Could not get isp irq csiline\n"); + goto err_cam; + } + + pm_runtime_enable(dev); + + stfcamss->nclks = ARRAY_SIZE(stfcamss_clocks); + stfcamss->sys_clk = stfcamss_clocks; + + ret = devm_clk_bulk_get(dev, stfcamss->nclks, stfcamss->sys_clk); + if (ret) { + st_err(ST_CAMSS, "Failed to get clk controls\n"); + return ret; + } + + stfcamss->nrsts = ARRAY_SIZE(stfcamss_resets); + stfcamss->sys_rst = stfcamss_resets; + + ret = devm_reset_control_bulk_get_shared(dev, stfcamss->nrsts, + stfcamss->sys_rst); + if (ret) { + st_err(ST_CAMSS, "Failed to get reset controls\n"); + return ret; + } + + ret = stfcamss_get_mem_res(pdev, stfcamss); + if (ret) { + st_err(ST_CAMSS, "Could not map registers\n"); + goto err_cam; + } + + stfcamss->dev = dev; + platform_set_drvdata(pdev, stfcamss); + + v4l2_async_nf_init(&stfcamss->notifier); + + num_subdevs = stfcamss_of_parse_ports(stfcamss); + if (num_subdevs < 0) { + ret = num_subdevs; + goto err_cam_noti; + } + + ret = stfcamss_init_subdevices(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to init subdevice: %d\n", ret); + goto err_cam_noti; + } + + stfcamss->media_dev.dev = stfcamss->dev; + strscpy(stfcamss->media_dev.model, "Starfive Camera Subsystem", + sizeof(stfcamss->media_dev.model)); + strscpy(stfcamss->media_dev.serial, "0123456789ABCDEF", + sizeof(stfcamss->media_dev.serial)); + snprintf(stfcamss->media_dev.bus_info, + sizeof(stfcamss->media_dev.bus_info), + "%s:%s", dev_bus_name(dev), pdev->name); + stfcamss->media_dev.hw_revision = 0x01; + stfcamss->media_dev.ops = &stfcamss_media_ops; + media_device_init(&stfcamss->media_dev); + + stfcamss->v4l2_dev.mdev = &stfcamss->media_dev; + + ret = v4l2_device_register(stfcamss->dev, &stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to register V4L2 device: %d\n", ret); + goto err_cam_noti_med; + } + + ret = stfcamss_register_subdevices(stfcamss); + if (ret < 0) { + st_err(ST_CAMSS, "Failed to register subdevice: %d\n", ret); + goto err_cam_noti_med_vreg; + } + + if (num_subdevs) { + stfcamss->notifier.ops = &stfcamss_subdev_notifier_ops; + ret = v4l2_async_nf_register(&stfcamss->v4l2_dev, + &stfcamss->notifier); + if (ret) { + st_err(ST_CAMSS, + "Failed to register async subdev nodes: %d\n", + ret); + goto err_cam_noti_med_vreg_sub; + } + } else { + ret = v4l2_device_register_subdev_nodes(&stfcamss->v4l2_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register subdev nodes: %d\n", + ret); + goto err_cam_noti_med_vreg_sub; + } + + ret = media_device_register(&stfcamss->media_dev); + if (ret < 0) { + st_err(ST_CAMSS, + "Failed to register media device: %d\n", + ret); + goto err_cam_noti_med_vreg_sub_medreg; + } + } + + dev_info(dev, "stfcamss probe success!\n"); + return 0; + +err_cam_noti_med_vreg_sub_medreg: +err_cam_noti_med_vreg_sub: + stfcamss_unregister_subdevices(stfcamss); +err_cam_noti_med_vreg: + v4l2_device_unregister(&stfcamss->v4l2_dev); +err_cam_noti_med: + media_device_cleanup(&stfcamss->media_dev); +err_cam_noti: + v4l2_async_nf_cleanup(&stfcamss->notifier); +err_cam: + return ret; +} + +/* + * stfcamss_remove - Remove STFCAMSS platform device + * @pdev: Pointer to STFCAMSS platform device + * + * Always returns 0. + */ +static int stfcamss_remove(struct platform_device *pdev) +{ + struct stfcamss *stfcamss = platform_get_drvdata(pdev); + + dev_info(&pdev->dev, "remove done\n"); + + stfcamss_unregister_subdevices(stfcamss); + v4l2_device_unregister(&stfcamss->v4l2_dev); + media_device_cleanup(&stfcamss->media_dev); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id stfcamss_of_match[] = { + { .compatible = "starfive,jh7110-camss" }, + { /* end node */ }, +}; + +MODULE_DEVICE_TABLE(of, stfcamss_of_match); + +#ifdef CONFIG_PM_SLEEP +static int stfcamss_suspend(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + struct stfcamss_video *video; + struct video_device *vdev; + int i = 0; + + for (i = 0; i < VIN_LINE_MAX; i++) { + if (vin_dev->line[i].stream_count) { + vin_dev->line[i].stream_count++; + video = &vin_dev->line[i].video_out; + vdev = &vin_dev->line[i].video_out.vdev; + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || + !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + video_device_pipeline_stop(vdev); + video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); + v4l2_pipeline_pm_put(&vdev->entity); + } + } + + return pm_runtime_force_suspend(dev); +} + +static int stfcamss_resume(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + struct stfcamss_video *video; + struct video_device *vdev; + int i = 0; + int ret = 0; + + pm_runtime_force_resume(dev); + + for (i = 0; i < VIN_LINE_MAX; i++) { + if (vin_dev->line[i].stream_count) { + vin_dev->line[i].stream_count--; + video = &vin_dev->line[i].video_out; + vdev = &vin_dev->line[i].video_out.vdev; + + ret = v4l2_pipeline_pm_get(&vdev->entity); + if (ret < 0) + goto err; + + ret = video_device_pipeline_start( + vdev, &video->stfcamss->pipe); + if (ret < 0) + goto err_pm_put; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || + !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, + s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto err_pipeline_stop; + } + } + } + + return 0; + +err_pipeline_stop: + video_device_pipeline_stop(vdev); +err_pm_put: + v4l2_pipeline_pm_put(&vdev->entity); +err: + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int stfcamss_runtime_suspend(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + + reset_control_assert(stfcamss->sys_rst[STF_RST_ISP_TOP_AXI].rstc); + reset_control_assert(stfcamss->sys_rst[STF_RST_ISP_TOP_N].rstc); + clk_disable_unprepare(stfcamss->sys_clk[STF_CLK_ISP_AXI].clk); + clk_disable_unprepare(stfcamss->sys_clk[STF_CLK_ISPCORE_2X].clk); + + return 0; +} + +static int stfcamss_runtime_resume(struct device *dev) +{ + struct stfcamss *stfcamss = dev_get_drvdata(dev); + + clk_prepare_enable(stfcamss->sys_clk[STF_CLK_ISPCORE_2X].clk); + clk_prepare_enable(stfcamss->sys_clk[STF_CLK_ISP_AXI].clk); + reset_control_deassert(stfcamss->sys_rst[STF_RST_ISP_TOP_N].rstc); + reset_control_deassert(stfcamss->sys_rst[STF_RST_ISP_TOP_AXI].rstc); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops stfcamss_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(stfcamss_suspend, stfcamss_resume) + SET_RUNTIME_PM_OPS(stfcamss_runtime_suspend, + stfcamss_runtime_resume, + NULL) +}; + +static struct platform_driver stfcamss_driver = { + .probe = stfcamss_probe, + .remove = stfcamss_remove, + .driver = { + .name = DRV_NAME, + .pm = &stfcamss_pm_ops, + .of_match_table = of_match_ptr(stfcamss_of_match), + }, +}; + +module_platform_driver(stfcamss_driver); + +MODULE_AUTHOR("StarFive Corporation"); +MODULE_DESCRIPTION("StarFive Camera Subsystem driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/starfive/stf_camss.h b/drivers/media/platform/starfive/stf_camss.h new file mode 100644 index 0000000000000..a8c6579d62ed6 --- /dev/null +++ b/drivers/media/platform/starfive/stf_camss.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_camss.h + * + * Starfive Camera Subsystem driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_CAMSS_H +#define STF_CAMSS_H + +#include +#include +#include + +enum sensor_type { + SENSOR_VIN, + /* need replace sensor */ + SENSOR_ISP, +}; + +enum subdev_type { + SUBDEV_TYPE_VIN, + SUBDEV_TYPE_ISP, +}; + +#include "stf_common.h" +#include "stf_isp.h" +#include "stf_vin.h" + +#define DRV_NAME "starfive-camss" +#define STF_DVP_NAME "stf_dvp" +#define STF_CSI_NAME "cdns_csi2rx" +#define STF_ISP_NAME "stf_isp" +#define STF_VIN_NAME "stf_vin" + +#define STF_PAD_SINK 0 +#define STF_PAD_SRC 1 +#define STF_PADS_NUM 2 + +enum port_num { + PORT_NUMBER_DVP_SENSOR = 0, + PORT_NUMBER_CSI2RX +}; + +enum stf_clk { + STF_CLK_APB_FUNC = 0, + STF_CLK_WRAPPER_CLK_C, + STF_CLK_DVP_INV, + STF_CLK_AXIWR, + STF_CLK_MIPI_RX0_PXL, + STF_CLK_ISPCORE_2X, + STF_CLK_ISP_AXI, + STF_CLK_NUM +}; + +enum stf_rst { + STF_RST_WRAPPER_P = 0, + STF_RST_WRAPPER_C, + STF_RST_AXIRD, + STF_RST_AXIWR, + STF_RST_ISP_TOP_N, + STF_RST_ISP_TOP_AXI, + STF_RST_NUM +}; + +#define HOST_ENTITY_MAX 2 + +struct host_data { + struct media_entity *host_entity[HOST_ENTITY_MAX]; +}; + +struct stfcamss { + struct v4l2_device v4l2_dev; + struct media_device media_dev; + struct media_pipeline pipe; + struct device *dev; + struct stf_vin_dev *vin_dev; + struct stf_isp_dev *isp_dev; + struct v4l2_async_notifier notifier; + struct host_data host_data; + void __iomem *syscon_base; + void __iomem *isp_base; + int irq; + int isp_irq; + int isp_irq_csi; + int isp_irq_csiline; + struct clk_bulk_data *sys_clk; + int nclks; + struct reset_control_bulk_data *sys_rst; + int nrsts; + struct regmap *stf_aon_syscon; + u32 aon_gp_reg; +}; + +struct stfcamss_async_subdev { + struct v4l2_async_subdev asd; /* must be first */ + enum port_num port; +}; + +struct media_entity *stfcamss_find_sensor(struct media_entity *entity); + +#endif /* STF_CAMSS_H */ diff --git a/drivers/media/platform/starfive/stf_common.h b/drivers/media/platform/starfive/stf_common.h new file mode 100644 index 0000000000000..90f5e4c3061ca --- /dev/null +++ b/drivers/media/platform/starfive/stf_common.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_common.h + * + * StarFive Camera Subsystem - Common definitions + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_COMMON_H +#define STF_COMMON_H + +#include +#include + +enum { + ST_DVP = 0x0001, + ST_ISP = 0x0002, + ST_VIN = 0x0004, + ST_VIDEO = 0x0008, + ST_CAMSS = 0x0010, + ST_SENSOR = 0x0020, +}; + +enum { + ST_NONE = 0x00, + ST_ERR = 0x01, + ST_WARN = 0x02, + ST_INFO = 0x03, + ST_DEBUG = 0x04, +}; + +#ifdef STF_DEBUG +#define STFDBG_LEVEL ST_DEBUG +#define STFDBG_MASK 0x7F +#else +#define STFDBG_LEVEL ST_ERR +#define STFDBG_MASK 0x7F +#endif + +#define ST_MODULE2STRING(__module) ({ \ + char *__str; \ + \ + switch (__module) { \ + case ST_DVP: \ + __str = "st_dvp"; \ + break; \ + case ST_ISP: \ + __str = "st_isp"; \ + break; \ + case ST_VIN: \ + __str = "st_vin"; \ + break; \ + case ST_VIDEO: \ + __str = "st_video"; \ + break; \ + case ST_CAMSS: \ + __str = "st_camss"; \ + break; \ + case ST_SENSOR: \ + __str = "st_sensor"; \ + break; \ + default: \ + __str = "unknown"; \ + break; \ + } \ + \ + __str; \ + }) + +#define st_debug(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_INFO) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] debug: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_info(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_WARN) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] info: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_warn(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_ERR) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] warn: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_err(module, __fmt, arg...) \ + do { \ + if (STFDBG_LEVEL > ST_NONE) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] error: " __fmt, \ + ST_MODULE2STRING((module)), \ + ## arg); \ + } \ + } while (0) + +#define st_err_ratelimited(module, fmt, ...) \ + do { \ + static DEFINE_RATELIMIT_STATE(_rs, \ + DEFAULT_RATELIMIT_INTERVAL, \ + DEFAULT_RATELIMIT_BURST); \ + if (__ratelimit(&_rs) && STFDBG_LEVEL > ST_NONE) { \ + if (STFDBG_MASK & (module)) \ + pr_err("[%s] error: " fmt, \ + ST_MODULE2STRING((module)), \ + ##__VA_ARGS__); \ + } \ + } while (0) + +static inline u32 reg_read(void __iomem *base, u32 reg) +{ + return ioread32(base + reg); +} + +static inline void reg_write(void __iomem *base, u32 reg, u32 val) +{ + iowrite32(val, base + reg); +} + +static inline void reg_set_bit(void __iomem *base, u32 reg, u32 mask, u32 val) +{ + u32 value; + + value = ioread32(base + reg) & ~mask; + val &= mask; + val |= value; + iowrite32(val, base + reg); +} + +static inline void reg_set(void __iomem *base, u32 reg, u32 mask) +{ + iowrite32(ioread32(base + reg) | mask, base + reg); +} + +#endif /* STF_COMMON_H */ diff --git a/drivers/media/platform/starfive/stf_isp.c b/drivers/media/platform/starfive/stf_isp.c new file mode 100644 index 0000000000000..0c67d1d06e036 --- /dev/null +++ b/drivers/media/platform/starfive/stf_isp.c @@ -0,0 +1,1079 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp.c + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include +#include +#include + +#include "stf_camss.h" + +#define SINK_FORMATS_INDEX 0 +#define UO_FORMATS_INDEX 1 +#define RAW_FORMATS_INDEX 2 + +static int user_config_isp; + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel); + +static struct v4l2_rect * +__isp_get_compose(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which); + +static struct v4l2_rect * +__isp_get_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which); + +static struct v4l2_rect * +__isp_get_scale(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel); + +static const struct isp_format isp_formats_sink[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, +}; + +static const struct isp_format isp_formats_raw[] = { + { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, + { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, +}; + +static const struct isp_format isp_formats_compat_raw10[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, +}; + +static const struct isp_format isp_formats_compat_raw8[] = { + { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, + { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, + { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, + { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, +}; + +static const struct isp_format isp_formats_uo[] = { + { MEDIA_BUS_FMT_Y12_1X12, 8 }, +}; + +static const struct isp_format_table isp_formats_st7110[] = { + { isp_formats_sink, ARRAY_SIZE(isp_formats_sink) }, + { isp_formats_uo, ARRAY_SIZE(isp_formats_uo) }, + { isp_formats_raw, ARRAY_SIZE(isp_formats_raw) }, +}; + +int stf_isp_subdev_init(struct stfcamss *stfcamss) +{ + struct stf_isp_dev *isp_dev = stfcamss->isp_dev; + struct device *dev = stfcamss->dev; + + isp_dev->sdev_type = SUBDEV_TYPE_ISP; + isp_dev->hw_ops = &isp_ops; + isp_dev->stfcamss = stfcamss; + isp_dev->formats = isp_formats_st7110; + isp_dev->nformats = ARRAY_SIZE(isp_formats_st7110); + isp_dev->reg_buf = devm_kzalloc(dev, sizeof(*isp_dev->reg_buf), + GFP_KERNEL); + if (!isp_dev->reg_buf) + return -ENOMEM; + + mutex_init(&isp_dev->stream_lock); + mutex_init(&isp_dev->power_lock); + mutex_init(&isp_dev->setfile_lock); + atomic_set(&isp_dev->shadow_count, 0); + return 0; +} + +static int isp_set_power(struct v4l2_subdev *sd, int on) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + + mutex_lock(&isp_dev->power_lock); + if (on) { + if (isp_dev->power_count == 0) + st_debug(ST_ISP, "turn on isp\n"); + isp_dev->power_count++; + } else { + if (isp_dev->power_count == 0) + goto exit; + isp_dev->power_count--; + } +exit: + mutex_unlock(&isp_dev->power_lock); + + return 0; +} + +static struct v4l2_mbus_framefmt * +__isp_get_format(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&isp_dev->subdev, state, pad); + + return &isp_dev->fmt[pad]; +} + +static int isp_get_interface_type(struct media_entity *entity) +{ + struct v4l2_subdev *subdev; + struct media_pad *pad = &entity->pads[0]; + + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return -EINVAL; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return -EINVAL; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + st_debug(ST_ISP, "interface subdev name %s\n", subdev->name); + if (!strncmp(subdev->name, STF_CSI_NAME, strlen(STF_CSI_NAME))) + return INTERFACE_CSI; + + return -EINVAL; +} + +static int isp_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + int ret = 0, interface_type; + struct v4l2_mbus_framefmt *fmt; + struct v4l2_event src_ch = { 0 }; + + fmt = __isp_get_format(isp_dev, NULL, STF_ISP_PAD_SINK, + V4L2_SUBDEV_FORMAT_ACTIVE); + mutex_lock(&isp_dev->stream_lock); + if (enable) { + if (isp_dev->stream_count == 0) { + isp_dev->hw_ops->isp_clk_enable(isp_dev); + if (!user_config_isp) + isp_dev->hw_ops->isp_config_set(isp_dev); + interface_type = isp_get_interface_type(&sd->entity); + if (interface_type < 0) { + st_err(ST_ISP, + "%s, pipeline not config\n", __func__); + goto exit; + } + isp_dev->hw_ops->isp_set_format(isp_dev, + isp_dev->rect, + fmt->code, + interface_type); + isp_dev->hw_ops->isp_reset(isp_dev); + isp_dev->hw_ops->isp_stream_set(isp_dev, enable); + user_config_isp = 0; + } + isp_dev->stream_count++; + } else { + if (isp_dev->stream_count == 0) + goto exit; + if (isp_dev->stream_count == 1) { + isp_dev->hw_ops->isp_stream_set(isp_dev, enable); + isp_dev->hw_ops->isp_clk_disable(isp_dev); + } + isp_dev->stream_count--; + } + src_ch.type = V4L2_EVENT_SOURCE_CHANGE, + src_ch.u.src_change.changes = isp_dev->stream_count, + + v4l2_subdev_notify_event(sd, &src_ch); +exit: + mutex_unlock(&isp_dev->stream_lock); + + return ret; +} + +static int isp_sensor_fmt_to_index(struct stf_isp_dev *isp_dev) +{ + int ret, idx; + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct v4l2_subdev_format fmt; + const struct isp_format_table *formats; + + if (!isp_dev) + return -EINVAL; + + sensor = stfcamss_find_sensor(&isp_dev->subdev.entity); + if (!sensor) + return -EINVAL; + + subdev = media_entity_to_v4l2_subdev(sensor); + st_debug(ST_ISP, "Found sensor = %s\n", sensor->name); + + fmt.pad = 0; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret) { + st_warn(ST_ISP, "Sonser get format failed !!\n"); + return -EINVAL; + } + + st_debug(ST_ISP, "Got sensor format 0x%x !!\n", fmt.format.code); + + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + for (idx = 0; idx < formats->nfmts; idx++) { + if (formats->fmts[idx].code == fmt.format.code) { + st_info(ST_ISP, "Match sensor format to index %d\n", + idx); + return idx; + } + } + return -ERANGE; +} + +static int isp_fmt_to_index(const struct isp_format_table *f_table, + __u32 mbus_code, unsigned int pad) +{ + int i; + + for (i = 0; i < f_table->nfmts; i++) { + if (mbus_code == f_table->fmts[i].code) + break; + + if (pad == STF_ISP_PAD_SRC_RAW) { + if (mbus_code == isp_formats_compat_raw10[i].code || + mbus_code == isp_formats_compat_raw8[i].code) + break; + } + } + + return i; +} + +static void isp_try_format(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + const struct isp_format_table *formats; + unsigned int i; + u32 code = fmt->code; + u32 bpp; + + switch (pad) { + case STF_ISP_PAD_SINK: + /* Set format on sink pad */ + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + fmt->width = clamp_t(u32, + fmt->width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + fmt->height = clamp_t(u32, + fmt->height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + fmt->height &= ~0x1; + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->flags = 0; + break; + + case STF_ISP_PAD_SRC: + case STF_ISP_PAD_SRC_SS0: + case STF_ISP_PAD_SRC_SS1: + formats = &isp_dev->formats[UO_FORMATS_INDEX]; + break; + + case STF_ISP_PAD_SRC_RAW: + formats = &isp_dev->formats[RAW_FORMATS_INDEX]; + break; + } + + i = isp_fmt_to_index(formats, fmt->code, pad); + st_debug(ST_ISP, "%s pad=%d, code=%x format to index = %d\n", + __func__, pad, code, i); + + if (i >= formats->nfmts && pad == STF_ISP_PAD_SRC_RAW) { + int sensor_idx; + + sensor_idx = isp_sensor_fmt_to_index(isp_dev); + if (sensor_idx) + i = sensor_idx; + } + + if (pad != STF_ISP_PAD_SINK) + *fmt = *__isp_get_format(isp_dev, state, + STF_ISP_PAD_SINK, which); + + if (i >= formats->nfmts) { + fmt->code = formats->fmts[0].code; + bpp = formats->fmts[0].bpp; + st_info(ST_ISP, "Use default index 0 format = 0x%x\n", + fmt->code); + } else { + if (pad == STF_ISP_PAD_SRC_RAW) { + fmt->code = formats->fmts[i].code; + bpp = formats->fmts[i].bpp; + st_info(ST_ISP, + "use format from sink index %d = 0x%x\n", + i, fmt->code); + } else { + fmt->code = code; + bpp = formats->fmts[i].bpp; + st_info(ST_ISP, "Use input format = 0x%x\n", fmt->code); + } + } + + switch (pad) { + case STF_ISP_PAD_SINK: + break; + + case STF_ISP_PAD_SRC: + isp_dev->rect[ISP_COMPOSE].bpp = bpp; + break; + + case STF_ISP_PAD_SRC_SS0: + isp_dev->rect[ISP_SCALE_SS0].bpp = bpp; + break; + + case STF_ISP_PAD_SRC_SS1: + isp_dev->rect[ISP_SCALE_SS1].bpp = bpp; + break; + + case STF_ISP_PAD_SRC_RAW: + isp_dev->rect[ISP_CROP].bpp = bpp; + break; + } +} + +static int isp_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + const struct isp_format_table *formats; + + if (code->index >= isp_dev->nformats) + return -EINVAL; + if (code->pad == STF_ISP_PAD_SINK) { + formats = &isp_dev->formats[SINK_FORMATS_INDEX]; + code->code = formats->fmts[code->index].code; + } else { + struct v4l2_mbus_framefmt *sink_fmt; + + sink_fmt = __isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, + code->which); + + code->code = sink_fmt->code; + if (!code->code) + return -EINVAL; + } + code->flags = 0; + + return 0; +} + +static int isp_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + isp_try_format(isp_dev, state, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + isp_try_format(isp_dev, state, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +static int isp_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int isp_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + struct v4l2_subdev_selection sel = { 0 }; + struct v4l2_rect *rect = NULL; + int ret; + + st_debug(ST_ISP, "%s pad=%d, code=%x, which=%d\n", + __func__, fmt->reserved[0], fmt->format.code, fmt->which); + format = __isp_get_format(isp_dev, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + fmt->format = *format; + if (fmt->reserved[0] != 0) { + sel.which = fmt->which; + sel.pad = fmt->reserved[0]; + + switch (fmt->reserved[0]) { + case STF_ISP_PAD_SRC: + rect = __isp_get_compose(isp_dev, + state, fmt->which); + break; + + case STF_ISP_PAD_SRC_SS0: + case STF_ISP_PAD_SRC_SS1: + rect = __isp_get_scale(isp_dev, state, &sel); + break; + + case STF_ISP_PAD_SRC_RAW: + rect = __isp_get_crop(isp_dev, + state, fmt->which); + break; + + default: + break; + } + if (rect) { + fmt->format.width = rect->width; + fmt->format.height = rect->height; + } + } + mutex_unlock(&isp_dev->stream_lock); + goto out; + } else { + isp_try_format(isp_dev, state, fmt->pad, + &fmt->format, fmt->which); + *format = fmt->format; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Propagate the format from sink to source */ + if (fmt->pad == STF_ISP_PAD_SINK) { + /* Reset sink pad compose selection */ + sel.which = fmt->which; + sel.pad = STF_ISP_PAD_SINK; + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = fmt->format.width; + sel.r.height = fmt->format.height; + ret = isp_set_selection(sd, state, &sel); + if (ret < 0) + return ret; + } + +out: + return 0; +} + +static struct v4l2_rect * +__isp_get_compose(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&isp_dev->subdev, state, + STF_ISP_PAD_SINK); + + return &isp_dev->rect[ISP_COMPOSE].rect; +} + +static struct v4l2_rect * +__isp_get_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(&isp_dev->subdev, state, + STF_ISP_PAD_SINK); + + return &isp_dev->rect[ISP_CROP].rect; +} + +static struct v4l2_rect * +__isp_get_scale(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + int pad; + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_compose(&isp_dev->subdev, state, + STF_ISP_PAD_SINK); + if (sel->pad != STF_ISP_PAD_SRC_SS0 && sel->pad != STF_ISP_PAD_SRC_SS1) + return NULL; + + pad = sel->pad == STF_ISP_PAD_SRC_SS0 ? ISP_SCALE_SS0 : ISP_SCALE_SS1; + return &isp_dev->rect[pad].rect; +} + +static void isp_try_crop(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_mbus_framefmt *fmt; + + fmt = __isp_get_format(isp_dev, state, STF_ISP_PAD_SINK, which); + + if (rect->width > fmt->width) + rect->width = fmt->width; + + if (rect->width + rect->left > fmt->width) + rect->left = fmt->width - rect->width; + + if (rect->height > fmt->height) + rect->height = fmt->height; + + if (rect->height + rect->top > fmt->height) + rect->top = fmt->height - rect->height; + + if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) { + rect->left = 0; + rect->width = STFCAMSS_FRAME_MIN_WIDTH; + } + + if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) { + rect->top = 0; + rect->height = STFCAMSS_FRAME_MIN_HEIGHT; + } + rect->height &= ~0x1; +} + +static void isp_try_compose(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_rect *crop; + + crop = __isp_get_crop(isp_dev, state, which); + + if (rect->width > crop->width) + rect->width = crop->width; + + if (rect->height > crop->height) + rect->height = crop->height; + + if (crop->width > rect->width * SCALER_RATIO_MAX) + rect->width = + (crop->width + SCALER_RATIO_MAX - 1) / SCALER_RATIO_MAX; + + if (crop->height > rect->height * SCALER_RATIO_MAX) + rect->height = + (crop->height + SCALER_RATIO_MAX - 1) / SCALER_RATIO_MAX; + + if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) + rect->width = STFCAMSS_FRAME_MIN_WIDTH; + + if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) + rect->height = STFCAMSS_FRAME_MIN_HEIGHT; + rect->height &= ~0x1; +} + +static void isp_try_scale(struct stf_isp_dev *isp_dev, + struct v4l2_subdev_state *state, + struct v4l2_rect *rect, + enum v4l2_subdev_format_whence which) +{ + struct v4l2_rect *compose; + + compose = __isp_get_compose(isp_dev, state, which); + + if (rect->width > compose->width) + rect->width = compose->width; + + if (rect->width + rect->left > compose->width) + rect->left = compose->width - rect->width; + + if (rect->height > compose->height) + rect->height = compose->height; + + if (rect->height + rect->top > compose->height) + rect->top = compose->height - rect->height; + + if (rect->width < STFCAMSS_FRAME_MIN_WIDTH) { + rect->left = 0; + rect->width = STFCAMSS_FRAME_MIN_WIDTH; + } + + if (rect->height < STFCAMSS_FRAME_MIN_HEIGHT) { + rect->top = 0; + rect->height = STFCAMSS_FRAME_MIN_HEIGHT; + } + + rect->height &= ~0x1; +} + +static int isp_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format fmt = { 0 }; + struct v4l2_rect *rect; + int ret; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + fmt.pad = sel->pad; + fmt.which = sel->which; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = fmt.format.width; + sel->r.height = fmt.format.height; + break; + + case V4L2_SEL_TGT_CROP: + rect = __isp_get_crop(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + sel->r = *rect; + break; + + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + if (sel->pad > STF_ISP_PAD_SRC_SS1) + return -EINVAL; + rect = __isp_get_crop(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + sel->r.left = rect->left; + sel->r.top = rect->top; + sel->r.width = rect->width; + sel->r.height = rect->height; + break; + + case V4L2_SEL_TGT_COMPOSE: + if (sel->pad > STF_ISP_PAD_SRC_SS1) + return -EINVAL; + if (sel->pad == STF_ISP_PAD_SRC_SS0 || + sel->pad == STF_ISP_PAD_SRC_SS1) { + rect = __isp_get_scale(isp_dev, state, sel); + if (!rect) + return -EINVAL; + } else { + rect = __isp_get_compose(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + } + sel->r = *rect; + break; + + default: + return -EINVAL; + } + + st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n", + __func__, sel->pad, sel->r.left, + sel->r.top, sel->r.width, sel->r.height); + return 0; +} + +static int isp_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_selection *sel) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct v4l2_rect *rect; + int ret = 0; + + if (sel->target == V4L2_SEL_TGT_COMPOSE && + (sel->pad == STF_ISP_PAD_SINK || sel->pad == STF_ISP_PAD_SRC)) { + struct v4l2_subdev_format fmt = { 0 }; + int i; + + rect = __isp_get_compose(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + sel->r = *rect; + mutex_unlock(&isp_dev->stream_lock); + ret = 0; + goto out; + } else { + isp_try_compose(isp_dev, state, &sel->r, sel->which); + *rect = sel->r; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = STF_ISP_PAD_SRC; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + fmt.format.width = rect->width; + fmt.format.height = rect->height; + ret = isp_set_format(sd, state, &fmt); + + /* Reset scale */ + for (i = STF_ISP_PAD_SRC_SS0; i <= STF_ISP_PAD_SRC_SS1; i++) { + struct v4l2_subdev_selection scale = { 0 }; + + scale.which = sel->which; + scale.target = V4L2_SEL_TGT_COMPOSE; + scale.r = *rect; + scale.pad = i; + ret = isp_set_selection(sd, state, &scale); + } + } else if (sel->target == V4L2_SEL_TGT_COMPOSE && + ((sel->pad == STF_ISP_PAD_SRC_SS0) || + (sel->pad == STF_ISP_PAD_SRC_SS1))) { + struct v4l2_subdev_format fmt = { 0 }; + + rect = __isp_get_scale(isp_dev, state, sel); + if (!rect) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + sel->r = *rect; + mutex_unlock(&isp_dev->stream_lock); + ret = 0; + goto out; + } else { + isp_try_scale(isp_dev, state, &sel->r, sel->which); + *rect = sel->r; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Reset source pad format width and height */ + fmt.which = sel->which; + fmt.pad = sel->pad; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + fmt.format.width = rect->width; + fmt.format.height = rect->height; + ret = isp_set_format(sd, state, &fmt); + } else if (sel->target == V4L2_SEL_TGT_CROP) { + struct v4l2_subdev_selection compose = { 0 }; + int i; + + rect = __isp_get_crop(isp_dev, state, sel->which); + if (!rect) + return -EINVAL; + + mutex_lock(&isp_dev->stream_lock); + if (isp_dev->stream_count) { + sel->r = *rect; + mutex_unlock(&isp_dev->stream_lock); + ret = 0; + goto out; + } else { + isp_try_crop(isp_dev, state, &sel->r, sel->which); + *rect = sel->r; + } + mutex_unlock(&isp_dev->stream_lock); + + /* Reset source compose selection */ + compose.which = sel->which; + compose.target = V4L2_SEL_TGT_COMPOSE; + compose.r.width = rect->width; + compose.r.height = rect->height; + compose.pad = STF_ISP_PAD_SINK; + ret = isp_set_selection(sd, state, &compose); + + /* Reset source pad format width and height */ + for (i = STF_ISP_PAD_SRC_RAW; i < STF_ISP_PAD_MAX; i++) { + struct v4l2_subdev_format fmt = { 0 }; + + fmt.which = sel->which; + fmt.pad = i; + ret = isp_get_format(sd, state, &fmt); + if (ret < 0) + return ret; + + fmt.format.width = rect->width; + fmt.format.height = rect->height; + ret = isp_set_format(sd, state, &fmt); + } + } else { + ret = -EINVAL; + } + + st_info(ST_ISP, "%s pad = %d, left = %d, %d, %d, %d\n", + __func__, sel->pad, sel->r.left, + sel->r.top, sel->r.width, sel->r.height); +out: + return ret; +} + +static int isp_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = STF_ISP_PAD_SINK, + .which = + fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .width = 1920, + .height = 1080 + } + }; + + return isp_set_format(sd, fh ? fh->state : NULL, &format); +} + +static int isp_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_pad_remote_pad_first(local)) + return -EBUSY; + return 0; +} + +static int stf_isp_load_setfile(struct stf_isp_dev *isp_dev, char *file_name) +{ + struct device *dev = isp_dev->stfcamss->dev; + const struct firmware *fw; + u8 *buf = NULL; + int *regval_num; + int ret; + + st_debug(ST_ISP, "%s, file_name %s\n", __func__, file_name); + ret = request_firmware(&fw, file_name, dev); + if (ret < 0) { + st_err(ST_ISP, "firmware request failed (%d)\n", ret); + return ret; + } + buf = devm_kzalloc(dev, fw->size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + memcpy(buf, fw->data, fw->size); + + mutex_lock(&isp_dev->setfile_lock); + if (isp_dev->setfile.state == 1) + devm_kfree(dev, isp_dev->setfile.data); + isp_dev->setfile.data = buf; + isp_dev->setfile.size = fw->size; + isp_dev->setfile.state = 1; + regval_num = (int *)&buf[fw->size - sizeof(unsigned int)]; + isp_dev->setfile.settings.regval_num = *regval_num; + isp_dev->setfile.settings.regval = (struct regval_t *)buf; + mutex_unlock(&isp_dev->setfile_lock); + + st_debug(ST_ISP, "stf_isp setfile loaded size: %zu B, reg_nul: %d\n", + fw->size, isp_dev->setfile.settings.regval_num); + + release_firmware(fw); + return ret; +} + +static long stf_isp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + struct stfisp_fw_info *fw_info = arg; + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case VIDIOC_STFISP_LOAD_FW: + if (IS_ERR(fw_info)) { + st_err(ST_ISP, "fw_info failed, params invalid\n"); + return -EINVAL; + } + + ret = stf_isp_load_setfile(isp_dev, fw_info->filename); + break; + + case VIDIOC_STFISP_GET_REG: + ret = isp_dev->hw_ops->isp_reg_read(isp_dev, arg); + break; + + case VIDIOC_STFISP_SET_REG: + ret = isp_dev->hw_ops->isp_reg_write(isp_dev, arg); + break; + + case VIDIOC_STFISP_SHADOW_LOCK: + if (atomic_add_unless(&isp_dev->shadow_count, 1, 1)) + ret = 0; + else + ret = -EBUSY; + break; + + case VIDIOC_STFISP_SHADOW_UNLOCK: + if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0) + ret = -EINVAL; + else + ret = 0; + break; + + case VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER: + isp_dev->hw_ops->isp_shadow_trigger(isp_dev); + if (atomic_dec_if_positive(&isp_dev->shadow_count) < 0) + ret = -EINVAL; + else + ret = 0; + break; + + case VIDIOC_STFISP_SET_USER_CONFIG_ISP: + st_debug(ST_ISP, "Set user config isp\n"); + user_config_isp = 1; + break; + + default: + break; + } + return ret; +} + +int isp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct stf_isp_dev *isp_dev = v4l2_get_subdevdata(sd); + + while (atomic_dec_if_positive(&isp_dev->shadow_count) > 0) + st_warn(ST_ISP, "unlock the shadow lock!\n"); + + return 0; +} + +static int stf_isp_subscribe_event(struct v4l2_subdev *sd, + struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); + default: + st_debug(ST_ISP, "unspport subscribe_event\n"); + return -EINVAL; + } +} + +static const struct v4l2_subdev_core_ops isp_core_ops = { + .s_power = isp_set_power, + .ioctl = stf_isp_ioctl, + .subscribe_event = stf_isp_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops isp_video_ops = { + .s_stream = isp_set_stream, +}; + +static const struct v4l2_subdev_pad_ops isp_pad_ops = { + .enum_mbus_code = isp_enum_mbus_code, + .enum_frame_size = isp_enum_frame_size, + .get_fmt = isp_get_format, + .set_fmt = isp_set_format, + .get_selection = isp_get_selection, + .set_selection = isp_set_selection, +}; + +static const struct v4l2_subdev_ops isp_v4l2_ops = { + .core = &isp_core_ops, + .video = &isp_video_ops, + .pad = &isp_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops isp_v4l2_internal_ops = { + .open = isp_init_formats, + .close = isp_close, +}; + +static const struct media_entity_operations isp_media_ops = { + .link_setup = isp_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd = &isp_dev->subdev; + struct media_pad *pads = isp_dev->pads; + int ret; + + v4l2_subdev_init(sd, &isp_v4l2_ops); + sd->internal_ops = &isp_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d", STF_ISP_NAME, 0); + v4l2_set_subdevdata(sd, isp_dev); + + ret = isp_init_formats(sd, NULL); + if (ret < 0) { + st_err(ST_ISP, "Failed to init format: %d\n", ret); + return ret; + } + + pads[STF_ISP_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[STF_ISP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + pads[STF_ISP_PAD_SRC_SS0].flags = MEDIA_PAD_FL_SOURCE; + pads[STF_ISP_PAD_SRC_SS1].flags = MEDIA_PAD_FL_SOURCE; + pads[STF_ISP_PAD_SRC_RAW].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &isp_media_ops; + ret = media_entity_pads_init(&sd->entity, STF_ISP_PAD_MAX, pads); + if (ret < 0) { + st_err(ST_ISP, "Failed to init media entity: %d\n", ret); + return ret; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + st_err(ST_ISP, "Failed to register subdev: %d\n", ret); + goto err_sreg; + } + + return 0; + +err_sreg: + media_entity_cleanup(&sd->entity); + return ret; +} + +int stf_isp_unregister(struct stf_isp_dev *isp_dev) +{ + v4l2_device_unregister_subdev(&isp_dev->subdev); + media_entity_cleanup(&isp_dev->subdev.entity); + mutex_destroy(&isp_dev->stream_lock); + mutex_destroy(&isp_dev->power_lock); + mutex_destroy(&isp_dev->setfile_lock); + return 0; +} diff --git a/drivers/media/platform/starfive/stf_isp.h b/drivers/media/platform/starfive/stf_isp.h new file mode 100644 index 0000000000000..ca80b8d1f1cc1 --- /dev/null +++ b/drivers/media/platform/starfive/stf_isp.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_isp.h + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_ISP_H +#define STF_ISP_H + +#include +#include + +#define STF_ISP_SETFILE "stf_isp0_fw.bin" + +#define ISP_RAW_DATA_BITS 12 +#define SCALER_RATIO_MAX 1 +#define STF_ISP_REG_OFFSET_MAX 0x0FFF +#define STF_ISP_REG_DELAY_MAX 100 + +/* isp registers */ +#define ISP_REG_CSI_INPUT_EN_AND_STATUS 0x00000000 +#define ISP_REG_CSIINTS_ADDR 0x00000008 +#define ISP_REG_CSI_MODULE_CFG 0x00000010 +#define ISP_REG_SENSOR 0x00000014 +#define ISP_REG_RAW_FORMAT_CFG 0x00000018 +#define ISP_REG_PIC_CAPTURE_START_CFG 0x0000001C +#define ISP_REG_PIC_CAPTURE_END_CFG 0x00000020 +#define ISP_REG_DUMP_CFG_0 0x00000024 +#define ISP_REG_DUMP_CFG_1 0x00000028 +#define ISP_REG_SCD_CFG_0 0x00000098 +#define ISP_REG_SCD_CFG_1 0x0000009C +#define ISP_REG_SC_CFG_1 0x000000BC +#define ISP_REG_ISP_CTRL_0 0x00000A00 +#define ISP_REG_ISP_CTRL_1 0x00000A08 +#define ISP_REG_PIPELINE_XY_SIZE 0x00000A0C +#define ISP_REG_IESHD_ADDR 0x00000A50 +#define ISP_REG_Y_PLANE_START_ADDR 0x00000A80 +#define ISP_REG_UV_PLANE_START_ADDR 0x00000A84 +#define ISP_REG_STRIDE 0x00000A88 +#define ISP_REG_PIXEL_COORDINATE_GEN 0x00000A8C +#define ISP_REG_SS0AY 0x00000A94 +#define ISP_REG_SS0AUV 0x00000A98 +#define ISP_REG_SS0S 0x00000A9C +#define ISP_REG_SS0IW 0x00000AA8 +#define ISP_REG_SS1AY 0x00000AAC +#define ISP_REG_SS1AUV 0x00000AB0 +#define ISP_REG_SS1S 0x00000AB4 +#define ISP_REG_SS1IW 0x00000AC0 +#define ISP_REG_YHIST_CFG_4 0x00000CD8 +#define ISP_REG_ITIIWSR 0x00000B20 +#define ISP_REG_ITIDWLSR 0x00000B24 +#define ISP_REG_ITIDWYSAR 0x00000B28 +#define ISP_REG_ITIDWUSAR 0x00000B2C +#define ISP_REG_ITIDRYSAR 0x00000B30 +#define ISP_REG_ITIDRUSAR 0x00000B34 +#define ISP_REG_ITIPDFR 0x00000B38 +#define ISP_REG_ITIDRLSR 0x00000B3C +#define ISP_REG_ITIBSR 0x00000B40 +#define ISP_REG_ITIAIR 0x00000B44 +#define ISP_REG_ITIDPSR 0x00000B48 + +/* The output line of ISP */ +enum isp_line_id { + STF_ISP_LINE_INVALID = -1, + STF_ISP_LINE_SRC = 1, + STF_ISP_LINE_SRC_SS0, + STF_ISP_LINE_SRC_SS1, + STF_ISP_LINE_SRC_RAW, + STF_ISP_LINE_MAX = STF_ISP_LINE_SRC_RAW +}; + +/* pad id for media framework */ +enum isp_pad_id { + STF_ISP_PAD_SINK = 0, + STF_ISP_PAD_SRC, + STF_ISP_PAD_SRC_SS0, + STF_ISP_PAD_SRC_SS1, + STF_ISP_PAD_SRC_RAW, + STF_ISP_PAD_MAX +}; + +enum { + EN_INT_NONE = 0, + EN_INT_ISP_DONE = (0x1 << 24), + EN_INT_CSI_DONE = (0x1 << 25), + EN_INT_SC_DONE = (0x1 << 26), + EN_INT_LINE_INT = (0x1 << 27), + EN_INT_ALL = (0xF << 24), +}; + +enum { + INTERFACE_DVP = 0, + INTERFACE_CSI, +}; + +struct isp_format { + u32 code; + u8 bpp; +}; + +struct isp_format_table { + const struct isp_format *fmts; + int nfmts; +}; + +struct regval_t { + u32 addr; + u32 val; + u32 mask; + u32 delay_ms; +}; + +struct reg_table { + const struct regval_t *regval; + int regval_num; +}; + +struct isp_stream_format { + struct v4l2_rect rect; + u32 bpp; +}; + +struct isp_setfile { + struct reg_table settings; + const u8 *data; + unsigned int size; + unsigned int state; +}; + +enum { + ISP_CROP = 0, + ISP_COMPOSE, + ISP_SCALE_SS0, + ISP_SCALE_SS1, + ISP_RECT_MAX +}; + +struct stf_isp_dev { + enum subdev_type sdev_type; /* This member must be first */ + struct stfcamss *stfcamss; + struct v4l2_subdev subdev; + struct media_pad pads[STF_ISP_PAD_MAX]; + struct v4l2_mbus_framefmt fmt[STF_ISP_PAD_MAX]; + struct isp_stream_format rect[ISP_RECT_MAX]; + const struct isp_format_table *formats; + unsigned int nformats; + const struct isp_hw_ops *hw_ops; + struct mutex power_lock; /* serialize power control*/ + int power_count; + struct mutex stream_lock; /* serialize stream control */ + int stream_count; + atomic_t shadow_count; + + struct mutex setfile_lock; /* protects setting files */ + struct isp_setfile setfile; + + union reg_buf *reg_buf; +}; + +struct isp_hw_ops { + int (*isp_clk_enable)(struct stf_isp_dev *isp_dev); + int (*isp_clk_disable)(struct stf_isp_dev *isp_dev); + int (*isp_reset)(struct stf_isp_dev *isp_dev); + int (*isp_config_set)(struct stf_isp_dev *isp_dev); + int (*isp_set_format)(struct stf_isp_dev *isp_dev, + struct isp_stream_format *crop, + u32 mcode, int type); + int (*isp_stream_set)(struct stf_isp_dev *isp_dev, int on); + int (*isp_reg_read)(struct stf_isp_dev *isp_dev, void *arg); + int (*isp_reg_write)(struct stf_isp_dev *isp_dev, void *arg); + int (*isp_shadow_trigger)(struct stf_isp_dev *isp_dev); +}; + +extern const struct isp_hw_ops isp_ops; + +int stf_isp_subdev_init(struct stfcamss *stfcamss); +int stf_isp_register(struct stf_isp_dev *isp_dev, struct v4l2_device *v4l2_dev); +int stf_isp_unregister(struct stf_isp_dev *isp_dev); + +#endif /* STF_ISP_H */ diff --git a/drivers/media/platform/starfive/stf_isp_hw_ops.c b/drivers/media/platform/starfive/stf_isp_hw_ops.c new file mode 100644 index 0000000000000..b2aee00a78ad3 --- /dev/null +++ b/drivers/media/platform/starfive/stf_isp_hw_ops.c @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_isp_hw_ops.c + * + * Register interface file for StarFive ISP driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + * + */ +#include +#include + +#include "stf_camss.h" + +static const struct regval_t isp_reg_init_config_list[] = { + /* config DC */ + {0x00000044, 0x00000000, 0, 0}, + /* config DEC */ + {0x00000030, 0x00000000, 0, 0}, + /* config OBC */ + {0x00000034, 0x000000BB, 0, 0}, + {0x000002E0, 0x40404040, 0, 0}, + {0x000002E4, 0x40404040, 0, 0}, + {0x000002E8, 0x40404040, 0, 0}, + {0x000002EC, 0x40404040, 0, 0}, + {0x000002F0, 0x00000000, 0, 0}, + {0x000002F4, 0x00000000, 0, 0}, + {0x000002F8, 0x00000000, 0, 0}, + {0x000002FC, 0x00000000, 0, 0}, + /* config LCBQ */ + {0x00000074, 0x00009900, 0, 0}, + {0x0000007C, 0x01E40040, 0, 0}, + {0x00000300, 0x01000100, 0, 0}, + {0x00000304, 0x01000100, 0, 0}, + {0x00000308, 0x01000100, 0, 0}, + {0x0000030C, 0x01000100, 0, 0}, + {0x00000310, 0x01000100, 0, 0}, + {0x00000314, 0x01000100, 0, 0}, + {0x00000318, 0x01000100, 0, 0}, + {0x0000031C, 0x01000100, 0, 0}, + {0x00000320, 0x01000100, 0, 0}, + {0x00000324, 0x01000100, 0, 0}, + {0x00000328, 0x01000100, 0, 0}, + {0x0000032C, 0x01000100, 0, 0}, + {0x00000330, 0x00000100, 0, 0}, + {0x00000334, 0x01000100, 0, 0}, + {0x00000338, 0x01000100, 0, 0}, + {0x0000033C, 0x01000100, 0, 0}, + {0x00000340, 0x01000100, 0, 0}, + {0x00000344, 0x01000100, 0, 0}, + {0x00000348, 0x01000100, 0, 0}, + {0x0000034C, 0x01000100, 0, 0}, + {0x00000350, 0x01000100, 0, 0}, + {0x00000354, 0x01000100, 0, 0}, + {0x00000358, 0x01000100, 0, 0}, + {0x0000035C, 0x01000100, 0, 0}, + {0x00000360, 0x01000100, 0, 0}, + {0x00000364, 0x00000100, 0, 0}, + {0x00000368, 0x01000100, 0, 0}, + {0x0000036C, 0x01000100, 0, 0}, + {0x00000370, 0x01000100, 0, 0}, + {0x00000374, 0x01000100, 0, 0}, + {0x00000378, 0x01000100, 0, 0}, + {0x0000037C, 0x01000100, 0, 0}, + {0x00000380, 0x01000100, 0, 0}, + {0x00000384, 0x01000100, 0, 0}, + {0x00000388, 0x01000100, 0, 0}, + {0x0000038C, 0x01000100, 0, 0}, + {0x00000390, 0x01000100, 0, 0}, + {0x00000394, 0x01000100, 0, 0}, + {0x00000398, 0x00000100, 0, 0}, + {0x0000039C, 0x01000100, 0, 0}, + {0x000003A0, 0x01000100, 0, 0}, + {0x000003A4, 0x01000100, 0, 0}, + {0x000003A8, 0x01000100, 0, 0}, + {0x000003AC, 0x01000100, 0, 0}, + {0x000003B0, 0x01000100, 0, 0}, + {0x000003B4, 0x01000100, 0, 0}, + {0x000003B8, 0x01000100, 0, 0}, + {0x000003BC, 0x01000100, 0, 0}, + {0x000003C0, 0x01000100, 0, 0}, + {0x000003C4, 0x01000100, 0, 0}, + {0x000003C8, 0x01000100, 0, 0}, + {0x000003CC, 0x00000100, 0, 0}, + {0x00000400, 0x00000000, 0, 0}, + {0x00000404, 0x00000000, 0, 0}, + {0x00000408, 0x00000000, 0, 0}, + {0x0000040C, 0x00000000, 0, 0}, + {0x00000410, 0x00000000, 0, 0}, + {0x00000414, 0x00000000, 0, 0}, + {0x00000418, 0x00000000, 0, 0}, + {0x0000041C, 0x00000000, 0, 0}, + {0x00000420, 0x00000000, 0, 0}, + {0x00000424, 0x00000000, 0, 0}, + {0x00000428, 0x00000000, 0, 0}, + {0x0000042C, 0x00000000, 0, 0}, + {0x00000430, 0x00000000, 0, 0}, + {0x00000434, 0x00000000, 0, 0}, + {0x00000438, 0x00000000, 0, 0}, + {0x0000043C, 0x00000000, 0, 0}, + {0x00000440, 0x00000000, 0, 0}, + {0x00000444, 0x00000000, 0, 0}, + {0x00000448, 0x00000000, 0, 0}, + {0x0000044C, 0x00000000, 0, 0}, + {0x00000450, 0x00000000, 0, 0}, + {0x00000454, 0x00000000, 0, 0}, + {0x00000458, 0x00000000, 0, 0}, + {0x0000045C, 0x00000000, 0, 0}, + {0x00000460, 0x00000000, 0, 0}, + {0x00000464, 0x00000000, 0, 0}, + {0x00000468, 0x00000000, 0, 0}, + {0x0000046C, 0x00000000, 0, 0}, + {0x00000470, 0x00000000, 0, 0}, + {0x00000474, 0x00000000, 0, 0}, + {0x00000478, 0x00000000, 0, 0}, + {0x0000047C, 0x00000000, 0, 0}, + {0x00000480, 0x00000000, 0, 0}, + {0x00000484, 0x00000000, 0, 0}, + {0x00000488, 0x00000000, 0, 0}, + {0x0000048C, 0x00000000, 0, 0}, + {0x00000490, 0x00000000, 0, 0}, + {0x00000494, 0x00000000, 0, 0}, + {0x00000498, 0x00000000, 0, 0}, + {0x0000049C, 0x00000000, 0, 0}, + {0x000004A0, 0x00000000, 0, 0}, + {0x000004A4, 0x00000000, 0, 0}, + {0x000004A8, 0x00000000, 0, 0}, + {0x000004AC, 0x00000000, 0, 0}, + {0x000004B0, 0x00000000, 0, 0}, + {0x000004B4, 0x00000000, 0, 0}, + {0x000004B8, 0x00000000, 0, 0}, + {0x000004BC, 0x00000000, 0, 0}, + {0x000004C0, 0x00000000, 0, 0}, + {0x000004C4, 0x00000000, 0, 0}, + {0x000004C8, 0x00000000, 0, 0}, + {0x000004CC, 0x00000000, 0, 0}, + /* config OECF */ + {0x00000100, 0x00100000, 0, 0}, + {0x00000104, 0x00400020, 0, 0}, + {0x00000108, 0x00800060, 0, 0}, + {0x0000010C, 0x00C000A0, 0, 0}, + {0x00000110, 0x010000E0, 0, 0}, + {0x00000114, 0x02000180, 0, 0}, + {0x00000118, 0x03000280, 0, 0}, + {0x0000011C, 0x03FE0380, 0, 0}, + {0x00000120, 0x00100000, 0, 0}, + {0x00000124, 0x00400020, 0, 0}, + {0x00000128, 0x00800060, 0, 0}, + {0x0000012C, 0x00C000A0, 0, 0}, + {0x00000130, 0x010000E0, 0, 0}, + {0x00000134, 0x02000180, 0, 0}, + {0x00000138, 0x03000280, 0, 0}, + {0x0000013C, 0x03FE0380, 0, 0}, + {0x00000140, 0x00100000, 0, 0}, + {0x00000144, 0x00400020, 0, 0}, + {0x00000148, 0x00800060, 0, 0}, + {0x0000014C, 0x00C000A0, 0, 0}, + {0x00000150, 0x010000E0, 0, 0}, + {0x00000154, 0x02000180, 0, 0}, + {0x00000158, 0x03000280, 0, 0}, + {0x0000015C, 0x03FE0380, 0, 0}, + {0x00000160, 0x00100000, 0, 0}, + {0x00000164, 0x00400020, 0, 0}, + {0x00000168, 0x00800060, 0, 0}, + {0x0000016C, 0x00C000A0, 0, 0}, + {0x00000170, 0x010000E0, 0, 0}, + {0x00000174, 0x02000180, 0, 0}, + {0x00000178, 0x03000280, 0, 0}, + {0x0000017C, 0x03FE0380, 0, 0}, + {0x00000180, 0x00100000, 0, 0}, + {0x00000184, 0x00400020, 0, 0}, + {0x00000188, 0x00800060, 0, 0}, + {0x0000018C, 0x00C000A0, 0, 0}, + {0x00000190, 0x010000E0, 0, 0}, + {0x00000194, 0x02000180, 0, 0}, + {0x00000198, 0x03000280, 0, 0}, + {0x0000019C, 0x03FE0380, 0, 0}, + {0x000001A0, 0x00100000, 0, 0}, + {0x000001A4, 0x00400020, 0, 0}, + {0x000001A8, 0x00800060, 0, 0}, + {0x000001AC, 0x00C000A0, 0, 0}, + {0x000001B0, 0x010000E0, 0, 0}, + {0x000001B4, 0x02000180, 0, 0}, + {0x000001B8, 0x03000280, 0, 0}, + {0x000001BC, 0x03FE0380, 0, 0}, + {0x000001C0, 0x00100000, 0, 0}, + {0x000001C4, 0x00400020, 0, 0}, + {0x000001C8, 0x00800060, 0, 0}, + {0x000001CC, 0x00C000A0, 0, 0}, + {0x000001D0, 0x010000E0, 0, 0}, + {0x000001D4, 0x02000180, 0, 0}, + {0x000001D8, 0x03000280, 0, 0}, + {0x000001DC, 0x03FE0380, 0, 0}, + {0x000001E0, 0x00100000, 0, 0}, + {0x000001E4, 0x00400020, 0, 0}, + {0x000001E8, 0x00800060, 0, 0}, + {0x000001EC, 0x00C000A0, 0, 0}, + {0x000001F0, 0x010000E0, 0, 0}, + {0x000001F4, 0x02000180, 0, 0}, + {0x000001F8, 0x03000280, 0, 0}, + {0x000001FC, 0x03FE0380, 0, 0}, + {0x00000200, 0x00800080, 0, 0}, + {0x00000204, 0x00800080, 0, 0}, + {0x00000208, 0x00800080, 0, 0}, + {0x0000020C, 0x00800080, 0, 0}, + {0x00000210, 0x00800080, 0, 0}, + {0x00000214, 0x00800080, 0, 0}, + {0x00000218, 0x00800080, 0, 0}, + {0x0000021C, 0x00800080, 0, 0}, + {0x00000220, 0x00800080, 0, 0}, + {0x00000224, 0x00800080, 0, 0}, + {0x00000228, 0x00800080, 0, 0}, + {0x0000022C, 0x00800080, 0, 0}, + {0x00000230, 0x00800080, 0, 0}, + {0x00000234, 0x00800080, 0, 0}, + {0x00000238, 0x00800080, 0, 0}, + {0x0000023C, 0x00800080, 0, 0}, + {0x00000240, 0x00800080, 0, 0}, + {0x00000244, 0x00800080, 0, 0}, + {0x00000248, 0x00800080, 0, 0}, + {0x0000024C, 0x00800080, 0, 0}, + {0x00000250, 0x00800080, 0, 0}, + {0x00000254, 0x00800080, 0, 0}, + {0x00000258, 0x00800080, 0, 0}, + {0x0000025C, 0x00800080, 0, 0}, + {0x00000260, 0x00800080, 0, 0}, + {0x00000264, 0x00800080, 0, 0}, + {0x00000268, 0x00800080, 0, 0}, + {0x0000026C, 0x00800080, 0, 0}, + {0x00000270, 0x00800080, 0, 0}, + {0x00000274, 0x00800080, 0, 0}, + {0x00000278, 0x00800080, 0, 0}, + {0x0000027C, 0x00800080, 0, 0}, + /* config OECFHM */ + {0x000003D0, 0x04000000, 0, 0}, + {0x000003D4, 0x0C000800, 0, 0}, + {0x000003D8, 0x00000FFF, 0, 0}, + {0x000003DC, 0x08000800, 0, 0}, + {0x000003E0, 0x08000800, 0, 0}, + {0x000003E4, 0x00000800, 0, 0}, + /* config LCCF */ + {0x00000050, 0x021C03C0, 0, 0}, + {0x00000058, 0x0000000B, 0, 0}, + {0x000000E0, 0x00000000, 0, 0}, + {0x000000E4, 0x00000000, 0, 0}, + {0x000000E8, 0x00000000, 0, 0}, + {0x000000EC, 0x00000000, 0, 0}, + /* config AWB */ + {0x00000280, 0x00000000, 0, 0}, + {0x00000284, 0x00000000, 0, 0}, + {0x00000288, 0x00000000, 0, 0}, + {0x0000028C, 0x00000000, 0, 0}, + {0x00000290, 0x00000000, 0, 0}, + {0x00000294, 0x00000000, 0, 0}, + {0x00000298, 0x00000000, 0, 0}, + {0x0000029C, 0x00000000, 0, 0}, + {0x000002A0, 0x00000000, 0, 0}, + {0x000002A4, 0x00000000, 0, 0}, + {0x000002A8, 0x00000000, 0, 0}, + {0x000002AC, 0x00000000, 0, 0}, + {0x000002B0, 0x00000000, 0, 0}, + {0x000002B4, 0x00000000, 0, 0}, + {0x000002B8, 0x00000000, 0, 0}, + {0x000002BC, 0x00000000, 0, 0}, + {0x000002C0, 0x00800080, 0, 0}, + {0x000002C4, 0x00800080, 0, 0}, + {0x000002C8, 0x00800080, 0, 0}, + {0x000002CC, 0x00800080, 0, 0}, + {0x000002D0, 0x00800080, 0, 0}, + {0x000002D4, 0x00800080, 0, 0}, + {0x000002D8, 0x00800080, 0, 0}, + {0x000002DC, 0x00800080, 0, 0}, + /* config CTC and DBC filter */ + {0x00000A10, 0x41400040, 0, 0}, + {0x00000A14, 0x02000200, 0, 0}, + /* config CFA */ + {0x00000018, 0x000011BB, 0, 0}, + {0x00000A1C, 0x00000032, 0, 0}, + /* config CCM */ + {0x00000C40, 0x00060000, 0, 0}, + {0x00000C44, 0x00000000, 0, 0}, + {0x00000C48, 0x00000000, 0, 0}, + {0x00000C4C, 0x00000000, 0, 0}, + {0x00000C50, 0x00000000, 0, 0}, + {0x00000C54, 0x00000000, 0, 0}, + {0x00000C58, 0x00000000, 0, 0}, + {0x00000C5C, 0x00000000, 0, 0}, + {0x00000C60, 0x00000000, 0, 0}, + {0x00000C64, 0x00000000, 0, 0}, + {0x00000C68, 0x00000000, 0, 0}, + {0x00000C6C, 0x00000000, 0, 0}, + {0x00000C70, 0x00000080, 0, 0}, + {0x00000C74, 0x00000000, 0, 0}, + {0x00000C78, 0x00000000, 0, 0}, + {0x00000C7C, 0x00000000, 0, 0}, + {0x00000C80, 0x00000080, 0, 0}, + {0x00000C84, 0x00000000, 0, 0}, + {0x00000C88, 0x00000000, 0, 0}, + {0x00000C8C, 0x00000000, 0, 0}, + {0x00000C90, 0x00000080, 0, 0}, + {0x00000C94, 0x00000000, 0, 0}, + {0x00000C98, 0x00000000, 0, 0}, + {0x00000C9C, 0x00000000, 0, 0}, + {0x00000CA0, 0x00000700, 0, 0}, + {0x00000CA4, 0x00000200, 0, 0}, + /* config GMARGB */ + {0x00000E00, 0x24000000, 0, 0}, + {0x00000E04, 0x08000020, 0, 0}, + {0x00000E08, 0x08000040, 0, 0}, + {0x00000E0C, 0x08000060, 0, 0}, + {0x00000E10, 0x08000080, 0, 0}, + {0x00000E14, 0x080000A0, 0, 0}, + {0x00000E18, 0x080000C0, 0, 0}, + {0x00000E1C, 0x080000E0, 0, 0}, + {0x00000E20, 0x08000100, 0, 0}, + {0x00000E24, 0x08000180, 0, 0}, + {0x00000E28, 0x08000200, 0, 0}, + {0x00000E2C, 0x08000280, 0, 0}, + {0x00000E30, 0x08000300, 0, 0}, + {0x00000E34, 0x08000380, 0, 0}, + {0x00000E38, 0x080003FE, 0, 0}, + /* config R2Y */ + {0x00000E40, 0x0000004C, 0, 0}, + {0x00000E44, 0x00000097, 0, 0}, + {0x00000E48, 0x0000001D, 0, 0}, + {0x00000E4C, 0x000001D5, 0, 0}, + {0x00000E50, 0x000001AC, 0, 0}, + {0x00000E54, 0x00000080, 0, 0}, + {0x00000E58, 0x00000080, 0, 0}, + {0x00000E5C, 0x00000194, 0, 0}, + {0x00000E60, 0x000001EC, 0, 0}, + /* config YCRV */ + {0x00000F00, 0x00000000, 0, 0}, + {0x00000F04, 0x00000010, 0, 0}, + {0x00000F08, 0x00000020, 0, 0}, + {0x00000F0C, 0x00000030, 0, 0}, + {0x00000F10, 0x00000040, 0, 0}, + {0x00000F14, 0x00000050, 0, 0}, + {0x00000F18, 0x00000060, 0, 0}, + {0x00000F1C, 0x00000070, 0, 0}, + {0x00000F20, 0x00000080, 0, 0}, + {0x00000F24, 0x00000090, 0, 0}, + {0x00000F28, 0x000000A0, 0, 0}, + {0x00000F2C, 0x000000B0, 0, 0}, + {0x00000F30, 0x000000C0, 0, 0}, + {0x00000F34, 0x000000D0, 0, 0}, + {0x00000F38, 0x000000E0, 0, 0}, + {0x00000F3C, 0x000000F0, 0, 0}, + {0x00000F40, 0x00000100, 0, 0}, + {0x00000F44, 0x00000110, 0, 0}, + {0x00000F48, 0x00000120, 0, 0}, + {0x00000F4C, 0x00000130, 0, 0}, + {0x00000F50, 0x00000140, 0, 0}, + {0x00000F54, 0x00000150, 0, 0}, + {0x00000F58, 0x00000160, 0, 0}, + {0x00000F5C, 0x00000170, 0, 0}, + {0x00000F60, 0x00000180, 0, 0}, + {0x00000F64, 0x00000190, 0, 0}, + {0x00000F68, 0x000001A0, 0, 0}, + {0x00000F6C, 0x000001B0, 0, 0}, + {0x00000F70, 0x000001C0, 0, 0}, + {0x00000F74, 0x000001D0, 0, 0}, + {0x00000F78, 0x000001E0, 0, 0}, + {0x00000F7C, 0x000001F0, 0, 0}, + {0x00000F80, 0x00000200, 0, 0}, + {0x00000F84, 0x00000210, 0, 0}, + {0x00000F88, 0x00000220, 0, 0}, + {0x00000F8C, 0x00000230, 0, 0}, + {0x00000F90, 0x00000240, 0, 0}, + {0x00000F94, 0x00000250, 0, 0}, + {0x00000F98, 0x00000260, 0, 0}, + {0x00000F9C, 0x00000270, 0, 0}, + {0x00000FA0, 0x00000280, 0, 0}, + {0x00000FA4, 0x00000290, 0, 0}, + {0x00000FA8, 0x000002A0, 0, 0}, + {0x00000FAC, 0x000002B0, 0, 0}, + {0x00000FB0, 0x000002C0, 0, 0}, + {0x00000FB4, 0x000002D0, 0, 0}, + {0x00000FB8, 0x000002E0, 0, 0}, + {0x00000FBC, 0x000002F0, 0, 0}, + {0x00000FC0, 0x00000300, 0, 0}, + {0x00000FC4, 0x00000310, 0, 0}, + {0x00000FC8, 0x00000320, 0, 0}, + {0x00000FCC, 0x00000330, 0, 0}, + {0x00000FD0, 0x00000340, 0, 0}, + {0x00000FD4, 0x00000350, 0, 0}, + {0x00000FD8, 0x00000360, 0, 0}, + {0x00000FDC, 0x00000370, 0, 0}, + {0x00000FE0, 0x00000380, 0, 0}, + {0x00000FE4, 0x00000390, 0, 0}, + {0x00000FE8, 0x000003A0, 0, 0}, + {0x00000FEC, 0x000003B0, 0, 0}, + {0x00000FF0, 0x000003C0, 0, 0}, + {0x00000FF4, 0x000003D0, 0, 0}, + {0x00000FF8, 0x000003E0, 0, 0}, + {0x00000FFC, 0x000003F0, 0, 0}, + /* config Sharp */ + {0x00000E80, 0x00070F00, 0, 0}, + {0x00000E84, 0x00180F00, 0, 0}, + {0x00000E88, 0x00800F00, 0, 0}, + {0x00000E8C, 0x01000F00, 0, 0}, + {0x00000E90, 0x00100F00, 0, 0}, + {0x00000E94, 0x00600F00, 0, 0}, + {0x00000E98, 0x01000F00, 0, 0}, + {0x00000E9C, 0x01900F00, 0, 0}, + {0x00000EA0, 0x00000F00, 0, 0}, + {0x00000EA4, 0x00000F00, 0, 0}, + {0x00000EA8, 0x00000F00, 0, 0}, + {0x00000EAC, 0x00000F00, 0, 0}, + {0x00000EB0, 0x00000F00, 0, 0}, + {0x00000EB4, 0x00000F00, 0, 0}, + {0x00000EB8, 0x00000F00, 0, 0}, + {0x00000EBC, 0x10000000, 0, 0}, + {0x00000EC0, 0x10000000, 0, 0}, + {0x00000EC4, 0x10000000, 0, 0}, + {0x00000EC8, 0x10000000, 0, 0}, + {0x00000ECC, 0x10000000, 0, 0}, + {0x00000ED0, 0x10000000, 0, 0}, + {0x00000ED4, 0x88000D7C, 0, 0}, + {0x00000ED8, 0x00C00040, 0, 0}, + {0x00000EDC, 0xFF000000, 0, 0}, + {0x00000EE0, 0x00A00040, 0, 0}, + {0x00000EE4, 0x00000000, 0, 0}, + {0x00000EE8, 0x00000000, 0, 0}, + /* config DNYUV */ + {0x00000C00, 0x00777777, 0, 0}, + {0x00000C04, 0x00007777, 0, 0}, + {0x00000C08, 0x00777777, 0, 0}, + {0x00000C0C, 0x00007777, 0, 0}, + {0x00000C10, 0x00600040, 0, 0}, + {0x00000C14, 0x00D80090, 0, 0}, + {0x00000C18, 0x01E60144, 0, 0}, + {0x00000C1C, 0x00600040, 0, 0}, + {0x00000C20, 0x00D80090, 0, 0}, + {0x00000C24, 0x01E60144, 0, 0}, + /* config SAT */ + {0x00000A30, 0x00000100, 0, 0}, + {0x00000A34, 0x001F0001, 0, 0}, + {0x00000A38, 0x00000000, 0, 0}, + {0x00000A3C, 0x00000100, 0, 0}, + {0x00000A40, 0x00000008, 0, 0}, + {0x00000A54, 0x04010001, 0, 0}, + {0x00000A58, 0x03FF0001, 0, 0}, + /* config OBA */ + {0x00000090, 0x04380000, 0, 0}, + {0x00000094, 0x04390780, 0, 0}, + /* config SC */ + {0x0000009C, 0x01000000, 0, 0}, + {0x000000B8, 0x000C0000, 0, 0}, + {0x000000BC, 0xC010151D, 0, 0}, + {0x000000C0, 0x01F1BF08, 0, 0}, + {0x000000C4, 0xFF00FF00, 0, 0}, + {0x000000C8, 0xFF00FF00, 0, 0}, + {0x000000CC, 0xFFFF0000, 0, 0}, + {0x000000D0, 0xFFFF0000, 0, 0}, + {0x000000D4, 0xFFFF0000, 0, 0}, + {0x000000D8, 0x01050107, 0, 0}, + {0x000004D0, 0x00000000, 0, 0}, + {0x000004D4, 0x00000000, 0, 0}, + {0x000004D8, 0x00000000, 0, 0}, + {0x000004DC, 0x00000000, 0, 0}, + {0x000004E0, 0x00000000, 0, 0}, + {0x000004E4, 0x00000000, 0, 0}, + {0x000004E8, 0x00000000, 0, 0}, + {0x000004EC, 0x00000000, 0, 0}, + {0x000004F0, 0x00100000, 0, 0}, + {0x000004F4, 0x00000000, 0, 0}, + {0x000004F8, 0x03D20000, 0, 0}, + {0x000004FC, 0x00000000, 0, 0}, + {0x00000500, 0x00950000, 0, 0}, + {0x00000504, 0x00000000, 0, 0}, + {0x00000508, 0x00253000, 0, 0}, + {0x0000050C, 0x00000000, 0, 0}, + {0x00000510, 0x00000000, 0, 0}, + {0x00000514, 0x00000000, 0, 0}, + {0x00000518, 0x00000000, 0, 0}, + {0x0000051C, 0x00000000, 0, 0}, + {0x00000520, 0x00000000, 0, 0}, + {0x00000524, 0x00000000, 0, 0}, + {0x00000528, 0x00000000, 0, 0}, + {0x0000052C, 0x00000000, 0, 0}, + {0x00000530, 0x00000000, 0, 0}, + {0x00000534, 0x00000000, 0, 0}, + {0x00000538, 0xFFFFFFF0, 0, 0}, + {0x0000053C, 0x8FFFFFFF, 0, 0}, + {0x00000540, 0x0000001E, 0, 0}, + {0x00000544, 0x00000000, 0, 0}, + {0x00000548, 0x00000000, 0, 0}, + {0x0000054C, 0xF0F20000, 0, 0}, + {0x000005D0, 0xFF00FF00, 0, 0}, + {0x000005D4, 0xFF00FF00, 0, 0}, + /* config YHIST */ + {0x00000CC8, 0x00000000, 0, 0}, + {0x00000CCC, 0x0437077F, 0, 0}, + {0x00000CD0, 0x00010002, 0, 0}, + {0x00000CD4, 0x00000000, 0, 0}, + /* config CBAR */ + {0x00000600, 0x043E0782, 0, 0}, + {0x00000604, 0x00000000, 0, 0}, + {0x00000608, 0x0437077F, 0, 0}, + {0x0000060C, 0x00443150, 0, 0}, + {0x00000610, 0x00000000, 0, 0}, + {0x00000614, 0x08880888, 0, 0}, + {0x00000618, 0x02220222, 0, 0}, + {0x0000061C, 0x04440444, 0, 0}, + {0x00000620, 0x08880888, 0, 0}, + {0x00000624, 0x0AAA0AAA, 0, 0}, + {0x00000628, 0x0CCC0CCC, 0, 0}, + {0x0000062C, 0x0EEE0EEE, 0, 0}, + {0x00000630, 0x0FFF0FFF, 0, 0}, + {0x00000634, 0x08880888, 0, 0}, + {0x00000638, 0x02220222, 0, 0}, + {0x0000063C, 0x04440444, 0, 0}, + {0x00000640, 0x08880888, 0, 0}, + {0x00000644, 0x0AAA0AAA, 0, 0}, + {0x00000648, 0x0CCC0CCC, 0, 0}, + {0x0000064C, 0x0EEE0EEE, 0, 0}, + {0x00000650, 0x0FFF0FFF, 0, 0}, + /* config sensor */ + {0x00000014, 0x0000000c, 0, 0}, + /* config CROP */ + {0x0000001C, 0x00000000, 0, 0}, + {0x00000020, 0x0437077F, 0, 0}, + /* config isp pileline X/Y size */ + {0x00000A0C, 0x04380780, 0, 0}, + /* config CSI dump */ + {0x00000028, 0x00030B80, 0, 0}, + /* config UO */ + {0x00000A88, 0x00000780, 0, 0}, + /* NV12 */ + {0x00000A8C, 0x00000000, 0, 0}, + /* NV21 + *{0x00000A8C, 0x00000020, 0, 0}, + */ + {0x00000A90, 0x00000000, 0, 0}, + {0x00000A9C, 0x00000780, 0, 0}, + {0x00000AA0, 0x00000002, 0, 0}, + {0x00000AA4, 0x00000002, 0, 0}, + {0x00000AA8, 0x07800438, 0, 0}, + {0x00000AB4, 0x00000780, 0, 0}, + {0x00000AB8, 0x00000002, 0, 0}, + {0x00000ABC, 0x00000002, 0, 0}, + {0x00000AC0, 0x07800438, 0, 0}, + {0x00000AC4, 0x00000000, 0, 0}, + /* config TIL */ + {0x00000B20, 0x04380780, 0, 0}, + {0x00000B24, 0x00000960, 0, 0}, + {0x00000B38, 0x00030003, 0, 0}, + {0x00000B3C, 0x00000960, 0, 0}, + {0x00000B44, 0x00000000, 0, 0}, + {0x00000B48, 0x00000000, 0, 0}, + /* Enable DEC/OBC/OECF/LCCF/AWB/SC/DUMP */ + {0x00000010, 0x000A00D6, 0x00000000, 0x00}, + /* Enable CFA/CAR/CCM/GMARGB/R2Y/SHRP/SAT/DNYUV/YCRV/YHIST/CTC/DBC */ + {0x00000A08, 0x107A01BE, 0x00000000, 0x00}, +}; + +const struct reg_table isp_reg_init_settings[] = { + {isp_reg_init_config_list, ARRAY_SIZE(isp_reg_init_config_list)}, +}; + +static const struct regval_t isp_reg_start_config_list[] = { + /* ENABLE UO/Multi-Frame and Reset ISP */ + {0x00000A00, 0x00120002, 0x00000000, 0x0A}, + /* ENABLE UO/Multi-Frame and Leave ISP reset */ + {0x00000A00, 0x00120000, 0x00000000, 0x0A}, + /* Config ISP shadow mode as next-vsync */ + {0x00000A50, 0x00000002, 0x00000000, 0x00}, + /* ENABLE UO/Multi-Frame and Enable ISP */ + {0x00000A00, 0x00120001, 0x00000000, 0x0A}, + /* Config CSI shadow mode as immediate to fetch current setting */ + {0x00000008, 0x00010004, 0x00000000, 0x0A}, + /* Config CSI shadow mode as next-vsync */ + {0x00000008, 0x00020004, 0x00000000, 0x00}, + /* Enable CSI */ + {0x00000000, 0x00000001, 0x00000000, 0x0A}, +}; + +const struct reg_table isp_reg_start_settings[] = { + {isp_reg_start_config_list, ARRAY_SIZE(isp_reg_start_config_list)}, +}; + +static struct regval_t isp_format_reg_list[] = { + {0x0000001C, 0x00000000, 0x00000000, 0}, + {0x00000020, 0x0437077F, 0x00000000, 0}, + {0x00000A0C, 0x04380780, 0x00000000, 0}, + {0x00000A88, 0x00000780, 0x00000000, 0}, + {0x00000018, 0x000011BB, 0x00000000, 0}, + {0x00000A08, 0x10000000, 0xF0000000, 0}, + {0x00000028, 0x00030B80, 0x0003FFFF, 0}, + {0x00000AA8, 0x07800438, 0x00000000, 0}, + {0x00000A9C, 0x00000780, 0x00000000, 0}, + {0x00000AC0, 0x07800438, 0x00000000, 0}, + {0x00000AB4, 0x00000780, 0x00000000, 0}, + {0x00000B20, 0x04380780, 0x00000000, 0}, + {0x00000B24, 0x00000960, 0x00000000, 0}, + {0x00000B3C, 0x00000960, 0x00000000, 0}, + {0x00000014, 0x00000008, 0x00000000, 0}, +}; + +const struct reg_table isp_format_settings[] = { + {isp_format_reg_list, ARRAY_SIZE(isp_format_reg_list)}, +}; + +static void isp_load_regs(void __iomem *ispbase, const struct reg_table *table) +{ + int j; + u32 delay_ms, reg_addr, mask, val; + + for (j = 0; j < table->regval_num; j++) { + delay_ms = table->regval[j].delay_ms; + reg_addr = table->regval[j].addr; + val = table->regval[j].val; + mask = table->regval[j].mask; + + if (reg_addr % 4 || + reg_addr > STF_ISP_REG_OFFSET_MAX || + delay_ms > STF_ISP_REG_DELAY_MAX) + continue; + + if (mask) + reg_set_bit(ispbase, reg_addr, mask, val); + else + reg_write(ispbase, reg_addr, val); + + if (delay_ms) + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + } +} + +static void isp_load_regs_exclude_csi_isp_enable(void __iomem *ispbase, + const struct reg_table *table) +{ + int j; + u32 delay_ms, reg_addr, mask, val; + + for (j = 0; j < table->regval_num; j++) { + delay_ms = table->regval[j].delay_ms; + reg_addr = table->regval[j].addr; + val = table->regval[j].val; + mask = table->regval[j].mask; + + if (reg_addr % 4 || + reg_addr > STF_ISP_REG_OFFSET_MAX || + delay_ms > STF_ISP_REG_DELAY_MAX || + (reg_addr == ISP_REG_CSI_INPUT_EN_AND_STATUS && (val & 0x01)) || + (reg_addr == ISP_REG_ISP_CTRL_0 && (val & 0x01))) + continue; + + if (mask) + reg_set_bit(ispbase, reg_addr, mask, val); + else + reg_write(ispbase, reg_addr, val); + if (delay_ms) + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + } +} + +static int stf_isp_clk_enable(struct stf_isp_dev *isp_dev) +{ + struct stfcamss *stfcamss = isp_dev->stfcamss; + + clk_prepare_enable(stfcamss->sys_clk[STF_CLK_WRAPPER_CLK_C].clk); + reset_control_deassert(stfcamss->sys_rst[STF_RST_WRAPPER_C].rstc); + reset_control_deassert(stfcamss->sys_rst[STF_RST_WRAPPER_P].rstc); + + return 0; +} + +static int stf_isp_clk_disable(struct stf_isp_dev *isp_dev) +{ + struct stfcamss *stfcamss = isp_dev->stfcamss; + + reset_control_assert(stfcamss->sys_rst[STF_RST_WRAPPER_C].rstc); + reset_control_assert(stfcamss->sys_rst[STF_RST_WRAPPER_P].rstc); + clk_disable_unprepare(stfcamss->sys_clk[STF_CLK_WRAPPER_CLK_C].clk); + + return 0; +} + +static void __iomem *stf_isp_get_ispbase(struct stf_isp_dev *isp_dev) +{ + void __iomem *base = isp_dev->stfcamss->isp_base; + + return base; +} + +static int stf_isp_reset(struct stf_isp_dev *isp_dev) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), BIT(1)); + reg_set_bit(ispbase, ISP_REG_ISP_CTRL_0, BIT(1), 0); + + return 0; +} + +static int stf_isp_config_set(struct stf_isp_dev *isp_dev) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + mutex_lock(&isp_dev->setfile_lock); + isp_load_regs(ispbase, isp_reg_init_settings); + if (isp_dev->setfile.state) { + st_info(ST_ISP, "%s, Program extra ISP setting!\n", __func__); + isp_load_regs_exclude_csi_isp_enable(ispbase, + &isp_dev->setfile.settings); + } + + mutex_unlock(&isp_dev->setfile_lock); + + return 0; +} + +static int stf_isp_set_format(struct stf_isp_dev *isp_dev, + struct isp_stream_format *crop_array, + u32 mcode, + int type) +{ + struct v4l2_rect *crop = &crop_array[ISP_COMPOSE].rect; + u32 bpp = crop_array[ISP_COMPOSE].bpp; + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + u32 val, val1; + + st_debug(ST_ISP, "interface type is %d(%s)\n", + type, type == INTERFACE_CSI ? "CSI" : "DVP"); + + val = crop->left + (crop->top << 16); + isp_format_reg_list[0].addr = ISP_REG_PIC_CAPTURE_START_CFG; + isp_format_reg_list[0].val = val; + + val = (crop->width + crop->left - 1) + + ((crop->height + crop->top - 1) << 16); + isp_format_reg_list[1].addr = ISP_REG_PIC_CAPTURE_END_CFG; + isp_format_reg_list[1].val = val; + + val = crop->width + (crop->height << 16); + isp_format_reg_list[2].addr = ISP_REG_PIPELINE_XY_SIZE; + isp_format_reg_list[2].val = val; + + isp_format_reg_list[3].addr = ISP_REG_STRIDE; + isp_format_reg_list[3].val = ALIGN(crop->width * bpp / 8, + STFCAMSS_FRAME_WIDTH_ALIGN_8); + + switch (mcode) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB8_1X8: + /* 3 2 3 2 1 0 1 0 B Gb B Gb Gr R Gr R */ + val = 0x0000EE44; + val1 = 0x00000000; + break; + case MEDIA_BUS_FMT_SGRBG10_1X10: + case MEDIA_BUS_FMT_SGRBG8_1X8: + /* 2 3 2 3 0 1 0 1, Gb B Gb B R Gr R Gr */ + val = 0x0000BB11; + val1 = 0x20000000; + break; + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG8_1X8: + /* 1 0 1 0 3 2 3 2, Gr R Gr R B Gb B Gb */ + val = 0x000044EE; + val1 = 0x30000000; + break; + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR8_1X8: + /* 0 1 0 1 2 3 2 3 R Gr R Gr Gb B Gb B */ + val = 0x000011BB; + val1 = 0x10000000; + break; + default: + st_err(ST_ISP, "UNKNOWN format\n"); + val = 0x000011BB; + val1 = 0x10000000; + break; + } + + isp_format_reg_list[4].addr = ISP_REG_RAW_FORMAT_CFG; + isp_format_reg_list[4].val = val; + + isp_format_reg_list[5].addr = ISP_REG_ISP_CTRL_1; + isp_format_reg_list[5].val = val1; + isp_format_reg_list[5].mask = 0xF0000000; + + st_info(ST_ISP, + "src left: %d, top: %d, width = %d, height = %d, bpp = %d\n", + crop->left, crop->top, crop->width, crop->height, bpp); + + crop = &crop_array[ISP_CROP].rect; + bpp = crop_array[ISP_CROP].bpp; + val = ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_128); + isp_format_reg_list[6].addr = ISP_REG_DUMP_CFG_1; + isp_format_reg_list[6].val = val | 3 << 16; + isp_format_reg_list[6].mask = 0x0003FFFF; + + st_info(ST_ISP, + "raw left: %d, top: %d, width = %d, height = %d, bpp = %d\n", + crop->left, crop->top, crop->width, crop->height, bpp); + + crop = &crop_array[ISP_SCALE_SS0].rect; + bpp = crop_array[ISP_SCALE_SS0].bpp; + isp_format_reg_list[7].addr = ISP_REG_SS0IW; + isp_format_reg_list[7].val = (crop->width << 16) + crop->height; + isp_format_reg_list[8].addr = ISP_REG_SS0S; + isp_format_reg_list[8].val = + ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8); + + st_info(ST_ISP, + "ss0 left: %d, top: %d, width = %d, height = %d, bpp = %d\n", + crop->left, crop->top, crop->width, crop->height, bpp); + + crop = &crop_array[ISP_SCALE_SS1].rect; + bpp = crop_array[ISP_SCALE_SS1].bpp; + isp_format_reg_list[9].addr = ISP_REG_SS1IW; + isp_format_reg_list[9].val = (crop->width << 16) + crop->height; + isp_format_reg_list[10].addr = ISP_REG_SS1S; + isp_format_reg_list[10].val = + ALIGN(crop->width * bpp / 8, STFCAMSS_FRAME_WIDTH_ALIGN_8); + + isp_format_reg_list[14].addr = ISP_REG_SENSOR; + isp_format_reg_list[14].val = 0x00000000; + if (type == INTERFACE_CSI) + isp_format_reg_list[14].val |= 0x01; + + isp_load_regs(ispbase, isp_format_settings); + return 0; +} + +static int stf_isp_stream_set(struct stf_isp_dev *isp_dev, int on) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + if (on) + isp_load_regs(ispbase, isp_reg_start_settings); + + return 0; +} + +static int stf_isp_reg_read(struct stf_isp_dev *isp_dev, void *arg) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + struct isp_reg_param *reg_param = arg; + union reg_buf *reg_buf = isp_dev->reg_buf; + u32 size; + unsigned long r; + + if (!reg_param->reg_buf) { + st_err(ST_ISP, + "Failed to access register.The pointer is NULL!\n"); + return -EINVAL; + } + + size = 0; + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + break; + + case STF_ISP_REG_METHOD_SERIES: + if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + break; + + case STF_ISP_REG_METHOD_MODULE: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + case STF_ISP_REG_METHOD_TABLE: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_TABLE_2: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 3; + break; + + case STF_ISP_REG_METHOD_TABLE_3: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 4; + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + default: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + } + + memset(reg_buf, 0, sizeof(union reg_buf)); + if (size) { + r = copy_from_user((u8 *)reg_buf->buffer, + (u8 *)reg_param->reg_buf->buffer, size); + if (r) { + st_err(ST_ISP, "Failed to copy buffer data\n"); + return -EIO; + } + } + + size = 0; + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + reg_buf->buffer[0] = reg_read(ispbase, + reg_param->reg_info.offset); + size = sizeof(u32); + break; + + case STF_ISP_REG_METHOD_SERIES: + for (r = 0; r < reg_param->reg_info.length; r++) + reg_buf->buffer[r] = + reg_read(ispbase, + reg_param->reg_info.offset + (r * 4)); + + size = sizeof(u32) * reg_param->reg_info.length; + break; + + case STF_ISP_REG_METHOD_MODULE: + break; + + case STF_ISP_REG_METHOD_TABLE: + for (r = 0; r < reg_param->reg_info.length; r++) + reg_buf->reg_tbl[r].value = + reg_read(ispbase, reg_buf->reg_tbl[r].offset); + + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_TABLE_2: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl2[r].mask) + reg_buf->reg_tbl2[r].value = + reg_read(ispbase, reg_buf->reg_tbl2[r].offset) & + reg_buf->reg_tbl2[r].mask; + else + reg_buf->reg_tbl2[r].value = + reg_read(ispbase, + reg_buf->reg_tbl2[r].offset); + } + + size = sizeof(u32) * reg_param->reg_info.length * 3; + break; + + case STF_ISP_REG_METHOD_TABLE_3: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl3[r].mask) + reg_buf->reg_tbl3[r].value = + reg_read(ispbase, reg_buf->reg_tbl3[r].offset) & + reg_buf->reg_tbl3[r].mask; + else + reg_buf->reg_tbl3[r].value = + reg_read(ispbase, + reg_buf->reg_tbl3[r].offset); + + if (reg_buf->reg_tbl3[r].delay_ms) + usleep_range(1000 * reg_buf->reg_tbl3[r].delay_ms, + 1000 * reg_buf->reg_tbl3[r].delay_ms + 100); + } + + size = sizeof(u32) * reg_param->reg_info.length * 4; + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + break; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + break; + + default: + break; + } + + r = copy_to_user((u8 *)reg_param->reg_buf->buffer, + (u8 *)reg_buf->buffer, size); + if (r) { + st_err(ST_ISP, + "Failed to copy the reg_param->buffer value\n"); + return -EIO; + } + + return 0; +} + +static int stf_isp_soft_rdma(struct stf_isp_dev *isp_dev, u32 rdma_addr) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + struct isp_rdma_info *rdma_info = NULL; + s32 len; + u32 offset; + int ret = 0; + + rdma_info = phys_to_virt(rdma_addr); + while (1) { + if (rdma_info->tag == RDMA_END) + break; + + if (rdma_info->tag == RDMA_WR_ONE) { + reg_write(ispbase, rdma_info->offset, rdma_info->param); + rdma_info++; + } else if (rdma_info->tag == RDMA_WR_SRL) { + offset = rdma_info->offset; + len = rdma_info->param; + rdma_info++; + while (len > 0) { + reg_write(ispbase, offset, rdma_info->param); + offset += 4; + len--; + if (len > 0) { + reg_write(ispbase, + offset, rdma_info->value); + len--; + } + offset += 4; + rdma_info++; + } + } else if (rdma_info->tag == RDMA_LINK) { + rdma_info = phys_to_virt(rdma_info->param); + } else if (rdma_info->tag == RDMA_SINT) { + /* not support this command. */ + rdma_info++; + } else { + rdma_info++; + } + } + + return ret; +} + +static int stf_isp_reg_write(struct stf_isp_dev *isp_dev, void *arg) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + struct isp_reg_param *reg_param = arg; + struct isp_rdma_info *rdma_info = NULL; + union reg_buf *reg_buf = isp_dev->reg_buf; + s32 len; + u32 offset; + u32 size; + unsigned long r; + int ret = 0; + + if (!reg_param->reg_buf && + reg_param->reg_info.method != STF_ISP_REG_METHOD_SOFT_RDMA) { + st_err(ST_ISP, "Failed to access register.\n"); + return -EINVAL; + } + + size = 0; + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + size = sizeof(u32); + break; + + case STF_ISP_REG_METHOD_SERIES: + if (reg_param->reg_info.length > STF_ISP_REG_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length; + break; + + case STF_ISP_REG_METHOD_MODULE: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + + case STF_ISP_REG_METHOD_TABLE: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_TABLE_2: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_2_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 3; + break; + + case STF_ISP_REG_METHOD_TABLE_3: + if (reg_param->reg_info.length > STF_ISP_REG_TBL_3_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 4; + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + if (reg_param->reg_info.length > + STF_ISP_REG_SMPL_PACK_BUF_SIZE) { + st_err(ST_ISP, + "The length = 0x%08X is out of size!\n", + reg_param->reg_info.length); + return -EINVAL; + } + size = sizeof(u32) * reg_param->reg_info.length * 2; + break; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + break; + + default: + st_err(ST_ISP, "The method = %d is not supported!\n", + reg_param->reg_info.method); + return -ENOTTY; + } + + memset(reg_buf, 0, sizeof(union reg_buf)); + if (size) { + r = copy_from_user((u8 *)reg_buf->buffer, + (u8 *)reg_param->reg_buf->buffer, size); + if (r) { + st_err(ST_ISP, "Failed to copy buffer data\n"); + return -EIO; + } + } + + switch (reg_param->reg_info.method) { + case STF_ISP_REG_METHOD_ONE_REG: + reg_write(ispbase, + reg_param->reg_info.offset, reg_buf->buffer[0]); + break; + + case STF_ISP_REG_METHOD_SERIES: + for (r = 0; r < reg_param->reg_info.length; r++) { + reg_write(ispbase, + reg_param->reg_info.offset + (r * 4), + reg_buf->buffer[r]); + } + break; + + case STF_ISP_REG_METHOD_MODULE: + /* This mode is not supported.*/ + break; + + case STF_ISP_REG_METHOD_TABLE: + for (r = 0; r < reg_param->reg_info.length; r++) { + reg_write(ispbase, reg_buf->reg_tbl[r].offset, + reg_buf->reg_tbl[r].value); + } + break; + + case STF_ISP_REG_METHOD_TABLE_2: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl2[r].mask) { + reg_set_bit(ispbase, + reg_buf->reg_tbl2[r].offset, + reg_buf->reg_tbl2[r].mask, + reg_buf->reg_tbl2[r].value); + } else { + reg_write(ispbase, reg_buf->reg_tbl2[r].offset, + reg_buf->reg_tbl2[r].value); + } + } + break; + + case STF_ISP_REG_METHOD_TABLE_3: + for (r = 0; r < reg_param->reg_info.length; r++) { + if (reg_buf->reg_tbl3[r].mask) + reg_set_bit(ispbase, + reg_buf->reg_tbl3[r].offset, + reg_buf->reg_tbl3[r].mask, + reg_buf->reg_tbl3[r].value); + else + reg_write(ispbase, reg_buf->reg_tbl3[r].offset, + reg_buf->reg_tbl3[r].value); + + if (reg_buf->reg_tbl3[r].delay_ms) + usleep_range(1000 * reg_buf->reg_tbl3[r].delay_ms, + 1000 * reg_buf->reg_tbl3[r].delay_ms + 100); + } + break; + + case STF_ISP_REG_METHOD_SMPL_PACK: + size = reg_param->reg_info.length; + rdma_info = ®_buf->rdma_cmd[0]; + while (size) { + if (rdma_info->tag == RDMA_END) + break; + + if (rdma_info->tag == RDMA_WR_ONE) { + reg_write(ispbase, + rdma_info->offset, rdma_info->param); + rdma_info++; + size--; + } else if (rdma_info->tag == RDMA_WR_SRL) { + offset = rdma_info->offset; + len = rdma_info->param; + rdma_info++; + size--; + while (size && (len > 0)) { + reg_write(ispbase, + offset, rdma_info->param); + offset += 4; + len--; + if (len > 0) { + reg_write(ispbase, + offset, + rdma_info->value); + len--; + } + offset += 4; + rdma_info++; + size--; + } + } else { + rdma_info++; + size--; + } + } + break; + + case STF_ISP_REG_METHOD_SOFT_RDMA: + /* + * Simulation the hardware RDMA behavior to debug and verify + * the RDMA chain. + */ + ret = stf_isp_soft_rdma(isp_dev, reg_param->reg_info.offset); + break; + + default: + break; + } + + return ret; +} + +static int stf_isp_shadow_trigger(struct stf_isp_dev *isp_dev) +{ + void __iomem *ispbase = stf_isp_get_ispbase(isp_dev); + + /* shadow update */ + reg_set_bit(ispbase, + ISP_REG_CSIINTS_ADDR, (BIT(17) | BIT(16)), 0x30000); + reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, (BIT(1) | BIT(0)), 0x3); + + return 0; +} + +const struct isp_hw_ops isp_ops = { + .isp_clk_enable = stf_isp_clk_enable, + .isp_clk_disable = stf_isp_clk_disable, + .isp_reset = stf_isp_reset, + .isp_config_set = stf_isp_config_set, + .isp_set_format = stf_isp_set_format, + .isp_stream_set = stf_isp_stream_set, + .isp_reg_read = stf_isp_reg_read, + .isp_reg_write = stf_isp_reg_write, + .isp_shadow_trigger = stf_isp_shadow_trigger, +}; diff --git a/drivers/media/platform/starfive/stf_video.c b/drivers/media/platform/starfive/stf_video.c new file mode 100644 index 0000000000000..23d393e6fd78b --- /dev/null +++ b/drivers/media/platform/starfive/stf_video.c @@ -0,0 +1,1286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_video.c + * + * StarFive Camera Subsystem - V4L2 device node + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "stf_camss.h" +#include "stf_video.h" + +static const struct stfcamss_format_info formats_pix_wr[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, + { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10, 1, + { { 1, 1 } }, { { 1, 1 } }, { 10 } }, +}; + +static const struct stfcamss_format_info formats_raw_isp[] = { + { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, + { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12, 1, + { { 1, 1 } }, { { 1, 1 } }, { 12 } }, +}; + +static const struct stfcamss_format_info formats_pix_isp[] = { + { MEDIA_BUS_FMT_Y12_1X12, V4L2_PIX_FMT_NV12, 1, + { { 1, 1 } }, { { 2, 3 } }, { 8 } }, +}; + +/* ----------------------------------------------------------------------------- + * Helper functions + */ + +static int video_find_format(u32 code, u32 pixelformat, + const struct stfcamss_format_info *formats, + unsigned int nformats) +{ + int i; + + for (i = 0; i < nformats; i++) { + if (formats[i].code == code && + formats[i].pixelformat == pixelformat) + return i; + } + + for (i = 0; i < nformats; i++) + if (formats[i].code == code) + return i; + + for (i = 0; i < nformats; i++) + if (formats[i].pixelformat == pixelformat) + return i; + + return -EINVAL; +} + +static int __video_try_fmt(struct stfcamss_video *video, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix; + const struct stfcamss_format_info *fi; + u32 width, height; + u32 bpl; + int i; + + st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type); + pix = &f->fmt.pix; + + for (i = 0; i < video->nformats; i++) + if (pix->pixelformat == video->formats[i].pixelformat) + break; + + if (i == video->nformats) + i = 0; /* default format */ + + fi = &video->formats[i]; + width = pix->width; + height = pix->height; + + memset(pix, 0, sizeof(*pix)); + + pix->pixelformat = fi->pixelformat; + pix->width = clamp_t(u32, width, STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + pix->height = clamp_t(u32, height, STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + bpl = pix->width / fi->hsub[0].numerator * + fi->hsub[0].denominator * fi->bpp[0] / 8; + bpl = ALIGN(bpl, video->bpl_alignment); + pix->bytesperline = bpl; + pix->sizeimage = pix->height / fi->vsub[0].numerator * + fi->vsub[0].denominator * bpl; + + pix->field = V4L2_FIELD_NONE; + pix->colorspace = V4L2_COLORSPACE_SRGB; + pix->flags = 0; + pix->ycbcr_enc = + V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); + pix->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + pix->colorspace, + pix->ycbcr_enc); + pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); + + st_info(ST_VIDEO, "w, h = %d, %d, bpp = %d\n", + pix->width, pix->height, fi->bpp[0]); + st_info(ST_VIDEO, "i = %d, s = 0x%x\n", i, pix->sizeimage); + + return 0; +} + +static int stf_video_init_format(struct stfcamss_video *video) +{ + int ret; + struct v4l2_format format = { + .type = video->type, + .fmt.pix = { + .width = 1920, + .height = 1080, + .pixelformat = V4L2_PIX_FMT_RGB565, + }, + }; + + ret = __video_try_fmt(video, &format); + + if (ret < 0) + return ret; + + video->active_fmt = format; + + return 0; +} + +/* ----------------------------------------------------------------------------- + * Video queue operations + */ + +static int video_queue_setup(struct vb2_queue *q, + unsigned int *num_buffers, + unsigned int *num_planes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + const struct v4l2_pix_format *format = &video->active_fmt.fmt.pix; + + st_debug(ST_VIDEO, "%s, planes = %d\n", __func__, *num_planes); + + if (*num_planes) { + if (*num_planes != 1) + return -EINVAL; + + if (sizes[0] < format->sizeimage) + return -EINVAL; + } + + *num_planes = 1; + sizes[0] = format->sizeimage; + if (!sizes[0]) + st_err(ST_VIDEO, "%s: error size is zero!!!\n", __func__); + + st_info(ST_VIDEO, "%s, planes = %d, size = %d\n", + __func__, *num_planes, sizes[0]); + + return 0; +} + +static int video_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct stfcamss_buffer *buffer = + container_of(vbuf, struct stfcamss_buffer, vb); + const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; + dma_addr_t *paddr; + + buffer->sizeimage = 0; + + paddr = vb2_plane_cookie(vb, 0); + buffer->sizeimage = vb2_plane_size(vb, 0); + buffer->addr[0] = *paddr; + if (fmt->pixelformat == V4L2_PIX_FMT_NV12 || + fmt->pixelformat == V4L2_PIX_FMT_NV21 || + fmt->pixelformat == V4L2_PIX_FMT_NV16 || + fmt->pixelformat == V4L2_PIX_FMT_NV61) + buffer->addr[1] = + buffer->addr[0] + fmt->bytesperline * fmt->height; + + return 0; +} + +static int video_buf_prepare(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + const struct v4l2_pix_format *fmt = &video->active_fmt.fmt.pix; + + if (fmt->sizeimage > vb2_plane_size(vb, 0)) { + st_err(ST_VIDEO, "sizeimage = %d, plane size = %d\n", + fmt->sizeimage, (unsigned int)vb2_plane_size(vb, 0)); + return -EINVAL; + } + vb2_set_plane_payload(vb, 0, fmt->sizeimage); + + vbuf->field = V4L2_FIELD_NONE; + + return 0; +} + +static void video_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + struct stfcamss_video *video = vb2_get_drv_priv(vb->vb2_queue); + struct stfcamss_buffer *buffer = + container_of(vbuf, struct stfcamss_buffer, vb); + + video->ops->queue_buffer(video, buffer); +} + +/* + * video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format + * @mbus: v4l2_mbus_framefmt format (input) + * @pix: v4l2_pix_format_mplane format (output) + * @f: a pointer to formats array element to be used for the conversion + * @alignment: bytesperline alignment value + * + * Fill the output pix structure with information from the input mbus format. + * + * Return 0 on success or a negative error code otherwise + */ +static int video_mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix, + const struct stfcamss_format_info *f, + unsigned int alignment) +{ + u32 bytesperline; + + memset(pix, 0, sizeof(*pix)); + v4l2_fill_pix_format(pix, mbus); + pix->pixelformat = f->pixelformat; + bytesperline = pix->width / f->hsub[0].numerator * + f->hsub[0].denominator * f->bpp[0] / 8; + bytesperline = ALIGN(bytesperline, alignment); + pix->bytesperline = bytesperline; + pix->sizeimage = pix->height / f->vsub[0].numerator * + f->vsub[0].denominator * bytesperline; + return 0; +} + +static struct v4l2_subdev *video_remote_subdev(struct stfcamss_video *video, + u32 *pad) +{ + struct media_pad *remote; + + remote = media_pad_remote_pad_first(&video->pad); + + if (!remote || !is_media_entity_v4l2_subdev(remote->entity)) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +static int video_get_subdev_format(struct stfcamss_video *video, + struct v4l2_format *format) +{ + struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + u32 pixelformat; + u32 pad; + int ret; + + subdev = video_remote_subdev(video, &pad); + if (!subdev) + return -EPIPE; + + fmt.pad = pad; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret) + return ret; + + pixelformat = pix->pixelformat; + ret = video_find_format(fmt.format.code, pixelformat, + video->formats, video->nformats); + if (ret < 0) + return ret; + + format->type = video->type; + + return video_mbus_to_pix(&fmt.format, &format->fmt.pix, + &video->formats[ret], video->bpl_alignment); +} + +static int video_check_format(struct stfcamss_video *video) +{ + struct v4l2_pix_format *pix = &video->active_fmt.fmt.pix; + struct v4l2_format format; + struct v4l2_pix_format *sd_pix = &format.fmt.pix; + int ret; + + sd_pix->pixelformat = pix->pixelformat; + ret = video_get_subdev_format(video, &format); + if (ret < 0) + return ret; + + if (pix->pixelformat != sd_pix->pixelformat || + pix->height > sd_pix->height || + pix->width > sd_pix->width || + pix->field != format.fmt.pix.field) { + st_err(ST_VIDEO, + "%s, not match:\n" + "0x%x 0x%x\n0x%x 0x%x\n0x%x 0x%x\n", + __func__, + pix->pixelformat, sd_pix->pixelformat, + pix->height, sd_pix->height, + pix->field, format.fmt.pix.field); + return -EPIPE; + } + + return 0; +} + +static int video_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + int ret; + + ret = video_device_pipeline_start(vdev, &video->stfcamss->pipe); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to media_pipeline_start: %d\n", ret); + return ret; + } + + ret = video_check_format(video); + if (ret < 0) + goto error; + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto error; + } + return 0; + +error: + video_device_pipeline_stop(vdev); + video->ops->flush_buffers(video, VB2_BUF_STATE_QUEUED); + return ret; +} + +static void video_stop_streaming(struct vb2_queue *q) +{ + struct stfcamss_video *video = vb2_get_drv_priv(q); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct media_pad *pad; + struct v4l2_subdev *subdev; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + v4l2_subdev_call(subdev, video, s_stream, 0); + } + + video_device_pipeline_stop(vdev); + video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR); +} + +static const struct vb2_ops stf_video_vb2_q_ops = { + .queue_setup = video_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .buf_init = video_buf_init, + .buf_prepare = video_buf_prepare, + .buf_queue = video_buf_queue, + .start_streaming = video_start_streaming, + .stop_streaming = video_stop_streaming, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 ioctls + */ + +static int video_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct stfcamss_video *video = video_drvdata(file); + + strscpy(cap->driver, "stf camss", sizeof(cap->driver)); + strscpy(cap->card, "Starfive Camera Subsystem", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + dev_name(video->stfcamss->dev)); + return 0; +} + +static int video_get_pfmt_by_index(struct stfcamss_video *video, int ndx) +{ + int i, j, k; + + /* find index "i" of "k"th unique pixelformat in formats array */ + k = -1; + for (i = 0; i < video->nformats; i++) { + for (j = 0; j < i; j++) { + if (video->formats[i].pixelformat == + video->formats[j].pixelformat) + break; + } + + if (j == i) + k++; + + if (k == ndx) + return i; + } + + return -EINVAL; +} + +static int video_get_pfmt_by_mcode(struct stfcamss_video *video, u32 mcode) +{ + int i; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].code == mcode) + return i; + } + + return -EINVAL; +} + +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct stfcamss_video *video = video_drvdata(file); + int i; + + st_debug(ST_VIDEO, "%s:\n0x%x 0x%x\n 0x%x, 0x%x\n0x%x\n", + __func__, + f->type, video->type, + f->index, video->nformats, + f->mbus_code); + + if (f->type != video->type) + return -EINVAL; + if (f->index >= video->nformats) + return -EINVAL; + + if (f->mbus_code) { + /* Each entry in formats[] table has unique mbus_code */ + if (f->index > 0) + return -EINVAL; + + i = video_get_pfmt_by_mcode(video, f->mbus_code); + } else { + i = video_get_pfmt_by_index(video, f->index); + } + + if (i < 0) + return -EINVAL; + + f->pixelformat = video->formats[i].pixelformat; + + return 0; +} + +static int video_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct v4l2_subdev_frame_size_enum fse = {0}; + struct v4l2_subdev_mbus_code_enum code = {0}; + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity = &vdev->entity; + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct media_pad *pad; + bool support_selection = false; + int i; + int ret; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].pixelformat == fsize->pixel_format) + break; + } + + if (i == video->nformats) + return -EINVAL; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + if (v4l2_subdev_has_op(subdev, pad, set_selection)) { + support_selection = true; + break; + } + } + + if (support_selection) { + if (fsize->index) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH; + fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH; + fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT; + fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + } else { + entity = &vdev->entity; + sensor = stfcamss_find_sensor(entity); + if (!sensor) + return -ENOTTY; + + subdev = media_entity_to_v4l2_subdev(sensor); + code.index = 0; + code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, + enum_mbus_code, NULL, &code); + if (ret < 0) + return -EINVAL; + fse.index = fsize->index; + fse.code = code.code; + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(subdev, pad, + enum_frame_size, NULL, &fse); + if (ret < 0) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.min_width; + fsize->discrete.height = fse.min_height; + } + + return 0; +} + +static int video_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) +{ + int ret = 0; + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity = &vdev->entity; + struct media_entity *sensor; + struct v4l2_subdev *subdev; + struct v4l2_subdev_mbus_code_enum code = {0}; + struct v4l2_subdev_frame_interval_enum fie = {0}; + + sensor = stfcamss_find_sensor(entity); + if (!sensor) + return -ENOTTY; + fie.index = fival->index; + fie.width = fival->width; + fie.height = fival->height; + fie.which = V4L2_SUBDEV_FORMAT_ACTIVE; + subdev = media_entity_to_v4l2_subdev(sensor); + + code.index = 0; + code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = video_find_format(0, fival->pixel_format, + video->formats, video->nformats); + if (ret < 0) + return -EINVAL; + + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code); + if (ret < 0) + return -EINVAL; + + fie.code = code.code; + ret = v4l2_subdev_call(subdev, pad, enum_frame_interval, NULL, &fie); + if (ret < 0) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + + st_debug(ST_VIDEO, "%s, fmt.type = 0x%x\n", __func__, f->type); + st_debug(ST_VIDEO, "%s, active_fmt.type = 0x%x,0x%x\n", + __func__, video->active_fmt.type, + video->active_fmt.fmt.pix.pixelformat); + *f = video->active_fmt; + return 0; +} + +static int video_entity_s_fmt(struct stfcamss_video *video, + struct media_entity *entity, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt, + u32 dst_code) +{ + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_mbus_framefmt *mf = &fmt->format; + u32 width, height, code; + int ret, index = 0; + + code = mf->code; + width = mf->width; + height = mf->height; + subdev = media_entity_to_v4l2_subdev(entity); + while (1) { + if (index >= entity->num_pads) + break; + pad = &entity->pads[index]; + pad = media_pad_remote_pad_first(pad); + if (pad && is_media_entity_v4l2_subdev(pad->entity)) { + fmt->pad = index; + if (index) + mf->code = dst_code; + ret = v4l2_subdev_call(subdev, pad, + set_fmt, state, fmt); + st_warn(ST_VIDEO, + "\"%s\":%d pad fmt set to 0x%x %ux%u\n", + subdev->name, fmt->pad, mf->code, + mf->width, mf->height); + if (mf->code != code || + mf->width != width || + mf->height != height) { + st_warn(ST_VIDEO, + "\"%s\":%d,changed fmt to 0x%x %ux%u\n", + subdev->name, fmt->pad, mf->code, + mf->width, mf->height); + } + if (index) + ret = video_entity_s_fmt(video, pad->entity, + state, fmt, dst_code); + } + + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + index++; + } + return ret; +} + +static int video_getcrop_pad_id(int video_id) +{ + return stf_vin_map_isp_pad(video_id, STF_ISP_PAD_SRC); +} + +static int video_pipeline_s_fmt(struct stfcamss_video *video, + struct v4l2_subdev_state *state, + struct v4l2_format *f) +{ + struct video_device *vdev = &video->vdev; + struct media_entity *entity = &vdev->entity; + struct v4l2_subdev *subdev; + int ret, index; + struct v4l2_subdev_format fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .reserved = {video_getcrop_pad_id(video->id)} + }; + struct v4l2_mbus_framefmt *mf = &fmt.format; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct media_entity *sensor; + u32 width, height, code; + struct media_pad *pad; + + /* pix to mbus format */ + index = video_find_format(mf->code, + pix->pixelformat, + video->formats, video->nformats); + if (index < 0) + return index; + v4l2_fill_mbus_format(mf, pix, video->formats[index].code); + + code = mf->code; + width = mf->width; + height = mf->height; + sensor = stfcamss_find_sensor(entity); + if (sensor) { + subdev = media_entity_to_v4l2_subdev(sensor); + ret = v4l2_subdev_call(subdev, pad, set_fmt, state, &fmt); + st_warn(ST_VIDEO, + "\"%s\":%d pad fmt set to 0x%x %ux%u\n", + subdev->name, fmt.pad, mf->code, + mf->width, mf->height); + if (mf->code != code || + mf->width != width || + mf->height != height) { + st_warn(ST_VIDEO, + "\"%s\":%d, changed fmt to 0x%x %ux%u\n", + subdev->name, fmt.pad, mf->code, + mf->width, mf->height); + } + } else { + st_err(ST_VIDEO, "Can't find sensor\n"); + return -ENOTTY; + } + /* + * Starting from sensor subdevice, walk within + * pipeline and set format on each subdevice + */ + sensor = stfcamss_find_sensor(entity); + pad = media_pad_remote_pad_first(&sensor->pads[0]); + ret = video_entity_s_fmt(video, pad->entity, state, &fmt, code); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return ret; + + index = video_find_format(mf->code, + video->formats[index].pixelformat, + video->formats, video->nformats); + st_debug(ST_VIDEO, "%s, code=%x, index=%d\n", + __func__, mf->code, index); + + if (index < 0) + return index; + + video_mbus_to_pix(mf, pix, &video->formats[index], + video->bpl_alignment); + + ret = __video_try_fmt(video, f); + if (ret < 0) + return ret; + + return 0; +} + +static int video_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + int ret; + + st_debug(ST_VIDEO, "%s, fmt.type = 0x%x, v4l2fmt=%x\n", + __func__, f->type, f->fmt.pix.pixelformat); + + if (vb2_is_busy(&video->vb2_q)) + return -EBUSY; + + ret = __video_try_fmt(video, f); + if (ret < 0) + return ret; + + ret = video_pipeline_s_fmt(video, NULL, f); + + st_debug(ST_VIDEO, "%s, pixelformat=0x%x, ret=%d\n", + __func__, f->fmt.pix.pixelformat, ret); + if (ret < 0) + return ret; + + video->active_fmt = *f; + + return 0; +} + +static int video_try_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct stfcamss_video *video = video_drvdata(file); + + return __video_try_fmt(video, f); +} + +static int video_enum_input(struct file *file, void *fh, + struct v4l2_input *input) +{ + if (input->index > 0) + return -EINVAL; + + strscpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; +} + +static int video_g_input(struct file *file, void *fh, unsigned int *input) +{ + *input = 0; + + return 0; +} + +static int video_s_input(struct file *file, void *fh, unsigned int input) +{ + return input == 0 ? 0 : -EINVAL; +} + +static int video_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret, is_support = 0; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_g_parm_cap(vdev, subdev, p); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + if (!ret) + is_support = 1; + } + + return is_support ? 0 : ret; +} + +static int video_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_streamparm tmp_p; + int ret, is_support = 0; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + tmp_p = *p; + ret = v4l2_s_parm_cap(vdev, subdev, &tmp_p); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + if (!ret) { + is_support = 1; + *p = tmp_p; + } + } + + return is_support ? 0 : ret; +} + +static int video_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = video_getcrop_pad_id(video->id), + .target = s->target, + .r = s->r, + .flags = s->flags, + }; + int ret; + + st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n", + __func__, sel.target, s->target); + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sel); + if (!ret) { + s->r = sel.r; + s->flags = sel.flags; + break; + } + if (ret != -ENOIOCTLCMD) + break; + } + + return ret; +} + +static int video_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct stfcamss_video *video = video_drvdata(file); + struct video_device *vdev = &video->vdev; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_pad *pad; + struct v4l2_subdev_selection sel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = video_getcrop_pad_id(video->id), + .target = s->target, + .r = s->r, + .flags = s->flags, + }; + struct v4l2_pix_format *format = &video->active_fmt.fmt.pix; + int ret; + + st_debug(ST_VIDEO, "%s, target = 0x%x, 0x%x\n", + __func__, sel.target, s->target); + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + entity = &vdev->entity; + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + break; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sel); + if (!ret) { + s->r = sel.r; + s->flags = sel.flags; + format->width = s->r.width; + format->height = s->r.height; + ret = __video_try_fmt(video, &video->active_fmt); + if (ret < 0) + return ret; + break; + } + if (ret != -ENOIOCTLCMD) + break; + } + + st_debug(ST_VIDEO, "ret = 0x%x, -EINVAL = 0x%x\n", ret, -EINVAL); + + return ret; +} + +static int video_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct v4l2_subdev *subdev = NULL; + struct media_entity *entity = &fh->vdev->entity; + struct media_entity *sensor; + + sensor = stfcamss_find_sensor(entity); + if (sensor) + subdev = media_entity_to_v4l2_subdev(sensor); + + if (!subdev) + return -ENOTTY; + + fh->ctrl_handler = subdev->ctrl_handler; + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static int video_unsubscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + struct v4l2_subdev *subdev = NULL; + struct media_entity *entity = &fh->vdev->entity; + struct media_entity *sensor; + + sensor = stfcamss_find_sensor(entity); + if (sensor) + subdev = media_entity_to_v4l2_subdev(sensor); + + if (!subdev) + return -ENOTTY; + + fh->ctrl_handler = subdev->ctrl_handler; + return v4l2_event_unsubscribe(fh, sub); +} + +static const struct v4l2_ioctl_ops stf_vid_vin_ioctl_ops = { + .vidioc_querycap = video_querycap, + .vidioc_enum_fmt_vid_cap = video_enum_fmt, + .vidioc_enum_fmt_vid_out = video_enum_fmt, + .vidioc_enum_framesizes = video_enum_framesizes, + .vidioc_enum_frameintervals = video_enum_frameintervals, + .vidioc_g_fmt_vid_cap = video_g_fmt, + .vidioc_s_fmt_vid_cap = video_s_fmt, + .vidioc_try_fmt_vid_cap = video_try_fmt, + .vidioc_g_fmt_vid_out = video_g_fmt, + .vidioc_s_fmt_vid_out = video_s_fmt, + .vidioc_try_fmt_vid_out = video_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = video_enum_input, + .vidioc_g_input = video_g_input, + .vidioc_s_input = video_s_input, + .vidioc_g_parm = video_g_parm, + .vidioc_s_parm = video_s_parm, + .vidioc_subscribe_event = video_subscribe_event, + .vidioc_unsubscribe_event = video_unsubscribe_event, +}; + +static const struct v4l2_ioctl_ops stf_vid_isp_ioctl_ops = { + .vidioc_querycap = video_querycap, + .vidioc_enum_fmt_vid_cap = video_enum_fmt, + .vidioc_enum_fmt_vid_out = video_enum_fmt, + .vidioc_enum_framesizes = video_enum_framesizes, + .vidioc_enum_frameintervals = video_enum_frameintervals, + .vidioc_g_fmt_vid_cap = video_g_fmt, + .vidioc_s_fmt_vid_cap = video_s_fmt, + .vidioc_try_fmt_vid_cap = video_try_fmt, + .vidioc_g_fmt_vid_out = video_g_fmt, + .vidioc_s_fmt_vid_out = video_s_fmt, + .vidioc_try_fmt_vid_out = video_try_fmt, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_enum_input = video_enum_input, + .vidioc_g_input = video_g_input, + .vidioc_s_input = video_s_input, + .vidioc_g_parm = video_g_parm, + .vidioc_s_parm = video_s_parm, + .vidioc_s_selection = video_s_selection, + .vidioc_g_selection = video_g_selection, + .vidioc_subscribe_event = video_subscribe_event, + .vidioc_unsubscribe_event = video_unsubscribe_event, +}; + +/* ----------------------------------------------------------------------------- + * V4L2 file operations + */ + +static int video_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct stfcamss_video *video = video_drvdata(file); + struct v4l2_fh *vfh; + int ret; + + mutex_lock(&video->lock); + + vfh = kzalloc(sizeof(*vfh), GFP_KERNEL); + if (!vfh) { + ret = -ENOMEM; + goto error_alloc; + } + + v4l2_fh_init(vfh, vdev); + v4l2_fh_add(vfh); + + file->private_data = vfh; + + ret = v4l2_pipeline_pm_get(&vdev->entity); + if (ret < 0) { + st_err(ST_VIDEO, + "Failed to power up pipeline: %d\n", ret); + goto error_pm_use; + } + mutex_unlock(&video->lock); + + return 0; + +error_pm_use: + v4l2_fh_release(file); +error_alloc: + mutex_unlock(&video->lock); + return ret; +} + +static int video_release(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + + vb2_fop_release(file); + v4l2_pipeline_pm_put(&vdev->entity); + file->private_data = NULL; + + return 0; +} + +static const struct v4l2_file_operations stf_vid_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = video_ioctl2, + .open = video_open, + .release = video_release, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, + .read = vb2_fop_read, +}; + +/* ----------------------------------------------------------------------------- + * STFCAMSS video core + */ + +static void stf_video_release(struct video_device *vdev) +{ + struct stfcamss_video *video = video_get_drvdata(vdev); + + media_entity_cleanup(&vdev->entity); + + mutex_destroy(&video->q_lock); + mutex_destroy(&video->lock); +} + +int stf_video_register(struct stfcamss_video *video, + struct v4l2_device *v4l2_dev, const char *name) +{ + struct video_device *vdev; + struct vb2_queue *q; + struct media_pad *pad = &video->pad; + int ret; + enum isp_pad_id isp_pad; + + vdev = &video->vdev; + + mutex_init(&video->q_lock); + + q = &video->vb2_q; + q->drv_priv = video; + q->mem_ops = &vb2_dma_contig_memops; + q->ops = &stf_video_vb2_q_ops; + q->type = video->type; + q->io_modes = VB2_DMABUF | VB2_MMAP | VB2_READ; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->buf_struct_size = sizeof(struct stfcamss_buffer); + q->dev = video->stfcamss->dev; + q->lock = &video->q_lock; + q->min_buffers_needed = STFCAMSS_MIN_BUFFERS; + ret = vb2_queue_init(q); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to init vb2 queue: %d\n", ret); + goto err_vb2_init; + } + + pad->flags = MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(&vdev->entity, 1, pad); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to init video entity: %d\n", ret); + goto err_vb2_init; + } + + mutex_init(&video->lock); + + isp_pad = stf_vin_map_isp_pad(video->id, STF_ISP_PAD_SRC); + if (video->id == VIN_LINE_WR) { + video->formats = formats_pix_wr; + video->nformats = ARRAY_SIZE(formats_pix_wr); + video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8; + vdev->ioctl_ops = &stf_vid_vin_ioctl_ops; + } else if (isp_pad == STF_ISP_PAD_SRC || + isp_pad == STF_ISP_PAD_SRC_SS0 || + isp_pad == STF_ISP_PAD_SRC_SS1) { + video->formats = formats_pix_isp; + video->nformats = ARRAY_SIZE(formats_pix_isp); + video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_8; + vdev->ioctl_ops = &stf_vid_isp_ioctl_ops; + } else { + video->formats = formats_raw_isp; + video->nformats = ARRAY_SIZE(formats_raw_isp); + video->bpl_alignment = STFCAMSS_FRAME_WIDTH_ALIGN_128; + vdev->ioctl_ops = &stf_vid_isp_ioctl_ops; + } + + ret = stf_video_init_format(video); + if (ret < 0) { + st_err(ST_VIDEO, "Failed to init format: %d\n", ret); + goto err_vid_init_format; + } + + vdev->fops = &stf_vid_fops; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE; + vdev->vfl_dir = VFL_DIR_RX; + vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; + vdev->release = stf_video_release; + vdev->v4l2_dev = v4l2_dev; + vdev->queue = &video->vb2_q; + vdev->lock = &video->lock; + strscpy(vdev->name, name, sizeof(vdev->name)); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, video->id); + if (ret < 0) { + st_err(ST_VIDEO, + "Failed to register video device: %d\n", + ret); + goto err_vid_reg; + } + + video_set_drvdata(vdev, video); + return 0; + +err_vid_reg: +err_vid_init_format: + media_entity_cleanup(&vdev->entity); + mutex_destroy(&video->lock); +err_vb2_init: + mutex_destroy(&video->q_lock); + return ret; +} + +void stf_video_unregister(struct stfcamss_video *video) +{ + vb2_video_unregister_device(&video->vdev); +} diff --git a/drivers/media/platform/starfive/stf_video.h b/drivers/media/platform/starfive/stf_video.h new file mode 100644 index 0000000000000..b41257cde09a5 --- /dev/null +++ b/drivers/media/platform/starfive/stf_video.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_video.h + * + * StarFive Camera Subsystem - V4L2 device node + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_VIDEO_H +#define STF_VIDEO_H + +#include +#include +#include +#include +#include +#include + +#define STFCAMSS_FRAME_MIN_WIDTH 64 +#define STFCAMSS_FRAME_MAX_WIDTH 1920 +#define STFCAMSS_FRAME_MIN_HEIGHT 64 +#define STFCAMSS_FRAME_MAX_HEIGHT 1080 +#define STFCAMSS_FRAME_WIDTH_ALIGN_8 8 +#define STFCAMSS_FRAME_WIDTH_ALIGN_128 128 +#define STFCAMSS_MIN_BUFFERS 2 + +#define STFCAMSS_MAX_ENTITY_NAME_LEN 27 + +struct stfcamss_buffer { + struct vb2_v4l2_buffer vb; + dma_addr_t addr[3]; + struct list_head queue; + int sizeimage; +}; + +struct fract { + u8 numerator; + u8 denominator; +}; + +/* + * struct stfcamss_format_info - ISP media bus format information + * @code: V4L2 media bus format code + * @pixelformat: V4L2 pixel format FCC identifier + * @planes: Number of planes + * @hsub: Horizontal subsampling (for each plane) + * @vsub: Vertical subsampling (for each plane) + * @bpp: Bits per pixel when stored in memory (for each plane) + */ +struct stfcamss_format_info { + u32 code; + u32 pixelformat; + u8 planes; + struct fract hsub[3]; + struct fract vsub[3]; + u8 bpp[3]; +}; + +struct stfcamss_video { + struct stfcamss *stfcamss; + u8 id; + struct vb2_queue vb2_q; + struct video_device vdev; + struct media_pad pad; + struct media_pipeline pipe; + struct v4l2_format active_fmt; + enum v4l2_buf_type type; + const struct stfcamss_video_ops *ops; + struct mutex lock; /* serialize device access */ + struct mutex q_lock; /* protects the queue */ + unsigned int bpl_alignment; + const struct stfcamss_format_info *formats; + unsigned int nformats; +}; + +struct stfcamss_video_ops { + int (*queue_buffer)(struct stfcamss_video *vid, + struct stfcamss_buffer *buf); + int (*flush_buffers)(struct stfcamss_video *vid, + enum vb2_buffer_state state); +}; + +int stf_video_register(struct stfcamss_video *video, + struct v4l2_device *v4l2_dev, const char *name); + +void stf_video_unregister(struct stfcamss_video *video); + +#endif /* STF_VIDEO_H */ diff --git a/drivers/media/platform/starfive/stf_vin.c b/drivers/media/platform/starfive/stf_vin.c new file mode 100644 index 0000000000000..38d0e2d74280b --- /dev/null +++ b/drivers/media/platform/starfive/stf_vin.c @@ -0,0 +1,1314 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_vin.c + * + * StarFive Camera Subsystem - VIN Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include + +#include "stf_camss.h" + +#define vin_line_array(ptr_line) \ + ((const struct vin_line (*)[]) &(ptr_line)[-((ptr_line)->id)]) + +#define line_to_vin_dev(ptr_line) \ + container_of(vin_line_array(ptr_line), struct stf_vin_dev, line) + +#define VIN_FRAME_DROP_MAX_VAL 90 +#define VIN_FRAME_DROP_MIN_VAL 4 +#define VIN_FRAME_PER_SEC_MAX_VAL 90 + +/* ISP ctrl need 1 sec to let frames become stable. */ +#define VIN_FRAME_DROP_SEC_FOR_ISP_CTRL 1 + +static const struct vin_format vin_formats_wr[] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, 10}, + { MEDIA_BUS_FMT_SGRBG10_1X10, 10}, + { MEDIA_BUS_FMT_SGBRG10_1X10, 10}, + { MEDIA_BUS_FMT_SBGGR10_1X10, 10}, +}; + +static const struct vin_format vin_formats_raw[] = { + { MEDIA_BUS_FMT_SBGGR12_1X12, 12}, + { MEDIA_BUS_FMT_SRGGB12_1X12, 12}, + { MEDIA_BUS_FMT_SGRBG12_1X12, 12}, + { MEDIA_BUS_FMT_SGBRG12_1X12, 12}, +}; + +static const struct vin_format vin_formats_uo[] = { + { MEDIA_BUS_FMT_Y12_1X12, 8}, +}; + +static const struct vin_format_table vin_formats_table[] = { + /* VIN_LINE_WR */ + { vin_formats_wr, ARRAY_SIZE(vin_formats_wr) }, + /* VIN_LINE_ISP */ + { vin_formats_uo, ARRAY_SIZE(vin_formats_uo) }, + /* VIN_LINE_ISP_SS0 */ + { vin_formats_uo, ARRAY_SIZE(vin_formats_uo) }, + /* VIN_LINE_ISP_SS1 */ + { vin_formats_uo, ARRAY_SIZE(vin_formats_uo) }, + /* VIN_LINE_ISP_RAW */ + { vin_formats_raw, ARRAY_SIZE(vin_formats_raw) }, +}; + +static void vin_buffer_done(struct vin_line *line); +static void vin_change_buffer(struct vin_line *line); +static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output); +static void vin_output_init_addrs(struct vin_line *line); +static void vin_init_outputs(struct vin_line *line); +static struct v4l2_mbus_framefmt * +__vin_get_format(struct vin_line *line, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which); + +static char *vin_get_line_subdevname(int line_id) +{ + char *name = NULL; + + switch (line_id) { + case VIN_LINE_WR: + name = "wr"; + break; + case VIN_LINE_ISP: + name = "isp0"; + break; + case VIN_LINE_ISP_SS0: + name = "isp0_ss0"; + break; + case VIN_LINE_ISP_SS1: + name = "isp0_ss1"; + break; + case VIN_LINE_ISP_RAW: + name = "isp0_raw"; + break; + default: + name = "unknown"; + break; + } + return name; +} + +static enum isp_line_id vin_map_isp_line(enum vin_line_id line) +{ + enum isp_line_id line_id; + + if (line > VIN_LINE_WR && line < VIN_LINE_MAX) { + line_id = line % STF_ISP_LINE_MAX; + if (line_id == 0) + line_id = STF_ISP_LINE_SRC_RAW; + } else { + line_id = STF_ISP_LINE_INVALID; + } + + return line_id; +} + +enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line, enum isp_pad_id def) +{ + enum isp_pad_id pad_id; + + if (line == VIN_LINE_WR) + pad_id = STF_ISP_PAD_SINK; + else if ((line > VIN_LINE_WR) && (line < VIN_LINE_MAX)) + pad_id = vin_map_isp_line(line); + else + pad_id = def; + + return pad_id; +} + +int stf_vin_subdev_init(struct stfcamss *stfcamss) +{ + struct device *dev = stfcamss->dev; + struct stf_vin_dev *vin_dev = stfcamss->vin_dev; + int i, ret = 0; + + vin_dev->stfcamss = stfcamss; + vin_dev->hw_ops = &vin_ops; + + vin_dev->isr_ops = devm_kzalloc(dev, sizeof(*vin_dev->isr_ops), + GFP_KERNEL); + if (!vin_dev->isr_ops) + return -ENOMEM; + vin_dev->isr_ops->isr_buffer_done = vin_buffer_done; + vin_dev->isr_ops->isr_change_buffer = vin_change_buffer; + + atomic_set(&vin_dev->ref_count, 0); + + ret = devm_request_irq(dev, + stfcamss->irq, + vin_dev->hw_ops->vin_wr_irq_handler, + 0, "vin_axiwr_irq", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request irq\n"); + goto out; + } + + ret = devm_request_irq(dev, + stfcamss->isp_irq, + vin_dev->hw_ops->vin_isp_irq_handler, + 0, "vin_isp_irq", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request isp irq\n"); + goto out; + } + + ret = devm_request_irq(dev, + stfcamss->isp_irq_csi, + vin_dev->hw_ops->vin_isp_irq_csi_handler, + 0, "vin_isp_irq_csi", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request isp raw irq\n"); + goto out; + } + + ret = devm_request_irq(dev, + stfcamss->isp_irq_csiline, + vin_dev->hw_ops->vin_isp_irq_csiline_handler, + 0, "vin_isp_irq_csiline", vin_dev); + if (ret) { + st_err(ST_VIN, "failed to request isp irq csiline\n"); + goto out; + } + + mutex_init(&vin_dev->power_lock); + vin_dev->power_count = 0; + + for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++) { + struct dummy_buffer *dummy_buffer = &vin_dev->dummy_buffer[i]; + + mutex_init(&dummy_buffer->stream_lock); + dummy_buffer->nums = + i == 0 ? VIN_DUMMY_BUFFER_NUMS : ISP_DUMMY_BUFFER_NUMS; + dummy_buffer->stream_count = 0; + dummy_buffer->buffer = + devm_kzalloc(dev, + dummy_buffer->nums * sizeof(struct vin_dummy_buffer), + GFP_KERNEL); + atomic_set(&dummy_buffer->frame_skip, 0); + } + + for (i = VIN_LINE_WR; + i < STF_ISP_LINE_MAX + 1; i++) { + struct vin_line *l = &vin_dev->line[i]; + + l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + l->video_out.stfcamss = stfcamss; + l->id = i; + l->sdev_type = SUBDEV_TYPE_VIN; + l->formats = vin_formats_table[i].fmts; + l->nformats = vin_formats_table[i].nfmts; + spin_lock_init(&l->output_lock); + + mutex_init(&l->stream_lock); + l->stream_count = 0; + mutex_init(&l->power_lock); + l->power_count = 0; + } + + return 0; +out: + return ret; +} + +static enum link vin_get_link(struct media_entity *entity) +{ + struct v4l2_subdev *subdev; + struct media_pad *pad; + bool isp = false; + + while (1) { + pad = &entity->pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + return LINK_ERROR; + + pad = media_pad_remote_pad_first(pad); + if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) + return LINK_ERROR; + + entity = pad->entity; + subdev = media_entity_to_v4l2_subdev(entity); + + if (!strncmp(subdev->name, STF_CSI_NAME, + strlen(STF_CSI_NAME))) { + if (isp) + return LINK_CSI_TO_ISP; + else + return LINK_CSI_TO_WR; + } else if (!strncmp(subdev->name, STF_DVP_NAME, + strlen(STF_DVP_NAME))) { + if (isp) + return LINK_DVP_TO_ISP; + else + return LINK_DVP_TO_WR; + } else if (!strncmp(subdev->name, STF_ISP_NAME, + strlen(STF_ISP_NAME))) { + isp = true; + } else { + return LINK_ERROR; + } + } +} + +static int vin_set_power(struct v4l2_subdev *sd, int on) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + struct stfcamss *stfcamss = vin_dev->stfcamss; + enum link link; + + mutex_lock(&line->power_lock); + if (on) { + if (line->power_count == 0) + vin_init_outputs(line); + line->power_count++; + } else { + if (line->power_count == 0) { + st_err(ST_VIN, "line power off on power_count = 0\n"); + goto exit_line; + } + line->power_count--; + } +exit_line: + mutex_unlock(&line->power_lock); + + mutex_lock(&vin_dev->power_lock); + link = vin_get_link(&sd->entity); + if (link == LINK_ERROR) + goto exit; + + if (on) { + if (vin_dev->power_count == 0) { + pm_runtime_get_sync(stfcamss->dev); + vin_dev->hw_ops->vin_clk_enable(vin_dev, link); + } + vin_dev->power_count++; + } else { + if (vin_dev->power_count == 0) { + st_err(ST_VIN, "vin_dev power off on power_count=0\n"); + goto exit; + } + if (vin_dev->power_count == 1) { + vin_dev->hw_ops->vin_clk_disable(vin_dev, link); + pm_runtime_put_sync(stfcamss->dev); + } + vin_dev->power_count--; + } +exit: + + mutex_unlock(&vin_dev->power_lock); + + return 0; +} + +static unsigned int vin_get_frame_skip(struct vin_line *line) +{ + unsigned int frame_skip = 0; + unsigned int isp_ctrl_skip_frames = 0; + struct media_entity *sensor; + struct v4l2_subdev_frame_interval fi = { + .pad = 0, + }; + + sensor = stfcamss_find_sensor(&line->subdev.entity); + if (sensor) { + int fps = 0; + struct v4l2_subdev *subdev = + media_entity_to_v4l2_subdev(sensor); + + v4l2_subdev_call(subdev, video, g_frame_interval, &fi); + fps = fi.interval.denominator; + + if (fps > 0 && fps <= 90) + isp_ctrl_skip_frames = + fps * VIN_FRAME_DROP_SEC_FOR_ISP_CTRL; + + if (!fps) + st_debug(ST_VIN, + "%s, Failed to get sensor fps !\n", __func__); + + if (isp_ctrl_skip_frames <= VIN_FRAME_DROP_MIN_VAL) + isp_ctrl_skip_frames = VIN_FRAME_DROP_MIN_VAL; + + v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip); + + frame_skip += isp_ctrl_skip_frames; + + if (frame_skip > VIN_FRAME_DROP_MAX_VAL) + frame_skip = VIN_FRAME_DROP_MAX_VAL; + st_debug(ST_VIN, "%s, frame_skip %d\n", __func__, frame_skip); + } + + return frame_skip; +} + +static int vin_enable_output(struct vin_line *line) +{ + struct vin_output *output = &line->output; + unsigned long flags; + + spin_lock_irqsave(&line->output_lock, flags); + + output->state = VIN_OUTPUT_IDLE; + + output->buf[0] = vin_buf_get_pending(output); + + if (!output->buf[0] && output->buf[1]) { + output->buf[0] = output->buf[1]; + output->buf[1] = NULL; + } + + if (output->buf[0]) + output->state = VIN_OUTPUT_SINGLE; + + output->sequence = 0; + + vin_output_init_addrs(line); + spin_unlock_irqrestore(&line->output_lock, flags); + return 0; +} + +static int vin_disable_output(struct vin_line *line) +{ + struct vin_output *output = &line->output; + unsigned long flags; + + spin_lock_irqsave(&line->output_lock, flags); + + output->state = VIN_OUTPUT_OFF; + + spin_unlock_irqrestore(&line->output_lock, flags); + return 0; +} + +static u32 vin_line_to_dummy_module(struct vin_line *line) +{ + u32 dummy_module = 0; + + switch (line->id) { + case VIN_LINE_WR: + dummy_module = STF_DUMMY_VIN; + break; + case VIN_LINE_ISP: + case VIN_LINE_ISP_SS0: + case VIN_LINE_ISP_SS1: + case VIN_LINE_ISP_RAW: + dummy_module = STF_DUMMY_ISP; + break; + default: + dummy_module = STF_DUMMY_VIN; + break; + } + + return dummy_module; +} + +static int vin_alloc_dummy_buffer(struct stf_vin_dev *vin_dev, + struct v4l2_mbus_framefmt *fmt, + int dummy_module) +{ + struct device *dev = vin_dev->stfcamss->dev; + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct vin_dummy_buffer *buffer = NULL; + int ret = 0, i; + u32 aligns; + + for (i = 0; i < dummy_buffer->nums; i++) { + buffer = &vin_dev->dummy_buffer[dummy_module].buffer[i]; + buffer->width = fmt->width; + buffer->height = fmt->height; + buffer->mcode = fmt->code; + if (i == STF_VIN_PAD_SINK) { + aligns = ALIGN(fmt->width * 4, + STFCAMSS_FRAME_WIDTH_ALIGN_8); + buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height); + } else if (i == STF_ISP_PAD_SRC || + i == STF_ISP_PAD_SRC_SS0 || + i == STF_ISP_PAD_SRC_SS1) { + aligns = ALIGN(fmt->width, + STFCAMSS_FRAME_WIDTH_ALIGN_8); + buffer->buffer_size = + PAGE_ALIGN(aligns * fmt->height * 3 / 2); + } else if (i == STF_ISP_PAD_SRC_RAW) { + aligns = ALIGN(fmt->width * ISP_RAW_DATA_BITS / 8, + STFCAMSS_FRAME_WIDTH_ALIGN_128); + buffer->buffer_size = PAGE_ALIGN(aligns * fmt->height); + } else { + continue; + } + + buffer->vaddr = dma_alloc_coherent(dev, + buffer->buffer_size, + &buffer->paddr[0], + GFP_DMA | GFP_KERNEL); + + if (buffer->vaddr) { + if (i == STF_ISP_PAD_SRC || + i == STF_ISP_PAD_SRC_SS0 || + i == STF_ISP_PAD_SRC_SS1) + buffer->paddr[1] = + (dma_addr_t)(buffer->paddr[0] + aligns * fmt->height); + else + st_debug(ST_VIN, "signal plane\n"); + } + } + + return ret; +} + +static void vin_free_dummy_buffer(struct stf_vin_dev *vin_dev, int dummy_module) +{ + struct device *dev = vin_dev->stfcamss->dev; + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct vin_dummy_buffer *buffer = NULL; + int i; + + for (i = 0; i < dummy_buffer->nums; i++) { + buffer = &dummy_buffer->buffer[i]; + if (buffer->vaddr) + dma_free_coherent(dev, buffer->buffer_size, + buffer->vaddr, buffer->paddr[0]); + memset(buffer, 0, sizeof(struct vin_dummy_buffer)); + } +} + +static void vin_set_dummy_buffer(struct vin_line *line, u32 pad) +{ + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + int dummy_module = vin_line_to_dummy_module(line); + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct vin_dummy_buffer *buffer = NULL; + + switch (pad) { + case STF_VIN_PAD_SINK: + if (line->id == VIN_LINE_WR) { + buffer = &dummy_buffer->buffer[STF_VIN_PAD_SINK]; + vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, + buffer->paddr[0]); + vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, + buffer->paddr[0]); + } else { + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC]; + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0]; + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1]; + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW]; + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, + buffer->paddr[0]); + } + break; + case STF_ISP_PAD_SRC: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC]; + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + break; + case STF_ISP_PAD_SRC_SS0: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS0]; + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + break; + case STF_ISP_PAD_SRC_SS1: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_SS1]; + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, + buffer->paddr[0], + buffer->paddr[1]); + break; + case STF_ISP_PAD_SRC_RAW: + buffer = &dummy_buffer->buffer[STF_ISP_PAD_SRC_RAW]; + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, + buffer->paddr[0]); + break; + default: + break; + } +} + +static int vin_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + int dummy_module = vin_line_to_dummy_module(line); + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[dummy_module]; + struct v4l2_mbus_framefmt *fmt; + struct media_entity *sensor; + struct v4l2_subdev *sensor_sd; + struct v4l2_subdev_format sensor_fmt; + enum link link; + int ret; + + sensor = stfcamss_find_sensor(&line->subdev.entity); + if (!sensor) + return -EINVAL; + + sensor_sd = media_entity_to_v4l2_subdev(sensor); + + sensor_fmt.pad = 0; + sensor_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sensor_sd, pad, get_fmt, NULL, &sensor_fmt); + if (ret) { + st_warn(ST_VIN, "Sensor get format failed !!\n"); + return -EINVAL; + } + + fmt = __vin_get_format(line, NULL, + STF_VIN_PAD_SINK, V4L2_SUBDEV_FORMAT_ACTIVE); + mutex_lock(&dummy_buffer->stream_lock); + if (enable) { + if (dummy_buffer->stream_count == 0) { + vin_alloc_dummy_buffer(vin_dev, fmt, dummy_module); + vin_set_dummy_buffer(line, STF_VIN_PAD_SINK); + atomic_set(&dummy_buffer->frame_skip, + vin_get_frame_skip(line)); + } + dummy_buffer->stream_count++; + } else { + if (dummy_buffer->stream_count == 1) { + vin_free_dummy_buffer(vin_dev, dummy_module); + /* set buffer addr to zero */ + vin_set_dummy_buffer(line, STF_VIN_PAD_SINK); + } else { + vin_set_dummy_buffer(line, + stf_vin_map_isp_pad(line->id, STF_ISP_PAD_SINK)); + } + + dummy_buffer->stream_count--; + } + mutex_unlock(&dummy_buffer->stream_lock); + + mutex_lock(&line->stream_lock); + link = vin_get_link(&sd->entity); + if (link == LINK_ERROR) + goto exit; + + if (enable) { + if (line->stream_count == 0) { + vin_dev->hw_ops->vin_stream_set(vin_dev, 1, + sensor_fmt, link); + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 1); + vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 1); + } + } + line->stream_count++; + } else { + if (line->stream_count == 1) { + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_irq_enable(vin_dev, 0); + vin_dev->hw_ops->vin_wr_stream_set(vin_dev, 0); + } + vin_dev->hw_ops->vin_stream_set(vin_dev, 0, + sensor_fmt, link); + } + line->stream_count--; + } +exit: + mutex_unlock(&line->stream_lock); + + if (enable) + vin_enable_output(line); + else + vin_disable_output(line); + + return 0; +} + +static struct v4l2_mbus_framefmt * +__vin_get_format(struct vin_line *line, + struct v4l2_subdev_state *state, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(&line->subdev, state, pad); + return &line->fmt[pad]; +} + +static void vin_try_format(struct vin_line *line, + struct v4l2_subdev_state *state, + unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + unsigned int i; + + switch (pad) { + case STF_VIN_PAD_SINK: + /* Set format on sink pad */ + for (i = 0; i < line->nformats; i++) + if (fmt->code == line->formats[i].code) + break; + + /* If not found, use UYVY as default */ + if (i >= line->nformats) + fmt->code = line->formats[0].code; + + fmt->width = clamp_t(u32, fmt->width, + STFCAMSS_FRAME_MIN_WIDTH, + STFCAMSS_FRAME_MAX_WIDTH); + fmt->height = clamp_t(u32, fmt->height, + STFCAMSS_FRAME_MIN_HEIGHT, + STFCAMSS_FRAME_MAX_HEIGHT); + + fmt->field = V4L2_FIELD_NONE; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + fmt->flags = 0; + break; + + case STF_VIN_PAD_SRC: + /* Set and return a format same as sink pad */ + *fmt = *__vin_get_format(line, state, STF_VIN_PAD_SINK, which); + break; + } + + fmt->colorspace = V4L2_COLORSPACE_SRGB; +} + +static int vin_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + + if (code->index >= line->nformats) + return -EINVAL; + if (code->pad == STF_VIN_PAD_SINK) { + code->code = line->formats[code->index].code; + } else { + struct v4l2_mbus_framefmt *sink_fmt; + + sink_fmt = __vin_get_format(line, state, STF_VIN_PAD_SINK, + code->which); + + code->code = sink_fmt->code; + if (!code->code) + return -EINVAL; + } + code->flags = 0; + + return 0; +} + +static int vin_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + vin_try_format(line, state, fse->pad, &format, fse->which); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + vin_try_format(line, state, fse->pad, &format, fse->which); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +static int vin_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __vin_get_format(line, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +static int vin_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state, + struct v4l2_subdev_format *fmt) +{ + struct vin_line *line = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + st_debug(ST_VIDEO, "%s, pad %d, fmt code %x\n", + __func__, fmt->pad, fmt->format.code); + + format = __vin_get_format(line, state, fmt->pad, fmt->which); + if (!format) + return -EINVAL; + + mutex_lock(&line->stream_lock); + if (line->stream_count) { + fmt->format = *format; + mutex_unlock(&line->stream_lock); + goto out; + } else { + vin_try_format(line, state, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + } + mutex_unlock(&line->stream_lock); + + if (fmt->pad == STF_VIN_PAD_SINK) { + /* Propagate the format from sink to source */ + format = __vin_get_format(line, state, STF_VIN_PAD_SRC, + fmt->which); + + *format = fmt->format; + vin_try_format(line, state, STF_VIN_PAD_SRC, format, + fmt->which); + } + +out: + return 0; +} + +static int vin_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format = { + .pad = STF_VIN_PAD_SINK, + .which = fh ? + V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE, + .format = { + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .width = 1920, + .height = 1080 + } + }; + + return vin_set_format(sd, fh ? fh->state : NULL, &format); +} + +static void vin_output_init_addrs(struct vin_line *line) +{ + struct vin_output *output = &line->output; + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + dma_addr_t ping_addr; + dma_addr_t pong_addr; + dma_addr_t y_addr, uv_addr; + + output->active_buf = 0; + + if (output->buf[0]) { + ping_addr = output->buf[0]->addr[0]; + y_addr = output->buf[0]->addr[0]; + uv_addr = output->buf[0]->addr[1]; + } else { + return; + } + + if (output->buf[1]) + pong_addr = output->buf[1]->addr[0]; + else + pong_addr = ping_addr; + + switch (vin_map_isp_line(line->id)) { + case STF_ISP_LINE_SRC: + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, y_addr, uv_addr); + break; + case STF_ISP_LINE_SRC_SS0: + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, y_addr, uv_addr); + break; + case STF_ISP_LINE_SRC_SS1: + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, y_addr, uv_addr); + break; + case STF_ISP_LINE_SRC_RAW: + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, y_addr); + break; + default: + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, + ping_addr); + vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, + ping_addr); + } + break; + } +} + +static void vin_init_outputs(struct vin_line *line) +{ + struct vin_output *output = &line->output; + + output->state = VIN_OUTPUT_OFF; + output->buf[0] = NULL; + output->buf[1] = NULL; + output->active_buf = 0; + INIT_LIST_HEAD(&output->pending_bufs); + INIT_LIST_HEAD(&output->ready_bufs); +} + +static void vin_buf_add_ready(struct vin_output *output, + struct stfcamss_buffer *buffer) +{ + INIT_LIST_HEAD(&buffer->queue); + list_add_tail(&buffer->queue, &output->ready_bufs); +} + +static struct stfcamss_buffer *vin_buf_get_ready(struct vin_output *output) +{ + struct stfcamss_buffer *buffer = NULL; + + if (!list_empty(&output->ready_bufs)) { + buffer = list_first_entry(&output->ready_bufs, + struct stfcamss_buffer, + queue); + list_del(&buffer->queue); + } + + return buffer; +} + +static void vin_buf_add_pending(struct vin_output *output, + struct stfcamss_buffer *buffer) +{ + INIT_LIST_HEAD(&buffer->queue); + list_add_tail(&buffer->queue, &output->pending_bufs); +} + +static struct stfcamss_buffer *vin_buf_get_pending(struct vin_output *output) +{ + struct stfcamss_buffer *buffer = NULL; + + if (!list_empty(&output->pending_bufs)) { + buffer = list_first_entry(&output->pending_bufs, + struct stfcamss_buffer, + queue); + list_del(&buffer->queue); + } + + return buffer; +} + +static void vin_buf_update_on_last(struct vin_line *line) +{ + struct vin_output *output = &line->output; + + switch (output->state) { + case VIN_OUTPUT_CONTINUOUS: + output->state = VIN_OUTPUT_SINGLE; + output->active_buf = !output->active_buf; + break; + case VIN_OUTPUT_SINGLE: + output->state = VIN_OUTPUT_STOPPING; + break; + default: + st_err_ratelimited(ST_VIN, "Last buff in wrong state! %d\n", + output->state); + break; + } +} + +static void vin_buf_update_on_next(struct vin_line *line) +{ + struct vin_output *output = &line->output; + + switch (output->state) { + case VIN_OUTPUT_CONTINUOUS: + output->active_buf = !output->active_buf; + break; + case VIN_OUTPUT_SINGLE: + default: + break; + } +} + +static void vin_buf_update_on_new(struct vin_line *line, + struct vin_output *output, + struct stfcamss_buffer *new_buf) +{ + switch (output->state) { + case VIN_OUTPUT_SINGLE: + vin_buf_add_pending(output, new_buf); + break; + case VIN_OUTPUT_IDLE: + st_warn(ST_VIN, "Output idle buffer set!\n"); + if (!output->buf[0]) { + output->buf[0] = new_buf; + vin_output_init_addrs(line); + output->state = VIN_OUTPUT_SINGLE; + } else { + vin_buf_add_pending(output, new_buf); + st_warn(ST_VIN, "Output idle with buffer set!\n"); + } + break; + case VIN_OUTPUT_STOPPING: + if (output->last_buffer) { + output->buf[output->active_buf] = output->last_buffer; + output->last_buffer = NULL; + } else { + st_err(ST_VIN, "stop state lost lastbuffer!\n"); + } + output->state = VIN_OUTPUT_SINGLE; + vin_buf_add_pending(output, new_buf); + break; + case VIN_OUTPUT_CONTINUOUS: + default: + vin_buf_add_pending(output, new_buf); + break; + } +} + +static void vin_buf_flush(struct vin_output *output, + enum vb2_buffer_state state) +{ + struct stfcamss_buffer *buf; + struct stfcamss_buffer *t; + + list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->queue); + } + list_for_each_entry_safe(buf, t, &output->ready_bufs, queue) { + vb2_buffer_done(&buf->vb.vb2_buf, state); + list_del(&buf->queue); + } +} + +static void vin_buffer_done(struct vin_line *line) +{ + struct stfcamss_buffer *ready_buf; + struct vin_output *output = &line->output; + unsigned long flags; + u64 ts = ktime_get_ns(); + + if (output->state == VIN_OUTPUT_OFF || + output->state == VIN_OUTPUT_RESERVED) + return; + + spin_lock_irqsave(&line->output_lock, flags); + + while ((ready_buf = vin_buf_get_ready(output))) { + ready_buf->vb.vb2_buf.timestamp = ts; + ready_buf->vb.sequence = output->sequence++; + + vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + } + + spin_unlock_irqrestore(&line->output_lock, flags); +} + +static void vin_change_buffer(struct vin_line *line) +{ + struct stfcamss_buffer *ready_buf; + struct vin_output *output = &line->output; + struct stf_vin_dev *vin_dev = line_to_vin_dev(line); + dma_addr_t *new_addr; + unsigned long flags; + u32 active_index; + + if (output->state == VIN_OUTPUT_OFF || + output->state == VIN_OUTPUT_STOPPING || + output->state == VIN_OUTPUT_RESERVED || + output->state == VIN_OUTPUT_IDLE) + return; + + spin_lock_irqsave(&line->output_lock, flags); + + active_index = output->active_buf; + + ready_buf = output->buf[active_index]; + if (!ready_buf) { + st_err_ratelimited(ST_VIN, "Missing ready buf %d %d!\n", + active_index, output->state); + active_index = !active_index; + ready_buf = output->buf[active_index]; + if (!ready_buf) { + st_err_ratelimited(ST_VIN, "Missing ready buf2 %d %d!\n", + active_index, output->state); + goto out_unlock; + } + } + + /* Get next buffer */ + output->buf[active_index] = vin_buf_get_pending(output); + if (!output->buf[active_index]) { + /* No next buffer - set same address */ + new_addr = ready_buf->addr; + vin_buf_update_on_last(line); + } else { + new_addr = output->buf[active_index]->addr; + vin_buf_update_on_next(line); + } + + if (output->state == VIN_OUTPUT_STOPPING) { + output->last_buffer = ready_buf; + } else { + switch (vin_map_isp_line(line->id)) { + case STF_ISP_LINE_SRC: + vin_dev->hw_ops->vin_isp_set_yuv_addr(vin_dev, + new_addr[0], + new_addr[1]); + break; + case STF_ISP_LINE_SRC_SS0: + vin_dev->hw_ops->vin_isp_set_ss0_addr(vin_dev, + new_addr[0], + new_addr[1]); + break; + case STF_ISP_LINE_SRC_SS1: + vin_dev->hw_ops->vin_isp_set_ss1_addr(vin_dev, + new_addr[0], + new_addr[1]); + break; + case STF_ISP_LINE_SRC_RAW: + vin_dev->hw_ops->vin_isp_set_raw_addr(vin_dev, + new_addr[0]); + break; + default: + if (line->id == VIN_LINE_WR) { + vin_dev->hw_ops->vin_wr_set_ping_addr(vin_dev, + new_addr[0]); + vin_dev->hw_ops->vin_wr_set_pong_addr(vin_dev, + new_addr[0]); + } + break; + } + + vin_buf_add_ready(output, ready_buf); + } + + spin_unlock_irqrestore(&line->output_lock, flags); + return; + +out_unlock: + spin_unlock_irqrestore(&line->output_lock, flags); +} + +static int vin_queue_buffer(struct stfcamss_video *vid, + struct stfcamss_buffer *buf) +{ + struct vin_line *line = container_of(vid, struct vin_line, video_out); + struct vin_output *output; + unsigned long flags; + + output = &line->output; + + spin_lock_irqsave(&line->output_lock, flags); + + vin_buf_update_on_new(line, output, buf); + + spin_unlock_irqrestore(&line->output_lock, flags); + + return 0; +} + +static int vin_flush_buffers(struct stfcamss_video *vid, + enum vb2_buffer_state state) +{ + struct vin_line *line = container_of(vid, struct vin_line, video_out); + struct vin_output *output = &line->output; + unsigned long flags; + + spin_lock_irqsave(&line->output_lock, flags); + + vin_buf_flush(output, state); + if (output->buf[0]) + vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state); + + if (output->buf[1]) + vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state); + + if (output->last_buffer) { + vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state); + output->last_buffer = NULL; + } + output->buf[0] = NULL; + output->buf[1] = NULL; + + spin_unlock_irqrestore(&line->output_lock, flags); + return 0; +} + +static int vin_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + if (flags & MEDIA_LNK_FL_ENABLED) + if (media_pad_remote_pad_first(local)) + return -EBUSY; + return 0; +} + +static const struct v4l2_subdev_core_ops vin_core_ops = { + .s_power = vin_set_power, +}; + +static const struct v4l2_subdev_video_ops vin_video_ops = { + .s_stream = vin_set_stream, +}; + +static const struct v4l2_subdev_pad_ops vin_pad_ops = { + .enum_mbus_code = vin_enum_mbus_code, + .enum_frame_size = vin_enum_frame_size, + .get_fmt = vin_get_format, + .set_fmt = vin_set_format, +}; + +static const struct v4l2_subdev_ops vin_v4l2_ops = { + .core = &vin_core_ops, + .video = &vin_video_ops, + .pad = &vin_pad_ops, +}; + +static const struct v4l2_subdev_internal_ops vin_v4l2_internal_ops = { + .open = vin_init_formats, +}; + +static const struct stfcamss_video_ops stfcamss_vin_video_ops = { + .queue_buffer = vin_queue_buffer, + .flush_buffers = vin_flush_buffers, +}; + +static const struct media_entity_operations vin_media_ops = { + .link_setup = vin_link_setup, + .link_validate = v4l2_subdev_link_validate, +}; + +int stf_vin_register(struct stf_vin_dev *vin_dev, struct v4l2_device *v4l2_dev) +{ + struct v4l2_subdev *sd; + struct stfcamss_video *video_out; + struct media_pad *pads; + int ret; + int i; + + for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) { + char name[32]; + char *sub_name = vin_get_line_subdevname(i); + + sd = &vin_dev->line[i].subdev; + pads = vin_dev->line[i].pads; + video_out = &vin_dev->line[i].video_out; + video_out->id = i; + + v4l2_subdev_init(sd, &vin_v4l2_ops); + sd->internal_ops = &vin_v4l2_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", + STF_VIN_NAME, 0, sub_name); + v4l2_set_subdevdata(sd, &vin_dev->line[i]); + + ret = vin_init_formats(sd, NULL); + if (ret < 0) { + st_err(ST_VIN, "Failed to init format: %d\n", ret); + goto err_init; + } + + pads[STF_VIN_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[STF_VIN_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; + + sd->entity.function = + MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; + sd->entity.ops = &vin_media_ops; + ret = media_entity_pads_init(&sd->entity, + STF_VIN_PADS_NUM, pads); + if (ret < 0) { + st_err(ST_VIN, "Failed to init media entity: %d\n", + ret); + goto err_init; + } + + ret = v4l2_device_register_subdev(v4l2_dev, sd); + if (ret < 0) { + st_err(ST_VIN, "Failed to register subdev: %d\n", ret); + goto err_reg_subdev; + } + + video_out->ops = &stfcamss_vin_video_ops; + video_out->bpl_alignment = 16 * 8; + + snprintf(name, ARRAY_SIZE(name), "%s_%s%d", + sd->name, "video", i); + ret = stf_video_register(video_out, v4l2_dev, name); + if (ret < 0) { + st_err(ST_VIN, "Failed to register video node: %d\n", + ret); + goto err_vid_reg; + } + + ret = media_create_pad_link( + &sd->entity, STF_VIN_PAD_SRC, + &video_out->vdev.entity, 0, + MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); + if (ret < 0) { + st_err(ST_VIN, "Failed to link %s->%s entities: %d\n", + sd->entity.name, video_out->vdev.entity.name, + ret); + goto err_create_link; + } + } + + return 0; + +err_create_link: + stf_video_unregister(video_out); +err_vid_reg: + v4l2_device_unregister_subdev(sd); +err_reg_subdev: + media_entity_cleanup(&sd->entity); +err_init: + for (i--; i >= 0; i--) { + sd = &vin_dev->line[i].subdev; + video_out = &vin_dev->line[i].video_out; + + stf_video_unregister(video_out); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + } + return ret; +} + +int stf_vin_unregister(struct stf_vin_dev *vin_dev) +{ + struct v4l2_subdev *sd; + struct stfcamss_video *video_out; + int i; + + mutex_destroy(&vin_dev->power_lock); + for (i = 0; i < STF_DUMMY_MODULE_NUMS; i++) + mutex_destroy(&vin_dev->dummy_buffer[i].stream_lock); + + for (i = 0; i < STF_ISP_LINE_MAX + 1; i++) { + sd = &vin_dev->line[i].subdev; + video_out = &vin_dev->line[i].video_out; + + stf_video_unregister(video_out); + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + mutex_destroy(&vin_dev->line[i].stream_lock); + mutex_destroy(&vin_dev->line[i].power_lock); + } + return 0; +} diff --git a/drivers/media/platform/starfive/stf_vin.h b/drivers/media/platform/starfive/stf_vin.h new file mode 100644 index 0000000000000..7a0f39e6d2b9b --- /dev/null +++ b/drivers/media/platform/starfive/stf_vin.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * stf_vin.h + * + * StarFive Camera Subsystem - VIN Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_VIN_H +#define STF_VIN_H + +#include +#include +#include + +#include "stf_video.h" + +#define SYSCONSAIF_SYSCFG(x) (x) + +/* syscon offset 0 */ +#define U0_VIN_CNFG_AXI_DVP_EN BIT(2) + +/* syscon offset 20 */ +#define U0_VIN_CNFG_AXIWR0_CHANNEL_SEL GENMASK(3, 0) +#define U0_VIN_CNFG_AXIWR0_EN BIT(4) + +/* syscon offset 32 */ +#define U0_VIN_CNFG_AXIWR0_INTR_CLEAN BIT(0) +#define U0_VIN_CNFG_AXIWR0_INTR_MASK BIT(1) +#define U0_VIN_CNFG_AXIWR0_PIX_CNT_END GENMASK(12, 2) +#define U0_VIN_CNFG_AXIWR0_PIX_CT GENMASK(14, 13) +#define U0_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL GENMASK(16, 15) + +/* syscon offset 36 */ +#define U0_VIN_CNFG_DVP_HS_POS BIT(1) +#define U0_VIN_CNFG_DVP_SWAP_EN BIT(2) +#define U0_VIN_CNFG_DVP_VS_POS BIT(3) +#define U0_VIN_CNFG_GEN_EN_AXIRD BIT(4) +#define U0_VIN_CNFG_ISP_DVP_EN0 BIT(5) +#define U0_VIN_CNFG_MIPI_BYTE_EN_ISP0 GENMASK(7, 6) +#define U0_VIN_CNFG_MIPI_CHANNEL_SEL0 GENMASK(11, 8) +#define U0_VIN_CNFG_P_I_MIPI_HAEDER_EN0 BIT(12) +#define U0_VIN_CNFG_PIX_NUM GENMASK(16, 13) + +#define STF_VIN_PAD_SINK 0 +#define STF_VIN_PAD_SRC 1 +#define STF_VIN_PADS_NUM 2 + +#define ISP_DUMMY_BUFFER_NUMS STF_ISP_PAD_MAX +#define VIN_DUMMY_BUFFER_NUMS 1 + +enum { + STF_DUMMY_VIN, + STF_DUMMY_ISP, + STF_DUMMY_MODULE_NUMS, +}; + +enum link { + LINK_ERROR = -1, + LINK_DVP_TO_WR, + LINK_DVP_TO_ISP, + LINK_CSI_TO_WR, + LINK_CSI_TO_ISP, +}; + +struct vin_format { + u32 code; + u8 bpp; +}; + +struct vin_format_table { + const struct vin_format *fmts; + int nfmts; +}; + +enum vin_output_state { + VIN_OUTPUT_OFF, + VIN_OUTPUT_RESERVED, + VIN_OUTPUT_SINGLE, + VIN_OUTPUT_CONTINUOUS, + VIN_OUTPUT_IDLE, + VIN_OUTPUT_STOPPING +}; + +struct vin_output { + int active_buf; + struct stfcamss_buffer *buf[2]; + struct stfcamss_buffer *last_buffer; + struct list_head pending_bufs; + struct list_head ready_bufs; + enum vin_output_state state; + unsigned int sequence; + unsigned int frame_skip; +}; + +/* + * The vin output lines include all isp controller lines, + * and one vin_wr output line. + */ +enum vin_line_id { + VIN_LINE_NONE = -1, + VIN_LINE_WR = 0, + VIN_LINE_ISP, + VIN_LINE_ISP_SS0, + VIN_LINE_ISP_SS1, + VIN_LINE_ISP_RAW, + VIN_LINE_MAX, +}; + +struct vin_line { + enum subdev_type sdev_type; /* must be frist */ + enum vin_line_id id; + struct v4l2_subdev subdev; + struct media_pad pads[STF_VIN_PADS_NUM]; + struct v4l2_mbus_framefmt fmt[STF_VIN_PADS_NUM]; + struct stfcamss_video video_out; + struct mutex stream_lock; /* serialize stream control */ + int stream_count; + struct mutex power_lock; /* serialize pipeline control in power process*/ + int power_count; + struct vin_output output; /* pipeline and stream states */ + spinlock_t output_lock; + const struct vin_format *formats; + unsigned int nformats; +}; + +struct vin_dummy_buffer { + dma_addr_t paddr[3]; + void *vaddr; + u32 buffer_size; + u32 width; + u32 height; + u32 mcode; +}; + +struct dummy_buffer { + struct vin_dummy_buffer *buffer; + u32 nums; + struct mutex stream_lock; /* protects buffer data */ + int stream_count; + atomic_t frame_skip; +}; + +struct vin_isr_ops { + void (*isr_buffer_done)(struct vin_line *line); + void (*isr_change_buffer)(struct vin_line *line); +}; + +struct stf_vin_dev { + struct stfcamss *stfcamss; + struct vin_line line[VIN_LINE_MAX]; + struct dummy_buffer dummy_buffer[STF_DUMMY_MODULE_NUMS]; + const struct vin_hw_ops *hw_ops; + struct vin_isr_ops *isr_ops; + atomic_t ref_count; + struct mutex power_lock; /* serialize power control*/ + int power_count; +}; + +struct vin_hw_ops { + int (*vin_clk_enable)(struct stf_vin_dev *vin_dev, enum link link); + int (*vin_clk_disable)(struct stf_vin_dev *vin_dev, enum link link); + int (*vin_wr_stream_set)(struct stf_vin_dev *vin_dev, int on); + int (*vin_stream_set)(struct stf_vin_dev *vin_dev, int on, + struct v4l2_subdev_format sensor_fmt, + enum link link); + void (*vin_wr_irq_enable)(struct stf_vin_dev *vin_dev, int enable); + void (*vin_wr_set_ping_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t addr); + void (*vin_wr_set_pong_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t addr); + void (*vin_isp_set_yuv_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr); + void (*vin_isp_set_raw_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t raw_addr); + void (*vin_isp_set_ss0_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr); + void (*vin_isp_set_ss1_addr)(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr); + irqreturn_t (*vin_wr_irq_handler)(int irq, void *priv); + irqreturn_t (*vin_isp_irq_handler)(int irq, void *priv); + irqreturn_t (*vin_isp_irq_csi_handler)(int irq, void *priv); + irqreturn_t (*vin_isp_irq_csiline_handler)(int irq, void *priv); +}; + +extern const struct vin_hw_ops vin_ops; + +int stf_vin_subdev_init(struct stfcamss *stfcamss); +int stf_vin_register(struct stf_vin_dev *vin_dev, struct v4l2_device *v4l2_dev); +int stf_vin_unregister(struct stf_vin_dev *vin_dev); +enum isp_pad_id stf_vin_map_isp_pad(enum vin_line_id line, enum isp_pad_id def); + +#endif /* STF_VIN_H */ diff --git a/drivers/media/platform/starfive/stf_vin_hw_ops.c b/drivers/media/platform/starfive/stf_vin_hw_ops.c new file mode 100644 index 0000000000000..98306c4d4d892 --- /dev/null +++ b/drivers/media/platform/starfive/stf_vin_hw_ops.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * stf_vin_hw_ops.c + * + * Register interface file for StarFive VIN module driver + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ +#include "stf_camss.h" + +static void vin_intr_clear(void __iomem *syscon_base) +{ + reg_set_bit(syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x1); + reg_set_bit(syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x0); +} + +static irqreturn_t stf_vin_wr_irq_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + struct stfcamss *stfcamss = vin_dev->stfcamss; + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[STF_DUMMY_VIN]; + + if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) { + vin_dev->isr_ops->isr_change_buffer(&vin_dev->line[VIN_LINE_WR]); + vin_dev->isr_ops->isr_buffer_done(&vin_dev->line[VIN_LINE_WR]); + } + + vin_intr_clear(stfcamss->syscon_base); + + return IRQ_HANDLED; +} + +static void __iomem *stf_vin_get_ispbase(struct stf_vin_dev *vin_dev) +{ + void __iomem *base = vin_dev->stfcamss->isp_base; + + return base; +} + +static irqreturn_t stf_vin_isp_irq_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + u32 int_status; + + int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0); + + if (int_status & BIT(24)) { + if ((int_status & BIT(11))) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_SS0]); + + if ((int_status & BIT(12))) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_SS1]); + + if ((int_status & BIT(20))) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP]); + + if (int_status & BIT(25)) + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_RAW]); + + /* clear interrupt */ + reg_write(ispbase, + ISP_REG_ISP_CTRL_0, + (int_status & ~EN_INT_ALL) | + EN_INT_ISP_DONE | + EN_INT_CSI_DONE | + EN_INT_SC_DONE); + + } else { + st_debug(ST_VIN, "%s, Unknown interrupt!\n", __func__); + } + + return IRQ_HANDLED; +} + +static irqreturn_t stf_vin_isp_irq_csi_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + u32 int_status; + + int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0); + + if (int_status & BIT(25)) { + vin_dev->isr_ops->isr_buffer_done( + &vin_dev->line[VIN_LINE_ISP_RAW]); + + /* clear interrupt */ + reg_write(ispbase, + ISP_REG_ISP_CTRL_0, + (int_status & ~EN_INT_ALL) | EN_INT_CSI_DONE); + } else { + st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__); + } + + return IRQ_HANDLED; +} + +static irqreturn_t stf_vin_isp_irq_csiline_handler(int irq, void *priv) +{ + struct stf_vin_dev *vin_dev = priv; + struct stf_isp_dev *isp_dev; + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + u32 int_status, value; + + isp_dev = vin_dev->stfcamss->isp_dev; + + int_status = reg_read(ispbase, ISP_REG_ISP_CTRL_0); + if (int_status & BIT(27)) { + struct dummy_buffer *dummy_buffer = + &vin_dev->dummy_buffer[STF_DUMMY_ISP]; + + if (!atomic_read(&isp_dev->shadow_count)) { + if (atomic_dec_if_positive(&dummy_buffer->frame_skip) < 0) { + if ((int_status & BIT(11))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP_SS0]); + if ((int_status & BIT(12))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP_SS1]); + if ((int_status & BIT(20))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP]); + value = reg_read(ispbase, + ISP_REG_CSI_MODULE_CFG); + if ((value & BIT(19))) + vin_dev->isr_ops->isr_change_buffer( + &vin_dev->line[VIN_LINE_ISP_RAW]); + } + + /* shadow update */ + reg_set_bit(ispbase, ISP_REG_CSIINTS_ADDR, + 0x30000, 0x30000); + reg_set_bit(ispbase, ISP_REG_IESHD_ADDR, + BIT(1) | BIT(0), 0x3); + } else { + st_err_ratelimited(ST_VIN, "skip this frame\n"); + } + + /* clear interrupt */ + reg_write(ispbase, ISP_REG_ISP_CTRL_0, + (int_status & ~EN_INT_ALL) | EN_INT_LINE_INT); + } else { + st_debug(ST_VIN, "%s, Unknown interrupt!!!\n", __func__); + } + + return IRQ_HANDLED; +} + +static int stf_vin_clk_enable(struct stf_vin_dev *vin_dev, enum link link) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + clk_set_rate(stfcamss->sys_clk[STF_CLK_APB_FUNC].clk, 49500000); + + switch (link) { + case LINK_DVP_TO_WR: + break; + case LINK_DVP_TO_ISP: + break; + case LINK_CSI_TO_WR: + clk_set_rate(stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk, + 198000000); + reset_control_deassert(stfcamss->sys_rst[STF_RST_AXIWR].rstc); + clk_set_parent(stfcamss->sys_clk[STF_CLK_AXIWR].clk, + stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk); + break; + case LINK_CSI_TO_ISP: + clk_set_rate(stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk, + 198000000); + clk_set_parent(stfcamss->sys_clk[STF_CLK_WRAPPER_CLK_C].clk, + stfcamss->sys_clk[STF_CLK_MIPI_RX0_PXL].clk); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stf_vin_clk_disable(struct stf_vin_dev *vin_dev, enum link link) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + switch (link) { + case LINK_DVP_TO_WR: + break; + case LINK_DVP_TO_ISP: + break; + case LINK_CSI_TO_WR: + reset_control_assert(stfcamss->sys_rst[STF_RST_AXIWR].rstc); + break; + case LINK_CSI_TO_ISP: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int stf_vin_wr_stream_set(struct stf_vin_dev *vin_dev, int on) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + /* make the axiwr alway on */ + if (on) + reg_set(stfcamss->syscon_base, + SYSCONSAIF_SYSCFG(20), U0_VIN_CNFG_AXIWR0_EN); + + return 0; +} + +static int stf_vin_stream_set(struct stf_vin_dev *vin_dev, int on, + struct v4l2_subdev_format sensor_fmt, + enum link link) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + switch (link) { + case LINK_DVP_TO_WR: + break; + case LINK_DVP_TO_ISP: + break; + case LINK_CSI_TO_WR: + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(20), + U0_VIN_CNFG_AXIWR0_CHANNEL_SEL, 0 << 0); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_PIX_CT, 1 << 13); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_PIXEL_HEIGH_BIT_SEL, 0 << 15); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_PIX_CNT_END, + (sensor_fmt.format.width / 4 - 1) << 2); + break; + case LINK_CSI_TO_ISP: + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_MIPI_BYTE_EN_ISP0, 0 << 6); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_MIPI_CHANNEL_SEL0, 0 << 8); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_PIX_NUM, 0 << 13); + + if (sensor_fmt.format.code == MEDIA_BUS_FMT_SRGGB10_1X10 || + sensor_fmt.format.code == MEDIA_BUS_FMT_SGRBG10_1X10 || + sensor_fmt.format.code == MEDIA_BUS_FMT_SGBRG10_1X10 || + sensor_fmt.format.code == MEDIA_BUS_FMT_SBGGR10_1X10) + reg_set_bit(stfcamss->syscon_base, + SYSCONSAIF_SYSCFG(36), + U0_VIN_CNFG_P_I_MIPI_HAEDER_EN0, + 1 << 12); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void stf_vin_wr_irq_enable(struct stf_vin_dev *vin_dev, int enable) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + unsigned int value = 0; + + if (enable) { + value = ~(0x1 << 1); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_MASK, value); + } else { + /* clear vin interrupt */ + value = 0x1 << 1; + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x1); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_CLEAN, 0x0); + reg_set_bit(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(28), + U0_VIN_CNFG_AXIWR0_INTR_MASK, value); + } +} + +static void stf_vin_wr_set_ping_addr(struct stf_vin_dev *vin_dev, + dma_addr_t addr) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + /* set the start address */ + reg_write(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(32), (long)addr); +} + +static void stf_vin_wr_set_pong_addr(struct stf_vin_dev *vin_dev, + dma_addr_t addr) +{ + struct stfcamss *stfcamss = vin_dev->stfcamss; + + /* set the start address */ + reg_write(stfcamss->syscon_base, SYSCONSAIF_SYSCFG(24), (long)addr); +} + +static void stf_vin_isp_set_yuv_addr(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_Y_PLANE_START_ADDR, y_addr); + reg_write(ispbase, ISP_REG_UV_PLANE_START_ADDR, uv_addr); +} + +static void stf_vin_isp_set_raw_addr(struct stf_vin_dev *vin_dev, + dma_addr_t raw_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_DUMP_CFG_0, raw_addr); +} + +static void stf_vin_isp_set_ss0_addr(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_SS0AY, y_addr); + reg_write(ispbase, ISP_REG_SS0AUV, uv_addr); +} + +static void stf_vin_isp_set_ss1_addr(struct stf_vin_dev *vin_dev, + dma_addr_t y_addr, dma_addr_t uv_addr) +{ + void __iomem *ispbase = stf_vin_get_ispbase(vin_dev); + + reg_write(ispbase, ISP_REG_SS1AY, y_addr); + reg_write(ispbase, ISP_REG_SS1AUV, uv_addr); +} + +const struct vin_hw_ops vin_ops = { + .vin_clk_enable = stf_vin_clk_enable, + .vin_clk_disable = stf_vin_clk_disable, + .vin_wr_stream_set = stf_vin_wr_stream_set, + .vin_wr_irq_enable = stf_vin_wr_irq_enable, + .vin_stream_set = stf_vin_stream_set, + .vin_wr_set_ping_addr = stf_vin_wr_set_ping_addr, + .vin_wr_set_pong_addr = stf_vin_wr_set_pong_addr, + .vin_isp_set_yuv_addr = stf_vin_isp_set_yuv_addr, + .vin_isp_set_raw_addr = stf_vin_isp_set_raw_addr, + .vin_isp_set_ss0_addr = stf_vin_isp_set_ss0_addr, + .vin_isp_set_ss1_addr = stf_vin_isp_set_ss1_addr, + .vin_wr_irq_handler = stf_vin_wr_irq_handler, + .vin_isp_irq_handler = stf_vin_isp_irq_handler, + .vin_isp_irq_csi_handler = stf_vin_isp_irq_csi_handler, + .vin_isp_irq_csiline_handler = stf_vin_isp_irq_csiline_handler, +}; diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index f77511fe4e873..5f5a997f21f36 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -165,6 +165,18 @@ config DWMAC_SOCFPGA for the stmmac device driver. This driver is used for arria5 and cyclone5 FPGA SoCs. +config DWMAC_STARFIVE + tristate "StarFive dwmac support" + depends on OF && (ARCH_STARFIVE || COMPILE_TEST) + select MFD_SYSCON + default m if ARCH_STARFIVE + help + Support for ethernet controllers on StarFive RISC-V SoCs + + This selects the StarFive platform specific glue layer support for + the stmmac device driver. This driver is used for StarFive JH7110 + ethernet controller. + config DWMAC_STI tristate "STi GMAC support" default ARCH_STI diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 057e4bab5c08e..8738fdbb4b2dc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_DWMAC_OXNAS) += dwmac-oxnas.o obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o +obj-$(CONFIG_DWMAC_STARFIVE) += dwmac-starfive.o obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o obj-$(CONFIG_DWMAC_STM32) += dwmac-stm32.o obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c new file mode 100644 index 0000000000000..84690c8f0250e --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive DWMAC platform driver + * + * Copyright (C) 2021 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + * + */ + +#include +#include +#include + +#include "stmmac_platform.h" + +#define STARFIVE_DWMAC_PHY_INFT_RGMII 0x1 +#define STARFIVE_DWMAC_PHY_INFT_RMII 0x4 +#define STARFIVE_DWMAC_PHY_INFT_FIELD 0x7U + +struct starfive_dwmac { + struct device *dev; + struct clk *clk_tx; +}; + +static void starfive_dwmac_fix_mac_speed(void *priv, unsigned int speed) +{ + struct starfive_dwmac *dwmac = priv; + unsigned long rate; + int err; + + switch (speed) { + case SPEED_1000: + rate = 125000000; + break; + case SPEED_100: + rate = 25000000; + break; + case SPEED_10: + rate = 2500000; + break; + default: + dev_err(dwmac->dev, "invalid speed %u\n", speed); + break; + } + + err = clk_set_rate(dwmac->clk_tx, rate); + if (err) + dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate); +} + +static int starfive_dwmac_set_mode(struct plat_stmmacenet_data *plat_dat) +{ + struct starfive_dwmac *dwmac = plat_dat->bsp_priv; + struct regmap *regmap; + unsigned int args[2]; + unsigned int mode; + + switch (plat_dat->interface) { + case PHY_INTERFACE_MODE_RMII: + mode = STARFIVE_DWMAC_PHY_INFT_RMII; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + mode = STARFIVE_DWMAC_PHY_INFT_RGMII; + break; + + default: + dev_err(dwmac->dev, "unsupported interface %d\n", + plat_dat->interface); + return -EINVAL; + } + + regmap = syscon_regmap_lookup_by_phandle_args(dwmac->dev->of_node, + "starfive,syscon", + 2, args); + if (IS_ERR(regmap)) { + dev_err(dwmac->dev, "syscon regmap failed.\n"); + return -ENXIO; + } + + /* args[0]:offset args[1]: shift */ + return regmap_update_bits(regmap, args[0], + STARFIVE_DWMAC_PHY_INFT_FIELD << args[1], + mode << args[1]); +} + +static int starfive_dwmac_probe(struct platform_device *pdev) +{ + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + struct starfive_dwmac *dwmac; + struct clk *clk_gtx; + int err; + + err = stmmac_get_platform_resources(pdev, &stmmac_res); + if (err) + return err; + + plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); + if (IS_ERR(plat_dat)) { + dev_err(&pdev->dev, "dt configuration failed\n"); + return PTR_ERR(plat_dat); + } + + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); + if (!dwmac) + return -ENOMEM; + + dwmac->clk_tx = devm_clk_get_enabled(&pdev->dev, "tx"); + if (IS_ERR(dwmac->clk_tx)) + return dev_err_probe(&pdev->dev, PTR_ERR(dwmac->clk_tx), + "error getting tx clock\n"); + + clk_gtx = devm_clk_get_enabled(&pdev->dev, "gtx"); + if (IS_ERR(clk_gtx)) + return dev_err_probe(&pdev->dev, PTR_ERR(clk_gtx), + "error getting gtx clock\n"); + + /* Generally, the rgmii_tx clock is provided by the internal clock, + * which needs to match the corresponding clock frequency according + * to different speeds. If the rgmii_tx clock is provided by the + * external rgmii_rxin, there is no need to configure the clock + * internally, because rgmii_rxin will be adaptively adjusted. + */ + if (!device_property_read_bool(&pdev->dev, "starfive,tx-use-rgmii-clk")) + plat_dat->fix_mac_speed = starfive_dwmac_fix_mac_speed; + + dwmac->dev = &pdev->dev; + plat_dat->bsp_priv = dwmac; + plat_dat->dma_cfg->dche = true; + + err = starfive_dwmac_set_mode(plat_dat); + if (err) { + dev_err(&pdev->dev, "dwmac set mode failed.\n"); + return err; + } + + err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (err) { + stmmac_remove_config_dt(pdev, plat_dat); + return err; + } + + return 0; +} + +static const struct of_device_id starfive_dwmac_match[] = { + { .compatible = "starfive,jh7110-dwmac" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_dwmac_match); + +static struct platform_driver starfive_dwmac_driver = { + .probe = starfive_dwmac_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "starfive-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = starfive_dwmac_match, + }, +}; +module_platform_driver(starfive_dwmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("StarFive DWMAC platform driver"); +MODULE_AUTHOR("Emil Renner Berthing "); +MODULE_AUTHOR("Samin Guo "); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 067a40fe0a23f..eb0b2898daa3d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -519,7 +519,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, u8 *mac) if (of_device_is_compatible(np, "snps,dwmac-4.00") || of_device_is_compatible(np, "snps,dwmac-4.10a") || of_device_is_compatible(np, "snps,dwmac-4.20a") || - of_device_is_compatible(np, "snps,dwmac-5.10a")) { + of_device_is_compatible(np, "snps,dwmac-5.10a") || + of_device_is_compatible(np, "snps,dwmac-5.20")) { plat->has_gmac4 = 1; plat->has_gmac = 0; plat->pmt = 1; diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 7bd00a11d074b..c4b2a86e2afba 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -91,6 +91,7 @@ source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" source "drivers/phy/socionext/Kconfig" source "drivers/phy/st/Kconfig" +source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" source "drivers/phy/tegra/Kconfig" source "drivers/phy/ti/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index 54f312c10a408..fb3dc9de61115 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -31,6 +31,7 @@ obj-y += allwinner/ \ samsung/ \ socionext/ \ st/ \ + starfive/ \ sunplus/ \ tegra/ \ ti/ \ diff --git a/drivers/phy/starfive/Kconfig b/drivers/phy/starfive/Kconfig new file mode 100644 index 0000000000000..df4b22b3aa8cd --- /dev/null +++ b/drivers/phy/starfive/Kconfig @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for StarFive platforms +# + +config PHY_STARFIVE_DPHY_RX + tristate "StarFive D-PHY RX Support" + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY + help + Choose this option if you have a StarFive D-PHY in your + system. If M is selected, the module will be called + phy-starfive-dphy-rx. + +config PHY_STARFIVE_JH7110_USB + tristate "Starfive JH7110 USB 2.0 PHY support" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this to support the StarFive USB 2.0 PHY, + used with the Cadence USB controller. + If M is selected, the module will be called + phy-jh7110-usb.ko. + +config PHY_STARFIVE_JH7110_PCIE + tristate "Starfive JH7110 PCIE 2.0/USB 3.0 PHY support" + depends on USB_SUPPORT + select GENERIC_PHY + select USB_PHY + help + Enable this to support the StarFive PCIe 2.0 PHY, + or used as USB 3.0 PHY. + If M is selected, the module will be called + phy-jh7110-pcie.ko. diff --git a/drivers/phy/starfive/Makefile b/drivers/phy/starfive/Makefile new file mode 100644 index 0000000000000..c3eaf1b34cbba --- /dev/null +++ b/drivers/phy/starfive/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_STARFIVE_DPHY_RX) += phy-starfive-dphy-rx.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_USB) += phy-jh7110-usb.o +obj-$(CONFIG_PHY_STARFIVE_JH7110_PCIE) += phy-jh7110-pcie.o diff --git a/drivers/phy/starfive/phy-jh7110-pcie.c b/drivers/phy/starfive/phy-jh7110-pcie.c new file mode 100644 index 0000000000000..30a8fa1f580d7 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-pcie.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 PCIe 2.0 PHY driver + * + * Copyright (C) 2023 Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PCIE_KVCO_LEVEL_OFF (0x28) +#define PCIE_USB3_PHY_PLL_CTL_OFF (0x7c) +#define PCIE_KVCO_TUNE_SIGNAL_OFF (0x80) +#define PCIE_USB3_PHY_ENABLE BIT(4) +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +struct jh7110_pcie_phy { + struct phy *phy; + void __iomem *regs; + enum phy_mode mode; +}; + +static void jh7110_usb3_mode_set(struct jh7110_pcie_phy *phy) +{ + /* Configuare spread-spectrum mode: down-spread-spectrum */ + writel(PCIE_USB3_PHY_ENABLE, phy->regs + PCIE_USB3_PHY_PLL_CTL_OFF); +} + +static void jh7110_pcie_mode_set(struct jh7110_pcie_phy *phy) +{ + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + writel(PHY_KVCO_FINE_TUNE_LEVEL, phy->regs + PCIE_KVCO_LEVEL_OFF); + writel(PHY_KVCO_FINE_TUNE_SIGNALS, phy->regs + PCIE_KVCO_TUNE_SIGNAL_OFF); +} + +static int jh7110_pcie_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_pcie_phy *phy = phy_get_drvdata(_phy); + + if (mode != phy->mode) { + switch (mode) { + case PHY_MODE_USB_HOST: + case PHY_MODE_USB_DEVICE: + case PHY_MODE_USB_OTG: + jh7110_usb3_mode_set(phy); + break; + case PHY_MODE_PCIE: + jh7110_pcie_mode_set(phy); + break; + default: + return -EINVAL; + } + + dev_info(&_phy->dev, "Changing phy mode to %d\n", mode); + phy->mode = mode; + } + + return 0; +} + +static int jh7110_pcie_phy_init(struct phy *_phy) +{ + return 0; +} + +static int jh7110_pcie_phy_exit(struct phy *_phy) +{ + return 0; +} + +static const struct phy_ops jh7110_pcie_phy_ops = { + .init = jh7110_pcie_phy_init, + .exit = jh7110_pcie_phy_exit, + .set_mode = jh7110_pcie_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_pcie_phy_probe(struct platform_device *pdev) +{ + struct jh7110_pcie_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return PTR_ERR(phy->regs); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_pcie_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Failed to map phy base\n"); + + platform_set_drvdata(pdev, phy); + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int jh7110_pcie_phy_remove(struct platform_device *pdev) +{ + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id jh7110_pcie_phy_of_match[] = { + { .compatible = "starfive,jh7110-pcie-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_pcie_phy_of_match); + +static struct platform_driver jh7110_pcie_phy_driver = { + .probe = jh7110_pcie_phy_probe, + .remove = jh7110_pcie_phy_remove, + .driver = { + .of_match_table = jh7110_pcie_phy_of_match, + .name = "jh7110-pcie-phy", + } +}; +module_platform_driver(jh7110_pcie_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 PCIe 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/starfive/phy-jh7110-usb.c b/drivers/phy/starfive/phy-jh7110-usb.c new file mode 100644 index 0000000000000..89db0b7b12240 --- /dev/null +++ b/drivers/phy/starfive/phy-jh7110-usb.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * StarFive JH7110 USB 2.0 PHY driver + * + * Copyright (C) 2023 Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_125M_CLK_RATE 125000000 +#define USB_LS_KEEPALIVE_OFF 0x4 +#define USB_LS_KEEPALIVE_ENABLE BIT(4) + +struct jh7110_usb2_phy { + struct phy *phy; + void __iomem *regs; + struct clk *usb_125m_clk; + struct clk *app_125; + enum usb_dr_mode dr_mode; +}; + +static void jh7110_usb2_mode_set(struct jh7110_usb2_phy *phy) +{ + unsigned int val; + + if (phy->dr_mode != USB_DR_MODE_PERIPHERAL) { + /* Enable the LS speed keep-alive signal */ + val = readl(phy->regs + USB_LS_KEEPALIVE_OFF); + val |= USB_LS_KEEPALIVE_ENABLE; + writel(val, phy->regs + USB_LS_KEEPALIVE_OFF); + } +} + +static int jh7110_usb2_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + int new_mode; + + switch (mode) { + case PHY_MODE_USB_HOST: + new_mode = USB_DR_MODE_HOST; + break; + case PHY_MODE_USB_DEVICE: + new_mode = USB_DR_MODE_PERIPHERAL; + break; + case PHY_MODE_USB_OTG: + new_mode = USB_DR_MODE_OTG; + break; + default: + return -EINVAL; + } + + if (new_mode != phy->dr_mode) { + dev_info(&_phy->dev, "Changing dr_mode to %d\n", new_mode); + phy->dr_mode = new_mode; + jh7110_usb2_mode_set(phy); + } + + return 0; +} + +static int jh7110_usb2_phy_init(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + int ret; + + ret = clk_set_rate(phy->usb_125m_clk, USB_125M_CLK_RATE); + if (ret) + return ret; + + ret = clk_prepare_enable(phy->app_125); + if (ret) + return ret; + + return 0; +} + +static int jh7110_usb2_phy_exit(struct phy *_phy) +{ + struct jh7110_usb2_phy *phy = phy_get_drvdata(_phy); + + clk_disable_unprepare(phy->app_125); + + return 0; +} + +static const struct phy_ops jh7110_usb2_phy_ops = { + .init = jh7110_usb2_phy_init, + .exit = jh7110_usb2_phy_exit, + .set_mode = jh7110_usb2_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int jh7110_usb_phy_probe(struct platform_device *pdev) +{ + struct jh7110_usb2_phy *phy; + struct device *dev = &pdev->dev; + struct phy_provider *phy_provider; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->usb_125m_clk = devm_clk_get(dev, "125m"); + if (IS_ERR(phy->usb_125m_clk)) + return dev_err_probe(dev, PTR_ERR(phy->usb_125m_clk), + "Failed to get 125m clock\n"); + + phy->app_125 = devm_clk_get(dev, "app_125"); + if (IS_ERR(phy->app_125)) + return dev_err_probe(dev, PTR_ERR(phy->app_125), + "Failed to get app 125m clock\n"); + + phy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->regs)) + return dev_err_probe(dev, PTR_ERR(phy->regs), + "Failed to map phy base\n"); + + phy->phy = devm_phy_create(dev, NULL, &jh7110_usb2_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->phy), + "Failed to create phy\n"); + + platform_set_drvdata(pdev, phy); + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int jh7110_usb_phy_remove(struct platform_device *pdev) +{ + struct jh7110_usb2_phy *phy = platform_get_drvdata(pdev); + + clk_disable_unprepare(phy->app_125); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id jh7110_usb_phy_of_match[] = { + { .compatible = "starfive,jh7110-usb-phy" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, jh7110_usb_phy_of_match); + +static struct platform_driver jh7110_usb_phy_driver = { + .probe = jh7110_usb_phy_probe, + .remove = jh7110_usb_phy_remove, + .driver = { + .of_match_table = jh7110_usb_phy_of_match, + .name = "jh7110-usb-phy", + } +}; +module_platform_driver(jh7110_usb_phy_driver); + +MODULE_DESCRIPTION("StarFive JH7110 USB 2.0 PHY driver"); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/starfive/phy-starfive-dphy-rx.c b/drivers/phy/starfive/phy-starfive-dphy-rx.c new file mode 100644 index 0000000000000..57071ec4cca86 --- /dev/null +++ b/drivers/phy/starfive/phy-starfive-dphy-rx.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * DPHY driver for the StarFive JH7110 SoC + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STF_DPHY_APBCFGSAIF__SYSCFG(x) (x) + +#define STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_EN BIT(6) +#define STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_IN GENMASK(12, 7) +#define STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_EN BIT(19) +#define STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_IN GENMASK(25, 20) + +#define STF_DPHY_DATA_BUS16_8 BIT(8) +#define STF_DPHY_DEBUG_MODE_SEL GENMASK(15, 9) + +#define STF_DPHY_ENABLE_CLK BIT(6) +#define STF_DPHY_ENABLE_CLK1 BIT(7) +#define STF_DPHY_ENABLE_LAN0 BIT(8) +#define STF_DPHY_ENABLE_LAN1 BIT(9) +#define STF_DPHY_ENABLE_LAN2 BIT(10) +#define STF_DPHY_ENABLE_LAN3 BIT(11) +#define STF_DPHY_GPI_EN GENMASK(17, 12) +#define STF_DPHY_HS_FREQ_CHANGE_CLK BIT(18) +#define STF_DPHY_HS_FREQ_CHANGE_CLK1 BIT(19) +#define STF_DPHY_LANE_SWAP_CLK GENMASK(22, 20) +#define STF_DPHY_LANE_SWAP_CLK1 GENMASK(25, 23) +#define STF_DPHY_LANE_SWAP_LAN0 GENMASK(28, 26) +#define STF_DPHY_LANE_SWAP_LAN1 GENMASK(31, 29) + +#define STF_DPHY_LANE_SWAP_LAN2 GENMASK(2, 0) +#define STF_DPHY_LANE_SWAP_LAN3 GENMASK(5, 3) +#define STF_DPHY_MP_TEST_EN BIT(6) +#define STF_DPHY_MP_TEST_MODE_SEL GENMASK(11, 7) +#define STF_DPHY_PLL_CLK_SEL GENMASK(21, 12) +#define STF_DPHY_PRECOUNTER_IN_CLK GENMASK(29, 22) + +#define STF_DPHY_PRECOUNTER_IN_CLK1 GENMASK(7, 0) +#define STF_DPHY_PRECOUNTER_IN_LAN0 GENMASK(15, 8) +#define STF_DPHY_PRECOUNTER_IN_LAN1 GENMASK(23, 16) +#define STF_DPHY_PRECOUNTER_IN_LAN2 GENMASK(31, 24) + +#define STF_DPHY_PRECOUNTER_IN_LAN3 GENMASK(7, 0) +#define STF_DPHY_RX_1C2C_SEL BIT(8) + +struct regval_t { + u32 addr; + u32 val; +}; + +struct stf_dphy { + struct device *dev; + void __iomem *regs; + struct clk *cfg_clk; + struct clk *ref_clk; + struct clk *tx_clk; + struct reset_control *rstc; + struct regulator *mipi_0p9; + struct phy *phy; + struct regmap *stf_aon_syscon; + unsigned int aon_gp_reg; +}; + +struct stf_dphy_info { + bool external_support; + int (*external_get)(struct stf_dphy *dphy); + void (*external_init)(struct stf_dphy *dphy); + void (*external_exit)(struct stf_dphy *dphy); +}; + +static const struct stf_dphy_info *stf_dphy_info; + +static const struct regval_t stf_dphy_init_list[] = { + { STF_DPHY_APBCFGSAIF__SYSCFG(4), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(8), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(12), 0x0000fff0 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(16), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(20), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(24), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(28), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(32), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(36), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(40), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(40), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(48), 0x24000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(52), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(56), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(60), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(64), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(68), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(72), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(76), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(80), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(84), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(88), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(92), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(96), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(100), 0x02000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(104), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(108), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(112), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(116), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(120), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(124), 0x0000000c }, + { STF_DPHY_APBCFGSAIF__SYSCFG(128), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(132), 0xcc500000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(136), 0x000000cc }, + { STF_DPHY_APBCFGSAIF__SYSCFG(140), 0x00000000 }, + { STF_DPHY_APBCFGSAIF__SYSCFG(144), 0x00000000 }, +}; + +static int stf_dphy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + int map[6] = {4, 0, 1, 2, 3, 5}; + int i; + + for (i = 0; i < ARRAY_SIZE(stf_dphy_init_list); i++) + writel(stf_dphy_init_list[i].val, + dphy->regs + stf_dphy_init_list[i].addr); + + writel(FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_EN, 1) | + FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL0_2D1C_EFUSE_IN, 0x1b) | + FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_EN, 1) | + FIELD_PREP(STF_DPHY_DA_CDPHY_R100_CTRL1_2D1C_EFUSE_IN, 0x1b), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(0)); + + writel(FIELD_PREP(STF_DPHY_DATA_BUS16_8, 0) | + FIELD_PREP(STF_DPHY_DEBUG_MODE_SEL, 0x5a), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(184)); + + writel(FIELD_PREP(STF_DPHY_ENABLE_CLK, 1) | + FIELD_PREP(STF_DPHY_ENABLE_CLK1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN0, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN1, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN2, 1) | + FIELD_PREP(STF_DPHY_ENABLE_LAN3, 1) | + FIELD_PREP(STF_DPHY_GPI_EN, 0) | + FIELD_PREP(STF_DPHY_HS_FREQ_CHANGE_CLK, 0) | + FIELD_PREP(STF_DPHY_HS_FREQ_CHANGE_CLK1, 0) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK, map[0]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_CLK1, map[5]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN0, map[1]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN1, map[2]), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(188)); + + writel(FIELD_PREP(STF_DPHY_LANE_SWAP_LAN2, map[3]) | + FIELD_PREP(STF_DPHY_LANE_SWAP_LAN3, map[4]) | + FIELD_PREP(STF_DPHY_MP_TEST_EN, 0) | + FIELD_PREP(STF_DPHY_MP_TEST_MODE_SEL, 0) | + FIELD_PREP(STF_DPHY_PLL_CLK_SEL, 0x37c) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK, 8), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(192)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_CLK1, 8) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN0, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN1, 7) | + FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN2, 7), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(196)); + + writel(FIELD_PREP(STF_DPHY_PRECOUNTER_IN_LAN3, 7) | + FIELD_PREP(STF_DPHY_RX_1C2C_SEL, 0), + dphy->regs + STF_DPHY_APBCFGSAIF__SYSCFG(200)); + + return 0; +} + +static int stf_dphy_init(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + int ret; + + ret = regulator_enable(dphy->mipi_0p9); + if (ret) + goto err_0p9; + + if (stf_dphy_info->external_support && stf_dphy_info->external_init) + stf_dphy_info->external_init(dphy); + + return 0; + +err_0p9: + return ret; +} + +static int stf_dphy_exit(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + if (stf_dphy_info->external_support && stf_dphy_info->external_exit) + stf_dphy_info->external_exit(dphy); + + regulator_disable(dphy->mipi_0p9); + + return 0; +} + +static int stf_dphy_power_on(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + clk_set_rate(dphy->cfg_clk, 99000000); + clk_set_rate(dphy->ref_clk, 49500000); + clk_set_rate(dphy->tx_clk, 19800000); + reset_control_deassert(dphy->rstc); + + return 0; +} + +static int stf_dphy_power_off(struct phy *phy) +{ + struct stf_dphy *dphy = phy_get_drvdata(phy); + + reset_control_assert(dphy->rstc); + + return 0; +} + +static const struct phy_ops stf_dphy_ops = { + .init = stf_dphy_init, + .exit = stf_dphy_exit, + .configure = stf_dphy_configure, + .power_on = stf_dphy_power_on, + .power_off = stf_dphy_power_off, +}; + +static int stf_dphy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct stf_dphy *dphy; + int ret; + + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); + if (!dphy) + return -ENOMEM; + stf_dphy_info = of_device_get_match_data(&pdev->dev); + dev_set_drvdata(&pdev->dev, dphy); + dphy->dev = &pdev->dev; + + dphy->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dphy->regs)) + return PTR_ERR(dphy->regs); + + dphy->cfg_clk = devm_clk_get(&pdev->dev, "cfg"); + if (IS_ERR(dphy->cfg_clk)) + return PTR_ERR(dphy->cfg_clk); + + dphy->ref_clk = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(dphy->ref_clk)) + return PTR_ERR(dphy->ref_clk); + + dphy->tx_clk = devm_clk_get(&pdev->dev, "tx"); + if (IS_ERR(dphy->tx_clk)) + return PTR_ERR(dphy->tx_clk); + + dphy->rstc = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(dphy->rstc)) + return PTR_ERR(dphy->rstc); + + dphy->mipi_0p9 = devm_regulator_get(&pdev->dev, "mipi_0p9"); + if (IS_ERR(dphy->mipi_0p9)) + return PTR_ERR(dphy->mipi_0p9); + + if (stf_dphy_info->external_support && stf_dphy_info->external_get) { + ret = stf_dphy_info->external_get(dphy); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get PHY external info\n"); + return ret; + } + } + + dphy->phy = devm_phy_create(&pdev->dev, NULL, &stf_dphy_ops); + if (IS_ERR(dphy->phy)) { + dev_err(&pdev->dev, "Failed to create PHY\n"); + return PTR_ERR(dphy->phy); + } + + phy_set_drvdata(dphy->phy, dphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static int stf_external_get(struct stf_dphy *dphy) +{ + struct of_phandle_args args; + int ret; + + ret = of_parse_phandle_with_fixed_args(dphy->dev->of_node, + "starfive,aon-syscon", + 1, 0, &args); + if (ret < 0) { + dev_err(dphy->dev, "Failed to parse starfive,aon-syscon\n"); + return -EINVAL; + } + + dphy->stf_aon_syscon = syscon_node_to_regmap(args.np); + of_node_put(args.np); + if (IS_ERR(dphy->stf_aon_syscon)) + return PTR_ERR(dphy->stf_aon_syscon); + + dphy->aon_gp_reg = args.args[0]; + + return 0; +} + +static void stf_external_init(struct stf_dphy *dphy) +{ + regmap_update_bits(dphy->stf_aon_syscon, dphy->aon_gp_reg, + BIT(31), BIT(31)); +} + +static void stf_external_exit(struct stf_dphy *dphy) +{ + regmap_update_bits(dphy->stf_aon_syscon, dphy->aon_gp_reg, + BIT(31), 0); +} + +static const struct stf_dphy_info starfive_dphy_info = { + .external_support = true, + .external_get = stf_external_get, + .external_init = stf_external_init, + .external_exit = stf_external_exit, +}; + +static const struct of_device_id stf_dphy_dt_ids[] = { + { + .compatible = "starfive,jh7110-dphy-rx", + .data = &starfive_dphy_info, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, stf_dphy_dt_ids); + +static struct platform_driver stf_dphy_driver = { + .probe = stf_dphy_probe, + .driver = { + .name = "starfive-dphy-rx", + .of_match_table = stf_dphy_dt_ids, + }, +}; +module_platform_driver(stf_dphy_driver); + +MODULE_AUTHOR("Jack Zhu "); +MODULE_AUTHOR("Changhuang Liang "); +MODULE_DESCRIPTION("StarFive DPHY RX driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index dae023d783a22..2307a0099994e 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -536,6 +536,16 @@ config PWM_SPRD To compile this driver as a module, choose M here: the module will be called pwm-sprd. +config PWM_STARFIVE_PTC + tristate "StarFive PWM PTC support" + depends on OF + depends on COMMON_CLK + help + Generic PWM framework driver for StarFive SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-starfive-ptc. + config PWM_STI tristate "STiH4xx PWM support" depends on ARCH_STI || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 7bf1a29f02b84..577f69904baa6 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o obj-$(CONFIG_PWM_SPRD) += pwm-sprd.o +obj-$(CONFIG_PWM_STARFIVE_PTC) += pwm-starfive-ptc.o obj-$(CONFIG_PWM_STI) += pwm-sti.o obj-$(CONFIG_PWM_STM32) += pwm-stm32.o obj-$(CONFIG_PWM_STM32_LP) += pwm-stm32-lp.o diff --git a/drivers/pwm/pwm-starfive-ptc.c b/drivers/pwm/pwm-starfive-ptc.c new file mode 100644 index 0000000000000..239df796d240c --- /dev/null +++ b/drivers/pwm/pwm-starfive-ptc.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PWM driver for the StarFive JH7110 SoC + * + * Copyright (C) 2018 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* how many parameters can be transferred to ptc */ +#define OF_PWM_N_CELLS 3 + +/* PTC Register offsets */ +#define REG_RPTC_CNTR 0x0 +#define REG_RPTC_HRC 0x4 +#define REG_RPTC_LRC 0x8 +#define REG_RPTC_CTRL 0xC + +/* Bit for PWM clock */ +#define BIT_PWM_CLOCK_EN 31 + +/* Bit for clock gen soft reset */ +#define BIT_CLK_GEN_SOFT_RESET 13 + +#define NS_PER_SECOND 1000000000 + +/* + * Access PTC register (cntr hrc lrc and ctrl), + * need to replace PWM_BASE_ADDR + */ +#define REG_PTC_BASE_ADDR_SUB(base, N) \ +((base) + (((N) > 3) ? (((N) % 4) * 0x10 + (1 << 15)) : ((N) * 0x10))) +#define REG_PTC_RPTC_CNTR(base, N) (REG_PTC_BASE_ADDR_SUB(base, N)) +#define REG_PTC_RPTC_HRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x4) +#define REG_PTC_RPTC_LRC(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0x8) +#define REG_PTC_RPTC_CTRL(base, N) (REG_PTC_BASE_ADDR_SUB(base, N) + 0xC) + +/* PTC_RPTC_CTRL */ +#define PTC_EN BIT(0) +#define PTC_ECLK BIT(1) +#define PTC_NEC BIT(2) +#define PTC_OE BIT(3) +#define PTC_SIGNLE BIT(4) +#define PTC_INTE BIT(5) +#define PTC_INT BIT(6) +#define PTC_CNTRRST BIT(7) +#define PTC_CAPTE BIT(8) + +struct starfive_pwm_ptc_device { + struct pwm_chip chip; + struct clk *clk; + struct reset_control *rst; + void __iomem *regs; + int irq; + unsigned int approx_freq;/*pwm apb clock frequency*/ +}; + +static inline +struct starfive_pwm_ptc_device *chip_to_starfive_ptc(struct pwm_chip *c) +{ + return container_of(c, struct starfive_pwm_ptc_device, chip); +} + +static int starfive_pwm_ptc_get_state(struct pwm_chip *chip, + struct pwm_device *dev, + struct pwm_state *state) +{ + struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip); + u32 data_lrc, data_hrc; + u32 pwm_clk_ns = 0; + + data_lrc = ioread32(REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm)); + data_hrc = ioread32(REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm)); + + pwm_clk_ns = NS_PER_SECOND / pwm->approx_freq; + + state->period = data_lrc * pwm_clk_ns; + state->duty_cycle = data_hrc * pwm_clk_ns; + state->polarity = PWM_POLARITY_NORMAL; + state->enabled = 1; + + return 0; +} + +static int starfive_pwm_ptc_apply(struct pwm_chip *chip, + struct pwm_device *dev, + struct pwm_state *state) +{ + struct starfive_pwm_ptc_device *pwm = chip_to_starfive_ptc(chip); + u32 data_hrc = 0; + u32 data_lrc = 0; + u32 period_data = 0; + u32 duty_data = 0; + s64 multi = pwm->approx_freq; + s64 div = NS_PER_SECOND; + void __iomem *reg_addr; + + if (state->duty_cycle > state->period) + state->duty_cycle = state->period; + + while (multi % 10 == 0 && div % 10 == 0 && multi > 0 && div > 0) { + multi /= 10; + div /= 10; + } + + period_data = (u32)(state->period * multi / div); + if (abs(period_data * div / multi - state->period) + > abs((period_data + 1) * div / multi - state->period) || + (state->period > 0 && period_data == 0)) + period_data += 1; + + if (state->enabled) { + duty_data = (u32)(state->duty_cycle * multi / div); + if (abs(duty_data * div / multi - state->duty_cycle) + > abs((duty_data + 1) * div / multi - state->duty_cycle) || + (state->duty_cycle > 0 && duty_data == 0)) + duty_data += 1; + } else { + duty_data = 0; + } + + if (state->polarity == PWM_POLARITY_NORMAL) + data_hrc = period_data - duty_data; + else + data_hrc = duty_data; + + data_lrc = period_data; + + reg_addr = REG_PTC_RPTC_HRC(pwm->regs, dev->hwpwm); + iowrite32(data_hrc, reg_addr); + + reg_addr = REG_PTC_RPTC_LRC(pwm->regs, dev->hwpwm); + iowrite32(data_lrc, reg_addr); + + reg_addr = REG_PTC_RPTC_CNTR(pwm->regs, dev->hwpwm); + iowrite32(0, reg_addr); + + reg_addr = REG_PTC_RPTC_CTRL(pwm->regs, dev->hwpwm); + iowrite32(PTC_EN | PTC_OE, reg_addr); + + return 0; +} + +static const struct pwm_ops starfive_pwm_ptc_ops = { + .get_state = starfive_pwm_ptc_get_state, + .apply = (void *)starfive_pwm_ptc_apply, + .owner = THIS_MODULE, +}; + +static int starfive_pwm_ptc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct starfive_pwm_ptc_device *pwm; + struct pwm_chip *chip; + int ret; + + pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL); + if (!pwm) + return -ENOMEM; + + chip = &pwm->chip; + chip->dev = dev; + chip->ops = &starfive_pwm_ptc_ops; + chip->npwm = 8; + + chip->of_pwm_n_cells = OF_PWM_N_CELLS; + chip->base = -1; + + pwm->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pwm->regs)) + return dev_err_probe(dev, PTR_ERR(pwm->regs), + "Unable to map IO resources\n"); + + pwm->clk = devm_clk_get(dev, NULL); + if (IS_ERR(pwm->clk)) + return dev_err_probe(dev, PTR_ERR(pwm->clk), + "Unable to get pwm clock\n"); + + pwm->rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(pwm->rst)) + return dev_err_probe(dev, PTR_ERR(pwm->rst), + "Unable to get pwm reset\n"); + + ret = clk_prepare_enable(pwm->clk); + if (ret) { + dev_err(dev, + "Failed to enable pwm clock, %d\n", ret); + return ret; + } + + reset_control_deassert(pwm->rst); + + pwm->approx_freq = (unsigned int)clk_get_rate(pwm->clk); + if (!pwm->approx_freq) + dev_err(dev, "get pwm apb clock rate failed.\n"); + + ret = devm_pwmchip_add(dev, chip); + if (ret < 0) { + dev_err(dev, "cannot register PTC: %d\n", ret); + clk_disable_unprepare(pwm->clk); + return ret; + } + + platform_set_drvdata(pdev, pwm); + + return 0; +} + +static int starfive_pwm_ptc_remove(struct platform_device *dev) +{ + struct starfive_pwm_ptc_device *pwm = platform_get_drvdata(dev); + struct pwm_chip *chip = &pwm->chip; + + pwmchip_remove(chip); + + return 0; +} + +static const struct of_device_id starfive_pwm_ptc_of_match[] = { + { .compatible = "starfive,jh7110-pwm" }, + {}, +}; +MODULE_DEVICE_TABLE(of, starfive_pwm_ptc_of_match); + +static struct platform_driver starfive_pwm_ptc_driver = { + .probe = starfive_pwm_ptc_probe, + .remove = starfive_pwm_ptc_remove, + .driver = { + .name = "pwm-starfive-ptc", + .of_match_table = starfive_pwm_ptc_of_match, + }, +}; +module_platform_driver(starfive_pwm_ptc_driver); + +MODULE_AUTHOR("Jenny Zhang "); +MODULE_AUTHOR("Hal Feng "); +MODULE_DESCRIPTION("StarFive PWM PTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index 2a52c990d4fec..6aa8f243b30c6 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -232,13 +232,6 @@ config RESET_SOCFPGA This enables the reset driver for the SoCFPGA ARMv7 platforms. This driver gets initialized early during platform init calls. -config RESET_STARFIVE_JH7100 - bool "StarFive JH7100 Reset Driver" - depends on SOC_STARFIVE || COMPILE_TEST - default SOC_STARFIVE - help - This enables the reset controller driver for the StarFive JH7100 SoC. - config RESET_SUNPLUS bool "Sunplus SoCs Reset Driver" if COMPILE_TEST default ARCH_SUNPLUS @@ -320,6 +313,7 @@ config RESET_ZYNQ help This enables the reset controller driver for Xilinx Zynq SoCs. +source "drivers/reset/starfive/Kconfig" source "drivers/reset/sti/Kconfig" source "drivers/reset/hisilicon/Kconfig" source "drivers/reset/tegra/Kconfig" diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 3e7e5fd633a8e..7fec5af6c9645 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-y += core.o obj-y += hisilicon/ +obj-y += starfive/ obj-$(CONFIG_ARCH_STI) += sti/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_RESET_A10SR) += reset-a10sr.o @@ -30,7 +31,6 @@ obj-$(CONFIG_RESET_RZG2L_USBPHY_CTRL) += reset-rzg2l-usbphy-ctrl.o obj-$(CONFIG_RESET_SCMI) += reset-scmi.o obj-$(CONFIG_RESET_SIMPLE) += reset-simple.o obj-$(CONFIG_RESET_SOCFPGA) += reset-socfpga.o -obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o obj-$(CONFIG_RESET_SUNPLUS) += reset-sunplus.o obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o obj-$(CONFIG_RESET_TI_SCI) += reset-ti-sci.o diff --git a/drivers/reset/reset-starfive-jh7100.c b/drivers/reset/reset-starfive-jh7100.c deleted file mode 100644 index fc44b2fb3e031..0000000000000 --- a/drivers/reset/reset-starfive-jh7100.c +++ /dev/null @@ -1,173 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Reset driver for the StarFive JH7100 SoC - * - * Copyright (C) 2021 Emil Renner Berthing - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* register offsets */ -#define JH7100_RESET_ASSERT0 0x00 -#define JH7100_RESET_ASSERT1 0x04 -#define JH7100_RESET_ASSERT2 0x08 -#define JH7100_RESET_ASSERT3 0x0c -#define JH7100_RESET_STATUS0 0x10 -#define JH7100_RESET_STATUS1 0x14 -#define JH7100_RESET_STATUS2 0x18 -#define JH7100_RESET_STATUS3 0x1c - -/* - * Writing a 1 to the n'th bit of the m'th ASSERT register asserts - * line 32m + n, and writing a 0 deasserts the same line. - * Most reset lines have their status inverted so a 0 bit in the STATUS - * register means the line is asserted and a 1 means it's deasserted. A few - * lines don't though, so store the expected value of the status registers when - * all lines are asserted. - */ -static const u64 jh7100_reset_asserted[2] = { - /* STATUS0 */ - BIT_ULL_MASK(JH7100_RST_U74) | - BIT_ULL_MASK(JH7100_RST_VP6_DRESET) | - BIT_ULL_MASK(JH7100_RST_VP6_BRESET) | - /* STATUS1 */ - BIT_ULL_MASK(JH7100_RST_HIFI4_DRESET) | - BIT_ULL_MASK(JH7100_RST_HIFI4_BRESET), - /* STATUS2 */ - BIT_ULL_MASK(JH7100_RST_E24) | - /* STATUS3 */ - 0, -}; - -struct jh7100_reset { - struct reset_controller_dev rcdev; - /* protect registers against concurrent read-modify-write */ - spinlock_t lock; - void __iomem *base; -}; - -static inline struct jh7100_reset * -jh7100_reset_from(struct reset_controller_dev *rcdev) -{ - return container_of(rcdev, struct jh7100_reset, rcdev); -} - -static int jh7100_reset_update(struct reset_controller_dev *rcdev, - unsigned long id, bool assert) -{ - struct jh7100_reset *data = jh7100_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_assert = data->base + JH7100_RESET_ASSERT0 + offset * sizeof(u64); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); - u64 done = jh7100_reset_asserted[offset] & mask; - u64 value; - unsigned long flags; - int ret; - - if (!assert) - done ^= mask; - - spin_lock_irqsave(&data->lock, flags); - - value = readq(reg_assert); - if (assert) - value |= mask; - else - value &= ~mask; - writeq(value, reg_assert); - - /* if the associated clock is gated, deasserting might otherwise hang forever */ - ret = readq_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); - - spin_unlock_irqrestore(&data->lock, flags); - return ret; -} - -static int jh7100_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return jh7100_reset_update(rcdev, id, true); -} - -static int jh7100_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - return jh7100_reset_update(rcdev, id, false); -} - -static int jh7100_reset_reset(struct reset_controller_dev *rcdev, - unsigned long id) -{ - int ret; - - ret = jh7100_reset_assert(rcdev, id); - if (ret) - return ret; - - return jh7100_reset_deassert(rcdev, id); -} - -static int jh7100_reset_status(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct jh7100_reset *data = jh7100_reset_from(rcdev); - unsigned long offset = BIT_ULL_WORD(id); - u64 mask = BIT_ULL_MASK(id); - void __iomem *reg_status = data->base + JH7100_RESET_STATUS0 + offset * sizeof(u64); - u64 value = readq(reg_status); - - return !((value ^ jh7100_reset_asserted[offset]) & mask); -} - -static const struct reset_control_ops jh7100_reset_ops = { - .assert = jh7100_reset_assert, - .deassert = jh7100_reset_deassert, - .reset = jh7100_reset_reset, - .status = jh7100_reset_status, -}; - -static int __init jh7100_reset_probe(struct platform_device *pdev) -{ - struct jh7100_reset *data; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(data->base)) - return PTR_ERR(data->base); - - data->rcdev.ops = &jh7100_reset_ops; - data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = JH7100_RSTN_END; - data->rcdev.dev = &pdev->dev; - data->rcdev.of_node = pdev->dev.of_node; - spin_lock_init(&data->lock); - - return devm_reset_controller_register(&pdev->dev, &data->rcdev); -} - -static const struct of_device_id jh7100_reset_dt_ids[] = { - { .compatible = "starfive,jh7100-reset" }, - { /* sentinel */ } -}; - -static struct platform_driver jh7100_reset_driver = { - .driver = { - .name = "jh7100-reset", - .of_match_table = jh7100_reset_dt_ids, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver_probe(jh7100_reset_driver, jh7100_reset_probe); diff --git a/drivers/reset/starfive/Kconfig b/drivers/reset/starfive/Kconfig new file mode 100644 index 0000000000000..1fa706a2c3dca --- /dev/null +++ b/drivers/reset/starfive/Kconfig @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config RESET_STARFIVE_JH71X0 + bool + +config RESET_STARFIVE_JH7100 + bool "StarFive JH7100 Reset Driver" + depends on ARCH_STARFIVE || COMPILE_TEST + select RESET_STARFIVE_JH71X0 + default ARCH_STARFIVE + help + This enables the reset controller driver for the StarFive JH7100 SoC. + +config RESET_STARFIVE_JH7110 + bool "StarFive JH7110 Reset Driver" + depends on AUXILIARY_BUS && CLK_STARFIVE_JH7110_SYS + select RESET_STARFIVE_JH71X0 + default ARCH_STARFIVE + help + This enables the reset controller driver for the StarFive JH7110 SoC. diff --git a/drivers/reset/starfive/Makefile b/drivers/reset/starfive/Makefile new file mode 100644 index 0000000000000..7a44b66fb9d5d --- /dev/null +++ b/drivers/reset/starfive/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_RESET_STARFIVE_JH71X0) += reset-starfive-jh71x0.o + +obj-$(CONFIG_RESET_STARFIVE_JH7100) += reset-starfive-jh7100.o +obj-$(CONFIG_RESET_STARFIVE_JH7110) += reset-starfive-jh7110.o diff --git a/drivers/reset/starfive/reset-starfive-jh7100.c b/drivers/reset/starfive/reset-starfive-jh7100.c new file mode 100644 index 0000000000000..2a56f7fd4ba77 --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh7100.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Reset driver for the StarFive JH7100 SoC + * + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include + +#include "reset-starfive-jh71x0.h" + +#include + +/* register offsets */ +#define JH7100_RESET_ASSERT0 0x00 +#define JH7100_RESET_ASSERT1 0x04 +#define JH7100_RESET_ASSERT2 0x08 +#define JH7100_RESET_ASSERT3 0x0c +#define JH7100_RESET_STATUS0 0x10 +#define JH7100_RESET_STATUS1 0x14 +#define JH7100_RESET_STATUS2 0x18 +#define JH7100_RESET_STATUS3 0x1c + +/* + * Writing a 1 to the n'th bit of the m'th ASSERT register asserts + * line 32m + n, and writing a 0 deasserts the same line. + * Most reset lines have their status inverted so a 0 bit in the STATUS + * register means the line is asserted and a 1 means it's deasserted. A few + * lines don't though, so store the expected value of the status registers when + * all lines are asserted. + */ +static const u32 jh7100_reset_asserted[4] = { + /* STATUS0 */ + BIT(JH7100_RST_U74 % 32) | + BIT(JH7100_RST_VP6_DRESET % 32) | + BIT(JH7100_RST_VP6_BRESET % 32), + /* STATUS1 */ + BIT(JH7100_RST_HIFI4_DRESET % 32) | + BIT(JH7100_RST_HIFI4_BRESET % 32), + /* STATUS2 */ + BIT(JH7100_RST_E24 % 32), + /* STATUS3 */ + 0, +}; + +static int __init jh7100_reset_probe(struct platform_device *pdev) +{ + void __iomem *base = devm_platform_ioremap_resource(pdev, 0); + + if (IS_ERR(base)) + return PTR_ERR(base); + + return reset_starfive_jh71x0_register(&pdev->dev, pdev->dev.of_node, + base + JH7100_RESET_ASSERT0, + base + JH7100_RESET_STATUS0, + jh7100_reset_asserted, + JH7100_RSTN_END, + THIS_MODULE); +} + +static const struct of_device_id jh7100_reset_dt_ids[] = { + { .compatible = "starfive,jh7100-reset" }, + { /* sentinel */ } +}; + +static struct platform_driver jh7100_reset_driver = { + .driver = { + .name = "jh7100-reset", + .of_match_table = jh7100_reset_dt_ids, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver_probe(jh7100_reset_driver, jh7100_reset_probe); diff --git a/drivers/reset/starfive/reset-starfive-jh7110.c b/drivers/reset/starfive/reset-starfive-jh7110.c new file mode 100644 index 0000000000000..6ad535c41087c --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh7110.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Reset driver for the StarFive JH7110 SoC + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include + +#include "reset-starfive-jh71x0.h" + +#include + +struct jh7110_reset_info { + unsigned int nr_resets; + unsigned int assert_offset; + unsigned int status_offset; +}; + +static const struct jh7110_reset_info jh7110_sys_info = { + .nr_resets = JH7110_SYSRST_END, + .assert_offset = 0x2F8, + .status_offset = 0x308, +}; + +static const struct jh7110_reset_info jh7110_aon_info = { + .nr_resets = JH7110_AONRST_END, + .assert_offset = 0x38, + .status_offset = 0x3C, +}; + +static const struct jh7110_reset_info jh7110_stg_info = { + .nr_resets = JH7110_STGRST_END, + .assert_offset = 0x74, + .status_offset = 0x78, +}; + +static const struct jh7110_reset_info jh7110_isp_info = { + .nr_resets = JH7110_ISPRST_END, + .assert_offset = 0x38, + .status_offset = 0x3C, +}; + +static const struct jh7110_reset_info jh7110_vout_info = { + .nr_resets = JH7110_VOUTRST_END, + .assert_offset = 0x48, + .status_offset = 0x4C, +}; + +static int jh7110_reset_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct jh7110_reset_info *info = (struct jh7110_reset_info *)(id->driver_data); + void __iomem **base = (void __iomem **)dev_get_drvdata(adev->dev.parent); + + if (!info || !base) + return -ENODEV; + + return reset_starfive_jh71x0_register(&adev->dev, adev->dev.parent->of_node, + *base + info->assert_offset, + *base + info->status_offset, + NULL, + info->nr_resets, + NULL); +} + +static const struct auxiliary_device_id jh7110_reset_ids[] = { + { + .name = "clk_starfive_jh71x0.reset-sys", + .driver_data = (kernel_ulong_t)&jh7110_sys_info, + }, + { + .name = "clk_starfive_jh71x0.reset-aon", + .driver_data = (kernel_ulong_t)&jh7110_aon_info, + }, + { + .name = "clk_starfive_jh71x0.reset-stg", + .driver_data = (kernel_ulong_t)&jh7110_stg_info, + }, + { + .name = "clk_starfive_jh71x0.reset-isp", + .driver_data = (kernel_ulong_t)&jh7110_isp_info, + }, + { + .name = "clk_starfive_jh71x0.reset-vout", + .driver_data = (kernel_ulong_t)&jh7110_vout_info, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(auxiliary, jh7110_reset_ids); + +static struct auxiliary_driver jh7110_reset_driver = { + .probe = jh7110_reset_probe, + .id_table = jh7110_reset_ids, +}; +module_auxiliary_driver(jh7110_reset_driver); + +MODULE_AUTHOR("Hal Feng "); +MODULE_DESCRIPTION("StarFive JH7110 reset driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.c b/drivers/reset/starfive/reset-starfive-jh71x0.c new file mode 100644 index 0000000000000..55bbbd2de52cf --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh71x0.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Reset driver for the StarFive JH71X0 SoCs + * + * Copyright (C) 2021 Emil Renner Berthing + */ + +#include +#include +#include +#include +#include +#include + +#include "reset-starfive-jh71x0.h" + +struct jh71x0_reset { + struct reset_controller_dev rcdev; + /* protect registers against concurrent read-modify-write */ + spinlock_t lock; + void __iomem *assert; + void __iomem *status; + const u32 *asserted; +}; + +static inline struct jh71x0_reset * +jh71x0_reset_from(struct reset_controller_dev *rcdev) +{ + return container_of(rcdev, struct jh71x0_reset, rcdev); +} + +static int jh71x0_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool assert) +{ + struct jh71x0_reset *data = jh71x0_reset_from(rcdev); + unsigned long offset = id / 32; + u32 mask = BIT(id % 32); + void __iomem *reg_assert = data->assert + offset * sizeof(u32); + void __iomem *reg_status = data->status + offset * sizeof(u32); + u32 done = data->asserted ? data->asserted[offset] & mask : 0; + u32 value; + unsigned long flags; + int ret; + + if (!assert) + done ^= mask; + + spin_lock_irqsave(&data->lock, flags); + + value = readl(reg_assert); + if (assert) + value |= mask; + else + value &= ~mask; + writel(value, reg_assert); + + /* if the associated clock is gated, deasserting might otherwise hang forever */ + ret = readl_poll_timeout_atomic(reg_status, value, (value & mask) == done, 0, 1000); + + spin_unlock_irqrestore(&data->lock, flags); + return ret; +} + +static int jh71x0_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return jh71x0_reset_update(rcdev, id, true); +} + +static int jh71x0_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return jh71x0_reset_update(rcdev, id, false); +} + +static int jh71x0_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = jh71x0_reset_assert(rcdev, id); + if (ret) + return ret; + + return jh71x0_reset_deassert(rcdev, id); +} + +static int jh71x0_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct jh71x0_reset *data = jh71x0_reset_from(rcdev); + unsigned long offset = id / 32; + u32 mask = BIT(id % 32); + void __iomem *reg_status = data->status + offset * sizeof(u32); + u32 value = readl(reg_status); + + return !((value ^ data->asserted[offset]) & mask); +} + +static const struct reset_control_ops jh71x0_reset_ops = { + .assert = jh71x0_reset_assert, + .deassert = jh71x0_reset_deassert, + .reset = jh71x0_reset_reset, + .status = jh71x0_reset_status, +}; + +int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, + void __iomem *assert, void __iomem *status, + const u32 *asserted, unsigned int nr_resets, + struct module *owner) +{ + struct jh71x0_reset *data; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rcdev.ops = &jh71x0_reset_ops; + data->rcdev.owner = owner; + data->rcdev.nr_resets = nr_resets; + data->rcdev.dev = dev; + data->rcdev.of_node = of_node; + + spin_lock_init(&data->lock); + data->assert = assert; + data->status = status; + data->asserted = asserted; + + return devm_reset_controller_register(dev, &data->rcdev); +} +EXPORT_SYMBOL_GPL(reset_starfive_jh71x0_register); diff --git a/drivers/reset/starfive/reset-starfive-jh71x0.h b/drivers/reset/starfive/reset-starfive-jh71x0.h new file mode 100644 index 0000000000000..db7d39a87f87d --- /dev/null +++ b/drivers/reset/starfive/reset-starfive-jh71x0.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2021 Emil Renner Berthing + */ + +#ifndef __RESET_STARFIVE_JH71X0_H +#define __RESET_STARFIVE_JH71X0_H + +int reset_starfive_jh71x0_register(struct device *dev, struct device_node *of_node, + void __iomem *assert, void __iomem *status, + const u32 *asserted, unsigned int nr_resets, + struct module *owner); + +#endif /* __RESET_STARFIVE_JH71X0_H */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 47bbba04fe3ae..839d426a741c0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -239,7 +239,7 @@ config SPI_CADENCE config SPI_CADENCE_QUADSPI tristate "Cadence Quad SPI controller" - depends on OF && (ARM || ARM64 || X86 || COMPILE_TEST) + depends on OF && (ARM || ARM64 || X86 || RISCV || COMPILE_TEST) help Enable support for the Cadence Quad SPI Flash controller. diff --git a/drivers/spi/spi-cadence-quadspi.c b/drivers/spi/spi-cadence-quadspi.c index 64b6a460d739b..c789fa9e2177f 100644 --- a/drivers/spi/spi-cadence-quadspi.c +++ b/drivers/spi/spi-cadence-quadspi.c @@ -1615,7 +1615,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi) static int cqspi_probe(struct platform_device *pdev) { const struct cqspi_driver_platdata *ddata; - struct reset_control *rstc, *rstc_ocp; + struct reset_control *rstc, *rstc_ocp, *rstc_ref; struct device *dev = &pdev->dev; struct spi_master *master; struct resource *res_ahb; @@ -1705,6 +1705,17 @@ static int cqspi_probe(struct platform_device *pdev) goto probe_reset_failed; } + if (of_device_is_compatible(pdev->dev.of_node, "starfive,jh7110-qspi")) { + rstc_ref = devm_reset_control_get_optional_exclusive(dev, "rstc_ref"); + if (IS_ERR(rstc_ref)) { + ret = PTR_ERR(rstc_ref); + dev_err(dev, "Cannot get QSPI REF reset.\n"); + goto probe_reset_failed; + } + reset_control_assert(rstc_ref); + reset_control_deassert(rstc_ref); + } + reset_control_assert(rstc); reset_control_deassert(rstc); @@ -1859,6 +1870,10 @@ static const struct cqspi_driver_platdata versal_ospi = { .get_dma_status = cqspi_get_versal_dma_status, }; +static const struct cqspi_driver_platdata jh7110_qspi = { + .quirks = CQSPI_DISABLE_DAC_MODE, +}; + static const struct of_device_id cqspi_dt_ids[] = { { .compatible = "cdns,qspi-nor", @@ -1884,6 +1899,10 @@ static const struct of_device_id cqspi_dt_ids[] = { .compatible = "intel,socfpga-qspi", .data = &socfpga_qspi, }, + { + .compatible = "starfive,jh7110-qspi", + .data = &jh7110_qspi, + }, { /* end of table */ } }; diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index b98ca0a1352a2..0a514b5915272 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -78,6 +78,17 @@ config USB_CDNS3_IMX For example, imx8qm and imx8qxp. +config USB_CDNS3_STARFIVE + tristate "Cadence USB3 support on StarFive SoC platforms" + depends on ARCH_STARFIVE || COMPILE_TEST + help + Say 'Y' or 'M' here if you are building for StarFive SoCs + platforms that contain Cadence USB3 controller core. + + e.g. JH7110. + + If you choose to build this driver as module it will + be dynamically linked and module will be called cdns3-starfive.ko endif if USB_CDNS_SUPPORT diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 61edb2f892764..48dfae75b5aaf 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -24,6 +24,7 @@ endif obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o +obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o cdnsp-udc-pci-y := cdnsp-pci.o diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 0000000000000..a99f98f852358 --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2022 Starfive, Inc. + * Author: Yanhong Wang + * Author: Mason Huo + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" + +#define USB_STRAP_HOST BIT(17) +#define USB_STRAP_DEVICE BIT(18) +#define USB_STRAP_MASK GENMASK(18, 16) + +#define USB_SUSPENDM_HOST BIT(19) +#define USB_SUSPENDM_MASK BIT(19) + +#define USB_SUSPENDM_BYPS BIT(20) +#define USB_REFCLK_MODE BIT(23) +#define USB_PLL_EN BIT(22) +#define USB_PDRSTN_SPLIT BIT(17) + +#define PCIE_CKREF_SRC_MASK GENMASK(19, 18) +#define PCIE_CLK_SEL_MASK GENMASK(21, 20) +#define PCIE_PHY_MODE BIT(20) +#define PCIE_PHY_MODE_MASK GENMASK(21, 20) +#define PCIE_USB3_BUS_WIDTH_MASK GENMASK(3, 2) +#define PCIE_USB3_RATE_MASK GENMASK(6, 5) +#define PCIE_USB3_RX_STANDBY_MASK BIT(7) +#define PCIE_USB3_PHY_ENABLE BIT(4) + +struct cdns_starfive { + struct device *dev; + struct regmap *stg_syscon; + struct regmap *sys_syscon; + struct reset_control *resets; + struct clk_bulk_data *clks; + int num_clks; + u32 sys_offset; + u32 stg_offset_4; + u32 stg_offset_196; + u32 stg_offset_328; + u32 stg_offset_500; + bool usb2_only; +}; + +static int cdns_mode_init(struct platform_device *pdev, + struct cdns_starfive *data, const char **out_mode) +{ + struct device_node *child; + const char *dr_mode = NULL; + + child = of_get_compatible_child(pdev->dev.of_node, "cdns,usb3"); + if (!child) { + return dev_err_probe(&pdev->dev, -ENODEV, + "Failed to find child node\n"); + } + + /* Init usb 2.0 utmi phy */ + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_SUSPENDM_BYPS, USB_SUSPENDM_BYPS); + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_PLL_EN, USB_PLL_EN); + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_REFCLK_MODE, USB_REFCLK_MODE); + + if (!of_find_property(child, "cdns3,usb3-phy", NULL)) { + /* Disconnect usb 3.0 phy mode */ + regmap_update_bits(data->sys_syscon, data->sys_offset, + USB_PDRSTN_SPLIT, USB_PDRSTN_SPLIT); + data->usb2_only = 1; + } else { + /* Config usb 3.0 pipe phy */ + regmap_update_bits(data->stg_syscon, data->stg_offset_196, + PCIE_CKREF_SRC_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_196, + PCIE_CLK_SEL_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_328, + PCIE_PHY_MODE_MASK, PCIE_PHY_MODE); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_BUS_WIDTH_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_RATE_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_RX_STANDBY_MASK, 0); + regmap_update_bits(data->stg_syscon, data->stg_offset_500, + PCIE_USB3_PHY_ENABLE, PCIE_USB3_PHY_ENABLE); + + /* Connect usb 3.0 phy mode */ + regmap_update_bits(data->sys_syscon, data->sys_offset, + USB_PDRSTN_SPLIT, 0); + } + + if (!of_property_read_string(child, "dr_mode", &dr_mode)) { + if (!strcmp(dr_mode, "host")) { + regmap_update_bits(data->stg_syscon, + data->stg_offset_4, + USB_STRAP_MASK, + USB_STRAP_HOST); + regmap_update_bits(data->stg_syscon, + data->stg_offset_4, + USB_SUSPENDM_MASK, + USB_SUSPENDM_HOST); + } else if (!strcmp(dr_mode, "peripheral")) { + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_STRAP_MASK, USB_STRAP_DEVICE); + regmap_update_bits(data->stg_syscon, data->stg_offset_4, + USB_SUSPENDM_MASK, 0); + } + } + + if (out_mode) + *out_mode = dr_mode; + + return 0; +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ + int ret; + + data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks); + if (data->num_clks < 0) + return dev_err_probe(data->dev, -ENODEV, + "Failed to get clocks\n"); + + ret = clk_bulk_prepare_enable(data->num_clks, data->clks); + if (ret) + return dev_err_probe(data->dev, ret, + "failed to enable clocks\n"); + + data->resets = devm_reset_control_array_get_exclusive(data->dev); + if (IS_ERR(data->resets)) { + ret = dev_err_probe(data->dev, PTR_ERR(data->resets), + "Failed to get resets"); + goto err_clk_init; + } + + ret = reset_control_deassert(data->resets); + if (ret) { + ret = dev_err_probe(data->dev, ret, + "failed to reset clocks\n"); + goto err_clk_init; + } + + return ret; + +err_clk_init: + clk_bulk_disable_unprepare(data->num_clks, data->clks); + return ret; +} + +static int cdns_starfive_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = pdev->dev.of_node; + struct cdns_starfive *data; + unsigned int args[4]; + const char *dr_mode; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + + data->dev = dev; + + data->stg_syscon = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,stg-syscon", 4, args); + + if (IS_ERR(data->stg_syscon)) + return dev_err_probe(dev, PTR_ERR(data->stg_syscon), + "Failed to parse starfive,stg-syscon\n"); + + data->stg_offset_4 = args[0]; + data->stg_offset_196 = args[1]; + data->stg_offset_328 = args[2]; + data->stg_offset_500 = args[3]; + + data->sys_syscon = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,sys-syscon", 1, args); + if (IS_ERR(data->sys_syscon)) + return dev_err_probe(dev, PTR_ERR(data->sys_syscon), + "Failed to parse starfive,sys-syscon\n"); + + data->sys_offset = args[0]; + + ret = cdns_mode_init(pdev, data, &dr_mode); + if (ret) + return ret; + + ret = cdns_clk_rst_init(data); + if (ret) + return ret; + + ret = of_platform_populate(node, NULL, NULL, dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to create children\n"); + + device_set_wakeup_capable(dev, true); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + dev_info(dev, "usb mode %s %s probe success\n", + dr_mode ? dr_mode : "unknown", data->usb2_only ? "2.0" : "3.0"); + + return 0; +} + +static int cdns_starfive_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int cdns_starfive_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_starfive *data = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + device_for_each_child(dev, NULL, cdns_starfive_remove_core); + + reset_control_assert(data->resets); + clk_bulk_disable_unprepare(data->num_clks, data->clks); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int cdns_starfive_resume(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(data->num_clks, data->clks); + if (ret) + return ret; + + ret = reset_control_deassert(data->resets); + if (ret) + return ret; + + return 0; +} + +static int cdns_starfive_suspend(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(data->num_clks, data->clks); + reset_control_assert(data->resets); + + return 0; +} +#endif + +static const struct dev_pm_ops cdns_starfive_pm_ops = { + SET_RUNTIME_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume) +}; + +static const struct of_device_id cdns_starfive_of_match[] = { + { .compatible = "starfive,jh7110-usb", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); + +static struct platform_driver cdns_starfive_driver = { + .probe = cdns_starfive_probe, + .remove = cdns_starfive_remove, + .driver = { + .name = "cdns3-starfive", + .of_match_table = cdns_starfive_of_match, + .pm = &cdns_starfive_pm_ops, + }, +}; +module_platform_driver(cdns_starfive_driver); + +MODULE_ALIAS("platform:cdns3-starfive"); +MODULE_AUTHOR("YanHong Wang "); +MODULE_AUTHOR("Mason Huo "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer"); diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index f0872970daf9a..f22138709bf5a 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1999,6 +1999,17 @@ config WATCHDOG_RTAS To compile this driver as a module, choose M here. The module will be called wdrtas. +# RISC-V Architecture + +config STARFIVE_WATCHDOG + tristate "StarFive Watchdog support" + depends on ARCH_STARFIVE || COMPILE_TEST + select WATCHDOG_CORE + default ARCH_STARFIVE + help + Say Y here to support the watchdog of StarFive JH7100 and JH7110 + SoC. This driver can also be built as a module if choose M. + # S390 Architecture config DIAG288_WATCHDOG diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 9cbf6580f16c9..b4c4ccf2d7038 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -192,6 +192,9 @@ obj-$(CONFIG_MEN_A21_WDT) += mena21_wdt.o obj-$(CONFIG_PSERIES_WDT) += pseries-wdt.o obj-$(CONFIG_WATCHDOG_RTAS) += wdrtas.o +# RISC-V Architecture +obj-$(CONFIG_STARFIVE_WATCHDOG) += starfive-wdt.o + # S390 Architecture obj-$(CONFIG_DIAG288_WATCHDOG) += diag288_wdt.o diff --git a/drivers/watchdog/starfive-wdt.c b/drivers/watchdog/starfive-wdt.c new file mode 100644 index 0000000000000..1995cceca51e4 --- /dev/null +++ b/drivers/watchdog/starfive-wdt.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Starfive Watchdog driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* JH7100 Watchdog register define */ +#define STARFIVE_WDT_JH7100_INTSTAUS 0x000 +#define STARFIVE_WDT_JH7100_CONTROL 0x104 +#define STARFIVE_WDT_JH7100_LOAD 0x108 +#define STARFIVE_WDT_JH7100_EN 0x110 +#define STARFIVE_WDT_JH7100_RELOAD 0x114 /* Write 0 or 1 to reload preset value */ +#define STARFIVE_WDT_JH7100_VALUE 0x118 +#define STARFIVE_WDT_JH7100_INTCLR 0x120 /* + * [0]: Write 1 to clear interrupt + * [1]: 1 mean clearing and 0 mean complete + * [31:2]: reserved. + */ +#define STARFIVE_WDT_JH7100_LOCK 0x13c /* write 0x378f0765 to unlock */ + +/* JH7110 Watchdog register define */ +#define STARFIVE_WDT_JH7110_LOAD 0x000 +#define STARFIVE_WDT_JH7110_VALUE 0x004 +#define STARFIVE_WDT_JH7110_CONTROL 0x008 /* + * [0]: reset enable; + * [1]: interrupt enable && watchdog enable + * [31:2]: reserved. + */ +#define STARFIVE_WDT_JH7110_INTCLR 0x00c /* clear intterupt and reload the counter */ +#define STARFIVE_WDT_JH7110_IMS 0x014 +#define STARFIVE_WDT_JH7110_LOCK 0xc00 /* write 0x1ACCE551 to unlock */ + +/* WDOGCONTROL */ +#define STARFIVE_WDT_ENABLE 0x1 +#define STARFIVE_WDT_EN_SHIFT 0 +#define STARFIVE_WDT_RESET_EN 0x1 +#define STARFIVE_WDT_JH7100_RST_EN_SHIFT 0 +#define STARFIVE_WDT_JH7110_RST_EN_SHIFT 1 + +/* WDOGLOCK */ +#define STARFIVE_WDT_JH7100_UNLOCK_KEY 0x378f0765 +#define STARFIVE_WDT_JH7110_UNLOCK_KEY 0x1acce551 + +/* WDOGINTCLR */ +#define STARFIVE_WDT_INTCLR 0x1 +#define STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT 1 /* Watchdog can clear interrupt when 0 */ + +#define STARFIVE_WDT_MAXCNT 0xffffffff +#define STARFIVE_WDT_DEFAULT_TIME (15) +#define STARFIVE_WDT_DELAY_US 0 +#define STARFIVE_WDT_TIMEOUT_US 10000 + +/* module parameter */ +#define STARFIVE_WDT_EARLY_ENA 0 + +static bool nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat; +static bool early_enable = STARFIVE_WDT_EARLY_ENA; + +module_param(heartbeat, int, 0); +module_param(early_enable, bool, 0); +module_param(nowayout, bool, 0); + +MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default=" + __MODULE_STRING(STARFIVE_WDT_DEFAULT_TIME) ")"); +MODULE_PARM_DESC(early_enable, + "Watchdog is started at boot time if set to 1, default=" + __MODULE_STRING(STARFIVE_WDT_EARLY_ENA)); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +struct starfive_wdt_variant { + unsigned int control; /* Watchdog Control Resgister for reset enable */ + unsigned int load; /* Watchdog Load register */ + unsigned int reload; /* Watchdog Reload Control register */ + unsigned int enable; /* Watchdog Enable Register */ + unsigned int value; /* Watchdog Counter Value Register */ + unsigned int int_clr; /* Watchdog Interrupt Clear Register */ + unsigned int unlock; /* Watchdog Lock Register */ + unsigned int int_status; /* Watchdog Interrupt Status Register */ + + u32 unlock_key; + char enrst_shift; + char en_shift; + bool intclr_check; /* whether need to check it before clearing interrupt */ + char intclr_ava_shift; + bool double_timeout; /* The watchdog need twice timeout to reboot */ +}; + +struct starfive_wdt { + struct watchdog_device wdd; + spinlock_t lock; /* spinlock for register handling */ + void __iomem *base; + struct clk *core_clk; + struct clk *apb_clk; + const struct starfive_wdt_variant *variant; + unsigned long freq; + u32 count; /* count of timeout */ + u32 reload; /* restore the count */ +}; + +/* Register layout and configuration for the JH7100 */ +static const struct starfive_wdt_variant starfive_wdt_jh7100_variant = { + .control = STARFIVE_WDT_JH7100_CONTROL, + .load = STARFIVE_WDT_JH7100_LOAD, + .reload = STARFIVE_WDT_JH7100_RELOAD, + .enable = STARFIVE_WDT_JH7100_EN, + .value = STARFIVE_WDT_JH7100_VALUE, + .int_clr = STARFIVE_WDT_JH7100_INTCLR, + .unlock = STARFIVE_WDT_JH7100_LOCK, + .unlock_key = STARFIVE_WDT_JH7100_UNLOCK_KEY, + .int_status = STARFIVE_WDT_JH7100_INTSTAUS, + .enrst_shift = STARFIVE_WDT_JH7100_RST_EN_SHIFT, + .en_shift = STARFIVE_WDT_EN_SHIFT, + .intclr_check = true, + .intclr_ava_shift = STARFIVE_WDT_JH7100_INTCLR_AVA_SHIFT, + .double_timeout = false, +}; + +/* Register layout and configuration for the JH7110 */ +static const struct starfive_wdt_variant starfive_wdt_jh7110_variant = { + .control = STARFIVE_WDT_JH7110_CONTROL, + .load = STARFIVE_WDT_JH7110_LOAD, + .enable = STARFIVE_WDT_JH7110_CONTROL, + .value = STARFIVE_WDT_JH7110_VALUE, + .int_clr = STARFIVE_WDT_JH7110_INTCLR, + .unlock = STARFIVE_WDT_JH7110_LOCK, + .unlock_key = STARFIVE_WDT_JH7110_UNLOCK_KEY, + .int_status = STARFIVE_WDT_JH7110_IMS, + .enrst_shift = STARFIVE_WDT_JH7110_RST_EN_SHIFT, + .en_shift = STARFIVE_WDT_EN_SHIFT, + .intclr_check = false, + .double_timeout = true, +}; + +static int starfive_wdt_enable_clock(struct starfive_wdt *wdt) +{ + int ret; + + ret = clk_prepare_enable(wdt->apb_clk); + if (ret) + return dev_err_probe(wdt->wdd.parent, ret, "failed to enable apb clock\n"); + + ret = clk_prepare_enable(wdt->core_clk); + if (ret) + return dev_err_probe(wdt->wdd.parent, ret, "failed to enable core clock\n"); + + return 0; +} + +static void starfive_wdt_disable_clock(struct starfive_wdt *wdt) +{ + clk_disable_unprepare(wdt->core_clk); + clk_disable_unprepare(wdt->apb_clk); +} + +static inline int starfive_wdt_get_clock(struct starfive_wdt *wdt) +{ + struct device *dev = wdt->wdd.parent; + + wdt->apb_clk = devm_clk_get(dev, "apb"); + if (IS_ERR(wdt->apb_clk)) + return dev_err_probe(dev, PTR_ERR(wdt->apb_clk), "failed to get apb clock\n"); + + wdt->core_clk = devm_clk_get(dev, "core"); + if (IS_ERR(wdt->core_clk)) + return dev_err_probe(dev, PTR_ERR(wdt->core_clk), "failed to get core clock\n"); + + return 0; +} + +static inline int starfive_wdt_reset_init(struct device *dev) +{ + struct reset_control *rsts; + int ret; + + rsts = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(rsts)) + return dev_err_probe(dev, PTR_ERR(rsts), "failed to get resets\n"); + + ret = reset_control_deassert(rsts); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert resets\n"); + + return 0; +} + +static u32 starfive_wdt_ticks_to_sec(struct starfive_wdt *wdt, u32 ticks) +{ + return DIV_ROUND_CLOSEST(ticks, wdt->freq); +} + +/* Write unlock-key to unlock. Write other value to lock. */ +static void starfive_wdt_unlock(struct starfive_wdt *wdt) +{ + spin_lock(&wdt->lock); + writel(wdt->variant->unlock_key, wdt->base + wdt->variant->unlock); +} + +static void starfive_wdt_lock(struct starfive_wdt *wdt) +{ + writel(~wdt->variant->unlock_key, wdt->base + wdt->variant->unlock); + spin_unlock(&wdt->lock); +} + +/* enable watchdog interrupt to reset/reboot */ +static void starfive_wdt_enable_reset(struct starfive_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->variant->control); + val |= STARFIVE_WDT_RESET_EN << wdt->variant->enrst_shift; + writel(val, wdt->base + wdt->variant->control); +} + +/* interrupt status whether has been raised from the counter */ +static bool starfive_wdt_raise_irq_status(struct starfive_wdt *wdt) +{ + return !!readl(wdt->base + wdt->variant->int_status); +} + +/* waiting interrupt can be free to clear */ +static int starfive_wdt_wait_int_free(struct starfive_wdt *wdt) +{ + u32 value; + + return readl_poll_timeout_atomic(wdt->base + wdt->variant->int_clr, value, + !(value & BIT(wdt->variant->intclr_ava_shift)), + STARFIVE_WDT_DELAY_US, STARFIVE_WDT_TIMEOUT_US); +} + +/* clear interrupt signal before initialization or reload */ +static int starfive_wdt_int_clr(struct starfive_wdt *wdt) +{ + int ret; + + if (wdt->variant->intclr_check) { + ret = starfive_wdt_wait_int_free(wdt); + if (ret) + return dev_err_probe(wdt->wdd.parent, ret, + "watchdog is not ready to clear interrupt.\n"); + } + writel(STARFIVE_WDT_INTCLR, wdt->base + wdt->variant->int_clr); + + return 0; +} + +static inline void starfive_wdt_set_count(struct starfive_wdt *wdt, u32 val) +{ + writel(val, wdt->base + wdt->variant->load); +} + +static inline u32 starfive_wdt_get_count(struct starfive_wdt *wdt) +{ + return readl(wdt->base + wdt->variant->value); +} + +/* enable watchdog */ +static inline void starfive_wdt_enable(struct starfive_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->variant->enable); + val |= STARFIVE_WDT_ENABLE << wdt->variant->en_shift; + writel(val, wdt->base + wdt->variant->enable); +} + +/* disable watchdog */ +static inline void starfive_wdt_disable(struct starfive_wdt *wdt) +{ + u32 val; + + val = readl(wdt->base + wdt->variant->enable); + val &= ~(STARFIVE_WDT_ENABLE << wdt->variant->en_shift); + writel(val, wdt->base + wdt->variant->enable); +} + +static inline void starfive_wdt_set_reload_count(struct starfive_wdt *wdt, u32 count) +{ + starfive_wdt_set_count(wdt, count); + + /* 7100 need set any value to reload register and could reload value to counter */ + if (wdt->variant->reload) + writel(0x1, wdt->base + wdt->variant->reload); +} + +static unsigned int starfive_wdt_max_timeout(struct starfive_wdt *wdt) +{ + if (wdt->variant->double_timeout) + return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, (wdt->freq / 2)) - 1; + + return DIV_ROUND_UP(STARFIVE_WDT_MAXCNT, wdt->freq) - 1; +} + +static unsigned int starfive_wdt_get_timeleft(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + u32 count; + + /* + * If the watchdog takes twice timeout and set half count value, + * timeleft value should add the count value before first timeout. + */ + count = starfive_wdt_get_count(wdt); + if (wdt->variant->double_timeout && !starfive_wdt_raise_irq_status(wdt)) + count += wdt->count; + + return starfive_wdt_ticks_to_sec(wdt, count); +} + +static int starfive_wdt_keepalive(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + int ret; + + starfive_wdt_unlock(wdt); + ret = starfive_wdt_int_clr(wdt); + if (ret) + goto exit; + + starfive_wdt_set_reload_count(wdt, wdt->count); + +exit: + /* exit with releasing spinlock and locking registers */ + starfive_wdt_lock(wdt); + return ret; +} + +static int starfive_wdt_start(struct starfive_wdt *wdt) +{ + int ret; + + starfive_wdt_unlock(wdt); + /* disable watchdog, to be safe */ + starfive_wdt_disable(wdt); + + starfive_wdt_enable_reset(wdt); + ret = starfive_wdt_int_clr(wdt); + if (ret) + goto exit; + + starfive_wdt_set_count(wdt, wdt->count); + starfive_wdt_enable(wdt); + +exit: + starfive_wdt_lock(wdt); + return ret; +} + +static void starfive_wdt_stop(struct starfive_wdt *wdt) +{ + starfive_wdt_unlock(wdt); + starfive_wdt_disable(wdt); + starfive_wdt_lock(wdt); +} + +static int starfive_wdt_pm_start(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + int ret = pm_runtime_get_sync(wdd->parent); + + if (ret < 0) + return ret; + + return starfive_wdt_start(wdt); +} + +static int starfive_wdt_pm_stop(struct watchdog_device *wdd) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + + starfive_wdt_stop(wdt); + return pm_runtime_put_sync(wdd->parent); +} + +static int starfive_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int timeout) +{ + struct starfive_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned long count = timeout * wdt->freq; + + /* some watchdogs take two timeouts to reset */ + if (wdt->variant->double_timeout) + count /= 2; + + wdt->count = count; + wdd->timeout = timeout; + + starfive_wdt_unlock(wdt); + starfive_wdt_disable(wdt); + starfive_wdt_set_reload_count(wdt, wdt->count); + starfive_wdt_enable(wdt); + starfive_wdt_lock(wdt); + + return 0; +} + +#define STARFIVE_WDT_OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) + +static const struct watchdog_info starfive_wdt_info = { + .options = STARFIVE_WDT_OPTIONS, + .identity = "StarFive Watchdog", +}; + +static const struct watchdog_ops starfive_wdt_ops = { + .owner = THIS_MODULE, + .start = starfive_wdt_pm_start, + .stop = starfive_wdt_pm_stop, + .ping = starfive_wdt_keepalive, + .set_timeout = starfive_wdt_set_timeout, + .get_timeleft = starfive_wdt_get_timeleft, +}; + +static int starfive_wdt_probe(struct platform_device *pdev) +{ + struct starfive_wdt *wdt; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(wdt->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(wdt->base), "error mapping registers\n"); + + wdt->wdd.parent = &pdev->dev; + ret = starfive_wdt_get_clock(wdt); + if (ret) + return ret; + + platform_set_drvdata(pdev, wdt); + pm_runtime_enable(&pdev->dev); + if (pm_runtime_enabled(&pdev->dev)) { + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + return ret; + } else { + /* runtime PM is disabled but clocks need to be enabled */ + ret = starfive_wdt_enable_clock(wdt); + if (ret) + return ret; + } + + ret = starfive_wdt_reset_init(&pdev->dev); + if (ret) + goto err_exit; + + watchdog_set_drvdata(&wdt->wdd, wdt); + wdt->wdd.info = &starfive_wdt_info; + wdt->wdd.ops = &starfive_wdt_ops; + wdt->variant = of_device_get_match_data(&pdev->dev); + spin_lock_init(&wdt->lock); + + wdt->freq = clk_get_rate(wdt->core_clk); + if (!wdt->freq) { + dev_err(&pdev->dev, "get clock rate failed.\n"); + ret = -EINVAL; + goto err_exit; + } + + wdt->wdd.min_timeout = 1; + wdt->wdd.max_timeout = starfive_wdt_max_timeout(wdt); + wdt->wdd.timeout = STARFIVE_WDT_DEFAULT_TIME; + watchdog_init_timeout(&wdt->wdd, heartbeat, &pdev->dev); + starfive_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout); + + watchdog_set_nowayout(&wdt->wdd, nowayout); + watchdog_stop_on_reboot(&wdt->wdd); + watchdog_stop_on_unregister(&wdt->wdd); + + if (early_enable) { + ret = starfive_wdt_start(wdt); + if (ret) + goto err_exit; + set_bit(WDOG_HW_RUNNING, &wdt->wdd.status); + } else { + starfive_wdt_stop(wdt); + } + + ret = watchdog_register_device(&wdt->wdd); + if (ret) + goto err_exit; + + if (!early_enable) + return pm_runtime_put_sync(&pdev->dev); + + return 0; + +err_exit: + starfive_wdt_disable_clock(wdt); + pm_runtime_disable(&pdev->dev); + + return ret; +} + +static int starfive_wdt_remove(struct platform_device *pdev) +{ + struct starfive_wdt *wdt = platform_get_drvdata(pdev); + + starfive_wdt_stop(wdt); + watchdog_unregister_device(&wdt->wdd); + + if (pm_runtime_enabled(&pdev->dev)) + pm_runtime_disable(&pdev->dev); + else + /* disable clock without PM */ + starfive_wdt_disable_clock(wdt); + + return 0; +} + +static void starfive_wdt_shutdown(struct platform_device *pdev) +{ + struct starfive_wdt *wdt = platform_get_drvdata(pdev); + + starfive_wdt_pm_stop(&wdt->wdd); +} + +#ifdef CONFIG_PM_SLEEP +static int starfive_wdt_suspend(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + + /* Save watchdog state, and turn it off. */ + wdt->reload = starfive_wdt_get_count(wdt); + + /* Note that WTCNT doesn't need to be saved. */ + starfive_wdt_stop(wdt); + + return pm_runtime_force_suspend(dev); +} + +static int starfive_wdt_resume(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + starfive_wdt_unlock(wdt); + /* Restore watchdog state. */ + starfive_wdt_set_reload_count(wdt, wdt->reload); + starfive_wdt_lock(wdt); + + return starfive_wdt_start(wdt); +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int starfive_wdt_runtime_suspend(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + + starfive_wdt_disable_clock(wdt); + + return 0; +} + +static int starfive_wdt_runtime_resume(struct device *dev) +{ + struct starfive_wdt *wdt = dev_get_drvdata(dev); + + return starfive_wdt_enable_clock(wdt); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops starfive_wdt_pm_ops = { + SET_RUNTIME_PM_OPS(starfive_wdt_runtime_suspend, starfive_wdt_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(starfive_wdt_suspend, starfive_wdt_resume) +}; + +static const struct of_device_id starfive_wdt_match[] = { + { .compatible = "starfive,jh7100-wdt", .data = &starfive_wdt_jh7100_variant }, + { .compatible = "starfive,jh7110-wdt", .data = &starfive_wdt_jh7110_variant }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_wdt_match); + +static struct platform_driver starfive_wdt_driver = { + .probe = starfive_wdt_probe, + .remove = starfive_wdt_remove, + .shutdown = starfive_wdt_shutdown, + .driver = { + .name = "starfive-wdt", + .pm = &starfive_wdt_pm_ops, + .of_match_table = of_match_ptr(starfive_wdt_match), + }, +}; +module_platform_driver(starfive_wdt_driver); + +MODULE_AUTHOR("Xingyu Wu "); +MODULE_AUTHOR("Samin Guo "); +MODULE_DESCRIPTION("StarFive Watchdog Device Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/dt-bindings/clock/starfive,jh7110-crg.h b/include/dt-bindings/clock/starfive,jh7110-crg.h new file mode 100644 index 0000000000000..06f41cd9fc8a3 --- /dev/null +++ b/include/dt-bindings/clock/starfive,jh7110-crg.h @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2022 Emil Renner Berthing + * Copyright 2022 StarFive Technology Co., Ltd. + */ + +#ifndef __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ +#define __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ + +/* PLL clocks */ +#define JH7110_CLK_PLL0_OUT 0 +#define JH7110_CLK_PLL1_OUT 1 +#define JH7110_CLK_PLL2_OUT 2 +#define JH7110_PLLCLK_END 3 + +/* SYSCRG clocks */ +#define JH7110_SYSCLK_CPU_ROOT 0 +#define JH7110_SYSCLK_CPU_CORE 1 +#define JH7110_SYSCLK_CPU_BUS 2 +#define JH7110_SYSCLK_GPU_ROOT 3 +#define JH7110_SYSCLK_PERH_ROOT 4 +#define JH7110_SYSCLK_BUS_ROOT 5 +#define JH7110_SYSCLK_NOCSTG_BUS 6 +#define JH7110_SYSCLK_AXI_CFG0 7 +#define JH7110_SYSCLK_STG_AXIAHB 8 +#define JH7110_SYSCLK_AHB0 9 +#define JH7110_SYSCLK_AHB1 10 +#define JH7110_SYSCLK_APB_BUS 11 +#define JH7110_SYSCLK_APB0 12 +#define JH7110_SYSCLK_PLL0_DIV2 13 +#define JH7110_SYSCLK_PLL1_DIV2 14 +#define JH7110_SYSCLK_PLL2_DIV2 15 +#define JH7110_SYSCLK_AUDIO_ROOT 16 +#define JH7110_SYSCLK_MCLK_INNER 17 +#define JH7110_SYSCLK_MCLK 18 +#define JH7110_SYSCLK_MCLK_OUT 19 +#define JH7110_SYSCLK_ISP_2X 20 +#define JH7110_SYSCLK_ISP_AXI 21 +#define JH7110_SYSCLK_GCLK0 22 +#define JH7110_SYSCLK_GCLK1 23 +#define JH7110_SYSCLK_GCLK2 24 +#define JH7110_SYSCLK_CORE 25 +#define JH7110_SYSCLK_CORE1 26 +#define JH7110_SYSCLK_CORE2 27 +#define JH7110_SYSCLK_CORE3 28 +#define JH7110_SYSCLK_CORE4 29 +#define JH7110_SYSCLK_DEBUG 30 +#define JH7110_SYSCLK_RTC_TOGGLE 31 +#define JH7110_SYSCLK_TRACE0 32 +#define JH7110_SYSCLK_TRACE1 33 +#define JH7110_SYSCLK_TRACE2 34 +#define JH7110_SYSCLK_TRACE3 35 +#define JH7110_SYSCLK_TRACE4 36 +#define JH7110_SYSCLK_TRACE_COM 37 +#define JH7110_SYSCLK_NOC_BUS_CPU_AXI 38 +#define JH7110_SYSCLK_NOC_BUS_AXICFG0_AXI 39 +#define JH7110_SYSCLK_OSC_DIV2 40 +#define JH7110_SYSCLK_PLL1_DIV4 41 +#define JH7110_SYSCLK_PLL1_DIV8 42 +#define JH7110_SYSCLK_DDR_BUS 43 +#define JH7110_SYSCLK_DDR_AXI 44 +#define JH7110_SYSCLK_GPU_CORE 45 +#define JH7110_SYSCLK_GPU_CORE_CLK 46 +#define JH7110_SYSCLK_GPU_SYS_CLK 47 +#define JH7110_SYSCLK_GPU_APB 48 +#define JH7110_SYSCLK_GPU_RTC_TOGGLE 49 +#define JH7110_SYSCLK_NOC_BUS_GPU_AXI 50 +#define JH7110_SYSCLK_ISP_TOP_CORE 51 +#define JH7110_SYSCLK_ISP_TOP_AXI 52 +#define JH7110_SYSCLK_NOC_BUS_ISP_AXI 53 +#define JH7110_SYSCLK_HIFI4_CORE 54 +#define JH7110_SYSCLK_HIFI4_AXI 55 +#define JH7110_SYSCLK_AXI_CFG1_MAIN 56 +#define JH7110_SYSCLK_AXI_CFG1_AHB 57 +#define JH7110_SYSCLK_VOUT_SRC 58 +#define JH7110_SYSCLK_VOUT_AXI 59 +#define JH7110_SYSCLK_NOC_BUS_DISP_AXI 60 +#define JH7110_SYSCLK_VOUT_TOP_AHB 61 +#define JH7110_SYSCLK_VOUT_TOP_AXI 62 +#define JH7110_SYSCLK_VOUT_TOP_HDMITX0_MCLK 63 +#define JH7110_SYSCLK_VOUT_TOP_MIPIPHY_REF 64 +#define JH7110_SYSCLK_JPEGC_AXI 65 +#define JH7110_SYSCLK_CODAJ12_AXI 66 +#define JH7110_SYSCLK_CODAJ12_CORE 67 +#define JH7110_SYSCLK_CODAJ12_APB 68 +#define JH7110_SYSCLK_VDEC_AXI 69 +#define JH7110_SYSCLK_WAVE511_AXI 70 +#define JH7110_SYSCLK_WAVE511_BPU 71 +#define JH7110_SYSCLK_WAVE511_VCE 72 +#define JH7110_SYSCLK_WAVE511_APB 73 +#define JH7110_SYSCLK_VDEC_JPG 74 +#define JH7110_SYSCLK_VDEC_MAIN 75 +#define JH7110_SYSCLK_NOC_BUS_VDEC_AXI 76 +#define JH7110_SYSCLK_VENC_AXI 77 +#define JH7110_SYSCLK_WAVE420L_AXI 78 +#define JH7110_SYSCLK_WAVE420L_BPU 79 +#define JH7110_SYSCLK_WAVE420L_VCE 80 +#define JH7110_SYSCLK_WAVE420L_APB 81 +#define JH7110_SYSCLK_NOC_BUS_VENC_AXI 82 +#define JH7110_SYSCLK_AXI_CFG0_MAIN_DIV 83 +#define JH7110_SYSCLK_AXI_CFG0_MAIN 84 +#define JH7110_SYSCLK_AXI_CFG0_HIFI4 85 +#define JH7110_SYSCLK_AXIMEM2_AXI 86 +#define JH7110_SYSCLK_QSPI_AHB 87 +#define JH7110_SYSCLK_QSPI_APB 88 +#define JH7110_SYSCLK_QSPI_REF_SRC 89 +#define JH7110_SYSCLK_QSPI_REF 90 +#define JH7110_SYSCLK_SDIO0_AHB 91 +#define JH7110_SYSCLK_SDIO1_AHB 92 +#define JH7110_SYSCLK_SDIO0_SDCARD 93 +#define JH7110_SYSCLK_SDIO1_SDCARD 94 +#define JH7110_SYSCLK_USB_125M 95 +#define JH7110_SYSCLK_NOC_BUS_STG_AXI 96 +#define JH7110_SYSCLK_GMAC1_AHB 97 +#define JH7110_SYSCLK_GMAC1_AXI 98 +#define JH7110_SYSCLK_GMAC_SRC 99 +#define JH7110_SYSCLK_GMAC1_GTXCLK 100 +#define JH7110_SYSCLK_GMAC1_RMII_RTX 101 +#define JH7110_SYSCLK_GMAC1_PTP 102 +#define JH7110_SYSCLK_GMAC1_RX 103 +#define JH7110_SYSCLK_GMAC1_RX_INV 104 +#define JH7110_SYSCLK_GMAC1_TX 105 +#define JH7110_SYSCLK_GMAC1_TX_INV 106 +#define JH7110_SYSCLK_GMAC1_GTXC 107 +#define JH7110_SYSCLK_GMAC0_GTXCLK 108 +#define JH7110_SYSCLK_GMAC0_PTP 109 +#define JH7110_SYSCLK_GMAC_PHY 110 +#define JH7110_SYSCLK_GMAC0_GTXC 111 +#define JH7110_SYSCLK_IOMUX_APB 112 +#define JH7110_SYSCLK_MAILBOX_APB 113 +#define JH7110_SYSCLK_INT_CTRL_APB 114 +#define JH7110_SYSCLK_CAN0_APB 115 +#define JH7110_SYSCLK_CAN0_TIMER 116 +#define JH7110_SYSCLK_CAN0_CAN 117 +#define JH7110_SYSCLK_CAN1_APB 118 +#define JH7110_SYSCLK_CAN1_TIMER 119 +#define JH7110_SYSCLK_CAN1_CAN 120 +#define JH7110_SYSCLK_PWM_APB 121 +#define JH7110_SYSCLK_WDT_APB 122 +#define JH7110_SYSCLK_WDT_CORE 123 +#define JH7110_SYSCLK_TIMER_APB 124 +#define JH7110_SYSCLK_TIMER0 125 +#define JH7110_SYSCLK_TIMER1 126 +#define JH7110_SYSCLK_TIMER2 127 +#define JH7110_SYSCLK_TIMER3 128 +#define JH7110_SYSCLK_TEMP_APB 129 +#define JH7110_SYSCLK_TEMP_CORE 130 +#define JH7110_SYSCLK_SPI0_APB 131 +#define JH7110_SYSCLK_SPI1_APB 132 +#define JH7110_SYSCLK_SPI2_APB 133 +#define JH7110_SYSCLK_SPI3_APB 134 +#define JH7110_SYSCLK_SPI4_APB 135 +#define JH7110_SYSCLK_SPI5_APB 136 +#define JH7110_SYSCLK_SPI6_APB 137 +#define JH7110_SYSCLK_I2C0_APB 138 +#define JH7110_SYSCLK_I2C1_APB 139 +#define JH7110_SYSCLK_I2C2_APB 140 +#define JH7110_SYSCLK_I2C3_APB 141 +#define JH7110_SYSCLK_I2C4_APB 142 +#define JH7110_SYSCLK_I2C5_APB 143 +#define JH7110_SYSCLK_I2C6_APB 144 +#define JH7110_SYSCLK_UART0_APB 145 +#define JH7110_SYSCLK_UART0_CORE 146 +#define JH7110_SYSCLK_UART1_APB 147 +#define JH7110_SYSCLK_UART1_CORE 148 +#define JH7110_SYSCLK_UART2_APB 149 +#define JH7110_SYSCLK_UART2_CORE 150 +#define JH7110_SYSCLK_UART3_APB 151 +#define JH7110_SYSCLK_UART3_CORE 152 +#define JH7110_SYSCLK_UART4_APB 153 +#define JH7110_SYSCLK_UART4_CORE 154 +#define JH7110_SYSCLK_UART5_APB 155 +#define JH7110_SYSCLK_UART5_CORE 156 +#define JH7110_SYSCLK_PWMDAC_APB 157 +#define JH7110_SYSCLK_PWMDAC_CORE 158 +#define JH7110_SYSCLK_SPDIF_APB 159 +#define JH7110_SYSCLK_SPDIF_CORE 160 +#define JH7110_SYSCLK_I2STX0_APB 161 +#define JH7110_SYSCLK_I2STX0_BCLK_MST 162 +#define JH7110_SYSCLK_I2STX0_BCLK_MST_INV 163 +#define JH7110_SYSCLK_I2STX0_LRCK_MST 164 +#define JH7110_SYSCLK_I2STX0_BCLK 165 +#define JH7110_SYSCLK_I2STX0_BCLK_INV 166 +#define JH7110_SYSCLK_I2STX0_LRCK 167 +#define JH7110_SYSCLK_I2STX1_APB 168 +#define JH7110_SYSCLK_I2STX1_BCLK_MST 169 +#define JH7110_SYSCLK_I2STX1_BCLK_MST_INV 170 +#define JH7110_SYSCLK_I2STX1_LRCK_MST 171 +#define JH7110_SYSCLK_I2STX1_BCLK 172 +#define JH7110_SYSCLK_I2STX1_BCLK_INV 173 +#define JH7110_SYSCLK_I2STX1_LRCK 174 +#define JH7110_SYSCLK_I2SRX_APB 175 +#define JH7110_SYSCLK_I2SRX_BCLK_MST 176 +#define JH7110_SYSCLK_I2SRX_BCLK_MST_INV 177 +#define JH7110_SYSCLK_I2SRX_LRCK_MST 178 +#define JH7110_SYSCLK_I2SRX_BCLK 179 +#define JH7110_SYSCLK_I2SRX_BCLK_INV 180 +#define JH7110_SYSCLK_I2SRX_LRCK 181 +#define JH7110_SYSCLK_PDM_DMIC 182 +#define JH7110_SYSCLK_PDM_APB 183 +#define JH7110_SYSCLK_TDM_AHB 184 +#define JH7110_SYSCLK_TDM_APB 185 +#define JH7110_SYSCLK_TDM_INTERNAL 186 +#define JH7110_SYSCLK_TDM_TDM 187 +#define JH7110_SYSCLK_TDM_TDM_INV 188 +#define JH7110_SYSCLK_JTAG_CERTIFICATION_TRNG 189 + +#define JH7110_SYSCLK_END 190 + +/* AONCRG clocks */ +#define JH7110_AONCLK_OSC_DIV4 0 +#define JH7110_AONCLK_APB_FUNC 1 +#define JH7110_AONCLK_GMAC0_AHB 2 +#define JH7110_AONCLK_GMAC0_AXI 3 +#define JH7110_AONCLK_GMAC0_RMII_RTX 4 +#define JH7110_AONCLK_GMAC0_TX 5 +#define JH7110_AONCLK_GMAC0_TX_INV 6 +#define JH7110_AONCLK_GMAC0_RX 7 +#define JH7110_AONCLK_GMAC0_RX_INV 8 +#define JH7110_AONCLK_OTPC_APB 9 +#define JH7110_AONCLK_RTC_APB 10 +#define JH7110_AONCLK_RTC_INTERNAL 11 +#define JH7110_AONCLK_RTC_32K 12 +#define JH7110_AONCLK_RTC_CAL 13 + +#define JH7110_AONCLK_END 14 + +/* STGCRG clocks */ +#define JH7110_STGCLK_HIFI4_CLK_CORE 0 +#define JH7110_STGCLK_USB0_APB 1 +#define JH7110_STGCLK_USB0_UTMI_APB 2 +#define JH7110_STGCLK_USB0_AXI 3 +#define JH7110_STGCLK_USB0_LPM 4 +#define JH7110_STGCLK_USB0_STB 5 +#define JH7110_STGCLK_USB0_APP_125 6 +#define JH7110_STGCLK_USB0_REFCLK 7 +#define JH7110_STGCLK_PCIE0_AXI_MST0 8 +#define JH7110_STGCLK_PCIE0_APB 9 +#define JH7110_STGCLK_PCIE0_TL 10 +#define JH7110_STGCLK_PCIE1_AXI_MST0 11 +#define JH7110_STGCLK_PCIE1_APB 12 +#define JH7110_STGCLK_PCIE1_TL 13 +#define JH7110_STGCLK_PCIE_SLV_MAIN 14 +#define JH7110_STGCLK_SEC_AHB 15 +#define JH7110_STGCLK_SEC_MISC_AHB 16 +#define JH7110_STGCLK_GRP0_MAIN 17 +#define JH7110_STGCLK_GRP0_BUS 18 +#define JH7110_STGCLK_GRP0_STG 19 +#define JH7110_STGCLK_GRP1_MAIN 20 +#define JH7110_STGCLK_GRP1_BUS 21 +#define JH7110_STGCLK_GRP1_STG 22 +#define JH7110_STGCLK_GRP1_HIFI 23 +#define JH7110_STGCLK_E2_RTC 24 +#define JH7110_STGCLK_E2_CORE 25 +#define JH7110_STGCLK_E2_DBG 26 +#define JH7110_STGCLK_DMA1P_AXI 27 +#define JH7110_STGCLK_DMA1P_AHB 28 + +#define JH7110_STGCLK_END 29 + +/* ISPCRG clocks */ +#define JH7110_ISPCLK_DOM4_APB_FUNC 0 +#define JH7110_ISPCLK_MIPI_RX0_PXL 1 +#define JH7110_ISPCLK_DVP_INV 2 +#define JH7110_ISPCLK_M31DPHY_CFG_IN 3 +#define JH7110_ISPCLK_M31DPHY_REF_IN 4 +#define JH7110_ISPCLK_M31DPHY_TX_ESC_LAN0 5 +#define JH7110_ISPCLK_VIN_ABP 6 +#define JH7110_ISPCLK_VIN_SYS 7 +#define JH7110_ISPCLK_VIN_PIXEL_IF0 8 +#define JH7110_ISPCLK_VIN_PIXEL_IF1 9 +#define JH7110_ISPCLK_VIN_PIXEL_IF2 10 +#define JH7110_ISPCLK_VIN_PIXEL_IF3 11 +#define JH7110_ISPCLK_VIN_P_AXI_WR 12 +#define JH7110_ISPCLK_ISPV2_TOP_WRAPPER_C 13 + +#define JH7110_ISPCLK_END 14 + +/* VOUTCRG clocks */ +#define JH7110_VOUTCLK_APB 0 +#define JH7110_VOUTCLK_DC8200_PIX 1 +#define JH7110_VOUTCLK_DSI_SYS 2 +#define JH7110_VOUTCLK_TX_ESC 3 +#define JH7110_VOUTCLK_DC8200_AXI 4 +#define JH7110_VOUTCLK_DC8200_CORE 5 +#define JH7110_VOUTCLK_DC8200_AHB 6 +#define JH7110_VOUTCLK_DC8200_PIX0 7 +#define JH7110_VOUTCLK_DC8200_PIX1 8 +#define JH7110_VOUTCLK_DOM_VOUT_TOP_LCD 9 +#define JH7110_VOUTCLK_DSITX_APB 10 +#define JH7110_VOUTCLK_DSITX_SYS 11 +#define JH7110_VOUTCLK_DSITX_DPI 12 +#define JH7110_VOUTCLK_DSITX_TXESC 13 +#define JH7110_VOUTCLK_MIPITX_DPHY_TXESC 14 +#define JH7110_VOUTCLK_HDMI_TX_MCLK 15 +#define JH7110_VOUTCLK_HDMI_TX_BCLK 16 +#define JH7110_VOUTCLK_HDMI_TX_SYS 17 + +#define JH7110_VOUTCLK_END 18 + +#endif /* __DT_BINDINGS_CLOCK_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/dt-bindings/reset/starfive,jh7110-crg.h b/include/dt-bindings/reset/starfive,jh7110-crg.h new file mode 100644 index 0000000000000..57857a4ac7b2f --- /dev/null +++ b/include/dt-bindings/reset/starfive,jh7110-crg.h @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright (C) 2022 Emil Renner Berthing + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#ifndef __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ +#define __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ + +/* SYSCRG resets */ +#define JH7110_SYSRST_JTAG_APB 0 +#define JH7110_SYSRST_SYSCON_APB 1 +#define JH7110_SYSRST_IOMUX_APB 2 +#define JH7110_SYSRST_BUS 3 +#define JH7110_SYSRST_DEBUG 4 +#define JH7110_SYSRST_CORE0 5 +#define JH7110_SYSRST_CORE1 6 +#define JH7110_SYSRST_CORE2 7 +#define JH7110_SYSRST_CORE3 8 +#define JH7110_SYSRST_CORE4 9 +#define JH7110_SYSRST_CORE0_ST 10 +#define JH7110_SYSRST_CORE1_ST 11 +#define JH7110_SYSRST_CORE2_ST 12 +#define JH7110_SYSRST_CORE3_ST 13 +#define JH7110_SYSRST_CORE4_ST 14 +#define JH7110_SYSRST_TRACE0 15 +#define JH7110_SYSRST_TRACE1 16 +#define JH7110_SYSRST_TRACE2 17 +#define JH7110_SYSRST_TRACE3 18 +#define JH7110_SYSRST_TRACE4 19 +#define JH7110_SYSRST_TRACE_COM 20 +#define JH7110_SYSRST_GPU_APB 21 +#define JH7110_SYSRST_GPU_DOMA 22 +#define JH7110_SYSRST_NOC_BUS_APB 23 +#define JH7110_SYSRST_NOC_BUS_AXICFG0_AXI 24 +#define JH7110_SYSRST_NOC_BUS_CPU_AXI 25 +#define JH7110_SYSRST_NOC_BUS_DISP_AXI 26 +#define JH7110_SYSRST_NOC_BUS_GPU_AXI 27 +#define JH7110_SYSRST_NOC_BUS_ISP_AXI 28 +#define JH7110_SYSRST_NOC_BUS_DDRC 29 +#define JH7110_SYSRST_NOC_BUS_STG_AXI 30 +#define JH7110_SYSRST_NOC_BUS_VDEC_AXI 31 + +#define JH7110_SYSRST_NOC_BUS_VENC_AXI 32 +#define JH7110_SYSRST_AXI_CFG1_AHB 33 +#define JH7110_SYSRST_AXI_CFG1_MAIN 34 +#define JH7110_SYSRST_AXI_CFG0_MAIN 35 +#define JH7110_SYSRST_AXI_CFG0_MAIN_DIV 36 +#define JH7110_SYSRST_AXI_CFG0_HIFI4 37 +#define JH7110_SYSRST_DDR_AXI 38 +#define JH7110_SYSRST_DDR_OSC 39 +#define JH7110_SYSRST_DDR_APB 40 +#define JH7110_SYSRST_ISP_TOP 41 +#define JH7110_SYSRST_ISP_TOP_AXI 42 +#define JH7110_SYSRST_VOUT_TOP_SRC 43 +#define JH7110_SYSRST_CODAJ12_AXI 44 +#define JH7110_SYSRST_CODAJ12_CORE 45 +#define JH7110_SYSRST_CODAJ12_APB 46 +#define JH7110_SYSRST_WAVE511_AXI 47 +#define JH7110_SYSRST_WAVE511_BPU 48 +#define JH7110_SYSRST_WAVE511_VCE 49 +#define JH7110_SYSRST_WAVE511_APB 50 +#define JH7110_SYSRST_VDEC_JPG 51 +#define JH7110_SYSRST_VDEC_MAIN 52 +#define JH7110_SYSRST_AXIMEM0_AXI 53 +#define JH7110_SYSRST_WAVE420L_AXI 54 +#define JH7110_SYSRST_WAVE420L_BPU 55 +#define JH7110_SYSRST_WAVE420L_VCE 56 +#define JH7110_SYSRST_WAVE420L_APB 57 +#define JH7110_SYSRST_AXIMEM1_AXI 58 +#define JH7110_SYSRST_AXIMEM2_AXI 59 +#define JH7110_SYSRST_INTMEM 60 +#define JH7110_SYSRST_QSPI_AHB 61 +#define JH7110_SYSRST_QSPI_APB 62 +#define JH7110_SYSRST_QSPI_REF 63 + +#define JH7110_SYSRST_SDIO0_AHB 64 +#define JH7110_SYSRST_SDIO1_AHB 65 +#define JH7110_SYSRST_GMAC1_AXI 66 +#define JH7110_SYSRST_GMAC1_AHB 67 +#define JH7110_SYSRST_MAILBOX_APB 68 +#define JH7110_SYSRST_SPI0_APB 69 +#define JH7110_SYSRST_SPI1_APB 70 +#define JH7110_SYSRST_SPI2_APB 71 +#define JH7110_SYSRST_SPI3_APB 72 +#define JH7110_SYSRST_SPI4_APB 73 +#define JH7110_SYSRST_SPI5_APB 74 +#define JH7110_SYSRST_SPI6_APB 75 +#define JH7110_SYSRST_I2C0_APB 76 +#define JH7110_SYSRST_I2C1_APB 77 +#define JH7110_SYSRST_I2C2_APB 78 +#define JH7110_SYSRST_I2C3_APB 79 +#define JH7110_SYSRST_I2C4_APB 80 +#define JH7110_SYSRST_I2C5_APB 81 +#define JH7110_SYSRST_I2C6_APB 82 +#define JH7110_SYSRST_UART0_APB 83 +#define JH7110_SYSRST_UART0_CORE 84 +#define JH7110_SYSRST_UART1_APB 85 +#define JH7110_SYSRST_UART1_CORE 86 +#define JH7110_SYSRST_UART2_APB 87 +#define JH7110_SYSRST_UART2_CORE 88 +#define JH7110_SYSRST_UART3_APB 89 +#define JH7110_SYSRST_UART3_CORE 90 +#define JH7110_SYSRST_UART4_APB 91 +#define JH7110_SYSRST_UART4_CORE 92 +#define JH7110_SYSRST_UART5_APB 93 +#define JH7110_SYSRST_UART5_CORE 94 +#define JH7110_SYSRST_SPDIF_APB 95 + +#define JH7110_SYSRST_PWMDAC_APB 96 +#define JH7110_SYSRST_PDM_DMIC 97 +#define JH7110_SYSRST_PDM_APB 98 +#define JH7110_SYSRST_I2SRX_APB 99 +#define JH7110_SYSRST_I2SRX_BCLK 100 +#define JH7110_SYSRST_I2STX0_APB 101 +#define JH7110_SYSRST_I2STX0_BCLK 102 +#define JH7110_SYSRST_I2STX1_APB 103 +#define JH7110_SYSRST_I2STX1_BCLK 104 +#define JH7110_SYSRST_TDM_AHB 105 +#define JH7110_SYSRST_TDM_CORE 106 +#define JH7110_SYSRST_TDM_APB 107 +#define JH7110_SYSRST_PWM_APB 108 +#define JH7110_SYSRST_WDT_APB 109 +#define JH7110_SYSRST_WDT_CORE 110 +#define JH7110_SYSRST_CAN0_APB 111 +#define JH7110_SYSRST_CAN0_CORE 112 +#define JH7110_SYSRST_CAN0_TIMER 113 +#define JH7110_SYSRST_CAN1_APB 114 +#define JH7110_SYSRST_CAN1_CORE 115 +#define JH7110_SYSRST_CAN1_TIMER 116 +#define JH7110_SYSRST_TIMER_APB 117 +#define JH7110_SYSRST_TIMER0 118 +#define JH7110_SYSRST_TIMER1 119 +#define JH7110_SYSRST_TIMER2 120 +#define JH7110_SYSRST_TIMER3 121 +#define JH7110_SYSRST_INT_CTRL_APB 122 +#define JH7110_SYSRST_TEMP_APB 123 +#define JH7110_SYSRST_TEMP_CORE 124 +#define JH7110_SYSRST_JTAG_CERTIFICATION 125 + +#define JH7110_SYSRST_END 126 + +/* AONCRG resets */ +#define JH7110_AONRST_GMAC0_AXI 0 +#define JH7110_AONRST_GMAC0_AHB 1 +#define JH7110_AONRST_IOMUX 2 +#define JH7110_AONRST_PMU_APB 3 +#define JH7110_AONRST_PMU_WKUP 4 +#define JH7110_AONRST_RTC_APB 5 +#define JH7110_AONRST_RTC_CAL 6 +#define JH7110_AONRST_RTC_32K 7 + +#define JH7110_AONRST_END 8 + +/* STGCRG resets */ +#define JH7110_STGRST_SYSCON 0 +#define JH7110_STGRST_HIFI4_CORE 1 +#define JH7110_STGRST_HIFI4_AXI 2 +#define JH7110_STGRST_SEC_AHB 3 +#define JH7110_STGRST_E24_CORE 4 +#define JH7110_STGRST_DMA1P_AXI 5 +#define JH7110_STGRST_DMA1P_AHB 6 +#define JH7110_STGRST_USB0_AXI 7 +#define JH7110_STGRST_USB0_APB 8 +#define JH7110_STGRST_USB0_UTMI_APB 9 +#define JH7110_STGRST_USB0_PWRUP 10 +#define JH7110_STGRST_PCIE0_AXI_MST0 11 +#define JH7110_STGRST_PCIE0_AXI_SLV0 12 +#define JH7110_STGRST_PCIE0_AXI_SLV 13 +#define JH7110_STGRST_PCIE0_BRG 14 +#define JH7110_STGRST_PCIE0_CORE 15 +#define JH7110_STGRST_PCIE0_APB 16 +#define JH7110_STGRST_PCIE1_AXI_MST0 17 +#define JH7110_STGRST_PCIE1_AXI_SLV0 18 +#define JH7110_STGRST_PCIE1_AXI_SLV 19 +#define JH7110_STGRST_PCIE1_BRG 20 +#define JH7110_STGRST_PCIE1_CORE 21 +#define JH7110_STGRST_PCIE1_APB 22 + +#define JH7110_STGRST_END 23 + +/* ISPCRG resets */ +#define JH7110_ISPRST_ISPV2_TOP_WRAPPER_P 0 +#define JH7110_ISPRST_ISPV2_TOP_WRAPPER_C 1 +#define JH7110_ISPRST_M31DPHY_HW 2 +#define JH7110_ISPRST_M31DPHY_B09_AON 3 +#define JH7110_ISPRST_VIN_ABP 4 +#define JH7110_ISPRST_VIN_PIXEL_IF0 5 +#define JH7110_ISPRST_VIN_PIXEL_IF1 6 +#define JH7110_ISPRST_VIN_PIXEL_IF2 7 +#define JH7110_ISPRST_VIN_PIXEL_IF3 8 +#define JH7110_ISPRST_VIN_SYS 9 +#define JH7110_ISPRST_VIN_P_AXI_RD 10 +#define JH7110_ISPRST_VIN_P_AXI_WR 11 + +#define JH7110_ISPRST_END 12 + +/* VOUTCRG resets */ +#define JH7110_VOUTRST_DC8200_AXI 0 +#define JH7110_VOUTRST_DC8200_AHB 1 +#define JH7110_VOUTRST_DC8200_CORE 2 +#define JH7110_VOUTRST_DSITX_DPI 3 +#define JH7110_VOUTRST_DSITX_APB 4 +#define JH7110_VOUTRST_DSITX_RXESC 5 +#define JH7110_VOUTRST_DSITX_SYS 6 +#define JH7110_VOUTRST_DSITX_TXBYTEHS 7 +#define JH7110_VOUTRST_DSITX_TXESC 8 +#define JH7110_VOUTRST_HDMI_TX_HDMI 9 +#define JH7110_VOUTRST_MIPITX_DPHY_SYS 10 +#define JH7110_VOUTRST_MIPITX_DPHY_TXBYTEHS 11 + +#define JH7110_VOUTRST_END 12 + +#endif /* __DT_BINDINGS_RESET_STARFIVE_JH7110_CRG_H__ */ diff --git a/include/uapi/linux/stf_isp_ioctl.h b/include/uapi/linux/stf_isp_ioctl.h new file mode 100644 index 0000000000000..84a584af3ab8f --- /dev/null +++ b/include/uapi/linux/stf_isp_ioctl.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * stf_isp_ioctl.h + * + * StarFive Camera Subsystem - ISP Module + * + * Copyright (C) 2021-2023 StarFive Technology Co., Ltd. + */ + +#ifndef STF_ISP_IOCTL_H +#define STF_ISP_IOCTL_H + +#include + +#define FILENAME_MAX_LEN 30 + +#define ISP_IOC ('V') +#define STF_ISP_REG_BUF_SIZE (768) +#define STF_ISP_REG_TBL_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2) +#define STF_ISP_REG_TBL_2_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 3) +#define STF_ISP_REG_TBL_3_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 4) +#define STF_ISP_REG_SMPL_PACK_BUF_SIZE (STF_ISP_REG_BUF_SIZE / 2) +#define RDMA_WR_ONE (0xA0) +#define RDMA_WR_SRL (0xA1) +#define RDMA_LINK (0xA2) +#define RDMA_SINT (0xA3) +#define RDMA_END (0xAF) +#define ENABLE_SS0_SS1 + +enum _STF_ISP_IOCTL { + STF_ISP_IOCTL_LOAD_FW = BASE_VIDIOC_PRIVATE + 1, + STF_ISP_IOCTL_DMABUF_ALLOC, + STF_ISP_IOCTL_DMABUF_FREE, + STF_ISP_IOCTL_GET_HW_VER, + STF_ISP_IOCTL_REG, + STF_ISP_IOCTL_SHADOW_LOCK, + STF_ISP_IOCTL_SHADOW_UNLOCK, + STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER, + STF_ISP_IOCTL_SET_USER_CONFIG_ISP, + STF_ISP_IOCTL_MAX +}; + +enum _STF_ISP_REG_METHOD { + STF_ISP_REG_METHOD_ONE_REG = 0, + STF_ISP_REG_METHOD_SERIES, + STF_ISP_REG_METHOD_MODULE, + STF_ISP_REG_METHOD_TABLE, + STF_ISP_REG_METHOD_TABLE_2, + STF_ISP_REG_METHOD_TABLE_3, + STF_ISP_REG_METHOD_SMPL_PACK, + STF_ISP_REG_METHOD_SOFT_RDMA, + STF_ISP_REG_METHOD_MAX +}; + +struct stfisp_fw_info { + char __user filename[FILENAME_MAX_LEN]; +}; + +struct dmabuf_create { + __u32 fd; + __u32 size; + __u32 paddr; +}; + +struct isp_rdma_info { + u32 param; + union { + u32 value; + struct { + u32 offset : 24; + u32 tag : 8; + }; + }; +}; + +struct isp_reg_info { + /* access method of register */ + u8 method; + /* offset indicated which register will be read/write */ + u32 offset; + /* length for indicated how much register will be read/write */ + u32 length; +}; + +union reg_buf { + u32 buffer[STF_ISP_REG_BUF_SIZE]; + struct { + u32 offset; + u32 value; + } reg_tbl[STF_ISP_REG_TBL_BUF_SIZE]; + struct { + u32 offset; + u32 value; + u32 mask; + } reg_tbl2[STF_ISP_REG_TBL_2_BUF_SIZE]; + struct { + u32 offset; + u32 value; + u32 mask; + u32 delay_ms; + } reg_tbl3[STF_ISP_REG_TBL_3_BUF_SIZE]; + struct isp_rdma_info rdma_cmd[STF_ISP_REG_SMPL_PACK_BUF_SIZE]; +}; + +struct isp_reg_param { + /* register read/write information */ + struct isp_reg_info reg_info; + /* buffer */ + union reg_buf *reg_buf; +}; + +#define VIDIOC_STFISP_LOAD_FW \ + _IOW(ISP_IOC, STF_ISP_IOCTL_LOAD_FW, struct stfisp_fw_info) +#define VIDIOC_STFISP_GET_REG \ + _IOWR(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param) +#define VIDIOC_STFISP_SET_REG \ + _IOW(ISP_IOC, STF_ISP_IOCTL_REG, struct isp_reg_param) +#define VIDIOC_STFISP_SHADOW_LOCK \ + _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_LOCK) +#define VIDIOC_STFISP_SHADOW_UNLOCK \ + _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK) +#define VIDIOC_STFISP_SHADOW_UNLOCK_N_TRIGGER \ + _IO(ISP_IOC, STF_ISP_IOCTL_SHADOW_UNLOCK_N_TRIGGER) +#define VIDIOC_STFISP_SET_USER_CONFIG_ISP \ + _IO(ISP_IOC, STF_ISP_IOCTL_SET_USER_CONFIG_ISP) + +#endif /* STF_ISP_IOCTL_H */