]> git.baikalelectronics.ru Git - kernel.git/commitdiff
SDK 6.2 baikal/aarch64/6.1.31.y baikal/aarch64/sdk6.2
authorBaikal Electronics <support@baikalelectronics.ru>
Wed, 5 Jul 2023 11:04:36 +0000 (14:04 +0300)
committerBaikal Electronics <support@baikalelectronics.ru>
Mon, 10 Jul 2023 13:35:42 +0000 (16:35 +0300)
127 files changed:
arch/arm64/Kconfig.platforms
arch/arm64/boot/dts/Makefile
arch/arm64/boot/dts/baikal/Makefile [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-coresight.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-dbm.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-dbm10.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-dbm20.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-mbm.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-mbm10.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-mbm20.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000-qemu-m.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bm1000.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-clocks.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-coresight.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-dbs-mezzanine-espi.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-dbs-mezzanine-qspi2.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-dbs-ov.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-dbs.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-dbs.dtsi [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000-qemu-s.dts [new file with mode: 0644]
arch/arm64/boot/dts/baikal/bs1000.dtsi [new file with mode: 0644]
arch/arm64/configs/baikal_defconfig [new file with mode: 0644]
drivers/acpi/pci_mcfg.c
drivers/ata/ahci_dwc.c
drivers/clk/Makefile
drivers/clk/baikal/Makefile [new file with mode: 0644]
drivers/clk/baikal/clk-bm1000.c [new file with mode: 0644]
drivers/clk/baikal/clk-bs1000.c [new file with mode: 0644]
drivers/clocksource/Kconfig
drivers/clocksource/Makefile
drivers/clocksource/baikal_dw_apb_timer.c [new file with mode: 0644]
drivers/cpufreq/Kconfig.arm
drivers/cpufreq/Makefile
drivers/cpufreq/baikal-cpufreq.c [new file with mode: 0644]
drivers/cpufreq/cpufreq-dt-platdev.c
drivers/dma/dw/acpi.c
drivers/dma/dw/core.c
drivers/dma/dw/internal.h
drivers/dma/dw/of.c
drivers/dma/dw/platform.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/baikal/Kconfig [new file with mode: 0644]
drivers/gpu/drm/baikal/Makefile [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal-hdmi.c [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_backlight.c [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_crtc.c [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_debugfs.c [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_drm.h [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_drv.c [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_panel.c [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_plane.c [new file with mode: 0644]
drivers/gpu/drm/baikal/baikal_vdu_regs.h [new file with mode: 0644]
drivers/gpu/drm/bridge/synopsys/Kconfig
drivers/gpu/drm/bridge/synopsys/Makefile
drivers/gpu/drm/bridge/synopsys/baikal-hdmi-ahb-audio.c [new file with mode: 0644]
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
drivers/gpu/drm/drm_panel.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-baikal-lvds.c [new file with mode: 0644]
drivers/gpu/drm/panfrost/panfrost_devfreq.c
drivers/gpu/drm/panfrost/panfrost_drv.c
drivers/gpu/drm/panfrost/panfrost_gpu.c
drivers/gpu/drm/panfrost/panfrost_job.c
drivers/gpu/drm/panfrost/panfrost_mmu.c
drivers/hwmon/Kconfig
drivers/hwmon/bt1-pvt.c
drivers/hwmon/bt1-pvt.h
drivers/hwtracing/coresight/coresight-cti-platform.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/Makefile
drivers/i2c/busses/i2c-baikal-smbus.c [new file with mode: 0644]
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/tp_bmc.c [new file with mode: 0644]
drivers/mmc/host/sdhci-of-dwcmshc.c
drivers/mmc/host/sdhci.c
drivers/mmc/host/sdhci.h
drivers/mtd/parsers/Kconfig
drivers/mtd/parsers/Makefile
drivers/mtd/parsers/baikal.c [new file with mode: 0644]
drivers/mtd/spi-nor/winbond.c
drivers/mux/Kconfig
drivers/mux/Makefile
drivers/mux/baikal.c [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/Kconfig
drivers/net/ethernet/stmicro/stmmac/Makefile
drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.h [new file with mode: 0644]
drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
drivers/pci/controller/dwc/Kconfig
drivers/pci/controller/dwc/Makefile
drivers/pci/controller/dwc/pcie-baikal-acpi.c [new file with mode: 0644]
drivers/pci/controller/dwc/pcie-baikal-core.c [new file with mode: 0644]
drivers/pci/controller/dwc/pcie-baikal-tune.c [new file with mode: 0644]
drivers/pci/controller/dwc/pcie-baikal.h [new file with mode: 0644]
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/baikal/Kconfig [new file with mode: 0644]
drivers/phy/baikal/Makefile [new file with mode: 0644]
drivers/phy/baikal/baikal-usb-phy.c [new file with mode: 0644]
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-baikal-espi.c [new file with mode: 0644]
drivers/tty/serial/8250/8250_dw.c
include/drm/bridge/dw_hdmi.h
include/drm/drm_panel.h
include/linux/dma/dw.h
include/linux/pci-ecam.h
sound/hda/hdac_controller.c
sound/pci/hda/Kconfig
sound/pci/hda/Makefile
sound/pci/hda/hda_baikal.c [new file with mode: 0644]
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/baikal/Kconfig [new file with mode: 0644]
sound/soc/baikal/Makefile [new file with mode: 0644]
sound/soc/baikal/baikal-i2s.c [new file with mode: 0644]
sound/soc/baikal/baikal-pio-pcm.c [new file with mode: 0644]
sound/soc/baikal/baikal-snd-simple-acpi.c [new file with mode: 0644]
sound/soc/baikal/baikal-snd.c [new file with mode: 0644]
sound/soc/baikal/local.h [new file with mode: 0644]
sound/soc/codecs/nau8822.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/dwc/dwc-i2s.c

index 76580b932e446a130f35b6ebde4b136899ea7b0a..fea21819f6c9d3b33bf09a51ca52acdb594d61b1 100644 (file)
@@ -33,6 +33,17 @@ config ARCH_APPLE
          This enables support for Apple's in-house ARM SoC family, starting
          with the Apple M1.
 
+config ARCH_BAIKAL
+       bool "Baikal Electronics SoC family"
+       select DW_APB_TIMER_OF
+       select GPIOLIB
+       select GPIO_DWAPB
+       select MULTIPLEXER
+       select OF_GPIO
+       select PINCTRL
+       help
+         This enables support for Baikal Electronics SoC family
+
 menuconfig ARCH_BCM
        bool "Broadcom SoC Support"
 
@@ -354,4 +365,15 @@ config ARCH_ZYNQMP
        help
          This enables support for Xilinx ZynqMP Family
 
+# The GPIO number here must be sorted by descending number.
+# In a case of multiplatform kernel we want the highest value
+# required by selected platforms
+config ARCH_NR_GPIO
+    int
+    default 0
+    help
+      Maximum number of GPIOs in the system.
+
+      If unsure, leave the default value.
+
 endmenu # "Platform selection"
index 7b107fa7414bf212827dc9a15c0e010f66453548..b02494b61441ef72b79ca8db2968589fb7887ccf 100644 (file)
@@ -8,6 +8,7 @@ subdir-y += amlogic
 subdir-y += apm
 subdir-y += apple
 subdir-y += arm
+subdir-y += baikal
 subdir-y += bitmain
 subdir-y += broadcom
 subdir-y += cavium
diff --git a/arch/arm64/boot/dts/baikal/Makefile b/arch/arm64/boot/dts/baikal/Makefile
new file mode 100644 (file)
index 0000000..1720115
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_ARCH_BAIKAL) += bm1000-dbm10.dtb
+dtb-$(CONFIG_ARCH_BAIKAL) += bm1000-dbm20.dtb
+dtb-$(CONFIG_ARCH_BAIKAL) += bm1000-mbm10.dtb
+dtb-$(CONFIG_ARCH_BAIKAL) += bm1000-mbm20.dtb
+dtb-$(CONFIG_ARCH_BAIKAL) += bm1000-qemu-m.dtb
+dtb-$(CONFIG_ARCH_BAIKAL) += bs1000-dbs.dtb
+dtb-$(CONFIG_ARCH_BAIKAL) += bs1000-dbs-ov.dtb
+dtb-$(CONFIG_ARCH_BAIKAL) += bs1000-qemu-s.dtb
diff --git a/arch/arm64/boot/dts/baikal/bm1000-coresight.dtsi b/arch/arm64/boot/dts/baikal/bm1000-coresight.dtsi
new file mode 100644 (file)
index 0000000..e4a6f94
--- /dev/null
@@ -0,0 +1,522 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for BE-M1000 SoC CoreSight subsystem
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ */
+
+#include <dt-bindings/arm/coresight-cti-dt.h>
+
+#define CA57_DEBUG(cl, c, base, n)                                             \
+       debug##c: debug@##base##10000 {                                         \
+               compatible = "arm,coresight-cpu-debug", "arm,primecell";        \
+               reg = <0x0 0x##base##10000 0x0 0x1000>;                         \
+               clocks = <&cs_pclk>;                                            \
+               clock-names = "apb_pclk";                                       \
+               cpu = <&cpu##c>;                                                \
+       };                                                                      \
+                                                                               \
+       etm##c: etm@##base##40000 {                                             \
+               compatible = "arm,coresight-etm4x", "arm,primecell";            \
+               reg = <0x0 0x##base##40000 0x0 0x1000>;                         \
+               clocks = <&cs_pclk>, <&cs_atclk>;                               \
+               clock-names = "apb_pclk", "atclk";                              \
+               cpu = <&cpu##c>;                                                \
+                                                                               \
+               out-ports {                                                     \
+                       port {                                                  \
+                               etm##c##_out_port: endpoint {                   \
+                                       remote-endpoint =                       \
+                                               <&funnel##cl##_in_port##n>;     \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+       };                                                                      \
+                                                                               \
+       cti##cl##_##n: cti@##base##20000 {                                      \
+               compatible = "arm,coresight-cti-v8-arch", "arm,coresight-cti",  \
+                            "arm,primecell";                                   \
+               reg = <0x0 0x##base##20000 0x0 0x1000>;                         \
+               clocks = <&cs_pclk>;                                            \
+               clock-names = "apb_pclk";                                       \
+                                                                               \
+               cpu = <&cpu##c>;                                                \
+               arm,cs-dev-assoc = <&etm##c>;                                   \
+       }
+
+#define CA57_CS(cl, c0, c1, cs_base, debug0_base, debug1_base)                 \
+       CA57_DEBUG(cl,c0,debug0_base,0);                                        \
+       CA57_DEBUG(cl,c1,debug1_base,1);                                        \
+                                                                               \
+       funnel##cl: funnel##cl {                                                \
+               /*                                                              \
+                * non-configurable replicators don't show up on the            \
+                * AMBA bus.  As such no need to add "arm,primecell".           \
+                */                                                             \
+               compatible = "arm,coresight-static-funnel";                     \
+                                                                               \
+               out-ports {                                                     \
+                       port {                                                  \
+                               funnel##cl##_out_port: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etf##cl##_in_port>;           \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+                                                                               \
+               in-ports {                                                      \
+                       #address-cells = <1>;                                   \
+                       #size-cells = <0>;                                      \
+                                                                               \
+                       port@0 {                                                \
+                               reg = <0>;                                      \
+                               funnel##cl##_in_port0: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etm##c0##_out_port>;          \
+                               };                                              \
+                       };                                                      \
+                                                                               \
+                       port@1 {                                                \
+                               reg = <1>;                                      \
+                               funnel##cl##_in_port1: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etm##c1##_out_port>;          \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+       };                                                                      \
+                                                                               \
+       etf##cl: etf@##cs_base { /*  ETF 512B */                                \
+               compatible = "arm,coresight-tmc", "arm,primecell";              \
+               reg = <0x0 0x##cs_base 0x0 0x1000>;                             \
+               clocks = <&cs_pclk>, <&cs_atclk>;                               \
+               clock-names = "apb_pclk", "atclk";                              \
+                                                                               \
+               out-ports {                                                     \
+                       port {                                                  \
+                               etf##cl##_out_port: endpoint {                  \
+                                       remote-endpoint =                       \
+                                               <&funnel_in_port##cl>;          \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+                                                                               \
+               in-ports {                                                      \
+                       port {                                                  \
+                               etf##cl##_in_port: endpoint {                   \
+                                       remote-endpoint =                       \
+                                               <&funnel##cl##_out_port>;       \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+       }
+
+/ {
+       clocks {
+               cs_pclk: cs_pclk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <250000000>;
+                       clock-output-names = "cs_pclk";
+               };
+
+               cs_atclk: cs_atclk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <250000000>;
+                       clock-output-names = "cs_atclk";
+               };
+       };
+
+       soc {
+               /* CA57 MM's */
+
+               CA57_CS(0,0,1,5009000,52,53);
+               CA57_CS(1,2,3,500a000,54,55);
+               CA57_CS(2,4,5,500b000,56,57);
+               CA57_CS(3,6,7,500c000,58,59);
+
+               /* CoreSight MM */
+
+               tpiu: tpiu@500e000 {
+                       compatible = "arm,coresight-tpiu", "arm,primecell";
+                       reg = <0x0 0x500e000 0x0 0x1000>;
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       in-ports {
+                               port {
+                                       tpiu_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&etb_out_port>;
+                                       };
+                               };
+                       };
+               };
+
+               etb: etb@5007000 {
+                       compatible = "arm,coresight-tmc", "arm,primecell";
+                       reg = <0x0 0x5007000 0x0 0x1000>;
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       out-ports {
+                               port {
+                                       etb_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&tpiu_in_port>;
+                                       };
+                               };
+                       };
+
+                       in-ports {
+                               port {
+                                       etb_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&replicator_out_port1>;
+                                       };
+                               };
+                       };
+               };
+
+               etr: etr@5008000 {
+                       compatible = "arm,coresight-tmc", "arm,primecell";
+                       reg = <0x0 0x5008000 0x0 0x1000>;
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       in-ports {
+                               port {
+                                       etr_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&replicator_out_port0>;
+                                       };
+                               };
+                       };
+               };
+
+               replicator: replicator@5010000 {
+                       compatible = "arm,coresight-dynamic-replicator", "arm,primecell";
+                       reg = <0x0 0x5010000 0x0 0x1000>;
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       out-ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               /* replicator output ports */
+                               port@0 {
+                                       reg = <0>;
+                                       replicator_out_port0: endpoint {
+                                               remote-endpoint =
+                                                       <&etr_in_port>;
+                                       };
+                               };
+
+                               port@1 {
+                                       reg = <1>;
+                                       replicator_out_port1: endpoint {
+                                               remote-endpoint =
+                                                       <&etb_in_port>;
+                                       };
+                               };
+                       };
+
+                       in-ports {
+                               port {
+                                       replicator_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&etf_out_port>;
+                                       };
+                               };
+                       };
+               };
+
+               etf: etf@500d000 { /*  ETF 2048B */
+                       compatible = "arm,coresight-tmc", "arm,primecell";
+                       reg = <0x0 0x500d000 0x0 0x1000>;
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       out-ports {
+                               port {
+                                       etf_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&replicator_in_port>;
+                                       };
+                               };
+                       };
+
+                       in-ports {
+                               port {
+                                       etf_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&funnel_out_port>;
+                                       };
+                               };
+                       };
+               };
+
+               funnel: funnel@5011000 {
+                       compatible = "arm,coresight-dynamic-funnel", "arm,primecell";
+                       reg = <0x0 0x5011000 0x0 0x1000>;
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       out-ports {
+                               port {
+                                       funnel_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&etf_in_port>;
+                                       };
+                               };
+                       };
+
+                       in-ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       funnel_in_port0: endpoint {
+                                               remote-endpoint =
+                                                       <&etf0_out_port>;
+                                       };
+                               };
+
+                               port@1 {
+                                       reg = <1>;
+                                       funnel_in_port1: endpoint {
+                                               remote-endpoint =
+                                                       <&etf1_out_port>;
+                                       };
+                               };
+
+                               port@2 {
+                                       reg = <2>;
+                                       funnel_in_port2: endpoint {
+                                               remote-endpoint =
+                                                       <&etf2_out_port>;
+                                       };
+                               };
+
+                               port@3 {
+                                       reg = <3>;
+                                       funnel_in_port3: endpoint {
+                                               remote-endpoint =
+                                                       <&etf3_out_port>;
+                                       };
+                               };
+
+                               port@4 {
+                                       reg = <4>;
+                                       funnel_in_port4: endpoint {
+                                               remote-endpoint =
+                                                       <&stm0_out_port>;
+                                       };
+                               };
+#if 0
+                               /*
+                                * Disable stm1 port due to coresight-stm driver
+                                * doesn't initialize stm node without stimulus area.
+                                */
+                               port@05 {
+                                       reg = <5>;
+                                       funnel_in_port5: endpoint {
+                                               remote-endpoint =
+                                                       <&stm1_out_port>;
+                                       };
+                               };
+#endif
+                       };
+               };
+
+               stm0: stm@5001000 {
+                       compatible = "arm,coresight-stm", "arm,primecell";
+                       reg = <0x0 0x5001000 0x0 0x1000>,
+                             <0x0 0x7000000 0x0 0x1000000>;
+                       reg-names = "stm-base", "stm-stimulus-base";
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       out-ports {
+                               port {
+                                       stm0_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&funnel_in_port4>;
+                                       };
+                               };
+                       };
+               };
+#if 0
+               /*
+                * Disable stm1 node due to coresight-stm driver
+                * doesn't initialize stm node without stimulus area.
+                */
+               stm1: stm@5002000 {
+                       compatible = "arm,coresight-stm", "arm,primecell";
+                       reg = <0x0 0x5002000 0x0 0x1000>;
+                       reg-names = "stm-base";
+                       clocks = <&cs_pclk>, <&cs_atclk>;
+                       clock-names = "apb_pclk", "atclk";
+
+                       out-ports {
+                               port {
+                                       stm1_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&funnel_in_port1>;
+                                       };
+                               };
+                       };
+               };
+#endif
+               cti0: cti@5004000 {
+                       compatible = "arm,coresight-cti", "arm,primecell";
+                       reg = <0x0 0x5004000 0x0 0x1000>;
+                       clocks = <&cs_pclk>;
+                       clock-names = "apb_pclk";
+
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       trig-conns@0 {
+                               reg = <0>;
+                               arm,trig-in-sigs = <0>;
+                               arm,trig-conn-name = "ccn_dbgwatchtrig";
+                       };
+
+                       trig-conns@1 {
+                               reg = <1>;
+                               arm,trig-in-sigs = <2 3 4 5>;
+                               arm,trig-in-types = <STM_TOUT_SPTE STM_TOUT_SW
+                                                    STM_TOUT_HETE STM_ASYNCOUT>;
+                               arm,trig-out-sigs = <6 7>;
+                               arm,trig-out-types = <STM_HWEVENT STM_HWEVENT>;
+                               arm,cs-dev-assoc = <&stm0>;
+                       };
+
+                       trig-conns@3 {
+                               reg = <3>;
+                               arm,trig-out-sigs = <0 1>;
+                               arm,trig-out-types = <GEN_HALTREQ
+                                                     GEN_RESTARTREQ>;
+                               arm,trig-conn-name = "cctr";
+                       };
+
+                       trig-conns@4 {
+                               reg = <4>;
+                               arm,trig-out-sigs = <2>;
+                               arm,trig-conn-name = "ccn_pmusnapshot";
+                       };
+
+                       trig-conns@5 {
+                               reg = <5>;
+                               arm,trig-out-sigs = <3 4>;
+                               arm,trig-out-types = <GEN_INTREQ GEN_INTREQ>;
+                               arm,trig-conn-name = "gic_cs_ppii";
+                       };
+#if 0
+                       /*
+                        * Disable stm1 events due to coresight-stm driver
+                        * doesn't initialize stm node without stimulus area.
+                        */
+                       trig-conns@6 {
+                               reg = <6>;
+                               arm,trig-in-sigs = <6 7>;
+                               arm,trig-in-types = <STM_TOUT_HETE STM_ASYNCOUT>;
+                               arm,cs-dev-assoc = <&stm1>;
+                       };
+#endif
+               };
+
+               cti1: cti@5005000 {
+                       compatible = "arm,coresight-cti", "arm,primecell";
+                       reg = <0x0 0x5005000 0x0 0x1000>;
+                       clocks = <&cs_pclk>;
+                       clock-names = "apb_pclk";
+
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       trig-conns@0 {
+                               reg = <0>;
+                               arm,trig-in-sigs = <0 1>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL>;
+                               arm,trig-out-sigs = <0 1>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etf0>;
+                       };
+
+                       trig-conns@1 {
+                               reg = <1>;
+                               arm,trig-in-sigs = <2 3>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL>;
+                               arm,trig-out-sigs = <2 3>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etf1>;
+                       };
+
+                       trig-conns@2 {
+                               reg = <2>;
+                               arm,trig-in-sigs = <4 5>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL>;
+                               arm,trig-out-sigs = <4 5>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etf2>;
+                       };
+
+                       trig-conns@3 {
+                               reg = <3>;
+                               arm,trig-in-sigs = <6 7>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL>;
+                               arm,trig-out-sigs = <6 7>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etf3>;
+                       };
+               };
+
+               cti2: cti@5006000 {
+                       compatible = "arm,coresight-cti", "arm,primecell";
+                       reg = <0x0 0x5006000 0x0 0x1000>;
+                       clocks = <&cs_pclk>;
+                       clock-names = "apb_pclk";
+
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       trig-conns@0 {
+                               reg = <0>;
+                               arm,trig-in-sigs = <0 1>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL>;
+                               arm,trig-out-sigs = <0 1>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etr>;
+                       };
+
+                       trig-conns@1 {
+                               reg = <1>;
+                               arm,trig-in-sigs = <2 3>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL>;
+                               arm,trig-out-sigs = <2 3>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etb>;
+                       };
+
+                       trig-conns@2 {
+                               reg = <2>;
+                               arm,trig-in-sigs = <4 5>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL>;
+                               arm,trig-out-sigs = <4 5>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etf>;
+                       };
+
+                       trig-conns@3 {
+                               reg = <3>;
+                               arm,trig-out-sigs = <6 7>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&tpiu>;
+                       };
+               };
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000-dbm.dtsi b/arch/arm64/boot/dts/baikal/bm1000-dbm.dtsi
new file mode 100644 (file)
index 0000000..f70bbcb
--- /dev/null
@@ -0,0 +1,410 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for Baikal Electronics DBM boards
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ */
+
+#include <dt-bindings/input/input.h>
+#include "bm1000.dtsi"
+
+/ {
+       aliases {
+               ethernet0       = &gmac0;
+               ethernet1       = &gmac1;
+               mdio-gpio0      = &mdio_gpio;
+       };
+
+       chosen { };
+
+       buttons-backlight {
+               compatible = "gpio-keys";
+               autorepeat;
+               button-brightness-down {
+                       label = "Brightness Down Button";
+                       linux,code = <KEY_BRIGHTNESSDOWN>;
+                       gpios = <&porta 18 GPIO_ACTIVE_LOW>;
+                       debounce-interval = <50>;
+               };
+               button-brightness-up {
+                       label = "Brightness Up Button";
+                       linux,code = <KEY_BRIGHTNESSUP>;
+                       gpios = <&porta 17 GPIO_ACTIVE_LOW>;
+                       debounce-interval = <50>;
+               };
+               button-brightness-toggle {
+                       label = "Brightness Toggle Button";
+                       linux,code = <KEY_BRIGHTNESS_TOGGLE>;
+                       gpios = <&porta 31 GPIO_ACTIVE_LOW>;
+                       debounce-interval = <50>;
+               };
+       };
+
+       panel {
+               /* Change status to "okay" to make use of LVDS LCD panel */
+               status = "disabled";
+               compatible = "panel-lvds";
+               width-mm = <223>;
+               height-mm = <125>;
+               data-mapping = "vesa-24";
+               panel-timing {
+                       /* 1920x1080 @ 60 Hz */
+                       /* Replace values below with actual panel timings */
+                       clock-frequency = <148500000>;
+                       hactive = <1920>;
+                       vactive = <1080>;
+                       hsync-len = <44>;
+                       hfront-porch = <88>;
+                       hback-porch = <148>;
+                       vsync-len = <5>;
+                       vfront-porch = <4>;
+                       vback-porch = <36>;
+               };
+               port {
+                       panel_in: endpoint@0 {
+                               remote-endpoint = <&vdu_lvds_out>;
+                       };
+               };
+       };
+
+       soc {
+               mdio_gpio: mdio {
+                       compatible = "baikal,mdio-gpio";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       mdc-pin = <&porta 28 GPIO_ACTIVE_HIGH>;
+                       mdio-pin = <&porta 29 GPIO_ACTIVE_HIGH>;
+                       rst-pin = <&porta 30 GPIO_ACTIVE_LOW>;
+                       clocks = <&gpio_clk>;
+                       clock-names = "gpioclk";
+                       status = "okay";
+
+                       ethphy2: ethernet-phy@c {
+                               compatible = "ethernet-phy-ieee802.3-c45";
+                               reg = <0xc>;
+                               phy-mode = "xgmii";
+                               mv,line-mode = "KR";
+                               mv,host-mode = "KX4";
+                       };
+
+                       ethphy3: ethernet-phy@e {
+                               compatible = "ethernet-phy-ieee802.3-c45";
+                               reg = <0xe>;
+                               phy-mode = "xgmii";
+                               mv,line-mode = "KR";
+                               mv,host-mode = "KX4";
+                       };
+               };
+       };
+
+       sound {
+               compatible = "baikal,snd-soc-baikal";
+               baikal,cpu-dai = <&i2s>;
+               baikal,audio-codec = <&tlv320aic3x>;
+               baikal,dai-name = "tlv320aic3x";
+               baikal,codec-name = "tlv320aic3x-hifi";
+               baikal,stream-name = "tlv320aic3x hifi";
+       };
+
+       sfp-xgmac0 {
+               compatible = "sff,sfp";
+               i2c-bus = <&i2c1>;
+       };
+
+       sfp-xgmac1 {
+               compatible = "sff,sfp";
+               i2c-bus = <&i2c1>;
+       };
+};
+
+&dmac_lsp {
+       status = "okay";
+};
+
+&dmac_m2m {
+       status = "okay";
+};
+
+&gmac0 {
+       status = "okay";
+       phy-handle = <&ethphy0>;
+       phy-mode = "rgmii-id";
+};
+
+&gmac1 {
+       status = "okay";
+       phy-handle = <&ethphy1>;
+       phy-mode = "rgmii-id";
+};
+
+&gpio32 {
+       status = "okay";
+};
+
+&hda {
+       status = "okay";
+};
+
+&hdmi {
+       status = "okay";
+};
+
+&i2c1 {
+       status = "okay";
+
+       tlv320aic3x: tlv320aic3x@18 {
+               #sound-dai-cells = <0>;
+               compatible = "ti,tlv320aic3x";
+               reg = <0x18>;
+               reset-gpios = <&porta 4 GPIO_ACTIVE_LOW>;
+               status = "okay";
+               ai3x-micbias-vg = <1>;
+               ai3x-ocmv = <1>;
+       };
+};
+
+&i2c2 {
+       status = "okay";
+};
+
+&i2s {
+       status = "okay";
+       sound-dai = <&tlv320aic3x>;
+};
+
+&mdio0 {
+       ethphy0: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+               txd0-skew-ps = <0>;
+               txd1-skew-ps = <0>;
+               txd2-skew-ps = <0>;
+               txd3-skew-ps = <0>;
+               txc-skew-ps = <0xff>;
+       };
+};
+
+&mdio1 {
+       ethphy1: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+               txd0-skew-ps = <0>;
+               txd1-skew-ps = <0>;
+               txd2-skew-ps = <0>;
+               txd3-skew-ps = <0>;
+               txc-skew-ps = <0xff>;
+       };
+};
+
+#if 0
+&mmc {
+       /* eMMC */
+       no-sdio;
+       no-sd;
+       non-removable;
+       bus-width = <8>;
+       max-frequency = <200000000>;
+       status = "okay";
+}
+#else
+&mmc {
+       /* SD */
+       no-mmc;
+       no-sdio;
+       disable-wp;
+       bus-width = <4>;
+       max-frequency = <25000000>;
+       status = "okay";
+};
+#endif
+
+&pcie0 {
+       status = "okay";
+       bm1000,gen3-eq-fb-mode = <0>;
+       bm1000,phy-rx-ctle = <0xf4>;
+       bm1000,phy-rx-dfe = <0>;
+};
+
+&pcie1 {
+       status = "okay";
+       bm1000,gen3-eq-fb-mode = <0>;
+       bm1000,phy-rx-ctle = <0x64>;
+       bm1000,phy-rx-dfe = <0>;
+};
+
+&pcie2 {
+       status = "okay";
+       bm1000,gen3-eq-fb-mode = <0>;
+       bm1000,phy-rx-ctle = <0x34>;
+       bm1000,phy-rx-dfe = <0>;
+};
+
+&pvt_cluster0 {
+       status = "okay";
+};
+
+&pvt_cluster1 {
+       status = "okay";
+};
+
+&pvt_cluster2 {
+       status = "okay";
+};
+
+&pvt_cluster3 {
+       status = "okay";
+};
+
+&pvt_mali {
+       status = "okay";
+};
+
+&sata0 {
+       status = "okay";
+};
+
+&sata1 {
+       status = "okay";
+};
+
+&smbus1 {
+       status = "okay";
+};
+
+&smbus2 {
+       status = "okay";
+};
+
+&spi0 {
+       num-cs = <4>;
+       cs-gpios = <&porta 24 GPIO_ACTIVE_LOW>, /* SS0 XP8 - DD53 normal flash */
+                  <&porta 25 GPIO_ACTIVE_LOW>, /* SS1 XP9 */
+                  <&porta 26 GPIO_ACTIVE_LOW>, /* SS2 XP10 */
+                  <&porta 27 GPIO_ACTIVE_LOW>; /* SS3 XP11 */
+       status = "okay";
+
+       flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <12500000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       partition@0 {
+                               label = "bl1";
+                               reg = <0x000000 0x40000>;
+                       };
+
+                       partition@40000 {
+                               label = "dtb";
+                               reg = <0x040000 0x40000>;
+                       };
+
+                       partition@80000 {
+                               label = "uefi-vars";
+                               reg = <0x080000 0xc0000>;
+                       };
+
+                       partition@140000 {
+                               label = "fip";
+                               reg = <0x140000 0x6c0000>;
+                       };
+               };
+       };
+};
+
+&spi1 {
+       num-cs = <8>;
+       cs-gpios = <&porta 0 GPIO_ACTIVE_LOW>;
+       status = "okay";
+
+       espi_test0@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>; /* CS #0 */
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-cpha;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+       };
+};
+
+&timer1 {
+       status = "okay";
+};
+
+&timer2 {
+       status = "okay";
+};
+
+&timer3 {
+       status = "okay";
+};
+
+&timer4 {
+       status = "okay";
+};
+
+&uart1 {
+       status = "okay";
+};
+
+&uart2 {
+       status = "okay";
+};
+
+&usb2 {
+       status = "okay";
+};
+
+&usb3 {
+       status = "okay";
+};
+
+&vdec {
+       status = "okay";
+};
+
+&vdu {
+       /*
+        * 'lvds-lanes' property is mandatory to enable LVDS. Valid values are 1, 2 or 4.
+        * It is also mandatory to provide actual values for 'enable-gpios' property.
+        * Also make sure that panel's 'status' (see above) is "okay".
+        */
+       status = "okay";
+       enable-gpios = <&porta 16 GPIO_ACTIVE_LOW>;
+       lvds-lanes = <2>;
+       backlight {
+               min-brightness-level = <10>;
+               default-brightness-level = <60>;
+               brightness-level-step = <2>;
+               pwm-frequency = <20000>;
+       };
+       ports {
+               port@1 {
+                       reg = <1>;
+                       vdu_lvds_out: endpoint@0 {
+                               remote-endpoint = <&panel_in>;
+                       };
+               };
+       };
+};
+
+&xgmac0 {
+       status = "okay";
+       ext-phy-handle = <&ethphy2>;
+};
+
+&xgmac1 {
+       status = "okay";
+       ext-phy-handle = <&ethphy3>;
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000-dbm10.dts b/arch/arm64/boot/dts/baikal/bm1000-dbm10.dts
new file mode 100644 (file)
index 0000000..e575163
--- /dev/null
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for Baikal Electronics DBM 1.0 board
+ * Copyright (C) 2022 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include "bm1000-dbm.dtsi"
+
+/ {
+       model = "Baikal Electronics DBM 1.0";
+       compatible = "baikal,dbm10", "baikal,bm1000";
+};
+
+&i2c1 {
+       rtc@56 {
+               compatible = "abracon,abeoz9";
+               reg = <0x56>;
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000-dbm20.dts b/arch/arm64/boot/dts/baikal/bm1000-dbm20.dts
new file mode 100644 (file)
index 0000000..12bcd1b
--- /dev/null
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for Baikal Electronics DBM 2.0 board
+ * Copyright (C) 2022 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include "bm1000-dbm.dtsi"
+
+/ {
+       model = "Baikal Electronics DBM 2.0";
+       compatible = "baikal,dbm20", "baikal,bm1000";
+};
+
+&i2c2 {
+       rtc@56 {
+               compatible = "abracon,abeoz9";
+               reg = <0x56>;
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000-mbm.dtsi b/arch/arm64/boot/dts/baikal/bm1000-mbm.dtsi
new file mode 100644 (file)
index 0000000..5964f25
--- /dev/null
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for MBM-compatible boards
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ */
+
+#include "bm1000.dtsi"
+
+/ {
+       aliases {
+               ethernet0 = &gmac0;
+               ethernet1 = &gmac1;
+       };
+
+       chosen { };
+
+       leds {
+               compatible = "gpio-leds";
+               led0 {
+                       gpios = <&porta 8 GPIO_ACTIVE_HIGH>;
+                       default-state = "keep";
+               };
+       };
+
+       sound {
+               compatible = "simple-audio-card";
+               simple-audio-card,name = "MITX-Sound-Card";
+               simple-audio-card,bitclock-master = <&codec0>;
+               simple-audio-card,frame-master = <&codec0>;
+               simple-audio-card,widgets =
+                       "Microphone", "Mic Jack",
+                       "Headphone", "Headphones",
+                       "Speaker", "AUX Out",
+                       "Line", "Line In";
+               simple-audio-card,routing =
+                       "Headphones", "RHP",
+                       "Headphones", "LHP",
+                       "AUX Out", "AUXOUT1",
+                       "AUX Out", "AUXOUT2",
+                       "L2", "Mic Jack",
+                       "R2", "Mic Jack",
+                       "LAUX", "Line In",
+                       "RAUX", "Line In";
+               simple-audio-card,mic-det-gpio = <&porta 26 GPIO_ACTIVE_LOW>;
+               simple-audio-card,format = "i2s";
+               simple-audio-card,cpu {
+                       sound-dai = <&i2s>;
+               };
+               codec0: simple-audio-card,codec {
+                       sound-dai = <&nau8822 0>;
+               };
+       };
+};
+
+&dmac_lsp {
+       status = "okay";
+};
+
+&dmac_m2m {
+       status = "okay";
+};
+
+&gmac0 {
+       status = "okay";
+       phy-handle = <&ethphy0>;
+       phy-mode = "rgmii-id";
+};
+
+&gmac1 {
+       status = "okay";
+       phy-handle = <&ethphy1>;
+       phy-mode = "rgmii-id";
+};
+
+&gpio32 {
+       status = "okay";
+};
+
+&hdmi {
+       status = "okay";
+};
+
+&i2c1 {
+       status = "okay";
+
+       bmc@8 {
+               compatible = "tp,mitx2-bmc";
+               reg = <0x08>;
+       };
+
+       nau8822: nau8822@1a {
+               compatible = "nuvoton,nau8822";
+               #sound-dai-cells = <1>;
+               reg = <0x1a>;
+       };
+
+       gpio@50 {
+               compatible = "nxp,pca9670";
+               #gpio-cells = <2>;
+               gpio-controller;
+               reg = <0x50>;
+       };
+
+       rtc@51 {
+               compatible = "nxp,pcf2129", "nxp,pcf2127";
+               reg = <0x51>;
+       };
+
+       /* FRU */
+       eeprom@53 {
+               compatible = "atmel,24c32";
+               pagesize = <32>;
+               reg = <0x53>;
+       };
+};
+
+&i2c2 {
+       status = "okay";
+};
+
+&i2s {
+       status = "okay";
+};
+
+&mdio0 {
+       ethphy0: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+               txd0-skew-ps = <0>;
+               txd1-skew-ps = <0>;
+               txd2-skew-ps = <0>;
+               txd3-skew-ps = <0>;
+               txc-skew-ps = <0xff>;
+       };
+};
+
+&mdio1 {
+       ethphy1: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+               txd0-skew-ps = <0>;
+               txd1-skew-ps = <0>;
+               txd2-skew-ps = <0>;
+               txd3-skew-ps = <0>;
+               txc-skew-ps = <0xff>;
+       };
+};
+
+&mmc {
+       /* SD */
+       no-mmc;
+       no-sdio;
+       disable-wp;
+       bus-width = <4>;
+       max-frequency = <25000000>;
+       status = "okay";
+};
+
+&pcie0 {
+       status = "okay";
+       reset-gpios = <&porta 6 GPIO_ACTIVE_LOW>;
+       bm1000,gen3-eq-fb-mode = <0>;
+       bm1000,phy-rx-ctle = <0x34>;
+       bm1000,phy-rx-dfe = <0>;
+};
+
+&pcie2 {
+       status = "okay";
+       reset-gpios = <&porta 3 GPIO_ACTIVE_LOW>;
+       bm1000,gen3-eq-fb-mode = <0>;
+       bm1000,phy-rx-ctle = <0x34>;
+       bm1000,phy-rx-dfe = <0>;
+};
+
+&porta {
+       pcieclk {
+               gpio-hog;
+               gpios = <1 GPIO_ACTIVE_LOW>;
+               output-high;
+               line-name = "pcie-x8-clock";
+       };
+};
+
+&pvt_cluster0 {
+       status = "okay";
+};
+
+&pvt_cluster1 {
+       status = "okay";
+};
+
+&pvt_cluster2 {
+       status = "okay";
+};
+
+&pvt_cluster3 {
+       status = "okay";
+};
+
+&pvt_mali {
+       status = "okay";
+};
+
+&sata0 {
+       status = "okay";
+};
+
+&sata1 {
+       status = "okay";
+};
+
+&smbus1 {
+       status = "okay";
+};
+
+&smbus2 {
+       status = "okay";
+};
+
+&spi0 {
+       num-cs = <4>;
+       cs-gpios = <0>;
+       status = "okay";
+};
+
+&timer1 {
+       status = "okay";
+};
+
+&timer2 {
+       status = "okay";
+};
+
+&timer3 {
+       status = "okay";
+};
+
+&timer4 {
+       status = "okay";
+};
+
+&uart1 {
+       status = "okay";
+};
+
+&uart2 {
+       status = "okay";
+};
+
+&usb2 {
+       status = "okay";
+};
+
+&usb3 {
+       status = "okay";
+};
+
+&vdec {
+       status = "okay";
+};
+
+&vdu {
+       status = "okay";
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000-mbm10.dts b/arch/arm64/boot/dts/baikal/bm1000-mbm10.dts
new file mode 100644 (file)
index 0000000..7dd80e5
--- /dev/null
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for MBM 1.0 compatible boards:
+ *   - TP-TF307-MB-A0 Rev.1.0 (BM1BM1-A)
+ *   - TF307-MB-S-C Rev.3.0
+ *
+ * Copyright (C) 2021-2022 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include "bm1000-mbm.dtsi"
+
+/ {
+       model = "Baikal Electronics MBM 1.0";
+       compatible = "baikal,mbm10", "baikal,bm1000";
+
+       sound {
+               simple-audio-card,hp-det-gpio = <&porta 27 GPIO_ACTIVE_HIGH>;
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000-mbm20.dts b/arch/arm64/boot/dts/baikal/bm1000-mbm20.dts
new file mode 100644 (file)
index 0000000..23ed4e3
--- /dev/null
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for MBM 2.0 compatible boards:
+ *   - TF307-MB-S-D Rev.4.0 (BM1BM1-D)
+ *
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/input/input.h>
+#include "bm1000-mbm.dtsi"
+
+/ {
+       model = "Baikal Electronics MBM 2.0";
+       compatible = "baikal,mbm20", "baikal,bm1000";
+
+       buttons-backlight {
+               compatible = "gpio-keys";
+               autorepeat;
+               button-brightness-down {
+                       label = "Brightness Down Button";
+                       linux,code = <KEY_BRIGHTNESSDOWN>;
+                       gpios = <&porta 18 GPIO_ACTIVE_LOW>;
+                       debounce-interval = <50>;
+               };
+               button-brightness-up {
+                       label = "Brightness Up Button";
+                       linux,code = <KEY_BRIGHTNESSUP>;
+                       gpios = <&porta 17 GPIO_ACTIVE_LOW>;
+                       debounce-interval = <50>;
+               };
+               button-brightness-toggle {
+                       label = "Brightness Toggle Button";
+                       linux,code = <KEY_BRIGHTNESS_TOGGLE>;
+                       gpios = <&porta 31 GPIO_ACTIVE_LOW>;
+                       debounce-interval = <50>;
+               };
+       };
+
+       panel {
+               /*
+                * In order to utilize LVDS LCD panel, make sure that
+                * 'status' is "okay" along with &vdu 'status' (see below).
+                */
+               status = "disabled";
+               compatible = "panel-lvds";
+               width-mm = <223>;
+               height-mm = <125>;
+               data-mapping = "vesa-24";
+               panel-timing {
+                       /* 1920x1080 @ 60 Hz */
+                       /* Replace values below with actual panel timings */
+                       clock-frequency = <148500000>;
+                       hactive = <1920>;
+                       vactive = <1080>;
+                       hsync-len = <44>;
+                       hfront-porch = <88>;
+                       hback-porch = <148>;
+                       vsync-len = <5>;
+                       vfront-porch = <4>;
+                       vback-porch = <36>;
+               };
+               port {
+                       panel_in: endpoint@0 {
+                               remote-endpoint = <&vdu_lvds_out>;
+                       };
+               };
+       };
+
+       sound {
+               simple-audio-card,hp-det-gpio = <&porta 29 GPIO_ACTIVE_HIGH>;
+       };
+};
+
+&gmac0 {
+       snps,reset-gpios = <&porta 19 GPIO_ACTIVE_LOW>;
+       snps,reset-delays-us = <0 10000 50000>;
+};
+
+&gmac1 {
+       snps,reset-gpios = <&porta 20 GPIO_ACTIVE_LOW>;
+       snps,reset-delays-us = <0 10000 50000>;
+};
+
+&vdu {
+       /*
+        * 'lvds-lanes' property is mandatory to enable LVDS. Valid values are 1, 2 or 4.
+        * It is also mandatory to provide actual values for 'enable-gpios' property.
+        * Also make sure that panel's 'status' (see above) is "okay".
+        */
+       enable-gpios = <&porta 16 GPIO_ACTIVE_LOW>;
+       lvds-lanes = <2>;
+       backlight {
+               min-brightness-level = <10>;
+               default-brightness-level = <60>;
+               brightness-level-step = <2>;
+               pwm-frequency = <20000>;
+       };
+       ports {
+               port@1 {
+                       reg = <1>;
+                       vdu_lvds_out: endpoint@0 {
+                               remote-endpoint = <&panel_in>;
+                       };
+               };
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000-qemu-m.dts b/arch/arm64/boot/dts/baikal/bm1000-qemu-m.dts
new file mode 100644 (file)
index 0000000..fd6b1c2
--- /dev/null
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for Baikal Electronics QEMU-M virtual platform
+ * Copyright (C) 2018-2023 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include "bm1000.dtsi"
+
+/ {
+       model = "Baikal Electronics QEMU-M";
+       compatible = "baikal,qemu-m", "baikal,bm1000";
+
+       aliases {
+               ethernet0 = &gmac0;
+               ethernet1 = &gmac1;
+       };
+
+       chosen { };
+
+       memory@80000000 {
+               device_type = "memory";
+               reg = <0x00000000 0x80000000 0x0 0x80000000>; /* 2 GiB */
+       };
+
+       /*
+        * Device is necessary for UEFI to boot on QEMU,
+        * need to replace it with something later.
+        */
+       flash@0 {
+               compatible = "cfi-flash";
+               reg = <0x0 0x4000000 0x0 0x4000000>;
+               bank-width = <0x4>;
+       };
+
+       panel: panel {
+               compatible = "auo,b133htn01";           /* 1920x1080 */
+               port {
+                       lcd_panel: endpoint {
+                               remote-endpoint = <&vdu_pads>;
+                       };
+               };
+       };
+};
+
+&gmac0 {
+       status = "okay";
+       phy-handle = <&ethphy0>;
+       phy-mode = "rgmii-id";
+};
+
+&gmac1 {
+       status = "okay";
+       phy-handle = <&ethphy1>;
+       phy-mode = "rgmii-id";
+};
+
+&mdio0 {
+       ethphy0: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+       };
+};
+
+&mdio1 {
+       ethphy1: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+       };
+};
+
+&mmc {
+       /* SD */
+       no-mmc;
+       no-sdio;
+       disable-wp;
+       bus-width = <4>;
+       max-frequency = <25000000>;
+       status = "okay";
+
+};
+
+&sata0 {
+       status = "okay";
+};
+
+&sata1 {
+       status = "okay";
+};
+
+&spi0 {
+       num-cs = <6>;
+       status = "okay";
+
+       flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <12500000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       partition@0 {
+                               label = "bl1";
+                               reg = <0x000000 0x40000>;
+                       };
+
+                       partition@40000 {
+                               label = "dtb";
+                               reg = <0x040000 0x40000>;
+                       };
+
+                       partition@80000 {
+                               label = "uefi-vars";
+                               reg = <0x080000 0xc0000>;
+                       };
+
+                       partition@140000 {
+                               label = "fip";
+                               reg = <0x140000 0x6c0000>;
+                       };
+               };
+       };
+};
+
+&uart1 {
+       status = "okay";
+       clocks = <&avlsp_cmu0 1>, <&apb_clk>, <&apb_clk>;
+       clock-names = "soc_uartclk", "apb_pclk", "baudclk";
+};
+
+&vdu {
+       port {
+               vdu_pads: endpoint {
+                       remote-endpoint = <&lcd_panel>;
+               };
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bm1000.dtsi b/arch/arm64/boot/dts/baikal/bm1000.dtsi
new file mode 100644 (file)
index 0000000..5007df2
--- /dev/null
@@ -0,0 +1,1399 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for BE-M1000 SoC
+ * Copyright (C) 2017-2023 Baikal Electronics, JSC
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#define CLUSTER_OPP_TABLE(idx)                                 \
+       cluster##idx##_opp: opp-table-cluster##idx {            \
+               compatible = "operating-points-v2";             \
+               opp-shared;                                     \
+                                                               \
+               opp-500000000 {                                 \
+                       opp-hz = /bits/ 64 <500000000>;         \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-600000000 {                                 \
+                       opp-hz = /bits/ 64 <600000000>;         \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-700000000 {                                 \
+                       opp-hz = /bits/ 64 <700000000>;         \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-800000000 {                                 \
+                       opp-hz = /bits/ 64 <800000000>;         \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-900000000 {                                 \
+                       opp-hz = /bits/ 64 <900000000>;         \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-1000000000 {                                \
+                       opp-hz = /bits/ 64 <1000000000>;        \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-1100000000 {                                \
+                       opp-hz = /bits/ 64 <1100000000>;        \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-1200000000 {                                \
+                       opp-hz = /bits/ 64 <1200000000>;        \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-1300000000 {                                \
+                       opp-hz = /bits/ 64 <1300000000>;        \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-1400000000 {                                \
+                       opp-hz = /bits/ 64 <1400000000>;        \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+               opp-1500000000 {                                \
+                       opp-hz = /bits/ 64 <1500000000>;        \
+                       clock-latency-ns = <10000000>;          \
+               };                                              \
+       }
+
+/ {
+       compatible = "baikal,bm1000";
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&gic>;
+
+       aliases {
+               i2c1    = &i2c1;
+               i2c2    = &i2c2;
+               i2c3    = &smbus1;
+               i2c4    = &smbus2;
+               serial0 = &uart1;
+               serial1 = &uart2;
+       };
+
+       clocks {
+               osc25: oscillator25 { /* external oscillator */
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <25000000>;
+                       clock-output-names = "osc25";
+               };
+               osc27: oscillator27 { /* external oscillator */
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <27000000>;
+                       clock-output-names = "osc27";
+               };
+               cpu_clk: cpu_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <1500000000>;
+                       clock-output-names = "cpuclk";
+               };
+               apb_clk: apb_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <200000000>;
+                       clock-output-names = "apb_pclk";
+               };
+               uart_clk: uart_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <7372800>;
+                       clock-output-names = "soc_uartclk";
+               };
+               i2c_clk: i2c_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <100000000>;
+                       clock-output-names = "soc_i2cclk";
+               };
+               smbus_clk: smbus_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "soc_smbusclk";
+               };
+               timer1_clk: timer1_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "soc_timer1clk";
+               };
+               timer2_clk: timer2_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "soc_timer2clk";
+               };
+               timer3_clk: timer3_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "soc_timer3clk";
+               };
+               timer4_clk: timer4_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "soc_timer4clk";
+               };
+               gpio_clk: gpio_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <1000000>;
+                       clock-output-names = "soc_gpioclk";
+               };
+               spi_clk: spi_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "soc_spiclk";
+               };
+               soc_ethclk: ethclk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "eth_clk";
+               };
+               soc_xgbeclk: xgbeclk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <156250000>;
+                       clock-output-names = "xgbe_clk";
+               };
+               soc_smc50mhz: clk50mhz {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <50000000>;
+                       clock-output-names = "smc_clk";
+               };
+               soc_faxiclk: refclk400mhz {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <400000000>;
+                       clock-output-names = "faxi_clk";
+               };
+               soc_tmp_clk: refclkXXXmhz {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <100000000>;
+                       clock-output-names = "tmpclk";
+               };
+               gpu_clk: gpu_clk {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <750000000>;
+                       clock-output-names = "gpuclk";
+               };
+               clk_ahb: clk_ahb {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <100000000>;
+                       clock-output-names = "clk_ahb";
+               };
+               clk_xin: clk_xin {
+                       compatible = "fixed-clock";
+                       #clock-cells = <0>;
+                       clock-frequency = <100000000>;
+                       clock-output-names = "clk_xin";
+               };
+               avlsp_cmu1_div7: avlsp_cmu1_div7 {
+                       compatible = "allwinner,sun4i-a10-pll3-2x-clk", "fixed-factor-clock";
+                       #clock-cells = <0>;
+                       clocks = <&avlsp_cmu1>;
+                       clock-div = <7>;
+                       clock-mult = <1>;
+                       clock-output-names = "lvds_clk";
+               };
+       };
+
+       psci {
+               compatible = "arm,psci-1.0", "arm,psci-0.2";
+               method = "smc";
+       };
+
+       cpus {
+               #address-cells = <2>;
+               #size-cells = <0>;
+
+               /* Do not use 'cpu-map'. It leads to wrong topology. */
+
+               cpu0: cpu@0 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x0>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster0_l2>;
+                       clocks = <&cluster0_cmu0>;
+                       operating-points-v2 = <&cluster0_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               cpu1: cpu@1 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x1>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster0_l2>;
+                       clocks = <&cluster0_cmu0>;
+                       operating-points-v2 = <&cluster0_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               cpu2: cpu@100 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x100>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster1_l2>;
+                       clocks = <&cluster1_cmu0>;
+                       operating-points-v2 = <&cluster1_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               cpu3: cpu@101 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x101>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster1_l2>;
+                       clocks = <&cluster1_cmu0>;
+                       operating-points-v2 = <&cluster1_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               cpu4: cpu@200 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x200>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster2_l2>;
+                       clocks = <&cluster2_cmu0>;
+                       operating-points-v2 = <&cluster2_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               cpu5: cpu@201 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x201>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster2_l2>;
+                       clocks = <&cluster2_cmu0>;
+                       operating-points-v2 = <&cluster2_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               cpu6: cpu@300 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x300>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster3_l2>;
+                       clocks = <&cluster3_cmu0>;
+                       operating-points-v2 = <&cluster3_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               cpu7: cpu@301 {
+                       device_type = "cpu";
+                       compatible = "arm,cortex-a57";
+                       reg = <0x0 0x301>;
+                       enable-method = "psci";
+                       i-cache-size = <0xc000>;
+                       i-cache-line-size = <64>;
+                       i-cache-sets = <256>;
+                       d-cache-size = <0x8000>;
+                       d-cache-line-size = <64>;
+                       d-cache-sets = <256>;
+                       next-level-cache = <&cluster3_l2>;
+                       clocks = <&cluster3_cmu0>;
+                       operating-points-v2 = <&cluster3_opp>;
+                       cpu-idle-states = <&CPU_SLEEP>;
+               };
+
+               idle-states {
+                       entry-method = "psci";
+
+                       CPU_SLEEP: cpu-sleep {
+                               compatible = "arm,idle-state";
+                               local-timer-stop;
+                               arm,psci-suspend-param = <0x0000001>;
+                               entry-latency-us = <300>;
+                               exit-latency-us = <1200>;
+                               min-residency-us = <2000>;
+                       };
+               };
+
+               cluster0_l2: l2-cache0 {
+                       compatible = "cache";
+                       cache-size = <0x100000>;
+                       cache-line-size = <64>;
+                       cache-sets = <1024>;
+                       cache-unified;
+                       cache-level = <2>;
+                       next-level-cache = <&l3>;
+               };
+
+               cluster1_l2: l2-cache1 {
+                       compatible = "cache";
+                       cache-size = <0x100000>;
+                       cache-line-size = <64>;
+                       cache-sets = <1024>;
+                       cache-unified;
+                       cache-level = <2>;
+                       next-level-cache = <&l3>;
+               };
+
+               cluster2_l2: l2-cache2 {
+                       compatible = "cache";
+                       cache-size = <0x100000>;
+                       cache-line-size = <64>;
+                       cache-sets = <1024>;
+                       cache-unified;
+                       cache-level = <2>;
+                       next-level-cache = <&l3>;
+               };
+
+               cluster3_l2: l2-cache3 {
+                       compatible = "cache";
+                       cache-size = <0x100000>;
+                       cache-line-size = <64>;
+                       cache-sets = <1024>;
+                       cache-unified;
+                       cache-level = <2>;
+                       next-level-cache = <&l3>;
+               };
+
+               l3: l3-cache {
+                       cache-size = <0x800000>;
+                       cache-unified;
+                       cache-level = <3>;
+               };
+       };
+
+       CLUSTER_OPP_TABLE(0);
+       CLUSTER_OPP_TABLE(1);
+       CLUSTER_OPP_TABLE(2);
+       CLUSTER_OPP_TABLE(3);
+
+       pmu {
+               compatible = "arm,cortex-a57-pmu";
+               interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW>;
+       };
+
+       timer {
+               compatible = "arm,armv8-timer";
+               interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+                            <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+                            <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+                            <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+       };
+
+       soc {
+               compatible = "simple-bus";
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               cluster0_cmu0: clock-controller@28000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x28000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-cluster0-cmu0";
+                       #clock-cells = <0>;
+                       clocks = <&osc25>;
+                       clock-frequency = <1500000000>;
+                       min = <500000000>;
+                       max = <1500000000>;
+               };
+
+               cluster1_cmu0: clock-controller@c000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0xc000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-cluster1-cmu0";
+                       #clock-cells = <0>;
+                       clocks = <&osc25>;
+                       clock-frequency = <1500000000>;
+                       min = <500000000>;
+                       max = <1500000000>;
+               };
+
+               cluster2_cmu0: clock-controller@a000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0xa000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-cluster2-cmu0";
+                       #clock-cells = <0>;
+                       clocks = <&osc25>;
+                       clock-frequency = <1500000000>;
+                       min = <500000000>;
+                       max = <1500000000>;
+               };
+
+               cluster3_cmu0: clock-controller@26000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x26000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-cluster3-cmu0";
+                       #clock-cells = <0>;
+                       clocks = <&osc25>;
+                       clock-frequency = <1500000000>;
+                       min = <500000000>;
+                       max = <1500000000>;
+               };
+
+               avlsp_cmu0: clock-controller@20000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x20000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-avlsp-cmu0";
+                       #clock-cells = <1>;
+                       clocks = <&osc25>;
+                       clock-frequency = <1200000000>;
+                       min = <800000>;
+                       max = <2100000000>;
+                       clock-names = "gpio",
+                                     "uart1",
+                                     "uart2",
+                                     "apb",
+                                     "spi",
+                                     "espi",
+                                     "i2c1",
+                                     "i2c2",
+                                     "timer1",
+                                     "timer2",
+                                     "timer3",
+                                     "timer4",
+                                     "hclk",
+                                     "smbus1",
+                                     "smbus2",
+                                     "hda_sys_clk",
+                                     "hda_clk48",
+                                     "mshc_axi",
+                                     "mshc_ahb",
+                                     "mshc_tx_x2",
+                                     "mshc_b",
+                                     "mshc_tm",
+                                     "mshc_cqetm",
+                                     "hwa_clu",
+                                     "hwa_clu_hf",
+                                     "hwa_axi",
+                                     "vdu_axi",
+                                     "smmu";
+                       clock-indices = <0>, <1>, <2>, <3>, <4>, <5>, <6>,
+                                       <7>, <8>, <9>, <10>, <11>, <12>, <13>,
+                                       <14>, <15>, <16>, <17>, <18>, <19>, <20>,
+                                       <21>, <22>, <23>, <24>, <25>, <26>, <27>;
+               };
+
+               avlsp_cmu1: clock-controller@20010000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x20010000 0x0 0x10000>;
+                       clock-output-names = "bm1000-avlsp-cmu1";
+                       #clock-cells = <0>;
+                       clocks = <&osc27>;
+                       clock-frequency = <1039500000>;
+                       min = <13500000>;
+                       max = <2100000000>;
+               };
+
+               mali_cmu0: clock-controller@2a000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x2a000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-mali-cmu0";
+                       #clock-cells = <0>;
+                       clocks = <&osc25>;
+                       clock-frequency = <750000000>;
+                       min = <400000000>;
+                       max = <800000000>;
+                       clock-names = "aclk";
+               };
+
+               usb_cmu0: clock-controller@2c000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x2c000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-usb-cmu0";
+                       #clock-cells = <1>;
+                       clocks = <&osc25>;
+                       clock-frequency = <800000000>;
+                       min = <100000000>;
+                       max = <800000000>;
+                       clock-names = "sata_ref_alt_clk",
+                                     "sata_aclk_u0",
+                                     "sata_aclk_u1",
+                                     "usb2_phy0_ref_clk",
+                                     "usb2_phy1_ref_clk",
+                                     "usb2_aclk",
+                                     "usb2_clk_sofitp",
+                                     "usb3_phy0_ref_clk",
+                                     "usb3_phy1_ref_clk",
+                                     "usb3_phy2_ref_clk",
+                                     "usb3_phy3_ref_clk",
+                                     "usb3_aclk",
+                                     "usb3_clk_sofitp",
+                                     "usb3_clk_suspend",
+                                     "smmu_aclk",
+                                     "dmac_aclk",
+                                     "gic_aclk";
+                       clock-indices = <0>, <1>, <2>, <3>, <4>, <5>,
+                                       <6>, <7>, <8>, <9>, <10>, <11>,
+                                       <12>, <13>, <14>, <15>, <16>;
+               };
+
+               xgbe_cmu0: clock-controller@30000000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x30000000 0x0 0x10000>;
+                       clock-output-names = "bm1000-xgbe-cmu0";
+                       #clock-cells = <1>;
+                       clocks = <&osc25>;
+                       clock-frequency = <1250000000>;
+                       min = <50000000>;
+                       max = <1250000000>;
+                       clock-names = "csr50mhz",
+                                     "gmac0_tx2",
+                                     "gmac1_tx2",
+                                     "hdmi_aclk",
+                                     "isfr";
+                       clock-indices = <0>, <10>, <13>, <15>, <17>;
+               };
+
+               xgbe_cmu1: clock-controller@30010000 {
+                       compatible = "baikal,bm1000-cmu";
+                       reg = <0x0 0x30010000 0x0 0x10000>;
+                       clock-output-names = "bm1000-xgbe-cmu1";
+                       #clock-cells = <1>;
+                       clocks = <&osc27>;
+                       clock-frequency = <25250000>;
+                       min = <13500000>;
+                       max = <600000000>;
+                       clock-names = "pixelclk";
+                       clock-indices = <0>;
+               };
+
+               pcie_gpr: syscon@2050000 {
+                       compatible = "baikal,bm1000-pcie-gpr", "syscon";
+                       reg = <0x0 0x2050000 0x0 0x100>;
+               };
+
+               pcie0: pcie@2200000 { /* PCIe x4 #0 */
+                       device_type = "pci";
+                       compatible = "baikal,bm1000-pcie";
+                       reg = <0x00000000 0x02200000 0x0 0x1000>,
+                             <0x00000040 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 426 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 427 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 429 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 430 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 431 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 432 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 433 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 434 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 435 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 436 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 437 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "aer", "pme", "msi",
+                                         "msi0", "msi1", "msi2", "msi3",
+                                         "msi4", "msi5", "msi6", "msi7";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00000000 0x0 0x4ff00000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40100000 0x0 0x40100000 0x0 0xfe00000>;
+                       num-lanes = <4>;
+                       num-viewport = <4>;
+                       msi-parent = <&its>;
+                       msi-map = <0x0 &its 0x0 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie1: pcie@2210000 { /* PCIe x4 #1 */
+                       device_type = "pci";
+                       compatible = "baikal,bm1000-pcie";
+                       reg = <0x00000000 0x02210000 0x0 0x1000>,
+                             <0x00000050 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 410 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 411 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 412 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 413 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "aer", "pme", "msi",
+                                         "msi0", "msi1", "msi2", "msi3",
+                                         "msi4", "msi5", "msi6", "msi7";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00100000 0x0 0x5ff00000 0x0 0x100000>,
+                                <0x82000000 0x0 0x50000000 0x0 0x50000000 0x0 0xff00000>;
+                       num-lanes = <4>;
+                       num-viewport = <4>;
+                       msi-parent = <&its>;
+                       msi-map = <0x0 &its 0x10000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie2: pcie@2220000 { /* PCIe x8 */
+                       device_type = "pci";
+                       compatible = "baikal,bm1000-pcie";
+                       reg = <0x00000000 0x02220000 0x0 0x1000>,
+                             <0x00000060 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 378 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 379 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 381 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 382 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 383 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 384 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 385 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 386 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 387 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 388 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 389 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "aer", "pme", "msi",
+                                         "msi0", "msi1", "msi2", "msi3",
+                                         "msi4", "msi5", "msi6", "msi7";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00200000 0x0 0x7ff00000 0x0 0x100000>,
+                                <0x82000000 0x0 0x60000000 0x0 0x60000000 0x0 0x1ff00000>;
+                       num-lanes = <8>;
+                       num-viewport = <16>;
+                       msi-parent = <&its>;
+                       msi-map = <0x0 &its 0x20000 0x10000>;
+                       status = "disabled";
+               };
+
+               ccn: ccn@9000000 {
+                       compatible = "arm,ccn-504";
+                       reg = <0x0 0x9000000 0 0x1000000>;
+                       interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>;
+               };
+
+               ddr0: memory-controller@e200000 {
+                       compatible = "baikal,bm1000-edac-mc";
+                       reg = <0x0 0x0e200000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 132 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 134 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       clocks = <&soc_smc50mhz>;
+                       clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               ddr1: memory-controller@22200000 {
+                       compatible = "baikal,bm1000-edac-mc";
+                       reg = <0x0 0x22200000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 143 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       clocks = <&soc_smc50mhz>;
+                       clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               gpio32: gpio@20200000 {
+                       compatible = "snps,dw-apb-gpio";
+                       reg = <0x0 0x20200000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       status = "disabled";
+
+                       porta: gpio-controller@0 {
+                               compatible = "snps,dw-apb-gpio-port";
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               ngpios = <32>;
+                               reg = <0>;
+                               interrupt-controller;
+                               #interrupt-cells = <2>;
+                               interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
+                       };
+               };
+
+               spi0: spi@20210000 {
+                       compatible = "snps,dw-apb-ssi";
+                       reg = <0x0 0x20210000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&avlsp_cmu0 4>;
+                       clock-names = "ssi_clk";
+                       status = "disabled";
+               };
+
+               i2s: i2s@20220000 {
+                       compatible = "snps,designware-i2s";
+                       reg = <0x0 0x20220000 0x0 0x10000>;
+                       #sound-dai-cells = <0>;
+                       system-clock-frequency = <12000000>;
+                       interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>, /* rx_da  */
+                                 /* <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>,    rx_or  */
+                                    <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>, /* tx_emp */
+                                    <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>; /* tx_or  */
+                       /*
+                        * dmas = <&dmac_lsp 0 1 0>,
+                        *        <&dmac_lsp 1 0 1>;
+                        * dma-names = "tx", "rx";
+                        */
+                       clocks = <&soc_tmp_clk>;
+                       clock-names = "i2sclk";
+                       status = "disabled";
+               };
+
+               uart1: serial@20230000 {
+                       compatible = "baikal,bm1000-uart", "snps,dw-apb-uart";
+                       reg = <0x0 0x20230000 0x0 0x100>;
+                       interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
+                       reg-shift = <2>;
+                       reg-io-width = <4>;
+                       clocks = <&avlsp_cmu0 1>, <&apb_clk>;
+                       clock-names = "baudclk", "apb_pclk";
+                       status = "disabled";
+               };
+
+               uart2: serial@20240000 {
+                       compatible = "baikal,bm1000-uart", "snps,dw-apb-uart";
+                       reg = <0x0 0x20240000 0x0 0x100>;
+                       interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
+                       reg-shift = <2>;
+                       reg-io-width = <4>;
+                       clocks = <&avlsp_cmu0 2>, <&apb_clk>;
+                       clock-names = "baudclk", "apb_pclk";
+                       status = "disabled";
+               };
+
+               i2c1: i2c@20250000 {
+                       compatible = "snps,designware-i2c";
+                       reg = <0x0 0x20250000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+                       i2c-sda-hold-time-ns = <500>;
+                       clock-frequency = <400000>;
+                       clocks = <&i2c_clk>;
+                       clock-names = "ref";
+                       status = "disabled";
+               };
+
+               i2c2: i2c@20260000 {
+                       compatible = "snps,designware-i2c";
+                       reg = <0x0 0x20260000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>;
+                       i2c-sda-hold-time-ns = <500>;
+                       clock-frequency = <400000>;
+                       clocks = <&i2c_clk>;
+                       clock-names = "ref";
+                       status = "disabled";
+               };
+
+               smbus1: i2c@20270000 {
+                       compatible = "baikal,bm1000-smbus";
+                       reg = <0x0 0x20270000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+                       clock-frequency = <100000>;
+                       clocks = <&smbus_clk>;
+                       clock-names = "soc_smbusclk";
+                       status = "disabled";
+               };
+
+               smbus2: i2c@20280000 {
+                       compatible = "baikal,bm1000-smbus";
+                       reg = <0x0 0x20280000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>;
+                       clock-frequency = <100000>;
+                       clocks = <&smbus_clk>;
+                       clock-names = "soc_smbusclk";
+                       status = "disabled";
+               };
+
+               timer1: timer@20290000 {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0x20290000 0x0 0x14>;
+                       interrupts = <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer1_clk>;
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               timer2: timer@20290014 {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0x20290014 0x0 0x14>;
+                       interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer2_clk>;
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               timer3: timer@20290028 {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0x20290028 0x0 0x14>;
+                       interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer3_clk>;
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               timer4: timer@2029003c {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0x2029003c 0x0 0x14>;
+                       interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer4_clk>;
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               spi1: spi@202a0000 {
+                       compatible = "baikal,bm1000-espi";
+                       reg = <0x0 0x202a0000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&avlsp_cmu0 5>;
+                       clock-names = "soc_espiclk";
+                       status = "disabled";
+                       /*
+                        * Block Configuration:
+                        * - master/slave
+                        * - 32-bit APB slave
+                        * - tx-fifo = rx-fifo = 256 byte
+                        * - 4 SPI IO channels
+                        * - 8 slave select IO channels
+                        * - DMA - missing
+                        * - M-flash controller - missing
+                        */
+               };
+
+               dmac_lsp: dma-controller@202b0000 {
+                       compatible = "snps,dma-spear1340";
+                       reg = <0x0 0x202b0000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 14 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 42 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 46 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 47 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+
+                       clocks = <&avlsp_cmu0 12>;
+                       clock-names = "hclk";
+
+                       dma-channels = <8>;
+                       dma-requests = <16>;
+                       dma-masters = <2>;
+                       #dma-cells = <4>;
+
+                       chan_allocation_order = <0>;
+                       chan_priority = <0>;
+                       block_size = <0xff>;
+                       data-width = <4 16>;
+                       multi-block = <1 1 1 1 1 1 1 1>;
+                       snps,max-burst-len = <32 32 32 32 32 32 32 32>;
+                       snps,dma-protection-control = <1>;
+
+                       status = "disabled";
+               };
+
+               hda: hda@202c0000 {
+                       compatible = "baikal,bm1000-hda";
+                       reg = <0x0 0x202c0000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&avlsp_cmu0 15>, <&avlsp_cmu0 16>;
+                       clock-names = "hda_sys_clk", "hda_clk48";
+                       status = "disabled";
+                       force-polling-mode;
+                       broken-response-irq;
+                       increment-codec-address;
+                       cyclic-codec-probe;
+               };
+
+               mmc: mmc@202e0000 {
+                       compatible = "baikal,dwcmshc-sdhci";
+                       reg = <0x0 0x202e0000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&avlsp_cmu0 18>, <&avlsp_cmu0 19>;
+                       clock-names = "bus", "core";
+                       no-1-8-v;
+                       status = "disabled";
+               };
+
+               vdec: vdec@24200000 {
+                       compatible = "baikal,d5500-vxd";
+                       reg = <0x0 0x24200000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 497 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+
+               mali: gpu@2a200000 {
+                       compatible = "arm,mali-midgard", "arm,mali-t628";
+                       reg = <0x0 0x2a200000 0x0 0x4000>;
+                       #cooling-cells = <2>; /* min followed by max */
+                       interrupts = <GIC_SPI 175 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "job", "mmu", "gpu";
+                       clocks = <&mali_cmu0>;
+                       clock-names = "gpuclk";
+                       operating-points-v2 = <&gpu_opp_table>;
+                       dma-coherent;
+
+                       gpu_opp_table: opp-table {
+                               compatible = "operating-points-v2", "operating-points-v2-mali";
+
+                               opp-400000000 {
+                                       opp-hz = /bits/ 64 <400000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                               opp-450000000 {
+                                       opp-hz = /bits/ 64 <450000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                               opp-500000000 {
+                                       opp-hz = /bits/ 64 <500000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                               opp-550000000 {
+                                       opp-hz = /bits/ 64 <550000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                               opp-600000000 {
+                                       opp-hz = /bits/ 64 <600000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                               opp-650000000 {
+                                       opp-hz = /bits/ 64 <650000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                               opp-700000000 {
+                                       opp-hz = /bits/ 64 <700000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                               opp-750000000 {
+                                       opp-hz = /bits/ 64 <750000000>;
+                                       clock-latency-ns = <10000000>;
+                               };
+                       };
+               };
+
+               usb2: usb@2c400000 {
+                       compatible = "snps,dwc3";
+                       reg = <0x0 0x2c400000 0x0 0x100000>;
+                       interrupts = <GIC_SPI 235 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "dwc_usb3";
+                       dr_mode="host";
+                       dma-coherent;
+                       clocks = <&usb_cmu0 5>, <&usb_cmu0 6>;
+                       clock-names = "bus_early", "ref";
+                       phys = <&usb_phy 0>;
+                       phy-names = "usb2-phy";
+               };
+
+               usb3: usb@2c500000 {
+                       compatible = "snps,dwc3";
+                       reg = <0x0 0x2c500000 0x0 0x100000>;
+                       interrupts = <GIC_SPI 237 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "dwc_usb3";
+                       dr_mode="host";
+                       dma-coherent;
+                       clocks = <&usb_cmu0 11>, <&usb_cmu0 12>, <&usb_cmu0 13>;
+                       clock-names = "bus_early", "ref", "suspend";
+                       phys = <&usb_phy 1>, <&usb_phy 2>;
+                       phy-names = "usb3-phy", "usb2-phy";
+               };
+
+               usb_phy: usb_phy {
+                       compatible = "baikal,bm1000-usb-phy";
+                       status = "okay";
+                       clocks = <&usb_cmu0 3>, <&usb_cmu0 4>, <&usb_cmu0 7>,
+                                <&usb_cmu0 8>, <&usb_cmu0 9>, <&usb_cmu0 10>;
+                       #phy-cells = <1>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       usb-phy@0 {
+                               reg = <0>;
+                               enable;
+                       };
+                       usb-phy@1 {
+                               reg = <1>;
+                               enable;
+                       };
+                       usb-phy@2 {
+                               reg = <2>;
+                               enable;
+                       };
+               };
+
+               sata0: sata@2c600000 {
+                       compatible = "baikal,bm1000-ahci";
+                       reg = <0x0 0x2c600000 0 0x10000>;
+                       interrupts = <GIC_SPI 233 IRQ_TYPE_LEVEL_HIGH>;
+                       ports-implemented = <1>;
+                       dma-coherent;
+                       clocks = <&soc_faxiclk>;
+                       clock-names = "aclk";
+                       status = "disabled";
+               };
+
+               sata1: sata@2c610000 {
+                       compatible = "baikal,bm1000-ahci";
+                       reg = <0x0 0x2c610000 0 0x10000>;
+                       interrupts = <GIC_SPI 234 IRQ_TYPE_LEVEL_HIGH>;
+                       ports-implemented = <1>;
+                       dma-coherent;
+                       clocks = <&soc_faxiclk>;
+                       clock-names = "aclk";
+                       status = "disabled";
+               };
+
+               dmac_m2m: dma-controller@2c630000 {
+                       compatible = "arm,pl330", "arm,primecell";
+                       reg = <0x0 0x2c630000 0 0x10000>;
+                       #dma-cells = <1>;
+                       #dma-channels = <8>;
+                       #dma-requests = <32>;
+                       interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 224 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 225 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 227 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 228 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 229 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 230 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 231 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&soc_faxiclk>;
+                       clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               gic: interrupt-controller@2d000000 {
+                       compatible = "arm,gic-v3";
+                       reg = <0x0 0x2d000000 0x0 0x10000>,  /* GICD */
+                             <0x0 0x2d100000 0x0 0x200000>, /* GICR */
+                             <0x0 0x10200000 0x0 0x2000>,   /* GICC */
+                             <0x0 0x10210000 0x0 0x1000>,   /* GICH */
+                             <0x0 0x10220000 0x0 0x2000>;   /* GICV */
+                       #interrupt-cells = <3>;
+                       #address-cells = <2>;
+                       #size-cells = <2>;
+                       ranges;
+                       interrupt-controller;
+                       interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+
+                       its: interrupt-controller@2d020000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x2d020000 0x0 0x20000>; /* GITS */
+                               msi-controller;
+                       };
+               };
+
+               xgmac0: ethernet@30200000 {
+                       compatible = "amd,xgbe-seattle-v1a";
+                       reg = <0x0 0x30200000 0x0 0x10000>,
+                             <0x0 0x30210000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 293 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 294 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 295 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 296 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 297 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 298 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 299 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 300 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 301 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 302 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 303 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 304 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 306 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 307 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 308 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 309 IRQ_TYPE_LEVEL_HIGH>;
+                       fsl,num-rx-queues = <3>;
+                       clocks = <&soc_xgbeclk>, <&soc_xgbeclk>, <&soc_xgbeclk>;
+                       clock-names = "dma_clk", "ptp_clk", "xgbe_clk";
+                       phy-mode = "xgmii";
+                       be,pcs-mode = "KX4";
+                       status = "disabled";
+                       amd,per-channel-interrupt;
+                       amd,speed-set = <0>;
+                       #stream-id-cells = <16>;
+               };
+
+               xgmac1: ethernet@30220000 {
+                       compatible = "amd,xgbe-seattle-v1a";
+                       reg = <0x0 0x30220000 0x0 0x10000>,
+                             <0x0 0x30230000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 310 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 311 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 312 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 313 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 314 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 315 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 316 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 317 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 318 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 319 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 320 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 321 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 322 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 323 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 324 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 325 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 326 IRQ_TYPE_LEVEL_HIGH>;
+                       fsl,num-rx-queues = <3>;
+                       clocks = <&soc_xgbeclk>, <&soc_xgbeclk>, <&soc_xgbeclk>;
+                       clock-names = "dma_clk", "ptp_clk", "xgbe_clk";
+                       phy-mode = "xgmii";
+                       be,pcs-mode = "KX4";
+                       status = "disabled";
+                       amd,per-channel-interrupt;
+                       amd,speed-set = <0>;
+                       #stream-id-cells = <16>;
+               };
+
+               stmmac_axi_setup: stmmac-axi-config {
+                       snps,wr_osr_lmt = <0x0>;
+                       snps,rd_osr_lmt = <0x0>;
+                       snps,blen = <0 0 0 0 0 0 4>;
+               };
+
+               gmac0: ethernet@30240000 {
+                       compatible = "baikal,bm1000-gmac";
+                       reg = <0x0 0x30240000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 291 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "macirq";
+                       max-speed = <1000>;
+                       clocks = <&soc_ethclk>, <&xgbe_cmu0 10>;
+                       clock-names = "stmmaceth", "tx2_clk";
+                       snps,axi-config = <&stmmac_axi_setup>;
+                       snps,fixed-burst;
+                       snps,no-pbl-x8;
+                       snps,txpbl = <4>;
+                       snps,rxpbl = <4>;
+                       status = "disabled";
+                       dma-coherent;
+
+                       mdio0: mdio {
+                               compatible = "snps,dwmac-mdio";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                       };
+               };
+
+               gmac1: ethernet@30250000 {
+                       compatible = "baikal,bm1000-gmac";
+                       reg = <0x0 0x30250000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 292 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "macirq";
+                       max-speed = <1000>;
+                       clocks = <&soc_ethclk>, <&xgbe_cmu0 13>;
+                       clock-names = "stmmaceth", "tx2_clk";
+                       snps,axi-config = <&stmmac_axi_setup>;
+                       snps,fixed-burst;
+                       snps,no-pbl-x8;
+                       snps,txpbl = <4>;
+                       snps,rxpbl = <4>;
+                       status = "disabled";
+                       dma-coherent;
+
+                       mdio1: mdio {
+                               compatible = "snps,dwmac-mdio";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                       };
+               };
+
+               vdu: vdu@202d0000 {
+                       compatible = "baikal,vdu";
+                       reg = <0x0 0x202d0000 0x0 0x1000>,
+                             <0x0 0x30260000 0x0 0x1000>;
+                       reg-names = "lvds_regs", "hdmi_regs";
+                       interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>,
+                               <GIC_SPI 329 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "lvds_irq", "hdmi_irq";
+                       clocks = <&avlsp_cmu1_div7>, <&xgbe_cmu1 0>;
+                       clock-names = "lvds_pclk", "hdmi_pclk";
+                       status = "disabled";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               port@0 {
+                                       reg = <0>;
+                                       vdu_hdmi_out: endpoint {
+                                               remote-endpoint = <&hdmi_tx_in>;
+                                       };
+                               };
+                       };
+               };
+
+               hdmi: hdmi@30280000 {
+                       compatible = "baikal,hdmi";
+                       reg = <0x0 0x30280000 0x0 0x20000>;
+                       reg-io-width = <4>;
+                       interrupts = <GIC_SPI 331 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&xgbe_cmu0 0>, <&xgbe_cmu0 17>;
+                       clock-names = "iahb", "isfr";
+                       status = "disabled";
+
+                       ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               port@0 {
+                                       reg = <0>;
+                                       hdmi_tx_in: endpoint {
+                                               remote-endpoint = <&vdu_hdmi_out>;
+                                       };
+                               };
+                               port@1 {
+                                       reg = <1>;
+                                       hdmi_tx_out: endpoint {
+                                               remote-endpoint = <&hdmi_con>;
+                                       };
+                               };
+                       };
+               };
+
+               pvt_cluster0: pvt@28200000 {
+                       compatible = "baikal,bm1000-pvt";
+                       reg = <0x0 0x28200000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_cluster1: pvt@c200000 {
+                       compatible = "baikal,bm1000-pvt";
+                       reg = <0x0 0xc200000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_cluster2: pvt@a200000 {
+                       compatible = "baikal,bm1000-pvt";
+                       reg = <0x0 0xa200000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_cluster3: pvt@26200000 {
+                       compatible = "baikal,bm1000-pvt";
+                       reg = <0x0 0x26200000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_mali: pvt@2a060000 {
+                       compatible = "baikal,bm1000-pvt";
+                       reg = <0x0 0x2a060000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+       };
+
+       hdmi-out {
+               compatible = "hdmi-connector";
+               label = "HDMI0 OUT";
+               type = "a";
+
+               port {
+                       hdmi_con: endpoint {
+                               remote-endpoint = <&hdmi_tx_out>;
+                       };
+               };
+       };
+};
+
+#include "bm1000-coresight.dtsi"
diff --git a/arch/arm64/boot/dts/baikal/bs1000-clocks.dtsi b/arch/arm64/boot/dts/baikal/bs1000-clocks.dtsi
new file mode 100644 (file)
index 0000000..585c84a
--- /dev/null
@@ -0,0 +1,619 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for BE-S1000 SoC clocks
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ */
+
+#define CLK_TYPE_NO    0
+#define CLK_TYPE_REFCLK        1       /* fout = 25 MHz or 100 MHz             */
+#define CLK_TYPE_PLL   2       /* fout = fref * NF / (NR * OD)         */
+#define CLK_TYPE_CLKCH 3       /* fout = fref / (VAL_CLKDIV + 1)       */
+#define CLK_TYPE_CLKEN 4       /* fout = fref / DIV_VAL                */
+#define CLK_TYPE_CLKDIV        5       /* fout = fref / 2^(DIV_VAL - 1)        */
+
+#define CA75_CLOCKS(num, base)                                         \
+       /* pll */                                                       \
+       cluster##num##_cmu0_pll:                                        \
+       cluster##num##_cmu0_pll {                                       \
+               compatible = "baikal,bs1000-cmu";                       \
+               reg = <(base + 0x0)>;                                   \
+               type = <CLK_TYPE_PLL>;                                  \
+               clocks = <&clk_ref>;                                    \
+               #clock-cells = <0>;                                     \
+               /*clock-frequency = <1600000000>;*/                     \
+               clock-output-names = "cmu0_pll";                        \
+       };                                                              \
+       cluster##num##_cmu1_pll:                                        \
+       cluster##num##_cmu1_pll {                                       \
+               compatible = "baikal,bs1000-cmu";                       \
+               reg = <(base + 0x10000)>;                               \
+               type = <CLK_TYPE_PLL>;                                  \
+               clocks = <&clk_ref>;                                    \
+               #clock-cells = <0>;                                     \
+               /*clock-frequency = <2000000000>;*/                     \
+               clock-output-names = "cmu1_pll";                        \
+       };                                                              \
+       /* clkch */                                                     \
+       cluster##num##_cmu0:                                            \
+       cluster##num##_cmu0 {                                           \
+               compatible = "baikal,bs1000-cmu";                       \
+               reg = <(base + 0x100)>;                                 \
+               type = <CLK_TYPE_CLKCH>;                                \
+               clocks = <&cluster##num##_cmu0_pll>;                    \
+               #clock-cells = <1>;                                     \
+               clock-output-names =                                    \
+                       "sclk",         /* 0 */                         \
+                       "periph",       /* 1 */                         \
+                       "pclk",         /* 2 */                         \
+                       "at",           /* 3 */                         \
+                       "gic",          /* 4 */                         \
+                       "cfg_int",      /* 5 */                         \
+                       "cfg_out",      /* 6 */                         \
+                       "bisr",         /* 7 */                         \
+                       "crp";          /* 8 */                         \
+       };                                                              \
+       /* clken */                                                     \
+       cluster##num##_cmu0_en0:                                        \
+       cluster##num##_cmu0_en0 {                                       \
+               compatible = "baikal,bs1000-cmu";                       \
+               reg = <(base + 0x500)>;                                 \
+               type = <CLK_TYPE_CLKEN>;                                \
+               clocks = <&cluster##num##_cmu0 0>; /* "sclk" */         \
+               #clock-cells = <0>;                                     \
+               clock-output-names = "clken";                           \
+       };                                                              \
+       cluster##num##_cmu0_en1:                                        \
+       cluster##num##_cmu0_en1 {                                       \
+               compatible = "baikal,bs1000-cmu";                       \
+               reg = <(base + 0x510)>;                                 \
+               type = <CLK_TYPE_CLKEN>;                                \
+               clocks = <&cluster##num##_cmu0 1>; /* "periph" */       \
+               #clock-cells = <1>;                                     \
+               clock-output-names =                                    \
+                       "cntclken",     /* 0 */                         \
+                       "tsclken";      /* 1 */                         \
+       };                                                              \
+       /* core */                                                      \
+       cluster##num##_cmu1:                                            \
+       cluster##num##_cmu1 {                                           \
+               compatible = "baikal,bs1000-cmu";                       \
+               reg = <(base + 0x10020)>;                               \
+               type = <CLK_TYPE_CLKDIV>;                               \
+               clocks = <&cluster##num##_cmu1_pll>;                    \
+               #clock-cells = <1>;                                     \
+               clock-output-names =                                    \
+                       "core0",        /* 0 */                         \
+                       "core1",        /* 1 */                         \
+                       "core2",        /* 2 */                         \
+                       "core3";        /* 3 */                         \
+       }
+
+/ {
+       soc {
+               clocks {
+                       compatible = "simple-bus";
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       /* TODO: it is fixed-clocks for QEMU, remove it */
+                       apb_clk: apb_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <200000000>;
+                               clock-output-names = "apb_pclk";
+                       };
+                       osc25: oscillator25 {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <25000000>;
+                               clock-output-names = "osc25";
+                       };
+                       osc27: oscillator27 {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <27000000>;
+                               clock-output-names = "osc27";
+                       };
+                       uart1_clk: uart1_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <8000000>;
+                               clock-output-names = "uart1_clk";
+                       };
+                       uart2_clk: uart2_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <8000000>;
+                               clock-output-names = "uart2_clk";
+                       };
+                       uart_clk: uart_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <7372800>;
+                               clock-output-names = "soc_uartclk";
+                       };
+                       i2c_clk: i2c_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <200000000>;
+                               clock-output-names = "soc_i2cclk";
+                       };
+                       timer1_clk: timer1_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <50000000>;
+                               clock-output-names = "soc_timer1clk";
+                       };
+                       timer2_clk: timer2_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <50000000>;
+                               clock-output-names = "soc_timer2clk";
+                       };
+                       timer3_clk: timer3_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <50000000>;
+                               clock-output-names = "soc_timer3clk";
+                       };
+                       timer4_clk: timer4_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <50000000>;
+                               clock-output-names = "soc_timer4clk";
+                       };
+                       wdt_clk: wdt_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <1000000>;
+                               clock-output-names = "wdtclk";
+                       };
+                       gpio_clk: gpio_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <1000000>;
+                               clock-output-names = "soc_gpioclk";
+                       };
+                       spi_clk: spi_clk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <50000000>;
+                               clock-output-names = "soc_spiclk";
+                       };
+                       soc_ethclk: ethclk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <50000000>;
+                               clock-output-names = "eth_clk";
+                       };
+                       soc_xgbeclk: xgbeclk {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <156250000>;
+                               clock-output-names = "xgbe_clk";
+                       };
+                       soc_smc50mhz: clk50mhz {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <50000000>;
+                               clock-output-names = "smc_clk";
+                       };
+                       soc_faxiclk: refclk400mhz {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <400000000>;
+                               clock-output-names = "faxi_clk";
+                       };
+                       soc_tmp_clk: refclkXXXmhz {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <100000000>;
+                               clock-output-names = "tmpclk";
+                       };
+                       clk_ahb: clk_ahb {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <100000000>;
+                               clock-output-names = "clk_ahb";
+                       };
+                       clk_xin: clk_xin {
+                               compatible = "fixed-clock";
+                               #clock-cells = <0>;
+                               clock-frequency = <100000000>;
+                               clock-output-names = "clk_xin";
+                       };
+
+                       /* CLK_REF */
+                       clk_ref: clk_ref {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0xdead>;
+                               type = <CLK_TYPE_REFCLK>;
+                               #clock-cells = <0>;
+                               //clock-frequency = <25000000>; // 25 or 100 MHz
+                               clock-output-names = "clk_ref";
+                       };
+
+                       /* CA75 */
+                       CA75_CLOCKS(0, 0x04000000);
+                       CA75_CLOCKS(1, 0x08000000);
+                       CA75_CLOCKS(2, 0x0c000000);
+                       CA75_CLOCKS(3, 0x10000000);
+                       CA75_CLOCKS(4, 0x14000000);
+                       CA75_CLOCKS(5, 0x18000000);
+                       CA75_CLOCKS(6, 0x1c000000);
+                       CA75_CLOCKS(7, 0x20000000);
+                       CA75_CLOCKS(8, 0x24000000);
+                       CA75_CLOCKS(9, 0x28000000);
+                       CA75_CLOCKS(10,0x2c000000);
+                       CA75_CLOCKS(11,0x30000000);
+
+                       /* PCIe */
+                       /* pci.pll */
+                       cmu_pci_0_pll: cmu_pci_0_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x38000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2000000000>; */
+                               clock-output-names = "pci_0_pll";
+                       };
+                       cmu_pci_1_pll: cmu_pci_1_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x3c000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2000000000>; */
+                               clock-output-names = "pci_1_pll";
+                       };
+                       cmu_pci_2_pll: cmu_pci_2_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x44000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2000000000>; */
+                               clock-output-names = "pci_2_pll";
+                       };
+                       cmu_pci_3_pll: cmu_pci_3_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x48000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2000000000>; */
+                               clock-output-names = "pci_3_pll";
+                       };
+                       cmu_pci_4_pll: cmu_pci_4_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x4c000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2000000000>; */
+                               clock-output-names = "pci_4_pll";
+                       };
+
+                       /* pci.clkch */
+                       cmu_pci_0: cmu_pci_0 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x38000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_pci_0_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "nic_cfg0m_ssa_x16p0s",         // 0
+                                       "nic_cfg1m_ssa_x8p1s",          // 1
+                                       "nic_cfg2m_mmu_tcus",           // 2
+                                       "nic_cfg3m_ssa_apbs",           // 3
+                                       "nic_cfg4m_nic_slv_gpvs",       // 4
+                                       "nic_slv0m_ssa_x16p0s",         // 5
+                                       "nic_slv1m_ssa_x8p1s",          // 6
+                                       "ssa_x16p0m_nic_mstr0s",        // 7
+                                       "ssa_x8p1m_nic_mstr1s",         // 8
+                                       "nic_mstrm_mmu_tbu0s",          // 9
+                                       "a4sw_intern",                  // 10
+                                       "phy_ref",                      // 11
+                                       "ssa_aux",                      // 12
+                                       "ahb2axim_axi2ahbs",            // 13
+                                       "ahb2ahbm_outp_ahbs",           // 14
+                                       "ssa_x16p0m_cxlas";             // 15
+                       };
+                       cmu_pci_1: cmu_pci_1 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x3c000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_pci_1_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "nic_cfg0m_ssa_x16p0s",         // 0
+                                       "nic_cfg1m_ssa_x8p1s",          // 1
+                                       "nic_cfg2m_mmu_tcus",           // 2
+                                       "nic_cfg3m_ssa_apbs",           // 3
+                                       "nic_cfg4m_nic_slv_gpvs",       // 4
+                                       "nic_slv0m_ssa_x16p0s",         // 5
+                                       "nic_slv1m_ssa_x8p1s",          // 6
+                                       "ssa_x16p0m_nic_mstr0s",        // 7
+                                       "ssa_x8p1m_nic_mstr1s",         // 8
+                                       "nic_mstrm_mmu_tbu0s",          // 9
+                                       "a4sw_intern",                  // 10
+                                       "phy_ref",                      // 11
+                                       "ssa_aux",                      // 12
+                                       "ahb2axim_axi2ahbs",            // 13
+                                       "ahb2ahbm_outp_ahbs",           // 14
+                                       "ssa_x16p0m_cxlas";             // 15
+                       };
+                       cmu_pci_2: cmu_pci_2 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x44000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_pci_2_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "a", // 0
+                                       "b"; // 1
+                       };
+                       cmu_pci_3: cmu_pci_3 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x48000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_pci_3_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "a", // 0
+                                       "b"; // 1
+                       };
+                       cmu_pci_4: cmu_pci_4 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x4c000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_pci_4_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "a", // 0
+                                       "b"; // 1
+                       };
+
+                       /* DDR */
+                       /* ddr.pll */
+                       cmu_ddr_0_pll: cmu_ddr_0_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x50000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2200000000>; */
+                               clock-output-names = "ddr_0_pll";
+                       };
+                       cmu_ddr_1_pll: cmu_ddr_1_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x54000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2200000000>; */
+                               clock-output-names = "ddr_1_pll";
+                       };
+                       cmu_ddr_2_pll: cmu_ddr_2_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x58000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2200000000>; */
+                               clock-output-names = "ddr_3_pll";
+                       };
+                       cmu_ddr_3_pll: cmu_ddr_3_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x60000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2200000000>; */
+                               clock-output-names = "ddr_3_pll";
+                       };
+                       cmu_ddr_4_pll: cmu_ddr_4_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x64000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2200000000>; */
+                               clock-output-names = "ddr_4_pll";
+                       };
+                       cmu_ddr_5_pll: cmu_ddr_5_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x68000000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <2200000000>; */
+                               clock-output-names = "ddr_5_pll";
+                       };
+
+                       /* ddr.clkch */
+                       cmu_ddr_0: cmu_ddr_0 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x50000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_ddr_0_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "ddr_ctrl_phy",         // 0
+                                       "ddr_axi_slv",          // 1
+                                       "ddr_apb_slv";          // 2
+                       };
+                       cmu_ddr_1: cmu_ddr_1 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x54000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_ddr_1_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "ddr_ctrl_phy",         // 0
+                                       "ddr_axi_slv",          // 1
+                                       "ddr_apb_slv",          // 2
+                                       "axi_ext_cfg",          // 3
+                                       "axi_middle_cfg",       // 4
+                                       "axi_top_cfg",          // 5
+                                       "axi_bottom_cfg";       // 6
+                       };
+                       cmu_ddr_2: cmu_ddr_2 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x58000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_ddr_2_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "ddr_ctrl_phy",         // 0
+                                       "ddr_axi_slv",          // 1
+                                       "ddr_apb_slv";          // 2
+                       };
+                       cmu_ddr_3: cmu_ddr_3 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x60000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_ddr_3_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "ddr_ctrl_phy",         // 0
+                                       "ddr_axi_slv",          // 1
+                                       "ddr_apb_slv";          // 2
+                       };
+                       cmu_ddr_4: cmu_ddr_4 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x64000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_ddr_4_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "ddr_ctrl_phy",         // 0
+                                       "ddr_axi_slv",          // 1
+                                       "ddr_apb_slv",          // 2
+                                       "axi_ext_cfg",          // 3
+                                       "axi_middle_cfg",       // 4
+                                       "axi_top_cfg",          // 5
+                                       "axi_bottom_cfg";       // 6
+                       };
+                       cmu_ddr_5: cmu_ddr_5 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x68000020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_ddr_5_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "ddr_ctrl_phy",         // 0
+                                       "ddr_axi_slv",          // 1
+                                       "ddr_apb_slv";          // 2
+                       };
+
+                       /* System Control */
+                       /* sc.pll */
+                       cmu_sc_0_pll: cmu_sc_0_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x00400000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <1200000000>; */
+                               clock-output-names = "sc_0_pll";
+                       };
+                       cmu_sc_1_pll: cmu_sc_1_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x00410000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <1200000000>; */
+                               clock-output-names = "sc_1_pll";
+                       };
+                       cmu_sc_2_pll: cmu_sc_2_pll {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x00420000>;
+                               type = <CLK_TYPE_PLL>;
+                               clocks = <&clk_ref>;
+                               #clock-cells = <0>;
+                               /* clock-frequency = <1500000000>; */
+                               clock-output-names = "sc_2_pll";
+                       };
+
+                       /* sc.clkch */
+                       cmu_sc_0: cmu_sc_0 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x00400020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_sc_0_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "cmn",                  // 0
+                                       "cmn_cs";               // 1
+                       };
+                       cmu_sc_1: cmu_sc_1 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x00410020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_sc_1_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "apb_cfg",              // 0
+                                       "scp_efuse",            // 1
+                                       "scp_ext_cfg",          // 2
+                                       "scp_apb_spi",          // 3
+                                       "scp_uart",             // 4
+                                       "scp_gpio8_x16",        // 5, additional divisor by 16
+                                       "scp_spi",              // 6
+                                       "scp_smbus_i2c0",       // 7
+                                       "scp_smbus_i2c1",       // 8
+                                       "gmac1_apb",            // 9
+                                       "gmac1_axi",            // 10
+                                       "gmac2_apb",            // 11
+                                       "gmac2_axi",            // 12
+                                       "usb_h",                // 13
+                                       "usb_ohci",             // 14
+                                       "lsp_apb",              // 15
+                                       "lsp_gpio_x16",         // 16, additional divisor by 16
+                                       "lsp_uart",             // 17
+                                       "lsp_uart_arm1",        // 18
+                                       "lsp_uart_arm2",        // 19
+                                       "lsp_spi1",             // 20
+                                       "lsp_spi2",             // 21
+                                       "lsp_espi",             // 22
+                                       "lsp_smbus_i2c2",       // 23
+                                       "lsp_smbus_i2c3",       // 24
+                                       "lsp_smbus_i2c4",       // 25
+                                       "lsp_smbus_i2c5",       // 26
+                                       "lsp_smbus_i2c6",       // 27
+                                       "lsp_timer1",           // 28
+                                       "lsp_timer2",           // 29
+                                       "lsp_timer3",           // 30
+                                       "lsp_timer4",           // 31
+                                       "lsp_wdt_x16",          // 32, additional divisor by 16
+                                       "tcu",                  // 33
+                                       "sc_axi_cfg",           // 34
+                                       "ca75_ahb_cfg";         // 35
+                       };
+                       cmu_sc_2: cmu_sc_2 {
+                               compatible = "baikal,bs1000-cmu";
+                               reg = <0x00420020>;
+                               type = <CLK_TYPE_CLKCH>;
+                               clocks = <&cmu_sc_2_pll>;
+                               #clock-cells = <1>;
+                               clock-output-names =
+                                       "scp",                  // 0
+                                       "gmac1_ptp",            // 1
+                                       "gmac1_txx2",           // 2
+                                       "gmac2_ptp",            // 3
+                                       "gmac2_txx2",           // 4
+                                       "gic_distr",            // 5
+                                       "dbg",                  // 6
+                                       "cnt_valueb",           // 7
+                                       "ts_valueb",            // 8
+                                       "lpd",                  // 9
+                                       "tbu",                  // 10
+                                       "hs_cfg",               // 11
+                                       "scp_bisr";             // 12
+                       };
+               };
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000-coresight.dtsi b/arch/arm64/boot/dts/baikal/bs1000-coresight.dtsi
new file mode 100644 (file)
index 0000000..440fc35
--- /dev/null
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for BE-S1000 SoC CoreSight subsystem
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ */
+
+#include <dt-bindings/arm/coresight-cti-dt.h>
+
+#define CA75_DEBUG(cl, c, base, n)                                             \
+       debug##c: debug@##base##n##10000 {                                      \
+               compatible = "arm,coresight-cpu-debug", "arm,primecell";        \
+               reg = <0x0 0x##base##n##10000 0x0 0x1000>;                      \
+               clocks = <&cluster##cl##_cmu0 2>;                               \
+               clock-names = "apb_pclk";                                       \
+               cpu = <&cpu##c>;                                                \
+       };                                                                      \
+                                                                               \
+       etm##c: etm@##base##n##40000 {                                          \
+               compatible = "arm,coresight-etm4x", "arm,primecell";            \
+               reg = <0x0 0x##base##n##40000 0x0 0x1000>;                      \
+               clocks = <&cluster##cl##_cmu0 2>;                               \
+               clock-names = "apb_pclk";                                       \
+               cpu = <&cpu##c>;                                                \
+                                                                               \
+               out-ports {                                                     \
+                       port {                                                  \
+                               etm##c##_out_port: endpoint {                   \
+                                       remote-endpoint =                       \
+                                               <&funnel##cl##_in_port##n>;     \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+       };                                                                      \
+                                                                               \
+       cti##cl##_##n: cti@##base##n##20000 {                                   \
+               compatible = "arm,coresight-cti-v8-arch", "arm,coresight-cti",  \
+                            "arm,primecell";                                   \
+               reg = <0x0 0x##base##n##20000 0x0 0x1000>;                      \
+               clocks = <&cluster##cl##_cmu0 2>;                               \
+               clock-names = "apb_pclk";                                       \
+               cpu = <&cpu##c>;                                                \
+               arm,cs-dev-assoc = <&etm##c>;                                   \
+       }
+
+#define CA75_CS(cl, c0, c1, c2, c3, cs_base, debug_base)                       \
+       CA75_DEBUG(cl,c0,debug_base,0);                                         \
+       CA75_DEBUG(cl,c1,debug_base,1);                                         \
+       CA75_DEBUG(cl,c2,debug_base,2);                                         \
+       CA75_DEBUG(cl,c3,debug_base,3);                                         \
+                                                                               \
+       funnel##cl: funnel@##cs_base##020000 {                                  \
+               compatible = "arm,coresight-dynamic-funnel", "arm,primecell";   \
+               reg = <0x0 0x##cs_base##020000 0x0 0x1000>;                     \
+               clocks = <&cluster##cl##_cmu0 2>;                               \
+               clock-names = "apb_pclk";                                       \
+                                                                               \
+               out-ports {                                                     \
+                       port {                                                  \
+                               funnel##cl##_out_port: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etb##cl##_in_port>;           \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+                                                                               \
+               in-ports {                                                      \
+                       #address-cells = <1>;                                   \
+                       #size-cells = <0>;                                      \
+                                                                               \
+                       port@0 {                                                \
+                               reg = <0>;                                      \
+                               funnel##cl##_in_port0: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etm##c0##_out_port>;          \
+                               };                                              \
+                       };                                                      \
+                                                                               \
+                       port@1 {                                                \
+                               reg = <1>;                                      \
+                               funnel##cl##_in_port1: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etm##c1##_out_port>;          \
+                               };                                              \
+                       };                                                      \
+                                                                               \
+                       port@2 {                                                \
+                               reg = <2>;                                      \
+                               funnel##cl##_in_port2: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etm##c2##_out_port>;          \
+                               };                                              \
+                       };                                                      \
+                                                                               \
+                       port@3 {                                                \
+                               reg = <3>;                                      \
+                               funnel##cl##_in_port3: endpoint {               \
+                                       remote-endpoint =                       \
+                                               <&etm##c3##_out_port>;          \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+       };                                                                      \
+                                                                               \
+       etb##cl: etb@##cs_base##030000 {                                        \
+               compatible = "arm,coresight-tmc", "arm,primecell";              \
+               reg = <0x0 0x##cs_base##030000 0x0 0x1000>;                     \
+               clocks = <&cluster##cl##_cmu0 2>;                               \
+               clock-names = "apb_pclk";                                       \
+                                                                               \
+               in-ports {                                                      \
+                       port {                                                  \
+                               etb##cl##_in_port: endpoint {                   \
+                                       remote-endpoint =                       \
+                                               <&funnel##cl##_out_port>;       \
+                               };                                              \
+                       };                                                      \
+               };                                                              \
+       };                                                                      \
+                                                                               \
+       cti##cl: cti@##cs_base##010000 {                                        \
+               compatible = "arm,coresight-cti", "arm,primecell";              \
+               reg = <0x0 0x##cs_base##010000 0x0 0x1000>;                     \
+               clocks = <&cluster##cl##_cmu0 2>;                               \
+               clock-names = "apb_pclk";                                       \
+                                                                               \
+               #address-cells = <1>;                                           \
+               #size-cells = <0>;                                              \
+                                                                               \
+               trig-conns@0 {                                                  \
+                       reg = <0>;                                              \
+                       arm,trig-in-sigs = <0 1 2>;                             \
+                       arm,trig-in-types = <SNK_ACQCOMP SNK_FULL               \
+                                            SNK_FLUSHCOMP>;                    \
+                       arm,trig-out-sigs = <0 1>;                              \
+                       arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;          \
+                       arm,cs-dev-assoc = <&etb##cl>;                          \
+               };                                                              \
+                                                                               \
+               trig-conns@1 {                                                  \
+                       reg = <1>;                                              \
+                       arm,trig-out-sigs = <2>;                                \
+                       arm,trig-conn-name = "pmu_snapshot";                    \
+               };                                                              \
+       }
+
+/ {
+       soc {
+               /* CA75 Blocks */
+
+               CA75_CS(0,0,1,2,3,6,7);
+               CA75_CS(1,4,5,6,7,a,b);
+               CA75_CS(2,8,9,10,11,e,f);
+               CA75_CS(3,12,13,14,15,12,13);
+               CA75_CS(4,16,17,18,19,16,17);
+               CA75_CS(5,20,21,22,23,1a,1b);
+               CA75_CS(6,24,25,26,27,1e,1f);
+               CA75_CS(7,28,29,30,31,22,23);
+               CA75_CS(8,32,33,34,35,26,27);
+               CA75_CS(9,36,37,38,39,2a,2b);
+               CA75_CS(10,40,41,42,43,2e,2f);
+               CA75_CS(11,44,45,46,47,32,33);
+
+               /* STM Block */
+
+               etr: etr@2080000 {
+                       compatible = "arm,coresight-tmc", "arm,primecell";
+                       reg = <0x0 0x2080000 0x0 0x1000>;
+                       clocks = <&cmu_sc_1 6>;
+                       clock-names = "apb_pclk";
+
+                       in-ports {
+                               port {
+                                       etr_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&etf_out_port>;
+                                       };
+                               };
+                       };
+               };
+
+               etf: etf@2070000 {
+                       compatible = "arm,coresight-tmc", "arm,primecell";
+                       reg = <0x0 0x2070000 0x0 0x1000>;
+                       clocks = <&cmu_sc_1 6>;
+                       clock-names = "apb_pclk";
+
+                       out-ports {
+                               port {
+                                       etf_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&etr_in_port>;
+                                       };
+                               };
+                       };
+
+                       in-ports {
+                               port {
+                                       etf_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&stm_out_port>;
+                                       };
+                               };
+                       };
+               };
+
+               stm: stm@2060000 {
+                       compatible = "arm,coresight-stm", "arm,primecell";
+                       reg = <0x0 0x2060000 0x0 0x1000>,
+                             <0x0 0x3000000 0x0 0x1000000>;
+                       reg-names = "stm-base", "stm-stimulus-base";
+                       clocks = <&cmu_sc_1 6>;
+                       clock-names = "apb_pclk";
+
+                       out-ports {
+                               port {
+                                       stm_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&etf_in_port>;
+                                       };
+                               };
+                       };
+               };
+
+               /* CMN Block */
+
+               funnel: funnel@2050000 {
+                       compatible = "arm,coresight-dynamic-funnel",
+                                    "arm,primecell";
+                       reg = <0x0 0x2050000 0x0 0x1000>;
+                       clocks = <&cmu_sc_1 6>;
+                       clock-names = "apb_pclk";
+
+                       out-ports {
+                               port {
+                                       funnel_out_port: endpoint {
+                                               remote-endpoint =
+                                                       <&etb_in_port>;
+                                       };
+                               };
+                       };
+
+                       in-ports {
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+
+                               port@0 {
+                                       reg = <0>;
+                                       funnel_in_port0: endpoint {
+                                       };
+                               };
+
+                               port@1 {
+                                       reg = <1>;
+                                       funnel_in_port1: endpoint {
+                                       };
+                               };
+                       };
+               };
+
+               etb: etb@2040000 {
+                       compatible = "arm,coresight-tmc", "arm,primecell";
+                       reg = <0x0 0x2040000 0x0 0x1000>;
+                       clocks = <&cmu_sc_1 6>;
+                       clock-names = "apb_pclk";
+
+                       in-ports {
+                               port {
+                                       etb_in_port: endpoint {
+                                               remote-endpoint =
+                                                       <&funnel_out_port>;
+                                       };
+                               };
+                       };
+               };
+
+               /* CTI */
+
+               cti: cti@2010000 {
+                       compatible = "arm,coresight-cti", "arm,primecell";
+                       reg = <0x0 0x2010000 0x0 0x1000>;
+                       clocks = <&cmu_sc_1 6>;
+                       clock-names = "apb_pclk";
+
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       trig-conns@0 {
+                               reg = <0>;
+                               arm,trig-out-sigs = <0 1>;
+                               arm,trig-out-types = <GEN_HALTREQ
+                                                     GEN_RESTARTREQ>;
+                               arm,trig-conn-name = "tsgen";
+                       };
+
+                       trig-conns@1 {
+                               reg = <1>;
+                               arm,trig-in-sigs = <0 1 2>;
+                               arm,trig-in-types = <SNK_FULL SNK_ACQCOMP
+                                                    SNK_FLUSHCOMP>;
+                               arm,trig-out-sigs = <2 3>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etb>;
+                       };
+
+                       trig-conns@2 {
+                               reg = <2>;
+                               arm,trig-in-sigs = <3 4>;
+                               arm,trig-conn-name = "cmn_debug_watchtrig";
+                       };
+
+                       trig-conns@3 {
+                               reg = <3>;
+                               arm,trig-out-sigs = <4>;
+                               arm,trig-conn-name = "cmn_pmu_snapshot";
+                       };
+
+                       trig-conns@4 {
+                               reg = <4>;
+                               arm,trig-in-sigs = <5 6 7>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL
+                                                    SNK_FLUSHCOMP>;
+                               arm,trig-out-sigs = <5 6>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etf>;
+                       };
+
+                       trig-conns@5 {
+                               reg = <5>;
+                               arm,trig-in-sigs = <8 9 10>;
+                               arm,trig-in-types = <SNK_ACQCOMP SNK_FULL
+                                                    SNK_FLUSHCOMP>;
+                               arm,trig-out-sigs = <7 8>;
+                               arm,trig-out-types = <SNK_FLUSHIN SNK_TRIGIN>;
+                               arm,cs-dev-assoc = <&etr>;
+                       };
+
+                       trig-conns@6 {
+                               reg = <6>;
+                               arm,trig-in-sigs = <11 12 13 14>;
+                               arm,trig-in-types = <STM_TOUT_SPTE STM_TOUT_SW
+                                                    STM_TOUT_HETE STM_ASYNCOUT>;
+                               arm,trig-out-sigs = <9 10 11 12>;
+                               arm,trig-out-types = <STM_HWEVENT STM_HWEVENT
+                                                     STM_HWEVENT STM_HWEVENT>;
+                               arm,cs-dev-assoc = <&stm>;
+                       };
+
+                       trig-conns@7 {
+                               reg = <7>;
+                               arm,trig-out-sigs = <13>;
+                               arm,trig-conn-name = "dpeventstatus";
+                       };
+
+                       trig-conns@8 {
+                               reg = <8>;
+                               arm,trig-out-sigs = <14 15 16 17>;
+                               arm,trig-out-types = <GEN_INTREQ GEN_INTREQ
+                                                     GEN_INTREQ GEN_INTREQ>;
+                               arm,trig-conn-name = "cti_interrupt";
+                       };
+               };
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000-dbs-mezzanine-espi.dtsi b/arch/arm64/boot/dts/baikal/bs1000-dbs-mezzanine-espi.dtsi
new file mode 100644 (file)
index 0000000..0ef4c1c
--- /dev/null
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for DBS socket X6.16 mezzanine board
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ */
+
+&espi {
+       num-cs = <4>;
+       status = "okay";
+
+       flash@0 {
+               compatible = "rohm,dh2228fv";
+               reg = <0>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+       };
+
+       flash@1 {
+               compatible = "rohm,dh2228fv";
+               reg = <1>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+       };
+
+       flash@2 {
+               compatible = "rohm,dh2228fv";
+               reg = <2>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+       };
+
+       flash@3 {
+               compatible = "rohm,dh2228fv";
+               reg = <3>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000-dbs-mezzanine-qspi2.dtsi b/arch/arm64/boot/dts/baikal/bs1000-dbs-mezzanine-qspi2.dtsi
new file mode 100644 (file)
index 0000000..929210b
--- /dev/null
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for DBS socket X11.16 mezzanine board
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ */
+
+&qspi2 {
+       num-cs = <2>;
+       status = "okay";
+
+       flash@0 {
+               compatible = "rohm,dh2228fv";
+               reg = <0>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+       };
+
+       flash@1 {
+               compatible = "rohm,dh2228fv";
+               reg = <1>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+       };
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000-dbs-ov.dts b/arch/arm64/boot/dts/baikal/bs1000-dbs-ov.dts
new file mode 100644 (file)
index 0000000..86113d2
--- /dev/null
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for DBS-OV compatible boards:
+ *   - DBS-OV vA0
+ *
+ * Copyright (C) 2022-2023 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include "bs1000-dbs.dtsi"
+
+/ {
+       model = "Baikal Electronics DBS-OV";
+       compatible = "baikal,dbs-ov", "baikal,bs1000";
+};
+
+&pcie0_p0 {
+       num-lanes = <16>;
+       status = "okay";
+};
+
+&pcie0_p0_ep {
+       num-lanes = <16>;
+       status = "okay";
+};
+
+/delete-node/ &pcie0_p0_ep;
+
+&pcie1_p0 {
+       num-lanes = <16>;
+       status = "okay";
+};
+
+&pcie1_p0_ep {
+       num-lanes = <16>;
+       status = "okay";
+};
+
+/delete-node/ &pcie1_p0_ep;
+
+&pcie2_p0 {
+       num-lanes = <16>;
+       status = "okay";
+};
+
+&pcie2_p0_ep {
+       num-lanes = <16>;
+       status = "okay";
+};
+
+/delete-node/ &pcie2_p0_ep;
+
+&pcie3_p0 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie3_p2 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie3_p3 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie4_p0 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie4_p2 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie4_p3 {
+       num-lanes = <4>;
+       status = "okay";
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000-dbs.dts b/arch/arm64/boot/dts/baikal/bs1000-dbs.dts
new file mode 100644 (file)
index 0000000..b260ac0
--- /dev/null
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for DBS compatible boards:
+ *   - DBS vA0
+ *
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include "bs1000-dbs.dtsi"
+
+/ {
+       model = "Baikal Electronics DBS";
+       compatible = "baikal,dbs", "baikal,bs1000";
+};
+
+&pcie0_p0 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie0_p0_ep {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+/delete-node/ &pcie0_p0_ep;
+
+&pcie0_p1 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie1_p0 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie1_p0_ep {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+/delete-node/ &pcie1_p0_ep;
+
+&pcie1_p1 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie2_p0 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie2_p0_ep {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+/delete-node/ &pcie2_p0_ep;
+
+&pcie2_p1 {
+       num-lanes = <8>;
+       status = "okay";
+};
+
+&pcie3_p0 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie3_p1 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie3_p2 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie3_p3 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie4_p0 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie4_p1 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie4_p2 {
+       num-lanes = <4>;
+       status = "okay";
+};
+
+&pcie4_p3 {
+       num-lanes = <4>;
+       status = "okay";
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000-dbs.dtsi b/arch/arm64/boot/dts/baikal/bs1000-dbs.dtsi
new file mode 100644 (file)
index 0000000..25b7001
--- /dev/null
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for DBS-compatible boards
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ */
+
+#include "bs1000.dtsi"
+
+/ {
+       aliases {
+               ethernet0 = &gmac0;
+               ethernet1 = &gmac1;
+       };
+
+       chosen { };
+};
+
+&ehci {
+       status = "okay";
+};
+
+&gmac0 {
+       status = "okay";
+       phy-handle = <&ethphy0>;
+       phy-mode = "rgmii-rxid";
+};
+
+&gmac1 {
+       status = "okay";
+       phy-handle = <&ethphy1>;
+       phy-mode = "rgmii-rxid";
+};
+
+&gpio32 {
+       status = "okay";
+};
+
+&i2c2 {
+       status = "okay";
+};
+
+&i2c3 {
+       status = "okay";
+};
+
+&i2c4 {
+       status = "okay";
+};
+
+&mdio0 {
+       ethphy0: ethernet-phy@1 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x1>;
+       };
+};
+
+&mdio1 {
+       ethphy1: ethernet-phy@1 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x1>;
+       };
+};
+
+&mux {
+       status = "okay";
+};
+
+&mux0 {
+       status = "okay";
+};
+
+&mux1 {
+       status = "okay";
+};
+
+&mux2 {
+       status = "okay";
+};
+
+&ohci {
+       status = "okay";
+};
+
+&pvt_cluster0 {
+       status = "okay";
+};
+
+&pvt_cluster1 {
+       status = "okay";
+};
+
+&pvt_cluster2 {
+       status = "okay";
+};
+
+&pvt_cluster3 {
+       status = "okay";
+};
+
+&pvt_cluster4 {
+       status = "okay";
+};
+
+&pvt_cluster5 {
+       status = "okay";
+};
+
+&pvt_cluster6 {
+       status = "okay";
+};
+
+&pvt_cluster7 {
+       status = "okay";
+};
+
+&pvt_cluster8 {
+       status = "okay";
+};
+
+&pvt_cluster9 {
+       status = "okay";
+};
+
+&pvt_cluster10 {
+       status = "okay";
+};
+
+&pvt_cluster11 {
+       status = "okay";
+};
+
+&pvt_ddr0 {
+       status = "okay";
+};
+
+&pvt_ddr1 {
+       status = "okay";
+};
+
+&pvt_ddr2 {
+       status = "okay";
+};
+
+&pvt_ddr3 {
+       status = "okay";
+};
+
+&pvt_ddr4 {
+       status = "okay";
+};
+
+&pvt_ddr5 {
+       status = "okay";
+};
+
+&pvt_pcie0 {
+       status = "okay";
+};
+
+&pvt_pcie1 {
+       status = "okay";
+};
+
+&pvt_pcie2 {
+       status = "okay";
+};
+
+&pvt_pcie3 {
+       status = "okay";
+};
+
+&pvt_pcie4 {
+       status = "okay";
+};
+
+&qspi1 {
+       num-cs = <4>;
+       status = "okay";
+
+       flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       partition@0 {
+                               label = "bl1";
+                               reg = <0x000000 0x40000>;
+                       };
+
+                       partition@40000 {
+                               label = "dtb";
+                               reg = <0x040000 0x40000>;
+                       };
+
+                       partition@80000 {
+                               label = "uefi-vars";
+                               reg = <0x080000 0xc0000>;
+                       };
+
+                       partition@140000 {
+                               label = "fip";
+                               reg = <0x140000 0x6c0000>;
+                       };
+               };
+       };
+};
+
+&timer1 {
+       status = "okay";
+};
+
+&timer2 {
+       status = "okay";
+};
+
+&timer3 {
+       status = "okay";
+};
+
+&timer4 {
+       status = "okay";
+};
+
+&uart_a1 {
+       status = "okay";
+};
+
+&uart_a2 {
+       status = "okay";
+};
+
+&uart_s {
+       status = "okay";
+};
+
+&wdt {
+       status = "okay";
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000-qemu-s.dts b/arch/arm64/boot/dts/baikal/bs1000-qemu-s.dts
new file mode 100644 (file)
index 0000000..16ec325
--- /dev/null
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree source for Baikal Electronics QEMU-S virtual platform
+ * Copyright (C) 2021-2022 Baikal Electronics, JSC
+ */
+
+/dts-v1/;
+
+#include "bs1000.dtsi"
+
+/ {
+       model = "Baikal Electronics QEMU-S";
+       compatible = "baikal,qemu-s", "baikal,bs1000";
+
+       aliases {
+               ethernet0 = &gmac0;
+               ethernet1 = &gmac1;
+       };
+
+       chosen { };
+
+       /*
+        * Device is necessary for UEFI to boot on QEMU,
+        * need to replace it with something later.
+        */
+       flash@0 {
+               compatible = "cfi-flash";
+               reg = <0x0 0x4000000 0x0 0x4000000>;
+               bank-width = <0x4>;
+       };
+};
+
+&ehci {
+       status = "okay";
+};
+
+&gmac0 {
+       status = "okay";
+       phy-handle = <&ethphy0>;
+       phy-mode = "rgmii-rxid";
+};
+
+&gmac1 {
+       status = "okay";
+       phy-handle = <&ethphy1>;
+       phy-mode = "rgmii-rxid";
+};
+
+&mdio0 {
+       ethphy0: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+       };
+};
+
+&mdio1 {
+       ethphy1: ethernet-phy@3 {
+               compatible = "ethernet-phy-ieee802.3-c22";
+               reg = <0x3>;
+       };
+};
+
+&ohci {
+       status = "okay";
+};
+
+&qspi1 {
+       num-cs = <4>;
+       status = "okay";
+
+       flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+               spi-max-frequency = <10000000>;
+               spi-tx-bus-width = <1>;
+               spi-rx-bus-width = <1>;
+               status = "okay";
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       partition@0 {
+                               label = "bl1";
+                               reg = <0x000000 0x40000>;
+                       };
+
+                       partition@40000 {
+                               label = "dtb";
+                               reg = <0x040000 0x40000>;
+                       };
+
+                       partition@80000 {
+                               label = "uefi-vars";
+                               reg = <0x080000 0xc0000>;
+                       };
+
+                       partition@140000 {
+                               label = "fip";
+                               reg = <0x140000 0x6c0000>;
+                       };
+               };
+       };
+};
+
+&timer1 {
+       status = "okay";
+};
+
+&uart_a1 {
+       status = "okay";
+};
+
+&uart_a2 {
+       status = "okay";
+};
+
+&uart_s {
+       status = "okay";
+};
diff --git a/arch/arm64/boot/dts/baikal/bs1000.dtsi b/arch/arm64/boot/dts/baikal/bs1000.dtsi
new file mode 100644 (file)
index 0000000..fdfff16
--- /dev/null
@@ -0,0 +1,1196 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Device tree include file for BE-S1000 SoC
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include "bs1000-clocks.dtsi"
+
+#define CA75_CLUSTER(group, a,b,c,d)                   \
+       cluster##group:                                 \
+       cluster##group {                                \
+               core0 {                                 \
+                       cpu = <&cpu##a>;                \
+               };                                      \
+               core1 {                                 \
+                       cpu = <&cpu##b>;                \
+               };                                      \
+               core2 {                                 \
+                       cpu = <&cpu##c>;                \
+               };                                      \
+               core3 {                                 \
+                       cpu = <&cpu##d>;                \
+               };                                      \
+               l3_##group: l3-cache##group {           \
+                       compatible = "cache";           \
+                       cache-size = <0x200000>;        \
+                       cache-line-size = <64>;         \
+                       cache-sets = <2048>;            \
+                       cache-unified;                  \
+                       cache-level = <3>;              \
+                       next-level-cache = <&l4>;       \
+               };                                      \
+       }
+
+#define CA75_CPU(core, group, affinity)                        \
+       cpu##core: cpu@##affinity {                     \
+               device_type = "cpu";                    \
+               compatible = "arm,cortex-a75";          \
+               reg = <0x0 0x##affinity>;               \
+               enable-method = "psci";                 \
+               i-cache-size = <0x10000>;               \
+               i-cache-line-size = <64>;               \
+               i-cache-sets = <256>;                   \
+               d-cache-size = <0x10000>;               \
+               d-cache-line-size = <64>;               \
+               d-cache-sets = <64>;                    \
+               next-level-cache = <&l2_##core>;        \
+               operating-points-v2 = <&cpu_opp_table>;         \
+               clocks = <&cluster##group##_cmu1 (core%4)>;     \
+               l2_##core: l2-cache {                           \
+                       compatible = "cache";                   \
+                       cache-size = <0x80000>;                 \
+                       cache-line-size = <64>;                 \
+                       cache-sets = <1024>;                    \
+                       cache-unified;                          \
+                       cache-level = <2>;                      \
+                       next-level-cache = <&l3_##group>;       \
+               };                                              \
+       }
+
+/ {
+       compatible = "baikal,bs1000";
+       #address-cells = <2>;
+       #size-cells = <2>;
+       interrupt-parent = <&gic>;
+
+       aliases {
+               i2c2    = &i2c2;
+               i2c3    = &i2c3;
+               i2c4    = &i2c4;
+               i2c5    = &i2c5;
+               i2c6    = &i2c6;
+               serial0 = &uart_a1;
+               serial1 = &uart_a2;
+               serial2 = &uart_s;
+       };
+
+       psci {
+               compatible = "arm,psci-1.0", "arm,psci-0.2";
+               method = "smc";
+       };
+
+       cpus {
+               #address-cells = <2>;
+               #size-cells = <0>;
+
+               cpu-map {
+                       CA75_CLUSTER(0,  0, 1, 2, 3 );
+                       CA75_CLUSTER(1,  4, 5, 6, 7 );
+                       CA75_CLUSTER(2,  8, 9, 10,11);
+                       CA75_CLUSTER(3,  12,13,14,15);
+                       CA75_CLUSTER(4,  16,17,18,19);
+                       CA75_CLUSTER(5,  20,21,22,23);
+                       CA75_CLUSTER(6,  24,25,26,27);
+                       CA75_CLUSTER(7,  28,29,30,31);
+                       CA75_CLUSTER(8,  32,33,34,35);
+                       CA75_CLUSTER(9,  36,37,38,39);
+                       CA75_CLUSTER(10, 40,41,42,43);
+                       CA75_CLUSTER(11, 44,45,46,47);
+               };
+
+               CA75_CPU(0,0,0);
+               CA75_CPU(1,0,100);
+               CA75_CPU(2,0,200);
+               CA75_CPU(3,0,300);
+               CA75_CPU(4,1,10000);
+               CA75_CPU(5,1,10100);
+               CA75_CPU(6,1,10200);
+               CA75_CPU(7,1,10300);
+               CA75_CPU(8,2,20000);
+               CA75_CPU(9,2,20100);
+               CA75_CPU(10,2,20200);
+               CA75_CPU(11,2,20300);
+               CA75_CPU(12,3,30000);
+               CA75_CPU(13,3,30100);
+               CA75_CPU(14,3,30200);
+               CA75_CPU(15,3,30300);
+               CA75_CPU(16,4,40000);
+               CA75_CPU(17,4,40100);
+               CA75_CPU(18,4,40200);
+               CA75_CPU(19,4,40300);
+               CA75_CPU(20,5,50000);
+               CA75_CPU(21,5,50100);
+               CA75_CPU(22,5,50200);
+               CA75_CPU(23,5,50300);
+               CA75_CPU(24,6,60000);
+               CA75_CPU(25,6,60100);
+               CA75_CPU(26,6,60200);
+               CA75_CPU(27,6,60300);
+               CA75_CPU(28,7,70000);
+               CA75_CPU(29,7,70100);
+               CA75_CPU(30,7,70200);
+               CA75_CPU(31,7,70300);
+               CA75_CPU(32,8,80000);
+               CA75_CPU(33,8,80100);
+               CA75_CPU(34,8,80200);
+               CA75_CPU(35,8,80300);
+               CA75_CPU(36,9,90000);
+               CA75_CPU(37,9,90100);
+               CA75_CPU(38,9,90200);
+               CA75_CPU(39,9,90300);
+               CA75_CPU(40,10,a0000);
+               CA75_CPU(41,10,a0100);
+               CA75_CPU(42,10,a0200);
+               CA75_CPU(43,10,a0300);
+               CA75_CPU(44,11,b0000);
+               CA75_CPU(45,11,b0100);
+               CA75_CPU(46,11,b0200);
+               CA75_CPU(47,11,b0300);
+
+               l4: l4-cache {
+                       cache-size = <0x2000000>;
+                       cache-unified;
+                       cache-level = <4>;
+               };
+       };
+
+       cpu_opp_table: opp_table {
+               compatible = "operating-points-v2";
+               opp-250000000 {
+                       opp-hz = /bits/ 64 <250000000>;
+               };
+               opp-500000000 {
+                       opp-hz = /bits/ 64 <500000000>;
+               };
+               opp-1000000000 {
+                       opp-hz = /bits/ 64 <1000000000>;
+               };
+               opp-2000000000 {
+                       opp-hz = /bits/ 64 <2000000000>;
+               };
+       };
+
+       pmu {
+               compatible = "arm,cortex-a75-pmu", "arm,armv8-pmuv3";
+               interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW>;
+       };
+
+       timer {
+               compatible = "arm,armv8-timer";
+               interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+                            <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+                            <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+                            <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+       };
+
+       soc {
+               compatible = "simple-bus";
+               #address-cells = <2>;
+               #size-cells = <2>;
+               ranges;
+
+               ohci: usb@a00000 {
+                       compatible = "generic-ohci";
+                       reg = <0x0 0xa00000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
+                       dr_mode = "host";
+                       dma-coherent;
+                       clocks = <&cmu_sc_1 13>, <&cmu_sc_1 14>;
+                       clock-names = "ohci_hclk", "ohci_clk48";
+                       status = "disabled";
+               };
+
+               ehci: usb@a10000 {
+                       compatible = "generic-ehci";
+                       reg = <0x0 0xa10000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
+                       dr_mode = "host";
+                       dma-coherent;
+                       clocks = <&cmu_sc_1 13>;
+                       clock-names = "ehci_hclk";
+                       status = "disabled";
+               };
+
+               gmac0: ethernet@a20000 {
+                       compatible = "baikal,bs1000-gmac";
+                       reg = <0x0 0xa20000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "macirq";
+                       clocks = <&cmu_sc_1 9>, <&cmu_sc_2 1>, <&cmu_sc_2 2>;
+                       clock-names = "stmmaceth", "ptp_ref", "tx2_clk";
+                       dma-coherent;
+                       status = "disabled";
+
+                       mdio0: mdio {
+                               compatible = "snps,dwmac-mdio";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                       };
+               };
+
+               gmac1: ethernet@a30000 {
+                       compatible = "baikal,bs1000-gmac";
+                       reg = <0x0 0xa30000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "macirq";
+                       clocks = <&cmu_sc_1 11>, <&cmu_sc_2 3>, <&cmu_sc_2 4>;
+                       clock-names = "stmmaceth", "ptp_ref", "tx2_clk";
+                       dma-coherent;
+                       status = "disabled";
+
+                       mdio1: mdio {
+                               compatible = "snps,dwmac-mdio";
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                       };
+               };
+
+               uart_a1: serial@c00000 {
+                       compatible = "arm,pl011", "arm,primecell";
+                       reg = <0x0 0xc00000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&uart1_clk>, <&apb_clk>;
+                       // clocks = <&cmu_sc_1 18>, <&cmu_sc_1 15>; // "lsp_uart_arm1", "lsp_apb"
+                       clock-names = "uartclk", "apb_pclk";
+                       status = "disabled";
+               };
+
+               uart_a2: serial@c10000 {
+                       compatible = "arm,pl011", "arm,primecell";
+                       reg = <0x0 0xc10000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&uart2_clk>, <&apb_clk>;
+                       // clocks = <&cmu_sc_1 19>, <&cmu_sc_1 15>; // "lsp_uart_arm2", "lsp_apb"
+                       clock-names = "uartclk", "apb_pclk";
+                       status = "disabled";
+               };
+
+               qspi1: spi@c20000 {
+                       compatible = "snps,dw-apb-ssi";
+                       reg = <0x0 0xc20000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 63 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&spi_clk>;
+                       // clocks = <&cmu_sc_1 20>; // "lsp_spi1"
+                       status = "disabled";
+               };
+
+               gpio32: gpio@c50000 {
+                       compatible = "snps,dw-apb-gpio";
+                       reg = <0x0 0xc50000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       clocks = <&gpio_clk>;
+                       // clocks = <&cmu_sc_1 16>; // "lsp_gpio_x16"
+                       status = "disabled";
+
+                       porta: gpio-controller@0 {
+                               compatible = "snps,dw-apb-gpio-port";
+                               gpio-controller;
+                               #gpio-cells = <2>;
+                               ngpios = <32>;
+                               reg = <0>;
+                               interrupt-controller;
+                               #interrupt-cells = <2>;
+                               interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
+                       };
+               };
+
+               i2c2: i2c@c90000 {
+                       compatible = "snps,designware-i2c";
+                       reg = <0x0 0xc90000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+                       i2c-sda-hold-time-ns = <500>;
+                       clock-frequency = <400000>;
+                       clocks = <&i2c_clk>;
+                       // clocks = <&cmu_sc_1 23>; // "lsp_smbus_i2c2"
+                       clock-names = "ref";
+                       status = "disabled";
+               };
+
+               i2c3: i2c@ca0000 {
+                       compatible = "snps,designware-i2c";
+                       reg = <0x0 0xca0000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+                       i2c-sda-hold-time-ns = <500>;
+                       clock-frequency = <400000>;
+                       clocks = <&i2c_clk>;
+                       // clocks = <&cmu_sc_1 24>; // "lsp_smbus_i2c3"
+                       clock-names = "ref";
+                       status = "disabled";
+               };
+
+               i2c4: i2c@cb0000 {
+                       compatible = "snps,designware-i2c";
+                       reg = <0x0 0xcb0000 0x0 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+                       interrupts = <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
+                       i2c-sda-hold-time-ns = <500>;
+                       clock-frequency = <400000>;
+                       clocks = <&i2c_clk>;
+                       // clocks = <&cmu_sc_1 25>; // "lsp_smbus_i2c4"
+                       clock-names = "ref";
+                       status = "disabled";
+               };
+
+               wdt: watchdog@ce0000 {
+                       compatible = "snps,dw-wdt";
+                       reg = <0x0 0xce0000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&wdt_clk 0>;
+                       // clocks = <&cmu_sc_1 32>; // "lsp_wdt_x16"
+                       clock-names = "tclk";
+                       snps,watchdog-tops = <0x000000ff 0x000001ff 0x000003ff 0x000007ff
+                                             0x0000ffff 0x0001ffff 0x0003ffff 0x0007ffff
+                                             0x000fffff 0x001fffff 0x003fffff 0x007fffff
+                                             0x00ffffff 0x01ffffff 0x03ffffff 0x07ffffff>;
+                       status = "disabled";
+               };
+
+               timer1: timer@cf0000 {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0xcf0000 0x0 0x14>;
+                       interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer1_clk>;
+                       // clocks = <&cmu_sc_1 28>; // "lsp_timer1"
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               timer2: timer@cf0014 {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0xcf0014 0x0 0x14>;
+                       interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer2_clk>;
+                       // clocks = <&cmu_sc_1 29>; // "lsp_timer2"
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               timer3: timer@cf0028 {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0xcf0028 0x0 0x14>;
+                       interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer3_clk>;
+                       // clocks = <&cmu_sc_1 30>; // "lsp_timer3"
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               timer4: timer@cf003c {
+                       compatible = "snps,dw-apb-timer";
+                       reg = <0x0 0xcf003c 0x0 0x14>;
+                       interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&timer4_clk>;
+                       //clocks = <&cmu_sc_1 31>; // "lsp_timer4"
+                       clock-names = "timer";
+                       status = "disabled";
+               };
+
+               gic: interrupt-controller@1000000 {
+                       compatible = "arm,gic-v3";
+                       reg = <0x0 0x1000000 0x0 0x10000>,  /* GICD */
+                             <0x0 0x1240000 0x0 0x600000>; /* GICR */
+                             /* TODO: optional ranges - GICC, GICH, GICV */
+                       #interrupt-cells = <3>;
+                       #address-cells = <2>;
+                       #size-cells = <2>;
+                       ranges;
+                       interrupt-controller;
+                       interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
+
+                       its0: interrupt-controller@1040000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1040000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its1: interrupt-controller@1060000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1060000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its2: interrupt-controller@1080000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1080000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its3: interrupt-controller@10a0000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x10a0000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its4: interrupt-controller@10c0000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x10c0000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its5: interrupt-controller@10e0000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x10e0000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its6: interrupt-controller@1100000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1100000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its7: interrupt-controller@1120000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1120000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its8: interrupt-controller@1140000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1140000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its9: interrupt-controller@1160000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1160000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its10: interrupt-controller@1180000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1180000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its11: interrupt-controller@11a0000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x11a0000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its12: interrupt-controller@11c0000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x11c0000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its13: interrupt-controller@11e0000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x11e0000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its14: interrupt-controller@1200000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1200000 0x0 0x20000>;
+                               msi-controller;
+                       };
+                       its15: interrupt-controller@1220000 {
+                               compatible = "arm,gic-v3-its";
+                               reg = <0x0 0x1220000 0x0 0x20000>;
+                               msi-controller;
+                       };
+               };
+
+               pcie0_p0: pcie@39000000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x39000000 0x0 0x400000>,
+                             <0x00000000 0x38d40000 0x0 0x100>,
+                             <0x00000700 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 145 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 147 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00000000 0x701 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x702 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its0>;
+                       msi-map = <0x0 &its0 0x0 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie0_p0_ep: pcie-ep@39000000 {
+                       compatible = "baikal,bs1000-pcie-ep";
+                       reg = <0x00000000 0x39000000 0x0 0x400000>,
+                             <0x00000000 0x38d40000 0x0 0x100>,
+                             <0x00000700 0x00000000 0x1 0x00000000>;
+                       reg-names = "dbi", "apb", "addr_space";
+                       num-ib-windows = <4>;
+                       num-ob-windows = <4>;
+                       status = "disabled";
+               };
+
+               pcie0_p1: pcie@39400000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x39400000 0x0 0x400000>,
+                             <0x00000000 0x38d41000 0x0 0x100>,
+                             <0x00000740 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00100000 0x741 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x742 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its0>;
+                       msi-map = <0x0 &its0 0x10000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie1_p0: pcie@3d000000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x3d000000 0x0 0x400000>,
+                             <0x00000000 0x3cd40000 0x0 0x100>,
+                             <0x00000780 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00200000 0x781 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x782 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its2>;
+                       msi-map = <0x0 &its2 0x0 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie1_p0_ep: pcie-ep@3d000000 {
+                       compatible = "baikal,bs1000-pcie-ep";
+                       reg = <0x00000000 0x3d000000 0x0 0x400000>,
+                             <0x00000000 0x3cd40000 0x0 0x100>,
+                             <0x00000780 0x00000000 0x1 0x00000000>;
+                       reg-names = "dbi", "apb", "addr_space";
+                       num-ib-windows = <4>;
+                       num-ob-windows = <4>;
+                       status = "disabled";
+               };
+
+               pcie1_p1: pcie@3d400000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x3d400000 0x0 0x400000>,
+                             <0x00000000 0x3cd41000 0x0 0x100>,
+                             <0x000007c0 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00300000 0x7c1 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x7c2 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its2>;
+                       msi-map = <0x0 &its2 0x10000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie2_p0: pcie@45000000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x45000000 0x0 0x400000>,
+                             <0x00000000 0x44d40000 0x0 0x100>,
+                             <0x00000500 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00400000 0x501 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x502 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its4>;
+                       msi-map = <0x0 &its4 0x0 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie2_p0_ep: pcie-ep@45000000 {
+                       compatible = "baikal,bs1000-pcie-ep";
+                       reg = <0x00000000 0x45000000 0x0 0x400000>,
+                             <0x00000000 0x44d40000 0x0 0x100>,
+                             <0x00000500 0x00000000 0x1 0x00000000>;
+                       reg-names = "dbi", "apb", "addr_space";
+                       num-ib-windows = <4>;
+                       num-ob-windows = <4>;
+                       status = "disabled";
+               };
+
+               pcie2_p1: pcie@45400000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x45400000 0x0 0x400000>,
+                             <0x00000000 0x44d41000 0x0 0x100>,
+                             <0x00000540 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00500000 0x541 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x542 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its4>;
+                       msi-map = <0x0 &its4 0x10000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie3_p0: pcie@49000000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x49000000 0x0 0x400000>,
+                             <0x00000000 0x48d40000 0x0 0x100>,
+                             <0x00000400 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 249 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 253 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00600000 0x401 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x402 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its6>;
+                       msi-map = <0x0 &its6 0x0 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie3_p1: pcie@49400000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x49400000 0x0 0x400000>,
+                             <0x00000000 0x48d41000 0x0 0x100>,
+                             <0x00000440 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 250 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 254 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00700000 0x441 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x442 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its7>;
+                       msi-map = <0x0 &its7 0x10000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie3_p2: pcie@49800000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x49800000 0x0 0x400000>,
+                             <0x00000000 0x48d42000 0x0 0x100>,
+                             <0x00000480 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 251 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 255 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00800000 0x481 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x482 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its8>;
+                       msi-map = <0x0 &its8 0x20000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie3_p3: pcie@49c00000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x49c00000 0x0 0x400000>,
+                             <0x00000000 0x48d43000 0x0 0x100>,
+                             <0x000004c0 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00900000 0x4c1 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x4c2 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its9>;
+                       msi-map = <0x0 &its9 0x30000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie4_p0: pcie@4d000000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x4d000000 0x0 0x400000>,
+                             <0x00000000 0x4cd40000 0x0 0x100>,
+                             <0x00000600 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 318 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 322 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00a00000 0x601 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x602 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its11>;
+                       msi-map = <0x0 &its11 0x0 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie4_p1: pcie@4d400000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x4d400000 0x0 0x400000>,
+                             <0x00000000 0x4cd41000 0x0 0x100>,
+                             <0x00000640 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 319 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 323 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00b00000 0x641 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x642 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its12>;
+                       msi-map = <0x0 &its12 0x10000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie4_p2: pcie@4d800000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x4d800000 0x0 0x400000>,
+                             <0x00000000 0x4cd42000 0x0 0x100>,
+                             <0x00000680 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 320 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 324 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00c00000 0x681 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x682 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its13>;
+                       msi-map = <0x0 &its13 0x20000 0x10000>;
+                       status = "disabled";
+               };
+
+               pcie4_p3: pcie@4dc00000 {
+                       device_type = "pci";
+                       compatible = "baikal,bs1000-pcie";
+                       reg = <0x00000000 0x4dc00000 0x0 0x400000>,
+                             <0x00000000 0x4cd43000 0x0 0x100>,
+                             <0x000006c0 0x00000000 0x0 0x10000000>;
+                       reg-names = "dbi", "apb", "config";
+                       bus-range = <0x0 0xff>;
+                       interrupts = <GIC_SPI 321 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 325 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "intr", "msi";
+                       #interrupt-cells = <1>;
+                       #address-cells = <3>;
+                       #size-cells = <2>;
+                       ranges = <0x81000000 0x0 0x00d00000 0x6c1 0x00000000 0x0 0x100000>,
+                                <0x82000000 0x0 0x40000000 0x6c2 0x00000000 0x0 0x40000000>;
+                       num-viewport = <4>;
+                       msi-parent = <&its14>;
+                       msi-map = <0x0 &its14 0x30000 0x10000>;
+                       status = "disabled";
+               };
+
+               ddr0: memory-controller@53000000 {
+                       compatible = "baikal,bs1000-edac-mc";
+                       reg = <0x0 0x53000000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 356 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 352 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       // clocks = <&cmu_ddr_0 0>;
+                       // clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               ddr1: memory-controller@57000000 {
+                       compatible = "baikal,bs1000-edac-mc";
+                       reg = <0x0 0x57000000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 375 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 373 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 372 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 369 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 371 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 370 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       // clocks = <&cmu_ddr_1 0>;
+                       // clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               ddr2: memory-controller@5b000000 {
+                       compatible = "baikal,bs1000-edac-mc";
+                       reg = <0x0 0x5b000000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 392 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 390 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 389 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 386 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 388 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 387 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       // clocks = <&cmu_ddr_2 0>;
+                       // clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               ddr3: memory-controller@63000000 {
+                       compatible = "baikal,bs1000-edac-mc";
+                       reg = <0x0 0x63000000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       // clocks = <&cmu_ddr_3 0>;
+                       // clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               ddr4: memory-controller@67000000 {
+                       compatible = "baikal,bs1000-edac-mc";
+                       reg = <0x0 0x67000000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 426 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 423 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 420 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 422 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       // clocks = <&cmu_ddr_4 0>;
+                       // clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               ddr5: memory-controller@6b000000 {
+                       compatible = "baikal,bs1000-edac-mc";
+                       reg = <0x0 0x6b000000 0x0 0x10000>;
+                       interrupts = <GIC_SPI 443 IRQ_TYPE_LEVEL_HIGH>, /* dfi_alert_err */
+                                    <GIC_SPI 441 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err */
+                                    <GIC_SPI 440 IRQ_TYPE_LEVEL_HIGH>, /* ecc_uncorrected_err */
+                                    <GIC_SPI 437 IRQ_TYPE_LEVEL_HIGH>, /* sbr_done */
+                                    <GIC_SPI 439 IRQ_TYPE_LEVEL_HIGH>, /* ecc_corrected_err_fault */
+                                    <GIC_SPI 438 IRQ_TYPE_LEVEL_HIGH>; /* ecc_uncorrected_err_fault */
+                       // clocks = <&cmu_ddr_5 0>;
+                       // clock-names = "apb_pclk";
+                       status = "disabled";
+               };
+
+               pvt_cluster0: pvt@4030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x4030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster1: pvt@8030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x8030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster2: pvt@c030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0xc030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster3: pvt@10030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x10030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster4: pvt@14030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x14030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster5: pvt@18030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x18030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster6: pvt@1c030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x1c030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster7: pvt@20030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x20030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster8: pvt@24030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x24030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster9: pvt@28030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x28030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster10: pvt@2c030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x2c030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_cluster11: pvt@30030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x30030000 0x0 0x1000>;
+                       status = "disabled";
+               };
+               pvt_pcie0: pvt@38030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x38030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_pcie1: pvt@3c030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x3c030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 206 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_pcie2: pvt@44030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x44030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_pcie3: pvt@48030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x48030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 275 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_pcie4: pvt@4c030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x4c030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 344 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_ddr0: pvt@50030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x50030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 363 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_ddr1: pvt@54030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x54030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 380 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_ddr2: pvt@58030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x58030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_ddr3: pvt@60030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x60030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 414 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_ddr4: pvt@64030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x64030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 431 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+               pvt_ddr5: pvt@68030000 {
+                       compatible = "baikal,bs1000-pvt";
+                       reg = <0x0 0x68030000 0x0 0x1000>;
+                       interrupts = <GIC_SPI 448 IRQ_TYPE_LEVEL_HIGH>;
+                       status = "disabled";
+               };
+
+               mux: mux-controller {
+                       compatible = "baikal,bs1000-lsp-mux";
+                       #address-cells = <2>;
+                       #size-cells = <2>;
+                       ranges;
+                       #mux-control-cells = <1>;
+                       status = "disabled";
+
+                       qspi2: spi@c30000 {
+                               compatible = "snps,dw-apb-ssi";
+                               reg = <0x0 0xc30000 0x0 0x1000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&spi_clk>;
+                               // clocks = <&cmu_sc_1 21>; // "lsp_spi2"
+                       };
+
+                       espi: spi@c40000 {
+                               compatible = "baikal,bs1000-espi";
+                               reg = <0x0 0xc40000 0x0 0x1000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
+                               clocks = <&spi_clk>;
+                               // clocks = <&cmu_sc_1 22>; // "lsp_espi"
+                       };
+
+                       gpio16: gpio@c60000 {
+                               compatible = "snps,dw-apb-gpio";
+                               reg = <0x0 0xc60000 0x0 0x1000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               clocks = <&gpio_clk>;
+                               // clocks = <&cmu_sc_1 16>; // "lsp_gpio_x16"
+
+                               portb: gpio-controller@0 {
+                                       compatible = "snps,dw-apb-gpio-port";
+                                       gpio-controller;
+                                       #gpio-cells = <2>;
+                                       ngpios = <16>;
+                                       reg = <0>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <2>;
+                                       interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>;
+                               };
+                       };
+
+                       gpio8_1: gpio@c70000 {
+                               compatible = "snps,dw-apb-gpio";
+                               reg = <0x0 0xc70000 0x0 0x1000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               clocks = <&gpio_clk>;
+                               // clocks = <&cmu_sc_1 16>; // "lsp_gpio_x16"
+
+                               portc: gpio-controller@0 {
+                                       compatible = "snps,dw-apb-gpio-port";
+                                       gpio-controller;
+                                       #gpio-cells = <2>;
+                                       ngpios = <8>;
+                                       reg = <0>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <2>;
+                                       interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>;
+                               };
+                       };
+
+                       gpio8_2: gpio@c80000 {
+                               compatible = "snps,dw-apb-gpio";
+                               reg = <0x0 0xc80000 0x0 0x1000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               clocks = <&gpio_clk>;
+                               // clocks = <&cmu_sc_1 16>; // "lsp_gpio_x16"
+
+                               portd: gpio-controller@0 {
+                                       compatible = "snps,dw-apb-gpio-port";
+                                       gpio-controller;
+                                       #gpio-cells = <2>;
+                                       ngpios = <8>;
+                                       reg = <0>;
+                                       interrupt-controller;
+                                       #interrupt-cells = <2>;
+                                       interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>;
+                               };
+                       };
+
+                       i2c5: i2c@cc0000 {
+                               compatible = "snps,designware-i2c";
+                               reg = <0x0 0xcc0000 0x0 0x1000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               interrupts = <GIC_SPI 68 IRQ_TYPE_LEVEL_HIGH>;
+                               i2c-sda-hold-time-ns = <500>;
+                               clock-frequency = <400000>;
+                               clocks = <&i2c_clk>;
+                               // clocks = <&cmu_sc_1 26>; // "lsp_smbus_i2c5"
+                               clock-names = "ref";
+                       };
+
+                       i2c6: i2c@cd0000 {
+                               compatible = "snps,designware-i2c";
+                               reg = <0x0 0xcd0000 0x0 0x1000>;
+                               #address-cells = <1>;
+                               #size-cells = <0>;
+                               interrupts = <GIC_SPI 69 IRQ_TYPE_LEVEL_HIGH>;
+                               i2c-sda-hold-time-ns = <500>;
+                               clock-frequency = <400000>;
+                               clocks = <&i2c_clk>;
+                               // clocks = <&cmu_sc_1 27>; // "lsp_smbus_i2c6"
+                               clock-names = "ref";
+                       };
+
+                       uart_s: serial@e00000 {
+                               compatible = "snps,dw-apb-uart";
+                               reg = <0x0 0xe00000 0x0 0x100>;
+                               interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+                               reg-shift = <2>;
+                               reg-io-width = <4>;
+                               clocks = <&uart_clk>, <&apb_clk>;
+                               // clocks = <&cmu_sc_1 17>, <&cmu_sc_1 15>; // "lsp_uart", "lsp_apb"
+                               clock-names = "baudclk", "apb_pclk";
+                       };
+               };
+
+               mux0: mux0 {
+                       compatible = "baikal,bs1000-lsp-mux-channel";
+                       mux-controls = <&mux 0>;
+                       mux-state0 = <&gpio8_1>;
+                       mux-state1 = <&uart_s &i2c5 &i2c6>;
+                       status = "disabled";
+               };
+
+               mux1: mux1 {
+                       compatible = "baikal,bs1000-lsp-mux-channel";
+                       mux-controls = <&mux 1>;
+                       mux-state0 = <&gpio8_2>;
+                       mux-state1 = <&qspi2>;
+                       status = "disabled";
+               };
+
+               mux2: mux2 {
+                       compatible = "baikal,bs1000-lsp-mux-channel";
+                       mux-controls = <&mux 2>;
+                       mux-state0 = <&gpio16>;
+                       mux-state1 = <&espi>;
+                       status = "disabled";
+               };
+       };
+};
+
+#include "bs1000-coresight.dtsi"
diff --git a/arch/arm64/configs/baikal_defconfig b/arch/arm64/configs/baikal_defconfig
new file mode 100644 (file)
index 0000000..a886644
--- /dev/null
@@ -0,0 +1,846 @@
+CONFIG_LOCALVERSION="-baikal-arm64"
+CONFIG_DEFAULT_HOSTNAME="baikal"
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
+CONFIG_NO_HZ_IDLE=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_BPF_JIT=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_USER_NS=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PROFILING=y
+CONFIG_ARCH_BAIKAL=y
+# CONFIG_ARM64_ERRATUM_826319 is not set
+# CONFIG_ARM64_ERRATUM_827319 is not set
+# CONFIG_ARM64_ERRATUM_824069 is not set
+# CONFIG_ARM64_ERRATUM_819472 is not set
+# CONFIG_ARM64_ERRATUM_832075 is not set
+# CONFIG_ARM64_ERRATUM_834220 is not set
+# CONFIG_ARM64_ERRATUM_845719 is not set
+# CONFIG_ARM64_ERRATUM_843419 is not set
+# CONFIG_ARM64_ERRATUM_1024718 is not set
+# CONFIG_ARM64_ERRATUM_1418040 is not set
+# CONFIG_ARM64_ERRATUM_1165522 is not set
+# CONFIG_ARM64_ERRATUM_1530923 is not set
+# CONFIG_ARM64_ERRATUM_2441007 is not set
+# CONFIG_ARM64_ERRATUM_1286807 is not set
+# CONFIG_ARM64_ERRATUM_1463225 is not set
+# CONFIG_ARM64_ERRATUM_1542419 is not set
+# CONFIG_ARM64_ERRATUM_1508412 is not set
+# CONFIG_ARM64_ERRATUM_2051678 is not set
+# CONFIG_ARM64_ERRATUM_2077057 is not set
+# CONFIG_ARM64_ERRATUM_2658417 is not set
+# CONFIG_ARM64_ERRATUM_2139208 is not set
+# CONFIG_ARM64_ERRATUM_2067961 is not set
+# CONFIG_ARM64_ERRATUM_2253138 is not set
+# CONFIG_ARM64_ERRATUM_2441009 is not set
+# CONFIG_ARM64_ERRATUM_2064142 is not set
+# CONFIG_ARM64_ERRATUM_2038923 is not set
+# CONFIG_ARM64_ERRATUM_1902691 is not set
+# CONFIG_ARM64_ERRATUM_2457168 is not set
+# CONFIG_CAVIUM_ERRATUM_22375 is not set
+# CONFIG_CAVIUM_ERRATUM_23154 is not set
+# CONFIG_CAVIUM_ERRATUM_27456 is not set
+# CONFIG_CAVIUM_ERRATUM_30115 is not set
+# CONFIG_CAVIUM_TX2_ERRATUM_219 is not set
+# CONFIG_FUJITSU_ERRATUM_010001 is not set
+# CONFIG_HISILICON_ERRATUM_161600802 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1003 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_1009 is not set
+# CONFIG_QCOM_QDF2400_ERRATUM_0065 is not set
+# CONFIG_QCOM_FALKOR_ERRATUM_E1041 is not set
+# CONFIG_NVIDIA_CARMEL_CNP_ERRATUM is not set
+# CONFIG_SOCIONEXT_SYNQUACER_PREITS is not set
+CONFIG_ARM64_VA_BITS_48=y
+CONFIG_SCHED_MC=y
+CONFIG_NR_CPUS=48
+CONFIG_HOTPLUG_CPU=y
+CONFIG_HZ_1000=y
+CONFIG_KEXEC_FILE=y
+CONFIG_COMPAT=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+# CONFIG_SUSPEND is not set
+CONFIG_PM=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_ARM_PSCI_CPUIDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_BAIKAL_CPUFREQ=y
+CONFIG_ACPI=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=y
+CONFIG_KPROBES=y
+CONFIG_JUMP_LABEL=y
+CONFIG_STATIC_KEYS_SELFTEST=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_BLK_DEV_THROTTLING=y
+CONFIG_BLK_WBT=y
+CONFIG_BLK_CGROUP_IOLATENCY=y
+CONFIG_BLK_CGROUP_IOCOST=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_IOSCHED_BFQ=y
+CONFIG_BFQ_GROUP_IOSCHED=y
+CONFIG_BINFMT_MISC=m
+CONFIG_ZSWAP=y
+CONFIG_Z3FOLD=m
+CONFIG_SLAB=y
+CONFIG_KSM=y
+CONFIG_TRANSPARENT_HUGEPAGE=y
+CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
+CONFIG_CMA=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_PACKET_DIAG=y
+CONFIG_UNIX=y
+CONFIG_UNIX_DIAG=y
+CONFIG_XFRM_USER=y
+CONFIG_XFRM_SUB_POLICY=y
+CONFIG_NET_KEY=y
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_FIB_TRIE_STATS=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE_DEMUX=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_NET_IPVTI=m
+CONFIG_NET_FOU=m
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_DIAG=m
+CONFIG_INET_UDP_DIAG=m
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_VEGAS=m
+CONFIG_TCP_CONG_NV=m
+CONFIG_TCP_CONG_SCALABLE=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_MD5SIG=y
+CONFIG_IPV6=m
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_MIP6=m
+CONFIG_IPV6_ILA=m
+CONFIG_IPV6_VTI=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_GRE=m
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETWORK_SECMARK=y
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_TIMEOUT=y
+CONFIG_NF_CONNTRACK_TIMESTAMP=y
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_SNMP=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_CT_NETLINK_TIMEOUT=m
+CONFIG_NF_CT_NETLINK_HELPER=m
+CONFIG_NETFILTER_NETLINK_GLUE_CT=y
+CONFIG_NF_TABLES=m
+CONFIG_NFT_NUMGEN=m
+CONFIG_NFT_CT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_AUDIT=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m
+CONFIG_NETFILTER_XT_TARGET_CT=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_SECMARK=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CGROUP=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPCOMP=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_SET_BITMAP_IP=m
+CONFIG_IP_SET_BITMAP_IPMAC=m
+CONFIG_IP_SET_BITMAP_PORT=m
+CONFIG_IP_SET_HASH_IP=m
+CONFIG_IP_SET_HASH_IPMARK=m
+CONFIG_IP_SET_HASH_IPPORT=m
+CONFIG_IP_SET_HASH_IPPORTIP=m
+CONFIG_IP_SET_HASH_IPPORTNET=m
+CONFIG_IP_SET_HASH_MAC=m
+CONFIG_IP_SET_HASH_NETPORTNET=m
+CONFIG_IP_SET_HASH_NET=m
+CONFIG_IP_SET_HASH_NETNET=m
+CONFIG_IP_SET_HASH_NETPORT=m
+CONFIG_IP_SET_HASH_NETIFACE=m
+CONFIG_IP_SET_LIST_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_IPV6=y
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_FO=m
+CONFIG_IP_VS_OVF=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+CONFIG_IP_VS_PE_SIP=m
+CONFIG_NF_LOG_ARP=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_SYNPROXY=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_NFT_BRIDGE_META=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_IP_SCTP=m
+CONFIG_SCTP_COOKIE_HMAC_SHA1=y
+CONFIG_RDS=m
+CONFIG_RDS_TCP=m
+CONFIG_TIPC=m
+CONFIG_ATM=m
+CONFIG_ATM_CLIP=m
+CONFIG_ATM_LANE=m
+CONFIG_ATM_MPOA=m
+CONFIG_ATM_BR2684=m
+CONFIG_ATM_BR2684_IPFILTER=y
+CONFIG_L2TP=m
+CONFIG_L2TP_DEBUGFS=m
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+CONFIG_BRIDGE=m
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_NET_DSA=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_VLAN_8021Q_MVRP=y
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_NL802154_EXPERIMENTAL=y
+CONFIG_MAC802154=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFB=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_MQPRIO=m
+CONFIG_NET_SCH_CHOKE=m
+CONFIG_NET_SCH_QFQ=m
+CONFIG_NET_SCH_CODEL=m
+CONFIG_NET_SCH_FQ_CODEL=m
+CONFIG_NET_SCH_FQ=m
+CONFIG_NET_SCH_HHF=m
+CONFIG_NET_SCH_PIE=m
+CONFIG_NET_SCH_PLUG=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_PERF=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_CLS_BPF=m
+CONFIG_NET_CLS_FLOWER=m
+CONFIG_NET_CLS_MATCHALL=m
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_U32=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_IPSET=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_SKBEDIT=m
+CONFIG_NET_ACT_CSUM=m
+CONFIG_NET_ACT_VLAN=m
+CONFIG_NET_ACT_BPF=m
+CONFIG_NET_ACT_CONNMARK=m
+CONFIG_NET_ACT_SKBMOD=m
+CONFIG_NET_ACT_IFE=m
+CONFIG_NET_ACT_TUNNEL_KEY=m
+CONFIG_NET_IFE_SKBMARK=m
+CONFIG_NET_IFE_SKBPRIO=m
+CONFIG_NET_IFE_SKBTCINDEX=m
+CONFIG_DCB=y
+CONFIG_BATMAN_ADV=m
+# CONFIG_BATMAN_ADV_BATMAN_V is not set
+CONFIG_BATMAN_ADV_NC=y
+CONFIG_OPENVSWITCH=m
+CONFIG_VSOCKETS=m
+CONFIG_VIRTIO_VSOCKETS=m
+CONFIG_NETLINK_DIAG=m
+CONFIG_HSR=m
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_NET_PKTGEN=y
+CONFIG_BT=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_MAC80211_MESH=y
+CONFIG_RFKILL=y
+CONFIG_RFKILL_GPIO=m
+CONFIG_NET_9P=m
+CONFIG_CEPH_LIB_USE_DNS_RESOLVER=y
+CONFIG_PCI=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIE_PTM=y
+CONFIG_PCI_REALLOC_ENABLE_AUTO=y
+CONFIG_PCI_STUB=m
+CONFIG_PCI_IOV=y
+CONFIG_PCI_PRI=y
+CONFIG_PCI_PASID=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_PCIE_BAIKAL_HOST=y
+CONFIG_PCIE_BAIKAL_EP=y
+CONFIG_PCI_ENDPOINT=y
+CONFIG_PCI_ENDPOINT_CONFIGFS=y
+CONFIG_PCI_EPF_TEST=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_CONNECTOR=y
+CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
+CONFIG_EFI_BOOTLOADER_CONTROL=y
+CONFIG_MTD=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_BAIKAL_PARTS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_DRBD=y
+CONFIG_BLK_DEV_NBD=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_ATA_OVER_ETH=m
+CONFIG_VIRTIO_BLK=m
+CONFIG_BLK_DEV_RBD=y
+CONFIG_BLK_DEV_NVME=y
+CONFIG_TP_BMC=y
+CONFIG_EEPROM_AT24=y
+CONFIG_EEPROM_AT25=m
+CONFIG_RAID_ATTRS=m
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=m
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_FC_ATTRS=m
+CONFIG_SCSI_SAS_LIBSAS=m
+CONFIG_SCSI_SRP_ATTRS=m
+CONFIG_ISCSI_TCP=m
+CONFIG_SCSI_HPSA=m
+CONFIG_SCSI_QLA_FC=m
+CONFIG_SCSI_VIRTIO=m
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_AHCI_DWC=y
+CONFIG_SATA_SIL24=m
+# CONFIG_ATA_SFF is not set
+CONFIG_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_MULTIPATH=m
+CONFIG_MD_FAULTY=m
+CONFIG_BCACHE=m
+CONFIG_BLK_DEV_DM=m
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_CACHE=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_RAID=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_QL=m
+CONFIG_DM_MULTIPATH_ST=m
+CONFIG_DM_UEVENT=y
+CONFIG_DM_SWITCH=m
+CONFIG_TARGET_CORE=m
+CONFIG_TCM_IBLOCK=m
+CONFIG_TCM_FILEIO=m
+CONFIG_TCM_PSCSI=m
+CONFIG_LOOPBACK_TARGET=m
+CONFIG_ISCSI_TARGET=m
+CONFIG_FIREWIRE=m
+CONFIG_FIREWIRE_OHCI=m
+CONFIG_FIREWIRE_SBP2=m
+CONFIG_FIREWIRE_NET=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=m
+CONFIG_NET_FC=y
+CONFIG_NET_TEAM=m
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+CONFIG_IPVLAN=m
+CONFIG_VXLAN=m
+CONFIG_GENEVE=m
+CONFIG_GTP=m
+CONFIG_MACSEC=m
+CONFIG_NETCONSOLE=m
+CONFIG_NETCONSOLE_DYNAMIC=y
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NLMON=m
+CONFIG_NET_VRF=m
+CONFIG_ATM_TCP=m
+CONFIG_AMD_XGBE=y
+CONFIG_E1000E=m
+CONFIG_IXGB=m
+CONFIG_IXGBE=m
+CONFIG_IXGBEVF=m
+CONFIG_STMMAC_ETH=y
+CONFIG_DWMAC_BAIKAL=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MICREL_PHY=y
+CONFIG_REALTEK_PHY=y
+CONFIG_MDIO_BITBANG=y
+CONFIG_MDIO_GPIO=m
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_ATH10K=m
+CONFIG_ATH10K_PCI=m
+CONFIG_RTL8192CE=m
+CONFIG_RTL8192SE=m
+CONFIG_RTL8192DE=m
+CONFIG_RTL8723AE=m
+CONFIG_RTL8723BE=m
+CONFIG_RTL8188EE=m
+CONFIG_RTL8192EE=m
+CONFIG_RTL8821AE=m
+# CONFIG_IEEE802154_DRIVERS is not set
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_EVDEV=y
+# CONFIG_MOUSE_PS2 is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=2
+CONFIG_SERIAL_8250_RUNTIME_UARTS=2
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_BAIKAL_SMBUS=y
+CONFIG_I2C_DESIGNWARE_SLAVE=y
+CONFIG_I2C_DESIGNWARE_PLATFORM=y
+CONFIG_SPI=y
+CONFIG_SPI_BAIKAL_ESPI=y
+CONFIG_SPI_DESIGNWARE=y
+CONFIG_SPI_DW_DMA=y
+CONFIG_SPI_DW_MMIO=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_GPIO_PCF857X=y
+CONFIG_SENSORS_BT1_PVT=y
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+# CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED is not set
+CONFIG_GPIO_WATCHDOG=m
+CONFIG_DW_WATCHDOG=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_PWC=m
+CONFIG_USB_S2255=m
+CONFIG_VIDEO_USBTV=m
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_V4L2=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_DRM=y
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+CONFIG_DRM_BAIKAL_VDU=y
+CONFIG_DRM_RADEON=m
+CONFIG_DRM_AMDGPU=m
+CONFIG_DRM_NOUVEAU=m
+CONFIG_DRM_VGEM=m
+CONFIG_DRM_QXL=m
+CONFIG_DRM_VIRTIO_GPU=m
+CONFIG_DRM_PANEL_BAIKAL_LVDS=y
+CONFIG_DRM_PANEL_LVDS=y
+CONFIG_DRM_BAIKAL_HDMI=y
+CONFIG_DRM_BAIKAL_HDMI_AHB_AUDIO=y
+CONFIG_DRM_CIRRUS_QEMU=m
+CONFIG_DRM_PANFROST=m
+CONFIG_FB=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_HRTIMER=m
+# CONFIG_SND_SUPPORT_OLD_API is not set
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQUENCER_OSS=m
+CONFIG_SND_DUMMY=m
+CONFIG_SND_HDA_BAIKAL=y
+CONFIG_SND_HDA_CODEC_REALTEK=y
+# CONFIG_SND_SPI is not set
+CONFIG_SND_USB_AUDIO=y
+# CONFIG_SND_FIREWIRE is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_BAIKAL_I2S=y
+CONFIG_SND_BAIKAL_PIO_PCM=y
+CONFIG_SND_SOC_AC97_CODEC=m
+CONFIG_SND_SOC_SPDIF=m
+CONFIG_SND_SOC_TLV320AIC3X_I2C=y
+CONFIG_SND_SOC_NAU8822=y
+CONFIG_SND_SIMPLE_CARD=y
+CONFIG_HID_BATTERY_STRENGTH=y
+CONFIG_HIDRAW=y
+CONFIG_UHID=m
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_HIDPP=m
+CONFIG_USB_ULPI_BUS=y
+CONFIG_USB=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_HCD_TEST_MODE=y
+CONFIG_USB_ACM=m
+CONFIG_USB_STORAGE=y
+CONFIG_USB_UAS=y
+CONFIG_USBIP_CORE=m
+CONFIG_USBIP_VHCI_HCD=m
+CONFIG_USBIP_HOST=m
+CONFIG_USB_DWC3=y
+CONFIG_USB_DWC3_ULPI=y
+# CONFIG_USB_DWC3_HAPS is not set
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_ULPI=y
+CONFIG_MMC=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_OF_DWCMSHC=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_USER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ABEOZ9=y
+CONFIG_RTC_DRV_PCF2127=y
+CONFIG_DMADEVICES=y
+CONFIG_PL330_DMA=m
+CONFIG_DW_DMAC=y
+CONFIG_VIRT_DRIVERS=y
+CONFIG_VIRTIO_PCI=m
+CONFIG_VIRTIO_BALLOON=m
+CONFIG_VIRTIO_INPUT=m
+CONFIG_VIRTIO_MMIO=m
+CONFIG_VHOST_NET=m
+CONFIG_VHOST_SCSI=m
+CONFIG_VHOST_VSOCK=m
+CONFIG_VHOST_CROSS_ENDIAN_LEGACY=y
+CONFIG_BAIKAL_DW_APB_TIMER=y
+# CONFIG_FSL_ERRATUM_A008585 is not set
+# CONFIG_HISILICON_ERRATUM_161010101 is not set
+# CONFIG_ARM64_ERRATUM_858921 is not set
+CONFIG_MAILBOX=y
+CONFIG_ARM_MHU=m
+CONFIG_ARM_SMMU=y
+CONFIG_ARM_SMMU_V3=y
+CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
+CONFIG_USB_PHY_BAIKAL=y
+CONFIG_ARM_CCN=y
+CONFIG_MUX_BAIKAL=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_XFS_FS=y
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_XFS_RT=y
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+CONFIG_FS_ENCRYPTION=y
+CONFIG_FANOTIFY=y
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_FSCACHE=y
+CONFIG_FSCACHE_STATS=y
+CONFIG_CACHEFILES=y
+CONFIG_ISO9660_FS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+CONFIG_FAT_DEFAULT_UTF8=y
+CONFIG_NTFS_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_CHILDREN=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_EFIVAR_FS=y
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_FILE_DIRECT=y
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_SQUASHFS_ZSTD=y
+CONFIG_PSTORE=y
+# CONFIG_PSTORE_DEFLATE_COMPRESS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_SWAP=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_NFS_V4_1_IMPLEMENTATION_ID_DOMAIN="y"
+CONFIG_NFS_V4_1_MIGRATION=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_BLOCKLAYOUT=y
+CONFIG_NFSD_SCSILAYOUT=y
+CONFIG_SUNRPC_DEBUG=y
+CONFIG_9P_FS=m
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_9P_FS_SECURITY=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_852=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_UTF8=y
+CONFIG_PERSISTENT_KEYRINGS=y
+CONFIG_ENCRYPTED_KEYS=y
+CONFIG_CRYPTO_CRYPTD=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_LZ4=y
+CONFIG_CRYPTO_LZ4HC=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_USER_API_HASH=y
+CONFIG_CRYPTO_USER_API_SKCIPHER=y
+CONFIG_CRYPTO_USER_API_AEAD=y
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=256
+CONFIG_IRQ_POLL=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_STRIP_ASM_SYMS=y
+CONFIG_PAGE_EXTENSION=y
+CONFIG_PAGE_POISONING=y
+CONFIG_IO_STRICT_DEVMEM=y
+CONFIG_CORESIGHT=m
+CONFIG_CORESIGHT_LINK_AND_SINK_TMC=m
+CONFIG_CORESIGHT_SINK_TPIU=m
+CONFIG_CORESIGHT_SOURCE_ETM4X=m
+CONFIG_ETM4X_IMPDEF_FEATURE=y
+CONFIG_CORESIGHT_STM=m
+CONFIG_CORESIGHT_CPU_DEBUG=m
+CONFIG_CORESIGHT_CTI=m
+CONFIG_CORESIGHT_TRBE=m
+CONFIG_TEST_USER_COPY=m
+CONFIG_TEST_BPF=m
+CONFIG_TEST_FIRMWARE=m
+CONFIG_TEST_STATIC_KEYS=m
index 860014b89b8ebe80fcfecb6107b56423f15770c0..f627d0cb45663662d28682b9929ca67278c94ae3 100644 (file)
@@ -171,6 +171,30 @@ static struct mcfg_fixup mcfg_quirks[] = {
        ALTRA_ECAM_QUIRK(1, 13),
        ALTRA_ECAM_QUIRK(1, 14),
        ALTRA_ECAM_QUIRK(1, 15),
+
+#define BAIKAL_ECAM(table_id, rev, seg, ops) \
+       { "BAIKAL", table_id, rev, seg, MCFG_BUS_ANY, ops }
+
+       /* Baikal-M Synopsys DesignWare PCIe */
+       BAIKAL_ECAM("BKLEMCFG", 1, 0, &baikal_m_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 1, 1, &baikal_m_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 1, 2, &baikal_m_pcie_ecam_ops),
+
+       /* Baikal-S Synopsys DesignWare PCIe */
+       BAIKAL_ECAM("BKLEMCFG", 2, 0, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 1, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 2, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 3, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 4, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 5, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 6, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 7, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 8, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 9, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 10, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 11, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 12, &baikal_s_pcie_ecam_ops),
+       BAIKAL_ECAM("BKLEMCFG", 2, 13, &baikal_s_pcie_ecam_ops),
 #endif /* ARM64 */
 
 #ifdef CONFIG_LOONGARCH
index 8fb66860db318d57dd86aab3a895427eb4707e36..a694e6c2dae4ec39fda648d5fe6cde2e74d8ef7a 100644 (file)
@@ -468,10 +468,15 @@ static struct ahci_dwc_plat_data ahci_bt1_plat = {
        .init = ahci_bt1_init,
 };
 
+static struct ahci_dwc_plat_data ahci_bm1000_plat = {
+       .pflags = AHCI_PLATFORM_GET_RESETS | AHCI_PLATFORM_RST_TRIGGER,
+};
+
 static const struct of_device_id ahci_dwc_of_match[] = {
        { .compatible = "snps,dwc-ahci", &ahci_dwc_plat },
        { .compatible = "snps,spear-ahci", &ahci_dwc_plat },
        { .compatible = "baikal,bt1-ahci", &ahci_bt1_plat },
+       { .compatible = "baikal,bm1000-ahci", &ahci_bm1000_plat },
        {},
 };
 MODULE_DEVICE_TABLE(of, ahci_dwc_of_match);
index e3ca0d058a25671f2c391a1a42748358e136256b..5e86cc0f7a25194ec9da32a966010f321774fece 100644 (file)
@@ -83,6 +83,7 @@ obj-y                                 += analogbits/
 obj-$(CONFIG_COMMON_CLK_AT91)          += at91/
 obj-$(CONFIG_ARCH_ARTPEC)              += axis/
 obj-$(CONFIG_ARC_PLAT_AXS10X)          += axs10x/
+obj-$(CONFIG_ARCH_BAIKAL)              += baikal/
 obj-$(CONFIG_CLK_BAIKAL_T1)            += baikal-t1/
 obj-y                                  += bcm/
 obj-$(CONFIG_ARCH_BERLIN)              += berlin/
diff --git a/drivers/clk/baikal/Makefile b/drivers/clk/baikal/Makefile
new file mode 100644 (file)
index 0000000..dc94047
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += clk-bm1000.o
+obj-y += clk-bs1000.o
diff --git a/drivers/clk/baikal/clk-bm1000.c b/drivers/clk/baikal/clk-bm1000.c
new file mode 100644 (file)
index 0000000..d986c8e
--- /dev/null
@@ -0,0 +1,841 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015-2023 Baikal Electronics, JSC
+ * Author: Ekaterina Skachko <ekaterina.skachko@baikalelectronics.ru>
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define BAIKAL_SMC_CMU_CMD     0x82000000
+#define CMU_PLL_SET_RATE       0
+#define CMU_PLL_GET_RATE       1
+#define CMU_PLL_ENABLE         2
+#define CMU_PLL_DISABLE                3
+#define CMU_PLL_ROUND_RATE     4
+#define CMU_PLL_IS_ENABLED     5
+#define CMU_CLK_CH_SET_RATE    6
+#define CMU_CLK_CH_GET_RATE    7
+#define CMU_CLK_CH_ENABLE      8
+#define CMU_CLK_CH_DISABLE     9
+#define CMU_CLK_CH_ROUND_RATE  10
+#define CMU_CLK_CH_IS_ENABLED  11
+
+struct baikal_clk_cmu {
+       struct clk_hw   hw;
+       u32             base;
+       unsigned int    parent;
+       const char      *name;
+       u32             is_clk_ch;
+};
+
+#define to_baikal_cmu(_hw) container_of(_hw, struct baikal_clk_cmu, hw)
+
+static int baikal_clk_enable(struct clk_hw *hw)
+{
+       struct arm_smccc_res res;
+       struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+       u32 cmd;
+
+       if (pclk->is_clk_ch)
+               cmd = CMU_CLK_CH_ENABLE;
+       else
+               cmd = CMU_PLL_ENABLE;
+
+       /* If clock valid */
+       arm_smccc_smc(BAIKAL_SMC_CMU_CMD, pclk->base, cmd, 0,
+                     pclk->parent, 0, 0, 0, &res);
+
+       pr_debug("%s(%s, %s@0x%x): %s\n",
+                __func__,
+                pclk->name,
+                pclk->is_clk_ch ? "clkch" : "pll",
+                pclk->base,
+                res.a0 ? "error" : "ok");
+
+       return res.a0;
+}
+
+static void baikal_clk_disable(struct clk_hw *hw)
+{
+       struct arm_smccc_res res;
+       struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+       u32 cmd;
+
+       if (pclk->is_clk_ch)
+               cmd = CMU_CLK_CH_DISABLE;
+       else
+               cmd = CMU_PLL_DISABLE;
+
+       /* If clock valid */
+       arm_smccc_smc(BAIKAL_SMC_CMU_CMD, pclk->base, cmd, 0,
+                     pclk->parent, 0, 0, 0, &res);
+
+       pr_debug("%s(%s, %s@0x%x): %s\n",
+                __func__,
+                pclk->name,
+                pclk->is_clk_ch ? "clkch" : "pll",
+                pclk->base,
+                res.a0 ? "error" : "ok");
+}
+
+static int baikal_clk_is_enabled(struct clk_hw *hw)
+{
+       struct arm_smccc_res res;
+       struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+       u32 cmd;
+
+       if (pclk->is_clk_ch)
+               cmd = CMU_CLK_CH_IS_ENABLED;
+       else
+               cmd = CMU_PLL_IS_ENABLED;
+
+       /* If clock valid */
+       arm_smccc_smc(BAIKAL_SMC_CMU_CMD, pclk->base, cmd, 0,
+                     pclk->parent, 0, 0, 0, &res);
+
+       pr_debug("%s(%s, %s@0x%x): %s\n",
+                __func__,
+                pclk->name,
+                pclk->is_clk_ch ? "clkch" : "pll",
+                pclk->base,
+                res.a0 ? "true" : "false");
+
+       return res.a0;
+}
+
+static unsigned long baikal_clk_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct arm_smccc_res res;
+       struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+       u32 cmd;
+       unsigned long parent;
+
+       if (pclk->is_clk_ch) {
+               cmd = CMU_CLK_CH_GET_RATE;
+               parent = pclk->parent;
+       } else {
+               cmd = CMU_PLL_GET_RATE;
+               parent = parent_rate;
+       }
+
+       /* If clock valid */
+       arm_smccc_smc(BAIKAL_SMC_CMU_CMD, pclk->base, cmd, 0,
+                     parent, 0, 0, 0, &res);
+
+       pr_debug("%s(%s, %s@0x%x): %ld Hz\n",
+                __func__,
+                pclk->name,
+                pclk->is_clk_ch ? "clkch" : "pll",
+                pclk->base,
+                res.a0);
+
+       /* Return actual freq */
+       return res.a0;
+}
+
+static int baikal_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long parent_rate)
+{
+       struct arm_smccc_res res;
+       struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+       u32 cmd;
+       unsigned long parent;
+
+       if (pclk->is_clk_ch) {
+               cmd = CMU_CLK_CH_SET_RATE;
+               parent = pclk->parent;
+       } else {
+               cmd = CMU_PLL_SET_RATE;
+               parent = parent_rate;
+       }
+
+       arm_smccc_smc(BAIKAL_SMC_CMU_CMD, pclk->base, cmd, rate,
+                     parent, 0, 0, 0, &res);
+
+       pr_debug("%s(%s, %s@0x%x, %ld Hz): %s\n",
+                __func__,
+                pclk->name,
+                pclk->is_clk_ch ? "clkch" : "pll",
+                pclk->base,
+                rate,
+                res.a0 ? "error" : "ok");
+
+       return res.a0;
+}
+
+static long baikal_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long *prate)
+{
+       struct arm_smccc_res res;
+       struct baikal_clk_cmu *pclk = to_baikal_cmu(hw);
+       u32 cmd;
+       unsigned long parent;
+
+       if (pclk->is_clk_ch) {
+               cmd = CMU_CLK_CH_ROUND_RATE;
+               parent = pclk->parent;
+       } else {
+               cmd = CMU_PLL_ROUND_RATE;
+               parent = *prate;
+       }
+
+       /* If clock valid */
+       arm_smccc_smc(BAIKAL_SMC_CMU_CMD, pclk->base, cmd, rate,
+                     parent, 0, 0, 0, &res);
+
+       pr_debug("%s(%s, %s@0x%x): %ld Hz\n",
+                __func__,
+                pclk->name,
+                pclk->is_clk_ch ? "clkch" : "pll",
+                pclk->base,
+                res.a0);
+
+       /* Return actual freq */
+       return res.a0;
+}
+
+static const struct clk_ops baikal_clk_ops = {
+       .enable      = baikal_clk_enable,
+       .disable     = baikal_clk_disable,
+       .is_enabled  = baikal_clk_is_enabled,
+       .recalc_rate = baikal_clk_recalc_rate,
+       .set_rate    = baikal_clk_set_rate,
+       .round_rate  = baikal_clk_round_rate,
+};
+
+static int baikal_clk_probe(struct platform_device *pdev)
+{
+       struct clk_init_data init;
+       struct clk_init_data *init_ch;
+       struct baikal_clk_cmu *cmu;
+       struct baikal_clk_cmu **cmu_ch;
+       struct device_node *node = pdev->dev.of_node;
+
+       struct clk *clk;
+       struct clk_onecell_data *clk_ch;
+
+       int i = 0, number, rc;
+       u32 index;
+       u64 base;
+       struct property *prop;
+       const __be32 *p;
+       const char *clk_ch_name;
+       const char *parent_name;
+
+       cmu = kmalloc(sizeof(*cmu), GFP_KERNEL);
+       if (!cmu)
+               return -ENOMEM;
+
+       of_property_read_string(node, "clock-output-names", &cmu->name);
+       of_property_read_u32(node, "clock-frequency", &cmu->parent);
+       rc = of_property_read_u64(node, "reg", &base);
+       if (rc)
+               return rc;
+
+       cmu->base = base;
+
+       parent_name = of_clk_get_parent_name(node, 0);
+
+       /* Setup clock init structure */
+       init.parent_names = &parent_name;
+       init.num_parents = 1;
+       init.name = cmu->name;
+       init.ops = &baikal_clk_ops;
+       init.flags = CLK_IGNORE_UNUSED;
+
+       cmu->hw.init = &init;
+       cmu->is_clk_ch = 0;
+
+       pr_debug("%s: add %s, parent %s\n",
+                __func__, cmu->name, parent_name ? parent_name : "null");
+
+       clk = clk_register(NULL, &cmu->hw);
+       if (IS_ERR(clk)) {
+               pr_err("%s: could not register clk %s\n", __func__, cmu->name);
+               return -ENOMEM;
+       }
+
+       /* Register the clock for lookup */
+       rc = clk_register_clkdev(clk, cmu->name, NULL);
+       if (rc != 0) {
+               pr_err("%s: could not register lookup clk %s\n",
+                      __func__, cmu->name);
+       }
+
+       /* FIXME: we probably shouldn't enable it here */
+       clk_prepare_enable(clk);
+
+       number = of_property_count_u32_elems(node, "clock-indices");
+
+       if (number > 0) {
+               clk_ch = kmalloc(sizeof(*clk_ch), GFP_KERNEL);
+               if (!clk_ch)
+                       return -ENOMEM;
+
+               /* Get the last index to find out max number of children*/
+               of_property_for_each_u32(node, "clock-indices", prop, p, index) {
+                       ;
+               }
+
+               clk_ch->clks = kcalloc(index + 1, sizeof(struct clk *), GFP_KERNEL);
+               clk_ch->clk_num = index + 1;
+               cmu_ch = kcalloc((index + 1), sizeof(struct baikal_clk_cmu *), GFP_KERNEL);
+               if (!cmu_ch) {
+                       kfree(clk_ch);
+                       return -ENOMEM;
+               }
+
+               init_ch = kcalloc((number + 1), sizeof(struct clk_init_data), GFP_KERNEL);
+               if (!init_ch) {
+                       kfree(cmu_ch);
+                       kfree(clk_ch);
+                       return -ENOMEM;
+               }
+
+               of_property_for_each_u32(node, "clock-indices", prop, p, index) {
+                       of_property_read_string_index(node, "clock-names",
+                                                     i, &clk_ch_name);
+                       pr_debug("%s: clkch <%s>, index %d, i %d\n", __func__, clk_ch_name, index, i);
+                       init_ch[i].parent_names = &cmu->name;
+                       init_ch[i].num_parents = 1;
+                       init_ch[i].name = clk_ch_name;
+                       init_ch[i].ops = &baikal_clk_ops;
+                       init_ch[i].flags = CLK_IGNORE_UNUSED;
+
+                       cmu_ch[index] = kmalloc(sizeof(*cmu_ch[index]), GFP_KERNEL);
+                       cmu_ch[index]->name = clk_ch_name;
+                       cmu_ch[index]->base = index;
+                       cmu_ch[index]->parent = cmu->base;
+                       cmu_ch[index]->is_clk_ch = 1;
+                       cmu_ch[index]->hw.init = &init_ch[i];
+                       clk_ch->clks[index] = clk_register(NULL, &cmu_ch[index]->hw);
+
+                       if (IS_ERR(clk_ch->clks[index])) {
+                               pr_err("%s: could not register clk %s\n",
+                                      __func__, clk_ch_name);
+                       }
+
+                       /* Register the clock for lookup */
+                       rc = clk_register_clkdev(clk_ch->clks[index], clk_ch_name, NULL);
+                       if (rc != 0) {
+                               pr_err("%s: could not register lookup clk %s\n",
+                                      __func__, clk_ch_name);
+                       }
+
+                       /* FIXME: we probably shouldn't enable it here */
+                       clk_prepare_enable(clk_ch->clks[index]);
+                       i++;
+               }
+
+               return of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, clk_ch);
+       }
+
+       return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
+}
+
+static int baikal_clk_remove(struct platform_device *pdev)
+{
+       of_clk_del_provider(pdev->dev.of_node);
+       return 0;
+}
+
+#ifdef CONFIG_ACPI
+const char *baikal_acpi_clk_osc25_str[] = { "osc25" };
+const char *baikal_acpi_clk_osc27_str[] = { "osc27" };
+
+static struct clk *baikal_acpi_clk_osc25;
+static struct clk *baikal_acpi_clk_osc27;
+
+#define BAIKAL_CMU_CLK_CH      0x0
+#define BAIKAL_FIXED_CLK       0x1
+#define BAIKAL_FIXED_FACTOR_CLK        0x2
+#define BAIKAL_CMU_CLK          0xffffffffffffffff
+
+struct baikal_acpi_clk_data {
+       struct clk *cmu_clk;
+       struct clk_lookup *cmu_clk_l;
+       struct clk_lookup **cmu_clk_refs_l;
+       struct clk **clk;
+       struct clk_lookup **clk_l;
+       u8 *type;
+       unsigned int clk_num;
+       unsigned int cmu_clk_refs_num;
+};
+
+static int baikal_acpi_clk_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct acpi_device *ref_dev, *adev = to_acpi_device_node(pdev->dev.fwnode);
+       struct clk_init_data init, *init_ch;
+       struct baikal_clk_cmu *cmu, *cmu_ch;
+       struct baikal_acpi_clk_data *clk_data = NULL;
+       union acpi_object *package, *element;
+       acpi_status status;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       int osc27, i, ret = 0;
+       char *str, *str2;
+
+       cmu = devm_kzalloc(dev, sizeof(*cmu), GFP_KERNEL);
+       if (!cmu)
+               return -ENOMEM;
+
+       status = acpi_evaluate_object_typed(adev->handle, "PROP", NULL, &buffer, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "failed to get PROP data\n");
+               return -ENODEV;
+       }
+
+       package = buffer.pointer;
+       if (package->package.count != 4) {
+               dev_err(dev, "invalid PROP data\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       element = &package->package.elements[0];
+       if (element->type != ACPI_TYPE_INTEGER) {
+               dev_err(dev, "failed to get CMU id\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       cmu->base = element->integer.value;
+
+       element = &package->package.elements[1];
+       if (element->type != ACPI_TYPE_STRING) {
+               dev_err(dev, "failed to get CMU clock name\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       str = devm_kzalloc(dev, element->string.length + 1, GFP_KERNEL);
+       if (!str) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       memcpy(str, element->string.pointer, element->string.length);
+       cmu->name = str;
+
+       element = &package->package.elements[2];
+       if (element->type != ACPI_TYPE_INTEGER) {
+               dev_err(dev, "failed to get CMU frequency\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       cmu->parent = element->integer.value;
+
+       element = &package->package.elements[3];
+       if (element->type != ACPI_TYPE_INTEGER) {
+               dev_err(dev, "failed to get CMU osc type\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       osc27 = element->integer.value ? 1 : 0;
+
+       acpi_os_free(buffer.pointer);
+       buffer.length = ACPI_ALLOCATE_BUFFER;
+       buffer.pointer = NULL;
+
+       init.parent_names = osc27 ? baikal_acpi_clk_osc27_str : baikal_acpi_clk_osc25_str;
+       init.num_parents = 1;
+       init.name = cmu->name;
+       init.ops = &baikal_clk_ops;
+       init.flags = CLK_IGNORE_UNUSED;
+
+       cmu->hw.init = &init;
+       cmu->is_clk_ch = 0;
+
+       clk_data = devm_kzalloc(dev, sizeof(*clk_data), GFP_KERNEL);
+       if (!clk_data)
+               return -ENOMEM;
+
+       clk_data->cmu_clk = clk_register(NULL, &cmu->hw);
+       if (IS_ERR(clk_data->cmu_clk)) {
+               dev_err(dev, "failed to register CMU clock\n");
+               return PTR_ERR(clk_data->cmu_clk);
+       }
+
+       clk_data->cmu_clk_l = clkdev_create(clk_data->cmu_clk, cmu->name, NULL);
+       if (!clk_data->cmu_clk_l) {
+               dev_err(dev, "failed to register CMU clock lookup\n");
+               clk_unregister(clk_data->cmu_clk);
+               return -ENOMEM;
+       }
+
+       clk_prepare_enable(clk_data->cmu_clk);
+
+       platform_set_drvdata(pdev, clk_data);
+
+       /* CPU clock */
+       status = acpi_evaluate_object_typed(adev->handle, "CMU", NULL, &buffer, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               buffer.pointer = NULL;
+               goto clk_channels;
+       }
+
+       package = buffer.pointer;
+       if (!package->package.count || package->package.count % 2) {
+               dev_err(dev, "invalid CMU data\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       clk_data->cmu_clk_refs_num = package->package.count >> 1;
+       clk_data->cmu_clk_refs_l = devm_kzalloc(dev,
+                                               clk_data->cmu_clk_refs_num * sizeof(struct clk_lookup *),
+                                               GFP_KERNEL);
+       if (!clk_data->cmu_clk_refs_l) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       for (i = 0; i < clk_data->cmu_clk_refs_num; ++i) {
+               ref_dev = NULL;
+
+               element = &package->package.elements[2 * i];
+               if (element->type == ACPI_TYPE_LOCAL_REFERENCE && element->reference.handle)
+                       ref_dev = acpi_fetch_acpi_dev(element->reference.handle);
+
+               element = &package->package.elements[2 * i + 1];
+               if (element->type == ACPI_TYPE_STRING) {
+                       str2 = devm_kzalloc(dev, element->string.length + 1, GFP_KERNEL);
+                       if (str2)
+                               memcpy(str2, element->string.pointer, element->string.length);
+               } else {
+                       str2 = NULL;
+               }
+
+               if (ref_dev && acpi_get_first_physical_node(ref_dev))
+                       clk_data->cmu_clk_refs_l[i] =
+                               clkdev_create(clk_data->cmu_clk, str2, "%s",
+                                             dev_name(acpi_get_first_physical_node(ref_dev)));
+               else
+                       clk_data->cmu_clk_refs_l[i] = clkdev_create(clk_data->cmu_clk, str2, NULL);
+       }
+
+       acpi_os_free(buffer.pointer);
+       buffer.length = ACPI_ALLOCATE_BUFFER;
+       buffer.pointer = NULL;
+
+clk_channels:
+       status = acpi_evaluate_object_typed(adev->handle, "CLKS", NULL, &buffer, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               buffer.pointer = NULL;
+               clk_data = NULL;
+               goto ret;
+       }
+
+       package = buffer.pointer;
+       if (!package->package.count || package->package.count % 4) {
+               dev_err(dev, "invalid CLKS data\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       clk_data->clk_num = package->package.count >> 2;
+       clk_data->clk = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct clk *), GFP_KERNEL);
+       if (!clk_data->clk) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       clk_data->clk_l = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct clk_lookup *),
+                                      GFP_KERNEL);
+       if (!clk_data->clk_l) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       clk_data->type = devm_kzalloc(dev, clk_data->clk_num * sizeof(u8), GFP_KERNEL);
+       if (!clk_data->type) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       init_ch = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct clk_init_data), GFP_KERNEL);
+       if (!init_ch) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       cmu_ch = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct baikal_clk_cmu), GFP_KERNEL);
+       if (!cmu_ch) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       for (i = 0; i < clk_data->clk_num; ++i) {
+               ref_dev = NULL;
+
+               element = &package->package.elements[4 * i];
+               if (element->type == ACPI_TYPE_LOCAL_REFERENCE && element->reference.handle)
+                       ref_dev = acpi_fetch_acpi_dev(element->reference.handle);
+
+               element = &package->package.elements[4 * i + 1];
+               if (element->type == ACPI_TYPE_STRING) {
+                       str = devm_kzalloc(dev, element->string.length + 1, GFP_KERNEL);
+                       if (str)
+                               memcpy(str, element->string.pointer, element->string.length);
+               } else {
+                       dev_err(dev, "failed to process clock device name #%i\n", i);
+                       continue;
+               }
+
+               element = &package->package.elements[4 * i + 2];
+               if (element->type == ACPI_TYPE_INTEGER) {
+                       if (element->integer.value != BAIKAL_CMU_CLK) {
+                               init_ch[i].parent_names = &cmu->name;
+                               init_ch[i].num_parents = 1;
+                               init_ch[i].name = str;
+                               init_ch[i].ops = &baikal_clk_ops;
+                               init_ch[i].flags = CLK_IGNORE_UNUSED;
+
+                               cmu_ch[i].name = str;
+                               cmu_ch[i].base = element->integer.value;
+                               cmu_ch[i].parent = cmu->base;
+                               cmu_ch[i].is_clk_ch = 1;
+                               cmu_ch[i].hw.init = &init_ch[i];
+
+                               clk_data->type[i] = BAIKAL_CMU_CLK_CH;
+                               clk_data->clk[i] = clk_register(ref_dev ? &ref_dev->dev : NULL,
+                                                               &cmu_ch[i].hw);
+                               if (IS_ERR(clk_data->clk[i])) {
+                                       dev_err(dev, "failed to register CMU channel clock #%i\n", i);
+                                       clk_data->clk[i] = NULL;
+                                       continue;
+                               }
+                       }
+               } else if (element->type == ACPI_TYPE_PACKAGE &&
+                          element->package.count == 3 &&
+                          element->package.elements[0].type == ACPI_TYPE_INTEGER &&
+                          element->package.elements[1].type == ACPI_TYPE_INTEGER &&
+                          element->package.elements[2].type == ACPI_TYPE_INTEGER) {
+                       /* Fixed clock */
+                       struct clk_hw *hw;
+                       u64 type = element->package.elements[0].integer.value;
+                       u64 val1 = element->package.elements[1].integer.value;
+                       u64 val2 = element->package.elements[2].integer.value;
+
+                       switch (type) {
+                       case BAIKAL_FIXED_CLK:
+                               clk_data->type[i] = BAIKAL_FIXED_CLK;
+                               hw = clk_hw_register_fixed_rate_with_accuracy(ref_dev ?
+                                                                             &ref_dev->dev : NULL,
+                                                                             str, NULL, 0,
+                                                                             val1, val2);
+                               if (IS_ERR(hw)) {
+                                       dev_err(dev, "failed to register fixed clock #%i\n", i);
+                                       continue;
+                               }
+                               clk_data->clk[i] = hw->clk;
+                               break;
+                       case BAIKAL_FIXED_FACTOR_CLK:
+                               clk_data->type[i] = BAIKAL_FIXED_FACTOR_CLK;
+                               hw = clk_hw_register_fixed_factor(ref_dev ? &ref_dev->dev : NULL,
+                                                                 str, cmu->name, 0, val1, val2);
+                               if (IS_ERR(hw)) {
+                                       dev_err(dev, "failed to register fixed-factor clock #%i\n", i);
+                                       continue;
+                               }
+                               clk_data->clk[i] = hw->clk;
+                               break;
+                       default:
+                               dev_err(dev, "failed to create clock #%i\n", i);
+                               continue;
+                       }
+               } else {
+                       dev_err(dev, "failed to process clock device id #%i\n", i);
+                       continue;
+               }
+
+               element = &package->package.elements[4 * i + 3];
+               if (element->type == ACPI_TYPE_STRING) {
+                       str2 = devm_kzalloc(dev, element->string.length + 1, GFP_KERNEL);
+                       if (str2)
+                               memcpy(str2, element->string.pointer, element->string.length);
+               } else {
+                       str2 = NULL;
+               }
+
+               if (ref_dev || str2) {
+                       if (!clk_data->clk[i]) {
+                               if (ref_dev)
+                                       clk_data->clk_l[i] = clkdev_create(clk_data->cmu_clk,
+                                                                          str2, "%s",
+                                                                          dev_name(&ref_dev->dev));
+                               else
+                                       clk_data->clk_l[i] = clkdev_create(clk_data->cmu_clk,
+                                                                          str2, NULL);
+                       } else {
+                               if (ref_dev)
+                                       clk_data->clk_l[i] = clkdev_create(clk_data->clk[i],
+                                                                          str2, "%s",
+                                                                          dev_name(&ref_dev->dev));
+                               else
+                                       clk_data->clk_l[i] = clkdev_create(clk_data->clk[i],
+                                                                          str2, NULL);
+                       }
+
+                       if (!clk_data->clk_l[i]) {
+                               dev_err(dev, "failed to register clock lookup #%i\n", i);
+                               clk_unregister(clk_data->clk[i]);
+                               clk_data->clk[i] = NULL;
+                               continue;
+                       }
+               }
+
+               clk_prepare_enable(clk_data->clk[i]);
+       }
+
+       clk_data = NULL;
+
+ret:
+       if (buffer.pointer)
+               acpi_os_free(buffer.pointer);
+
+       if (clk_data) {
+               clk_disable_unprepare(clk_data->cmu_clk);
+               clkdev_drop(clk_data->cmu_clk_l);
+               clk_unregister(clk_data->cmu_clk);
+       }
+
+       return ret;
+}
+
+static int baikal_acpi_clk_remove(struct platform_device *pdev)
+{
+       struct baikal_acpi_clk_data *clk_data = platform_get_drvdata(pdev);
+       int i;
+
+       if (clk_data) {
+               clk_disable_unprepare(clk_data->cmu_clk);
+               clkdev_drop(clk_data->cmu_clk_l);
+               clk_unregister(clk_data->cmu_clk);
+
+               for (i = 0; i < clk_data->cmu_clk_refs_num; ++i) {
+                       if (clk_data->cmu_clk_refs_l[i])
+                               clkdev_drop(clk_data->cmu_clk_refs_l[i]);
+               }
+
+               for (i = 0; i < clk_data->clk_num; ++i) {
+                       if (clk_data->clk_l[i])
+                               clkdev_drop(clk_data->clk_l[i]);
+                       if (clk_data->clk[i]) {
+                               switch (clk_data->type[i]) {
+                               case BAIKAL_FIXED_CLK:
+                                       clk_unregister_fixed_rate(clk_data->clk[i]);
+                                       break;
+                               case BAIKAL_FIXED_FACTOR_CLK:
+                                       clk_unregister_fixed_factor(clk_data->clk[i]);
+                                       break;
+                               default:
+                                       clk_unregister(clk_data->clk[i]);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static const struct acpi_device_id baikal_acpi_clk_device_ids[] = {
+       { "BKLE0001" },
+       { }
+};
+
+static struct platform_driver baikal_acpi_clk_driver = {
+       .probe          = baikal_acpi_clk_probe,
+       .remove         = baikal_acpi_clk_remove,
+       .driver         = {
+               .name   = "bm1000-cmu-acpi",
+               .acpi_match_table = ACPI_PTR(baikal_acpi_clk_device_ids)
+       }
+};
+
+static int __init bm1000_cmu_driver_acpi_init(void)
+{
+       if (!acpi_disabled) {
+               struct clk_lookup *baikal_acpi_clk_lookup_osc25;
+               struct clk_lookup *baikal_acpi_clk_lookup_osc27;
+
+               baikal_acpi_clk_osc25 = clk_register_fixed_rate(NULL, baikal_acpi_clk_osc25_str[0],
+                                                               NULL, 0, 25000000);
+               if (IS_ERR(baikal_acpi_clk_osc25)) {
+                       pr_err("%s: failed to register osc25 clock\n", __func__);
+                       return PTR_ERR(baikal_acpi_clk_osc25);
+               }
+
+               baikal_acpi_clk_osc27 = clk_register_fixed_rate(NULL, baikal_acpi_clk_osc27_str[0],
+                                                               NULL, 0, 27000000);
+               if (IS_ERR(baikal_acpi_clk_osc27)) {
+                       clk_unregister_fixed_rate(baikal_acpi_clk_osc25);
+                       pr_err("%s: failed to register osc27 clock\n", __func__);
+                       return PTR_ERR(baikal_acpi_clk_osc27);
+               }
+
+               baikal_acpi_clk_lookup_osc25 = clkdev_create(baikal_acpi_clk_osc25, NULL, "%s",
+                                                            baikal_acpi_clk_osc25_str[0]);
+               if (!baikal_acpi_clk_lookup_osc25) {
+                       clk_unregister_fixed_rate(baikal_acpi_clk_osc27);
+                       clk_unregister_fixed_rate(baikal_acpi_clk_osc25);
+                       pr_err("%s: failed to register osc25 clock lookup\n", __func__);
+                       return -ENOMEM;
+               }
+
+               baikal_acpi_clk_lookup_osc27 = clkdev_create(baikal_acpi_clk_osc27, NULL, "%s",
+                                                            baikal_acpi_clk_osc27_str[0]);
+               if (!baikal_acpi_clk_lookup_osc27) {
+                       clkdev_drop(baikal_acpi_clk_lookup_osc25);
+                       clk_unregister_fixed_rate(baikal_acpi_clk_osc27);
+                       clk_unregister_fixed_rate(baikal_acpi_clk_osc25);
+                       pr_err("%s: failed to register osc27 clock lookup\n", __func__);
+                       return -ENOMEM;
+               }
+
+               clk_prepare_enable(baikal_acpi_clk_osc25);
+               clk_prepare_enable(baikal_acpi_clk_osc27);
+
+               return platform_driver_register(&baikal_acpi_clk_driver);
+       }
+
+       return 0;
+}
+
+device_initcall(bm1000_cmu_driver_acpi_init);
+#endif
+
+static const struct of_device_id baikal_clk_of_match[] = {
+       { .compatible = "baikal,bm1000-cmu" },
+       { }
+};
+
+static struct platform_driver bm1000_cmu_driver = {
+       .probe  = baikal_clk_probe,
+       .remove = baikal_clk_remove,
+       .driver = {
+               .name = "bm1000-cmu",
+               .of_match_table = baikal_clk_of_match
+       }
+};
+module_platform_driver(bm1000_cmu_driver);
+
+MODULE_DESCRIPTION("Baikal BE-M1000 clock driver");
+MODULE_AUTHOR("Ekaterina Skachko <ekaterina.skachko@baikalelectronics.ru>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bm1000-cmu");
diff --git a/drivers/clk/baikal/clk-bs1000.c b/drivers/clk/baikal/clk-bs1000.c
new file mode 100644 (file)
index 0000000..a10c52e
--- /dev/null
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ * Author: Ekaterina Skachko <ekaterina.skachko@baikalelectronics.ru>
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#define BAIKAL_SMC_CLK_ROUND           0x82000400
+#define BAIKAL_SMC_CLK_SET             0x82000401
+#define BAIKAL_SMC_CLK_GET             0x82000402
+#define BAIKAL_SMC_CLK_ENABLE          0x82000403
+#define BAIKAL_SMC_CLK_DISABLE         0x82000404
+#define BAIKAL_SMC_CLK_IS_ENABLED      0x82000405
+
+struct baikal_clk {
+       struct clk_hw   hw;
+       u32             base;
+};
+
+#define to_baikal_clk(_hw) container_of(_hw, struct baikal_clk, hw)
+
+static int baikal_clk_enable(struct clk_hw *hw)
+{
+       struct baikal_clk *clk = to_baikal_clk(hw);
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(BAIKAL_SMC_CLK_ENABLE, clk->base, 0, 0, 0, 0, 0, 0, &res);
+       return res.a0;
+}
+
+static void baikal_clk_disable(struct clk_hw *hw)
+{
+       struct baikal_clk *clk = to_baikal_clk(hw);
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(BAIKAL_SMC_CLK_DISABLE, clk->base, 0, 0, 0, 0, 0, 0, &res);
+}
+
+static int baikal_clk_is_enabled(struct clk_hw *hw)
+{
+       struct baikal_clk *clk = to_baikal_clk(hw);
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(BAIKAL_SMC_CLK_IS_ENABLED, clk->base, 0, 0, 0, 0, 0, 0, &res);
+       return res.a0;
+}
+
+static unsigned long baikal_clk_recalc_rate(struct clk_hw *hw,
+                                           unsigned long parent_rate)
+{
+       struct baikal_clk *clk = to_baikal_clk(hw);
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(BAIKAL_SMC_CLK_GET, clk->base, 0, 0, 0, 0, 0, 0, &res);
+       return res.a0;
+}
+
+static int baikal_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+                              unsigned long parent_rate)
+{
+       struct baikal_clk *clk = to_baikal_clk(hw);
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(BAIKAL_SMC_CLK_SET, clk->base, rate, 0, 0, 0, 0, 0, &res);
+       return res.a0;
+}
+
+static long baikal_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+                                 unsigned long *prate)
+{
+       struct baikal_clk *clk = to_baikal_clk(hw);
+       struct arm_smccc_res res;
+
+       arm_smccc_smc(BAIKAL_SMC_CLK_ROUND, clk->base, rate, 0, 0, 0, 0, 0, &res);
+       return res.a0;
+}
+
+static const struct clk_ops baikal_clk_ops = {
+       .enable      = baikal_clk_enable,
+       .disable     = baikal_clk_disable,
+       .is_enabled  = baikal_clk_is_enabled,
+       .recalc_rate = baikal_clk_recalc_rate,
+       .set_rate    = baikal_clk_set_rate,
+       .round_rate  = baikal_clk_round_rate,
+};
+
+static int baikal_clk_probe(struct platform_device *pdev)
+{
+       struct device_node *node = pdev->dev.of_node;
+       struct clk_init_data init;
+       struct baikal_clk *cmu;
+       struct clk_onecell_data *clk_data;
+       const char *clk_name;
+       struct property *prop;
+       const __be32 *p;
+       int clk_index;
+       int clk_index_max;
+       int clk_index_cnt;
+       int clk_name_cnt;
+       int clk_cnt;
+       int i;
+       u32 base;
+       struct clk *clk;
+       int ret;
+       int multi;
+
+       ret = of_property_read_u32(node, "reg", &base);
+       if (ret)
+               base = 0;
+
+       clk_index_cnt = of_property_count_u32_elems(node, "clock-indices");
+       clk_name_cnt = of_property_count_strings(node, "clock-output-names");
+       clk_cnt = clk_index_cnt > clk_name_cnt ? clk_index_cnt : clk_name_cnt;
+       if (clk_cnt < 1)
+               clk_cnt = 1;
+
+       multi = clk_cnt > 1;
+
+       if (multi) {
+               clk_index_max = clk_cnt - 1;
+               of_property_for_each_u32(node, "clock-indices", prop, p, clk_index) {
+                       if (clk_index_max < clk_index)
+                               clk_index_max = clk_index;
+               }
+
+               clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+               clk_data->clks = kcalloc(clk_index_max + 1, sizeof(struct clk *), GFP_KERNEL);
+               clk_data->clk_num = clk_index_max + 1;
+       }
+
+       for (i = 0; i < clk_cnt; i++) {
+               ret = of_property_read_u32_index(node, "clock-indices", i, &clk_index);
+               if (ret)
+                       clk_index = i;
+
+               ret = of_property_read_string_index(node, "clock-output-names", i, &clk_name);
+               if (ret) {
+                       if (multi)
+                               init.name = kasprintf(GFP_KERNEL, "%s.%d", node->name, clk_index);
+                       else
+                               init.name = kasprintf(GFP_KERNEL, "%s",    node->name);
+               } else {
+                       init.name = kasprintf(GFP_KERNEL, "%s.%s", node->name, clk_name);
+               }
+
+               init.ops = &baikal_clk_ops;
+               init.flags = CLK_IGNORE_UNUSED;
+               init.parent_names = NULL;
+               init.num_parents = 0;
+
+               cmu = kmalloc(sizeof(*cmu), GFP_KERNEL);
+               cmu->base = base + 0x10 * clk_index;
+               cmu->hw.init = &init;
+
+               clk = clk_register(NULL, &cmu->hw);
+               if (!IS_ERR(clk)) {
+                       clk_register_clkdev(clk, init.name, NULL);
+                       if (multi)
+                               clk_data->clks[clk_index] = clk;
+               }
+       }
+
+       if (multi)
+               ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get, clk_data);
+       else
+               ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
+
+       return ret;
+}
+
+static int baikal_clk_remove(struct platform_device *pdev)
+{
+       of_clk_del_provider(pdev->dev.of_node);
+       return 0;
+}
+
+#ifdef CONFIG_ACPI
+const char *baikal_acpi_ref_clk_str[] = { "baikal_ref_clk" };
+
+static struct clk *baikal_acpi_ref_clk;
+
+struct baikal_acpi_clk_data {
+       struct clk *cmu_clk;
+       struct clk_lookup *cmu_clk_l;
+       struct clk **clk;
+       struct clk_lookup **clk_l;
+       unsigned int clk_num;
+};
+
+static int baikal_acpi_clk_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct acpi_device *ref_dev, *adev = to_acpi_device_node(pdev->dev.fwnode);
+       struct clk_init_data init, *init_ch;
+       struct baikal_clk *cmu, *cmu_ch;
+       struct baikal_acpi_clk_data *clk_data = NULL;
+       union acpi_object *package, *element;
+       acpi_status status;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       int size, i, index, ret = 0;
+       char *str, *str2;
+       const char *cmu_name;
+
+       cmu = devm_kzalloc(dev, sizeof(*cmu), GFP_KERNEL);
+       if (!cmu)
+               return -ENOMEM;
+
+       status = acpi_evaluate_object_typed(adev->handle, "PROP", NULL, &buffer, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "failed to get PROP data\n");
+               return -ENODEV;
+       }
+
+       package = buffer.pointer;
+       if (package->package.count != 2) {
+               dev_err(dev, "invalid PROP data\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       element = &package->package.elements[0];
+       if (element->type != ACPI_TYPE_INTEGER) {
+               dev_err(dev, "failed to get CMU id\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       cmu->base = element->integer.value;
+
+       element = &package->package.elements[1];
+       if (element->type != ACPI_TYPE_STRING) {
+               dev_err(dev, "failed to get CMU clock name\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       str = devm_kzalloc(dev, element->string.length + 1, GFP_KERNEL);
+       if (!str) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       memcpy(str, element->string.pointer, element->string.length);
+       cmu_name = str;
+
+       acpi_os_free(buffer.pointer);
+       buffer.length = ACPI_ALLOCATE_BUFFER;
+       buffer.pointer = NULL;
+
+       init.parent_names = baikal_acpi_ref_clk_str;
+       init.num_parents = 1;
+       init.name = cmu_name;
+       init.ops = &baikal_clk_ops;
+       init.flags = CLK_IGNORE_UNUSED;
+
+       cmu->hw.init = &init;
+
+       clk_data = devm_kzalloc(dev, sizeof(*clk_data), GFP_KERNEL);
+       if (!clk_data)
+               return -ENOMEM;
+
+       clk_data->cmu_clk = clk_register(NULL, &cmu->hw);
+       if (IS_ERR(clk_data->cmu_clk)) {
+               dev_err(dev, "failed to register CMU clock\n");
+               return PTR_ERR(clk_data->cmu_clk);
+       }
+
+       clk_data->cmu_clk_l = clkdev_create(clk_data->cmu_clk, cmu_name, NULL);
+       if (!clk_data->cmu_clk_l) {
+               dev_err(dev, "failed to register CMU clock lookup\n");
+               clk_unregister(clk_data->cmu_clk);
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, clk_data);
+
+       status = acpi_evaluate_object_typed(adev->handle, "CLKS", NULL, &buffer, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               buffer.pointer = NULL;
+               goto ret;
+       }
+
+       package = buffer.pointer;
+       if (!package->package.count || package->package.count % 4) {
+               dev_err(dev, "invalid CLKS data\n");
+               ret = -EINVAL;
+               goto ret;
+       }
+
+       clk_data->clk_num = package->package.count >> 2;
+       clk_data->clk = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct clk *), GFP_KERNEL);
+       if (!clk_data->clk) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       clk_data->clk_l = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct clk_lookup *), GFP_KERNEL);
+       if (!clk_data->clk_l) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       init_ch = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct clk_init_data), GFP_KERNEL);
+       if (!init_ch) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       cmu_ch = devm_kzalloc(dev, clk_data->clk_num * sizeof(struct baikal_clk), GFP_KERNEL);
+       if (!cmu_ch) {
+               ret = -ENOMEM;
+               goto ret;
+       }
+
+       for (i = 0; i < clk_data->clk_num; ++i) {
+               ref_dev = NULL;
+               size = 0;
+
+               element = &package->package.elements[4 * i];
+               if (element->type == ACPI_TYPE_LOCAL_REFERENCE && element->reference.handle)
+                       ref_dev = acpi_fetch_acpi_dev(element->reference.handle);
+
+               element = &package->package.elements[4 * i + 1];
+               if (element->type == ACPI_TYPE_STRING) {
+                       if (ref_dev)
+                               size = strlen(dev_name(&ref_dev->dev)) + 1;
+
+                       str = devm_kzalloc(dev, size + element->string.length + 1, GFP_KERNEL);
+                       if (str) {
+                               if (ref_dev) {
+                                       memcpy(str, dev_name(&ref_dev->dev), size - 1);
+                                       str[size - 1] = '_';
+                                       memcpy(str + size, element->string.pointer, element->string.length);
+                               } else {
+                                       memcpy(str, element->string.pointer, element->string.length);
+                               }
+                       }
+               } else {
+                       dev_err(dev, "failed to process clock device name #%i\n", i);
+                       continue;
+               }
+
+               element = &package->package.elements[4 * i + 2];
+               if (element->type == ACPI_TYPE_INTEGER) {
+                       index = element->integer.value;
+               } else {
+                       dev_err(dev, "failed to process clock device id #%i\n", i);
+                       continue;
+               }
+
+               element = &package->package.elements[4 * i + 3];
+               if (element->type == ACPI_TYPE_STRING) {
+                       str2 = devm_kzalloc(dev, element->string.length + 1, GFP_KERNEL);
+                       if (str2)
+                               memcpy(str2, element->string.pointer, element->string.length);
+               } else {
+                       str2 = NULL;
+               }
+
+               init_ch[i].parent_names = &cmu_name;
+               init_ch[i].num_parents = 1;
+               init_ch[i].name = str;
+               init_ch[i].ops = &baikal_clk_ops;
+               init_ch[i].flags = CLK_IGNORE_UNUSED;
+
+               cmu_ch[i].base = cmu->base + 0x20 + 0x10 * index;
+               cmu_ch[i].hw.init = &init_ch[i];
+
+               clk_data->clk[i] = clk_register(ref_dev ? &ref_dev->dev : NULL, &cmu_ch[i].hw);
+               if (IS_ERR(clk_data->clk[i])) {
+                       dev_err(dev, "failed to register CMU channel clock #%i\n", i);
+                       clk_data->clk[i] = NULL;
+                       continue;
+               }
+
+               if (ref_dev)
+                       clk_data->clk_l[i] = clkdev_create(clk_data->clk[i], str2, "%s", dev_name(&ref_dev->dev));
+               else
+                       clk_data->clk_l[i] = clkdev_create(clk_data->clk[i], str2, NULL);
+
+               if (!clk_data->clk_l[i]) {
+                       dev_err(dev, "failed to register CMU channel clock lookup #%i\n", i);
+                       clk_unregister(clk_data->clk[i]);
+                       clk_data->clk[i] = NULL;
+                       continue;
+               }
+       }
+
+       clk_data = NULL;
+
+ret:
+       if (buffer.pointer)
+               acpi_os_free(buffer.pointer);
+
+       if (clk_data) {
+               clk_disable_unprepare(clk_data->cmu_clk);
+               clkdev_drop(clk_data->cmu_clk_l);
+               clk_unregister(clk_data->cmu_clk);
+       }
+
+       return ret;
+}
+
+static int baikal_acpi_clk_remove(struct platform_device *pdev)
+{
+       struct baikal_acpi_clk_data *clk_data = platform_get_drvdata(pdev);
+       int i;
+
+       if (clk_data) {
+               clk_disable_unprepare(clk_data->cmu_clk);
+               clkdev_drop(clk_data->cmu_clk_l);
+               clk_unregister(clk_data->cmu_clk);
+
+               for (i = 0; i < clk_data->clk_num; ++i) {
+                       if (clk_data->clk_l[i])
+                               clkdev_drop(clk_data->clk_l[i]);
+                       if (clk_data->clk[i])
+                               clk_unregister(clk_data->clk[i]);
+               }
+       }
+
+       return 0;
+}
+
+static const struct acpi_device_id baikal_acpi_clk_device_ids[] = {
+       { "BKLE0001" },
+       { }
+};
+
+static struct platform_driver baikal_acpi_clk_driver = {
+       .probe          = baikal_acpi_clk_probe,
+       .remove         = baikal_acpi_clk_remove,
+       .driver         = {
+               .name   = "bs1000-cmu-acpi",
+               .acpi_match_table = ACPI_PTR(baikal_acpi_clk_device_ids)
+       }
+};
+
+static int __init baikal_acpi_clk_driver_init(void)
+{
+       if (!acpi_disabled) {
+               struct clk_lookup *baikal_acpi_ref_clk_lookup;
+
+               baikal_acpi_ref_clk = clk_register_fixed_rate(NULL, baikal_acpi_ref_clk_str[0], NULL, 0, 25000000);
+               if (IS_ERR(baikal_acpi_ref_clk)) {
+                       pr_err("%s: failed to register reference clock\n", __func__);
+                       return PTR_ERR(baikal_acpi_ref_clk);
+               }
+
+               baikal_acpi_ref_clk_lookup = clkdev_create(baikal_acpi_ref_clk, NULL, "%s", baikal_acpi_ref_clk_str[0]);
+               if (!baikal_acpi_ref_clk_lookup) {
+                       clk_unregister_fixed_rate(baikal_acpi_ref_clk);
+                       pr_err("%s: failed to register reference clock lookup\n", __func__);
+                       return -ENOMEM;
+               }
+
+               clk_prepare_enable(baikal_acpi_ref_clk);
+
+               return platform_driver_register(&baikal_acpi_clk_driver);
+       }
+
+       return 0;
+}
+
+device_initcall(baikal_acpi_clk_driver_init);
+#endif
+
+static const struct of_device_id baikal_clk_of_match[] = {
+       { .compatible = "baikal,bs1000-cmu" },
+       { /* sentinel */ }
+};
+
+static struct platform_driver bs1000_cmu_driver = {
+       .probe  = baikal_clk_probe,
+       .remove = baikal_clk_remove,
+       .driver = {
+               .name = "bs1000-cmu",
+               .of_match_table = baikal_clk_of_match
+       }
+};
+module_platform_driver(bs1000_cmu_driver);
+
+MODULE_DESCRIPTION("Baikal BE-S1000 clock driver");
+MODULE_AUTHOR("Ekaterina Skachko <ekaterina.skachko@baikalelectronics.ru>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bs1000-cmu");
index 4469e7f555e9793192e6d150a04ac55cf60bf583..9420cb338095f101e4235d087a0a03d3d81e0388 100644 (file)
@@ -73,6 +73,13 @@ config DW_APB_TIMER_OF
        select DW_APB_TIMER
        select TIMER_OF
 
+config BAIKAL_DW_APB_TIMER
+       bool "Baikal ACPI support for DW APB timer driver"
+       depends on ARCH_BAIKAL && ACPI
+       select DW_APB_TIMER
+       help
+         Enables the support for the dw_apb timer in ACPI.
+
 config FTTMR010_TIMER
        bool "Faraday Technology timer driver" if COMPILE_TEST
        depends on HAS_IOMEM
index 64ab547de97b9ad6bdea001834e50535e919d6a7..610a995efd41a2c2a0e8b7581c4a7d0dcd8e9d1f 100644 (file)
@@ -21,6 +21,7 @@ obj-$(CONFIG_OMAP_DM_TIMER)   += timer-ti-dm.o
 obj-$(CONFIG_OMAP_DM_SYSTIMER) += timer-ti-dm-systimer.o
 obj-$(CONFIG_DW_APB_TIMER)     += dw_apb_timer.o
 obj-$(CONFIG_DW_APB_TIMER_OF)  += dw_apb_timer_of.o
+obj-$(CONFIG_BAIKAL_DW_APB_TIMER)      += baikal_dw_apb_timer.o
 obj-$(CONFIG_FTTMR010_TIMER)   += timer-fttmr010.o
 obj-$(CONFIG_IXP4XX_TIMER)     += timer-ixp4xx.o
 obj-$(CONFIG_ROCKCHIP_TIMER)      += timer-rockchip.o
diff --git a/drivers/clocksource/baikal_dw_apb_timer.c b/drivers/clocksource/baikal_dw_apb_timer.c
new file mode 100644 (file)
index 0000000..e66d283
--- /dev/null
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Baikal DW APB timer driver for ACPI
+ *
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ * Author: Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ *
+ * Implementation based on dw_apb_timer_of.c
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/dw_apb_timer.h>
+#include <linux/platform_device.h>
+#include <linux/sched_clock.h>
+
+static int __init timer_get_base_and_rate(struct platform_device *pdev,
+                                         void __iomem **base, unsigned long *rate)
+{
+       struct device *dev = &pdev->dev;
+       struct clk *clk;
+       int ret;
+
+       *base = devm_platform_ioremap_resource(pdev, 0);
+       if (!*base) {
+               dev_err(dev, "Unable to map regs\n");
+               return PTR_ERR(*base);
+       }
+
+       clk = devm_clk_get(dev, "timer");
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       ret = clk_prepare_enable(clk);
+       if (ret)
+               return ret;
+
+       *rate = clk_get_rate(clk);
+       if (!*rate) {
+               clk_disable_unprepare(clk);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __init add_clockevent(struct platform_device *pdev)
+{
+       struct dw_apb_clock_event_device *ced;
+       void __iomem *iobase;
+       unsigned long rate;
+       int irq, ret;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "No IRQ for clock event timer\n");
+               return -EINVAL;
+       }
+
+       ret = timer_get_base_and_rate(pdev, &iobase, &rate);
+       if (ret)
+               return ret;
+
+       ced = dw_apb_clockevent_init(-1, pdev->name, 300, iobase, irq, rate);
+       if (!ced)
+               return -EINVAL;
+
+       dw_apb_clockevent_register(ced);
+
+       return 0;
+}
+
+static void __iomem *sched_io_base;
+static unsigned long sched_rate;
+
+static int __init add_clocksource(struct platform_device *pdev)
+{
+       struct dw_apb_clocksource *cs;
+       void __iomem *iobase;
+       unsigned long rate;
+       int ret;
+
+       ret = timer_get_base_and_rate(pdev, &iobase, &rate);
+       if (ret)
+               return ret;
+
+       cs = dw_apb_clocksource_init(300, pdev->name, iobase, rate);
+       if (!cs)
+               return -EINVAL;
+
+       dw_apb_clocksource_start(cs);
+       dw_apb_clocksource_register(cs);
+
+       sched_io_base = iobase + 0x04;
+       sched_rate = rate;
+
+       return 0;
+}
+
+static u64 notrace read_sched_clock(void)
+{
+       return ~readl_relaxed(sched_io_base);
+}
+
+static int num_called;
+
+static int __init baikal_dw_apb_timer_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       switch (num_called) {
+       case 1:
+               pr_debug("%s: found clocksource timer\n", __func__);
+               ret = add_clocksource(pdev);
+               if (ret)
+                       return ret;
+               sched_clock_register(read_sched_clock, 32, sched_rate);
+               break;
+       default:
+               pr_debug("%s: found clockevent timer\n", __func__);
+               ret = add_clockevent(pdev);
+               if (ret)
+                       return ret;
+               break;
+       }
+
+       ++num_called;
+
+       return 0;
+}
+
+static const struct of_device_id baikal_dw_apb_timer_of_match[] = {
+       { .compatible = "baikal,bm1000-dw-apb-timer" },
+       {}
+};
+
+static struct platform_driver baikal_dw_apb_timer = {
+       .driver = {
+               .name = "baikal-dw-apb-timer",
+               .of_match_table = baikal_dw_apb_timer_of_match,
+               .suppress_bind_attrs = true
+       },
+       .probe          = baikal_dw_apb_timer_probe
+};
+builtin_platform_driver(baikal_dw_apb_timer);
index 82e5de1f6f8c959f7519bd53a3bdf4dd000a74db..1d37974f3af8cb635b520211e89b33299afa7047 100644 (file)
@@ -357,3 +357,11 @@ config ARM_PXA2xx_CPUFREQ
          This add the CPUFreq driver support for Intel PXA2xx SOCs.
 
          If in doubt, say N.
+
+config BAIKAL_CPUFREQ
+       bool "Baikal-M cpufreq support for ACPI"
+       depends on ARCH_BAIKAL && ACPI
+       help
+         This adds the CPUFreq driver for Baikal-M.
+
+         If in doubt, say N.
index 49b98c62c5af59cc6e6236dc6399ad1e28516361..fcc10b06921a3d99b4c1cfbce0521adfe40a04d8 100644 (file)
@@ -91,6 +91,7 @@ obj-$(CONFIG_ARM_TEGRA186_CPUFREQ)    += tegra186-cpufreq.o
 obj-$(CONFIG_ARM_TEGRA194_CPUFREQ)     += tegra194-cpufreq.o
 obj-$(CONFIG_ARM_TI_CPUFREQ)           += ti-cpufreq.o
 obj-$(CONFIG_ARM_VEXPRESS_SPC_CPUFREQ) += vexpress-spc-cpufreq.o
+obj-$(CONFIG_BAIKAL_CPUFREQ)           += baikal-cpufreq.o
 
 
 ##################################################################################
diff --git a/drivers/cpufreq/baikal-cpufreq.c b/drivers/cpufreq/baikal-cpufreq.c
new file mode 100644 (file)
index 0000000..bc1c2b7
--- /dev/null
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Baikal-M cpufreq driver
+ *
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ * Author: Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ *
+ * Implementation based on cpufreq-dt.c
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/cpu.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+
+#include "../opp/opp.h"
+
+struct private_data {
+       struct list_head node;
+
+       cpumask_var_t cpus;
+       struct device *cpu_dev;
+       struct cpufreq_frequency_table *freq_table;
+};
+
+static LIST_HEAD(priv_list);
+
+static struct freq_attr *baikal_cpufreq_attr[] = {
+       &cpufreq_freq_attr_scaling_available_freqs,
+       NULL,
+};
+
+static int baikal_cpufreq_target_index(struct cpufreq_policy *policy,
+                                      unsigned int index)
+{
+       struct private_data *priv = policy->driver_data;
+       unsigned long freq = policy->freq_table[index].frequency;
+
+       return dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
+}
+
+static struct private_data *baikal_cpufreq_find_data(int cpu)
+{
+       struct private_data *priv;
+
+       list_for_each_entry(priv, &priv_list, node) {
+               if (cpumask_test_cpu(cpu, priv->cpus))
+                       return priv;
+       }
+
+       return NULL;
+}
+
+static int baikal_cpufreq_init(struct cpufreq_policy *policy)
+{
+       struct private_data *priv;
+       struct device *cpu_dev;
+       struct clk *cpu_clk;
+       unsigned int transition_latency;
+       int ret;
+
+       priv = baikal_cpufreq_find_data(policy->cpu);
+       if (!priv) {
+               pr_err("failed to find data for cpu%d\n", policy->cpu);
+               return -ENODEV;
+       }
+       cpu_dev = priv->cpu_dev;
+
+       cpu_clk = clk_get(cpu_dev, NULL);
+       if (IS_ERR(cpu_clk)) {
+               ret = PTR_ERR(cpu_clk);
+               dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret);
+               return ret;
+       }
+
+       transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
+       if (!transition_latency)
+               transition_latency = CPUFREQ_ETERNAL;
+
+       cpumask_copy(policy->cpus, priv->cpus);
+       policy->driver_data = priv;
+       policy->clk = cpu_clk;
+       policy->freq_table = priv->freq_table;
+       policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
+       policy->cpuinfo.transition_latency = transition_latency;
+       policy->dvfs_possible_from_any_cpu = true;
+
+       return 0;
+}
+
+static int baikal_cpufreq_online(struct cpufreq_policy *policy)
+{
+       return 0;
+}
+
+static int baikal_cpufreq_offline(struct cpufreq_policy *policy)
+{
+       return 0;
+}
+
+static int baikal_cpufreq_exit(struct cpufreq_policy *policy)
+{
+       clk_put(policy->clk);
+       return 0;
+}
+
+static struct cpufreq_driver baikal_cpufreq_driver = {
+       .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
+                CPUFREQ_IS_COOLING_DEV,
+       .verify = cpufreq_generic_frequency_table_verify,
+       .target_index = baikal_cpufreq_target_index,
+       .get = cpufreq_generic_get,
+       .init = baikal_cpufreq_init,
+       .exit = baikal_cpufreq_exit,
+       .online = baikal_cpufreq_online,
+       .offline = baikal_cpufreq_offline,
+       .name = "baikal-cpufreq",
+       .attr = baikal_cpufreq_attr,
+       .suspend = cpufreq_generic_suspend,
+};
+
+static unsigned long baikal_cpufreq_opp_table_hz[] = {
+       500000000,
+       600000000,
+       700000000,
+       800000000,
+       900000000,
+       1000000000,
+       1100000000,
+       1200000000,
+       1300000000,
+       1400000000,
+       1500000000
+};
+
+static struct dev_pm_opp *baikal_cpufreq_opp_get(struct opp_table *opp_table,
+                                                int num)
+{
+       struct dev_pm_opp *opp = NULL, *temp;
+
+       mutex_lock(&opp_table->lock);
+       list_for_each_entry(temp, &opp_table->opp_list, node) {
+               if (!temp->removed && temp->dynamic &&
+                   temp->rates[0] == baikal_cpufreq_opp_table_hz[num]) {
+                       opp = temp;
+                       break;
+               }
+       }
+       mutex_unlock(&opp_table->lock);
+
+       return opp;
+}
+
+static int baikal_cpufreq_add_opp(struct device *dev)
+{
+       struct opp_table *opp_table = NULL;
+       struct dev_pm_opp *opp;
+       int i, ret;
+
+       for (i = 0; i < ARRAY_SIZE(baikal_cpufreq_opp_table_hz); ++i) {
+               ret = dev_pm_opp_add(dev, baikal_cpufreq_opp_table_hz[i], 0);
+               if (ret)
+                       return ret;
+
+               if (!opp_table) {
+                       opp_table = dev_pm_opp_get_opp_table(dev);
+                       if (!IS_ERR_OR_NULL(opp_table))
+                               opp_table->clock_latency_ns_max = 10000000;
+               }
+
+               opp = baikal_cpufreq_opp_get(opp_table, i);
+               if (opp) {
+                       mutex_lock(&opp_table->lock);
+                       opp->clock_latency_ns = 10000000;
+                       mutex_unlock(&opp_table->lock);
+               }
+       }
+
+       if (!IS_ERR_OR_NULL(opp_table))
+               dev_pm_opp_put_opp_table(opp_table);
+
+       return 0;
+}
+
+static void baikal_dev_pm_opp_remove_all_dynamic(const struct cpumask *cpumask)
+{
+       struct device *cpu_dev;
+       int cpu;
+
+       if (cpumask_empty(cpumask))
+               return;
+
+       for_each_cpu(cpu, cpumask) {
+               cpu_dev = get_cpu_device(cpu);
+               if (!cpu_dev)
+                       continue;
+
+               dev_pm_opp_remove_all_dynamic(cpu_dev);
+       }
+}
+
+static int baikal_cpufreq_add_opp_table(const struct cpumask *cpumask)
+{
+       struct device *cpu_dev;
+       int cpu, ret;
+
+       if (cpumask_empty(cpumask))
+               return -ENODEV;
+
+       for_each_cpu(cpu, cpumask) {
+               cpu_dev = get_cpu_device(cpu);
+               if (!cpu_dev) {
+                       pr_err("%s: failed to get cpu%d device\n", __func__,
+                              cpu);
+                       ret = -ENODEV;
+                       goto remove_table;
+               }
+
+               ret = baikal_cpufreq_add_opp(cpu_dev);
+               if (ret)
+                       goto remove_table;
+       }
+
+       return 0;
+
+remove_table:
+       baikal_dev_pm_opp_remove_all_dynamic(cpumask);
+
+       return ret;
+}
+
+static int baikal_cpufreq_early_init(struct device *dev, int cpu)
+{
+       struct private_data *priv;
+       struct device *cpu_dev, *tmp;
+       int ret, i;
+
+       /* Check if this CPU is already covered by some other policy */
+       if (baikal_cpufreq_find_data(cpu))
+               return 0;
+
+       cpu_dev = get_cpu_device(cpu);
+       if (!cpu_dev)
+               return -EPROBE_DEFER;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL))
+               return -ENOMEM;
+
+       cpumask_set_cpu(cpu, priv->cpus);
+       priv->cpu_dev = cpu_dev;
+
+       for_each_possible_cpu(i) {
+               if (i == cpu_dev->id)
+                       continue;
+
+               tmp = get_cpu_device(i);
+               if (!tmp) {
+                       ret = -ENODEV;
+                       goto free_cpumask;
+               }
+
+               if (ACPI_COMPANION(cpu_dev)->dev.parent ==
+                   ACPI_COMPANION(tmp)->dev.parent)
+                       cpumask_set_cpu(i, priv->cpus);
+       }
+
+       ret = baikal_cpufreq_add_opp_table(priv->cpus);
+       if (ret)
+               goto free_cpumask;
+
+       ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &priv->freq_table);
+       if (ret) {
+               dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
+               goto out;
+       }
+
+       list_add(&priv->node, &priv_list);
+       return 0;
+
+out:
+       dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
+free_cpumask:
+       free_cpumask_var(priv->cpus);
+       return ret;
+}
+
+static void baikal_cpufreq_release(void)
+{
+       struct private_data *priv, *tmp;
+
+       list_for_each_entry_safe(priv, tmp, &priv_list, node) {
+               dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &priv->freq_table);
+               baikal_dev_pm_opp_remove_all_dynamic(priv->cpus);
+               free_cpumask_var(priv->cpus);
+               list_del(&priv->node);
+       }
+}
+
+static int baikal_cpufreq_probe(struct platform_device *pdev)
+{
+       int ret, cpu;
+
+       for_each_possible_cpu(cpu) {
+               ret = baikal_cpufreq_early_init(&pdev->dev, cpu);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to add cpufreq table: %d\n", ret);
+                       goto err;
+               }
+       }
+
+       ret = cpufreq_register_driver(&baikal_cpufreq_driver);
+       if (ret) {
+               dev_err(&pdev->dev, "failed register driver: %d\n", ret);
+               goto err;
+       }
+
+       return 0;
+err:
+       baikal_cpufreq_release();
+       return ret;
+}
+
+static int baikal_cpufreq_remove(struct platform_device *pdev)
+{
+       cpufreq_unregister_driver(&baikal_cpufreq_driver);
+       baikal_cpufreq_release();
+       return 0;
+}
+
+static struct platform_driver baikal_cpufreq_platform_driver = {
+       .driver = {
+               .name = "baikal-cpufreq",
+               .suppress_bind_attrs = true
+       },
+       .probe          = baikal_cpufreq_probe,
+       .remove         = baikal_cpufreq_remove
+};
+builtin_platform_driver(baikal_cpufreq_platform_driver);
+
+static int __init baikal_cpufreq_driver_init(void)
+{
+       if (!acpi_disabled)
+               return PTR_ERR_OR_ZERO(platform_device_register_data(NULL, "baikal-cpufreq", -1,
+                                                                    NULL, 0));
+
+       return 0;
+}
+core_initcall(baikal_cpufreq_driver_init);
+
+MODULE_ALIAS("platform:baikal-cpufreq");
+MODULE_AUTHOR("Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-M cpufreq driver");
+MODULE_LICENSE("GPL");
index 69a8742c0a7a362e4b7a6424ab1754dedeb83048..de1917aca5ea1dcb398f27d42b38848dc4fed8eb 100644 (file)
@@ -32,6 +32,9 @@ static const struct of_device_id allowlist[] __initconst = {
        { .compatible = "arm,integrator-ap", },
        { .compatible = "arm,integrator-cp", },
 
+       { .compatible = "baikal,bm1000", },
+       { .compatible = "baikal,bs1000", },
+
        { .compatible = "hisilicon,hi3660", },
 
        { .compatible = "fsl,imx27", },
index c510c109d2c3ad20eb5de6e2d4625759baa3846d..fbe06669c7608bf32a50ae9ee4c27e195a3e6de5 100644 (file)
@@ -16,6 +16,18 @@ static bool dw_dma_acpi_filter(struct dma_chan *chan, void *param)
                .m_master = 0,
                .p_master = 1,
        };
+       struct fwnode_reference_args args = {};
+
+       if (!fwnode_property_get_reference_args(dma_spec->dev->fwnode,
+                                               "baikal,masters", NULL, 3,
+                                               dma_spec->slave_id, &args)) {
+               if (args.nargs > 1) {
+                       slave.m_master = args.args[0];
+                       slave.p_master = args.args[1];
+               }
+               if (args.nargs == 3)
+                       slave.channels = args.args[2];
+       }
 
        return dw_dma_filter(chan, &slave);
 }
index 97ba3bfc10b1320ac2ec8086777fb411479a9811..39bf45553a2ea956f8501fed6238cae6b8cfbe93 100644 (file)
@@ -1070,6 +1070,8 @@ int do_dma_probe(struct dw_dma_chip *chip)
        unsigned int            dw_params;
        unsigned int            i;
        int                     err;
+       int                     irq_num;
+
 
        dw->pdata = devm_kzalloc(chip->dev, sizeof(*dw->pdata), GFP_KERNEL);
        if (!dw->pdata)
@@ -1141,10 +1143,13 @@ int do_dma_probe(struct dw_dma_chip *chip)
 
        tasklet_setup(&dw->tasklet, dw_dma_tasklet);
 
-       err = request_irq(chip->irq, dw_dma_interrupt, IRQF_SHARED,
-                         dw->name, dw);
-       if (err)
-               goto err_pdata;
+       irq_num = chip->irq_num;
+       while (irq_num--) {
+               err = request_irq(chip->irq[irq_num], dw_dma_interrupt,
+                                       IRQF_SHARED, dw->name, dw);
+               if (err)
+                       goto err_pdata;
+       }
 
        INIT_LIST_HEAD(&dw->dma.channels);
        for (i = 0; i < pdata->nr_channels; i++) {
@@ -1267,7 +1272,9 @@ int do_dma_probe(struct dw_dma_chip *chip)
        return 0;
 
 err_dma_register:
-       free_irq(chip->irq, dw);
+       irq_num = chip->irq_num;
+       while (irq_num--)
+               free_irq(chip->irq[irq_num], dw);
 err_pdata:
        pm_runtime_put_sync_suspend(chip->dev);
        return err;
@@ -1277,13 +1284,17 @@ int do_dma_remove(struct dw_dma_chip *chip)
 {
        struct dw_dma           *dw = chip->dw;
        struct dw_dma_chan      *dwc, *_dwc;
+       int                     irq_num;
 
        pm_runtime_get_sync(chip->dev);
 
        do_dw_dma_off(dw);
        dma_async_device_unregister(&dw->dma);
 
-       free_irq(chip->irq, dw);
+       irq_num = chip->irq_num;
+       while (irq_num--)
+               free_irq(chip->irq[irq_num], dw);
+
        tasklet_kill(&dw->tasklet);
 
        list_for_each_entry_safe(dwc, _dwc, &dw->dma.channels,
index 563ce73488db32e5a1ad6fd0db0d6920c1a995ef..b89baa0c067964313362af504b10a7f9af07422a 100644 (file)
@@ -33,15 +33,12 @@ static inline void dw_dma_acpi_controller_free(struct dw_dma *dw) {}
 
 struct platform_device;
 
+struct dw_dma_platform_data *dw_dma_parse_properties(struct platform_device *pdev);
+
 #ifdef CONFIG_OF
-struct dw_dma_platform_data *dw_dma_parse_dt(struct platform_device *pdev);
 void dw_dma_of_controller_register(struct dw_dma *dw);
 void dw_dma_of_controller_free(struct dw_dma *dw);
 #else
-static inline struct dw_dma_platform_data *dw_dma_parse_dt(struct platform_device *pdev)
-{
-       return NULL;
-}
 static inline void dw_dma_of_controller_register(struct dw_dma *dw) {}
 static inline void dw_dma_of_controller_free(struct dw_dma *dw) {}
 #endif
index 523ca806837cb3c028f2ae7c1b3b7be7275344f1..87561fa0acaac463d05ee8948221ac77bfe4ec2e 100644 (file)
@@ -46,58 +46,6 @@ static struct dma_chan *dw_dma_of_xlate(struct of_phandle_args *dma_spec,
        return dma_request_channel(cap, dw_dma_filter, &slave);
 }
 
-struct dw_dma_platform_data *dw_dma_parse_dt(struct platform_device *pdev)
-{
-       struct device_node *np = pdev->dev.of_node;
-       struct dw_dma_platform_data *pdata;
-       u32 tmp, arr[DW_DMA_MAX_NR_MASTERS];
-       u32 nr_masters;
-       u32 nr_channels;
-
-       if (of_property_read_u32(np, "dma-masters", &nr_masters))
-               return NULL;
-       if (nr_masters < 1 || nr_masters > DW_DMA_MAX_NR_MASTERS)
-               return NULL;
-
-       if (of_property_read_u32(np, "dma-channels", &nr_channels))
-               return NULL;
-       if (nr_channels > DW_DMA_MAX_NR_CHANNELS)
-               return NULL;
-
-       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return NULL;
-
-       pdata->nr_masters = nr_masters;
-       pdata->nr_channels = nr_channels;
-
-       of_property_read_u32(np, "chan_allocation_order", &pdata->chan_allocation_order);
-       of_property_read_u32(np, "chan_priority", &pdata->chan_priority);
-
-       of_property_read_u32(np, "block_size", &pdata->block_size);
-
-       /* Try deprecated property first */
-       if (!of_property_read_u32_array(np, "data_width", arr, nr_masters)) {
-               for (tmp = 0; tmp < nr_masters; tmp++)
-                       pdata->data_width[tmp] = BIT(arr[tmp] & 0x07);
-       }
-
-       /* If "data_width" and "data-width" both provided use the latter one */
-       of_property_read_u32_array(np, "data-width", pdata->data_width, nr_masters);
-
-       memset32(pdata->multi_block, 1, nr_channels);
-       of_property_read_u32_array(np, "multi-block", pdata->multi_block, nr_channels);
-
-       memset32(pdata->max_burst, DW_DMA_MAX_BURST, nr_channels);
-       of_property_read_u32_array(np, "snps,max-burst-len", pdata->max_burst, nr_channels);
-
-       of_property_read_u32(np, "snps,dma-protection-control", &pdata->protctl);
-       if (pdata->protctl > CHAN_PROTCTL_MASK)
-               return NULL;
-
-       return pdata;
-}
-
 void dw_dma_of_controller_register(struct dw_dma *dw)
 {
        struct device *dev = dw->dma.dev;
index 47f2292dba983e920f3f799d8c47ed7726eae470..edd997e764eb0c7a86ebc20e2647412c0b272e5f 100644 (file)
 
 #define DRV_NAME       "dw_dmac"
 
+struct dw_dma_platform_data *dw_dma_parse_properties(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct dw_dma_platform_data *pdata;
+       u32 tmp, arr[DW_DMA_MAX_NR_MASTERS];
+       u32 nr_masters;
+       u32 nr_channels;
+
+       if (device_property_read_u32(dev, "dma-masters", &nr_masters))
+               return NULL;
+       if (nr_masters < 1 || nr_masters > DW_DMA_MAX_NR_MASTERS)
+               return NULL;
+
+       if (device_property_read_u32(dev, "dma-channels", &nr_channels))
+               return NULL;
+       if (nr_channels > DW_DMA_MAX_NR_CHANNELS)
+               return NULL;
+
+       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       pdata->nr_masters = nr_masters;
+       pdata->nr_channels = nr_channels;
+
+       device_property_read_u32(dev, "chan_allocation_order", &pdata->chan_allocation_order);
+       device_property_read_u32(dev, "chan_priority", &pdata->chan_priority);
+
+       device_property_read_u32(dev, "block_size", &pdata->block_size);
+
+       /* Try deprecated property first */
+       if (!device_property_read_u32_array(dev, "data_width", arr, nr_masters)) {
+               for (tmp = 0; tmp < nr_masters; tmp++)
+                       pdata->data_width[tmp] = BIT(arr[tmp] & 0x07);
+       }
+
+       /* If "data_width" and "data-width" both provided use the latter one */
+       device_property_read_u32_array(dev, "data-width", pdata->data_width, nr_masters);
+
+       memset32(pdata->multi_block, 1, nr_channels);
+       device_property_read_u32_array(dev, "multi-block", pdata->multi_block, nr_channels);
+
+       memset32(pdata->max_burst, DW_DMA_MAX_BURST, nr_channels);
+       device_property_read_u32_array(dev, "snps,max-burst-len", pdata->max_burst, nr_channels);
+
+       device_property_read_u32(dev, "snps,dma-protection-control", &pdata->protctl);
+       if (pdata->protctl > CHAN_PROTCTL_MASK)
+               return NULL;
+
+       return pdata;
+}
+
 static int dw_probe(struct platform_device *pdev)
 {
        const struct dw_dma_chip_pdata *match;
        struct dw_dma_chip_pdata *data;
        struct dw_dma_chip *chip;
        struct device *dev = &pdev->dev;
+       int irq_num;
        int err;
 
        match = device_get_match_data(dev);
@@ -43,9 +96,15 @@ static int dw_probe(struct platform_device *pdev)
        if (!chip)
                return -ENOMEM;
 
-       chip->irq = platform_get_irq(pdev, 0);
-       if (chip->irq < 0)
-               return chip->irq;
+       irq_num = platform_irq_count(pdev);
+       if (!irq_num)
+               dev_err(&pdev->dev, "no irq found on device\n");
+
+       chip->irq = devm_kzalloc(dev, irq_num * sizeof(int), GFP_KERNEL);
+       chip->irq_num = irq_num;
+
+       while (irq_num--)
+               chip->irq[irq_num] = platform_get_irq(pdev, irq_num);
 
        chip->regs = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(chip->regs))
@@ -58,7 +117,7 @@ static int dw_probe(struct platform_device *pdev)
        if (!data->pdata)
                data->pdata = dev_get_platdata(dev);
        if (!data->pdata)
-               data->pdata = dw_dma_parse_dt(pdev);
+               data->pdata = dw_dma_parse_properties(pdev);
 
        chip->dev = dev;
        chip->id = pdev->id;
@@ -148,6 +207,7 @@ static const struct acpi_device_id dw_dma_acpi_id_table[] = {
        { "INTL9C60", (kernel_ulong_t)&dw_dma_chip_pdata },
        { "80862286", (kernel_ulong_t)&dw_dma_chip_pdata },
        { "808622C0", (kernel_ulong_t)&dw_dma_chip_pdata },
+       { "BKLE0005", (kernel_ulong_t)&dw_dma_chip_pdata },
 
        /* Elkhart Lake iDMA 32-bit (PSE DMA) */
        { "80864BB4", (kernel_ulong_t)&xbar_chip_pdata },
index f30f99166531fbaa1e473912582b4d331232b513..8080fece78148cdff42b21772490da0e6756b322 100644 (file)
@@ -233,6 +233,7 @@ config DRM_SCHED
 source "drivers/gpu/drm/i2c/Kconfig"
 
 source "drivers/gpu/drm/arm/Kconfig"
+source "drivers/gpu/drm/baikal/Kconfig"
 
 config DRM_RADEON
        tristate "ATI Radeon"
index 0b283e46f28b89ef4b2083bb34106b165f1ad09b..db0456c3dd20f87228880e61487a71a137067d76 100644 (file)
@@ -148,3 +148,4 @@ obj-y                       += gud/
 obj-$(CONFIG_DRM_HYPERV) += hyperv/
 obj-y                  += solomon/
 obj-$(CONFIG_DRM_SPRD) += sprd/
+obj-$(CONFIG_DRM_BAIKAL_VDU) += baikal/
diff --git a/drivers/gpu/drm/baikal/Kconfig b/drivers/gpu/drm/baikal/Kconfig
new file mode 100644 (file)
index 0000000..4a18055
--- /dev/null
@@ -0,0 +1,12 @@
+config DRM_BAIKAL_VDU
+       tristate "DRM Support for Baikal-M VDU"
+       depends on DRM
+       depends on ARM || ARM64 || COMPILE_TEST
+       depends on COMMON_CLK
+       select DRM_KMS_HELPER
+       select DRM_GEM_DMA_HELPER
+       select DRM_PANEL
+       select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
+       help
+         Choose this option for DRM support for the Baikal-M Video Display Unit (VDU).
+         If M is selected the module will be called baikal_vdu_drm.
diff --git a/drivers/gpu/drm/baikal/Makefile b/drivers/gpu/drm/baikal/Makefile
new file mode 100644 (file)
index 0000000..321d3be
--- /dev/null
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+baikal_vdu_drm-y +=    baikal_vdu_backlight.o \
+               baikal_vdu_crtc.o \
+               baikal_vdu_drv.o \
+               baikal_vdu_panel.o \
+               baikal_vdu_plane.o
+
+baikal_vdu_drm-$(CONFIG_DEBUG_FS) += baikal_vdu_debugfs.o
+
+obj-$(CONFIG_DRM_BAIKAL_VDU) += baikal_vdu_drm.o
+obj-$(CONFIG_DRM_BAIKAL_HDMI) += baikal-hdmi.o
diff --git a/drivers/gpu/drm/baikal/baikal-hdmi.c b/drivers/gpu/drm/baikal/baikal-hdmi.c
new file mode 100644 (file)
index 0000000..7d3d0f0
--- /dev/null
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Baikal Electronics BE-M1000 DesignWare HDMI 2.0 Tx PHY support driver
+ *
+ * Copyright (C) 2019-2022 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <drm/drm_modes.h>
+
+#include <drm/bridge/dw_hdmi.h>
+
+#include "baikal_vdu_drm.h"
+
+int fixed_clock = 0;
+int max_clock = 0;
+
+static const struct dw_hdmi_mpll_config baikal_hdmi_mpll_cfg[] = {
+       /* pixelclk      opmode  gmp         */
+       {  44900000, { { 0x00b3, 0x0000 }, }, },
+       {  90000000, { { 0x0072, 0x0001 }, }, },
+       { 182750000, { { 0x0051, 0x0002 }, }, },
+       { 340000000, { { 0x0040, 0x0003 }, }, },
+       { 594000000, { { 0x1a40, 0x0003 }, }, },
+       { ~0UL,      { { 0x0000, 0x0000 }, }, }
+};
+
+static const struct dw_hdmi_curr_ctrl baikal_hdmi_cur_ctr[] = {
+       /* pixelclk    current   */
+       {  44900000, { 0x0000, }, },
+       {  90000000, { 0x0008, }, },
+       { 182750000, { 0x001b, }, },
+       { 340000000, { 0x0036, }, },
+       { 594000000, { 0x003f, }, },
+       { ~0UL,      { 0x0000, }, }
+};
+
+static const struct dw_hdmi_phy_config baikal_hdmi_phy_cfg[] = {
+       /* pixelclk  symbol  term    vlev */
+       { 148250000, 0x8009, 0x0004, 0x0232},
+       { 218250000, 0x8009, 0x0004, 0x0230},
+       { 288000000, 0x8009, 0x0004, 0x0273},
+       { 340000000, 0x8029, 0x0004, 0x0273},
+       { 594000000, 0x8039, 0x0004, 0x014a},
+       { ~0UL,      0x0000, 0x0000, 0x0000}
+};
+
+static enum drm_mode_status
+baikal_hdmi_mode_valid(struct dw_hdmi *hdmi, void *data,
+                const struct drm_display_info *info,
+                const struct drm_display_mode *mode)
+{
+       if (mode->clock < 13500)
+               return MODE_CLOCK_LOW;
+       if (mode->clock >= 340000)
+               return MODE_CLOCK_HIGH;
+       if (fixed_clock && mode->clock != fixed_clock)
+               return MODE_BAD;
+       if (max_clock && mode->clock > max_clock)
+               return MODE_BAD;
+
+       return MODE_OK;
+}
+
+static struct dw_hdmi_plat_data baikal_dw_hdmi_plat_data = {
+       .mpll_cfg   = baikal_hdmi_mpll_cfg,
+       .cur_ctr    = baikal_hdmi_cur_ctr,
+       .phy_config = baikal_hdmi_phy_cfg,
+       .mode_valid = baikal_hdmi_mode_valid,
+};
+
+static int baikal_dw_hdmi_probe(struct platform_device *pdev)
+{
+       struct baikal_hdmi_bridge *priv;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, priv);
+
+       priv->hdmi = dw_hdmi_probe(pdev, &baikal_dw_hdmi_plat_data);
+       if (IS_ERR(priv->hdmi))
+               return PTR_ERR(priv->hdmi);
+
+       priv->bridge = dw_hdmi_get_bridge(priv->hdmi);
+       return 0;
+}
+
+static int baikal_dw_hdmi_remove(struct platform_device *pdev)
+{
+       struct baikal_hdmi_bridge *priv = platform_get_drvdata(pdev);
+
+       dw_hdmi_remove(priv->hdmi);
+       return 0;
+}
+
+static const struct of_device_id baikal_dw_hdmi_of_table[] = {
+       { .compatible = "baikal,hdmi" },
+       { /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, baikal_dw_hdmi_of_table);
+
+static struct platform_driver baikal_dw_hdmi_platform_driver = {
+       .probe          = baikal_dw_hdmi_probe,
+       .remove         = baikal_dw_hdmi_remove,
+       .driver         = {
+               .name   = "baikal-dw-hdmi",
+               .of_match_table = baikal_dw_hdmi_of_table,
+       },
+};
+
+module_param(fixed_clock, int, 0644);
+module_param(max_clock, int, 0644);
+
+module_platform_driver(baikal_dw_hdmi_platform_driver);
+
+MODULE_AUTHOR("Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal BE-M1000 SoC DesignWare HDMI 2.0 Tx + Gen2 PHY Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_backlight.c b/drivers/gpu/drm/baikal/baikal_vdu_backlight.c
new file mode 100644 (file)
index 0000000..c906de6
--- /dev/null
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+/**
+ * baikal_vdu_backlight.c
+ * Implementation of backlight functions for
+ * Baikal Electronics BE-M1000 SoC's VDU
+ */
+
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/module.h>
+
+#include <drm/drm_atomic_helper.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+#define BAIKAL_VDU_MIN_BRIGHTNESS      0
+#define BAIKAL_VDU_DEFAULT_BRIGHTNESS  50
+#define BAIKAL_VDU_BRIGHTNESS_STEP     5
+#define BAIKAL_VDU_DEFAULT_PWM_FREQ    10000
+
+static int baikal_vdu_backlight_update_status(struct backlight_device *bl_dev)
+{
+       struct baikal_vdu_private *priv = bl_get_data(bl_dev);
+       int brightness_on = 1;
+       int brightness = bl_dev->props.brightness;
+       u8 pwmdc;
+
+       if (bl_dev->props.power != FB_BLANK_UNBLANK ||
+           bl_dev->props.fb_blank != FB_BLANK_UNBLANK ||
+           bl_dev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) {
+               brightness_on = 0;
+               brightness = priv->min_brightness;
+       }
+
+       if (priv->enable_gpio)
+               gpiod_set_value_cansleep(priv->enable_gpio, brightness_on);
+
+       pwmdc = brightness ? ((brightness << 6) / 25 - 1) : 0;
+
+       writel(pwmdc, priv->regs + PWMDCR);
+
+       return 0;
+}
+
+static const struct backlight_ops baikal_vdu_backlight_ops = {
+       .options        = BL_CORE_SUSPENDRESUME,
+       .update_status  = baikal_vdu_backlight_update_status,
+};
+
+static void baikal_vdu_input_event(struct input_handle *handle,
+                                  unsigned int type, unsigned int code,
+                                  int value)
+{
+       struct baikal_vdu_private *priv = handle->private;
+       int brightness;
+
+       if (type != EV_KEY || value == 0)
+               return;
+
+       switch (code) {
+       case KEY_BRIGHTNESSDOWN:
+               brightness = priv->bl_dev->props.brightness -
+                            priv->brightness_step;
+               if (brightness >= priv->min_brightness)
+                       backlight_device_set_brightness(priv->bl_dev,
+                                                       brightness);
+               break;
+
+       case KEY_BRIGHTNESSUP:
+               brightness = priv->bl_dev->props.brightness +
+                            priv->brightness_step;
+               backlight_device_set_brightness(priv->bl_dev, brightness);
+               break;
+
+       case KEY_BRIGHTNESS_TOGGLE:
+               priv->brightness_on = !priv->brightness_on;
+               if (priv->brightness_on)
+                       backlight_enable(priv->bl_dev);
+               else
+                       backlight_disable(priv->bl_dev);
+               break;
+
+       default:
+               return;
+       }
+
+       backlight_force_update(priv->bl_dev, BACKLIGHT_UPDATE_HOTKEY);
+}
+
+static int baikal_vdu_input_connect(struct input_handler *handler,
+                                   struct input_dev *dev,
+                                   const struct input_device_id *id)
+{
+       struct input_handle *handle;
+       int ret;
+
+       handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+       if (!handle)
+               return -ENOMEM;
+
+       handle->private = handler->private;
+       handle->name = KBUILD_MODNAME;
+       handle->dev = dev;
+       handle->handler = handler;
+
+       ret = input_register_handle(handle);
+       if (ret)
+               goto err_free_handle;
+
+       ret = input_open_device(handle);
+       if (ret)
+               goto err_unregister_handle;
+
+       return 0;
+
+err_unregister_handle:
+       input_unregister_handle(handle);
+err_free_handle:
+       kfree(handle);
+       return ret;
+}
+
+static void baikal_vdu_input_disconnect(struct input_handle *handle)
+{
+       input_close_device(handle);
+       input_unregister_handle(handle);
+       kfree(handle);
+}
+
+static const struct input_device_id baikal_vdu_input_ids[] = {
+       {
+               .flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+               .evbit = { BIT_MASK(EV_KEY) },
+       },
+
+       { },    /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, baikal_vdu_input_ids);
+
+int baikal_vdu_backlight_create(struct drm_device *drm)
+{
+       struct baikal_vdu_crossbar *crossbar = drm_to_baikal_vdu_crossbar(drm);
+       struct baikal_vdu_private *priv = &crossbar->lvds;
+       struct device *dev = drm->dev;
+       struct backlight_properties props;
+       struct input_handler *handler;
+       struct fwnode_handle *node;
+       u32 min_brightness = BAIKAL_VDU_MIN_BRIGHTNESS;
+       u32 dfl_brightness = BAIKAL_VDU_DEFAULT_BRIGHTNESS;
+       u32 brightness_step = BAIKAL_VDU_BRIGHTNESS_STEP;
+       u32 pwm_frequency = 0;
+       int ret = 0;
+       unsigned long rate;
+       unsigned int pwmfr = 0;
+
+       priv->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_ASIS);
+       if (IS_ERR(priv->enable_gpio)) {
+               dev_warn(dev, "failed to get ENABLE GPIO\n");
+               priv->enable_gpio = NULL;
+       }
+
+       if (priv->enable_gpio && gpiod_get_direction(priv->enable_gpio) != 0)
+               gpiod_direction_output(priv->enable_gpio, 1);
+
+       node = fwnode_get_named_child_node(dev->fwnode, is_of_node(dev->fwnode) ?
+                                          "backlight" : "BCKL");
+       if (!node)
+               return 0;
+
+       fwnode_property_read_u32(node, "min-brightness-level", &min_brightness);
+       fwnode_property_read_u32(node, "default-brightness-level", &dfl_brightness);
+       fwnode_property_read_u32(node, "brightness-level-step", &brightness_step);
+       fwnode_property_read_u32(node, "pwm-frequency", &pwm_frequency);
+
+       if (pwm_frequency == 0) {
+               dev_warn(dev, "using default PWM frequency %u\n",
+                        BAIKAL_VDU_DEFAULT_PWM_FREQ);
+               pwm_frequency = BAIKAL_VDU_DEFAULT_PWM_FREQ;
+       }
+
+       memset(&props, 0, sizeof(props));
+       props.max_brightness = 100;
+       props.type = BACKLIGHT_RAW;
+       props.scale = BACKLIGHT_SCALE_LINEAR;
+
+       if (min_brightness > props.max_brightness) {
+               dev_warn(dev, "invalid min brightness level: %u, using %u\n",
+                        min_brightness, props.max_brightness);
+               min_brightness = props.max_brightness;
+       }
+
+       if (dfl_brightness > props.max_brightness ||
+           dfl_brightness < min_brightness) {
+               dev_warn(dev,
+                        "invalid default brightness level: %u, using %u\n",
+                        dfl_brightness, props.max_brightness);
+               dfl_brightness = props.max_brightness;
+       }
+
+       priv->min_brightness = min_brightness;
+       priv->brightness_step = brightness_step;
+       priv->brightness_on = true;
+
+       props.brightness = dfl_brightness;
+       props.power = FB_BLANK_UNBLANK;
+
+       priv->bl_dev =
+               devm_backlight_device_register(dev, dev_name(dev), dev, priv,
+                                              &baikal_vdu_backlight_ops,
+                                              &props);
+       if (IS_ERR(priv->bl_dev)) {
+               dev_err(dev, "failed to register backlight device\n");
+               ret = PTR_ERR(priv->bl_dev);
+               priv->bl_dev = NULL;
+               goto out;
+       }
+
+       handler = devm_kzalloc(dev, sizeof(*handler), GFP_KERNEL);
+       if (!handler) {
+               dev_err(dev, "failed to allocate input handler\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       handler->private = priv;
+       handler->event = baikal_vdu_input_event;
+       handler->connect = baikal_vdu_input_connect;
+       handler->disconnect = baikal_vdu_input_disconnect;
+       handler->name = KBUILD_MODNAME;
+       handler->id_table = baikal_vdu_input_ids;
+
+       ret = input_register_handler(handler);
+       if (ret) {
+               dev_err(dev, "failed to register input handler\n");
+               goto out;
+       }
+
+       /* Hold PWM Clock Domain Reset, disable clocking */
+       writel(0, priv->regs + PWMFR);
+
+       rate = clk_get_rate(priv->clk);
+       pwmfr |= PWMFR_PWMFCD(rate / pwm_frequency - 1) | PWMFR_PWMFCI;
+       writel(pwmfr, priv->regs + PWMFR);
+
+       /* Release PWM Clock Domain Reset, enable clocking */
+       writel(pwmfr | PWMFR_PWMPCR | PWMFR_PWMFCE, priv->regs + PWMFR);
+
+       backlight_update_status(priv->bl_dev);
+out:
+       fwnode_handle_put(node);
+       return ret;
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_crtc.c b/drivers/gpu/drm/baikal/baikal_vdu_crtc.c
new file mode 100644 (file)
index 0000000..ab369db
--- /dev/null
@@ -0,0 +1,288 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+/**
+ * baikal_vdu_crtc.c
+ * Implementation of the CRTC functions for Baikal Electronics BE-M1000 VDU driver
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/dma-buf.h>
+#include <linux/shmem_fs.h>
+#include <linux/version.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_vblank.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+irqreturn_t baikal_vdu_irq(int irq, void *data)
+{
+       struct baikal_vdu_private *priv = data;
+       irqreturn_t status = IRQ_NONE;
+       u32 raw_stat;
+       u32 irq_stat;
+
+       priv->counters[0]++;
+       irq_stat = readl(priv->regs + IVR);
+       raw_stat = readl(priv->regs + ISR);
+
+       if (raw_stat & INTR_UFU) {
+               priv->counters[4]++;
+               status = IRQ_HANDLED;
+       }
+
+       if (raw_stat & INTR_IFO) {
+               priv->counters[5]++;
+               status = IRQ_HANDLED;
+       }
+
+       if (raw_stat & INTR_OFU) {
+               priv->counters[6]++;
+               status = IRQ_HANDLED;
+       }
+
+       if (irq_stat & INTR_FER) {
+               priv->counters[11]++;
+               priv->counters[12] = readl(priv->regs + DBAR);
+               priv->counters[13] = readl(priv->regs + DCAR);
+               priv->counters[14] = readl(priv->regs + MRR);
+               status = IRQ_HANDLED;
+       }
+
+       priv->counters[3] |= raw_stat;
+
+       /* Clear all interrupts */
+       writel(raw_stat, priv->regs + ISR);
+
+       return status;
+}
+
+static void baikal_vdu_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct baikal_vdu_private *priv = crtc_to_baikal_vdu(crtc);
+       const struct drm_display_mode *mode = &crtc->state->mode;
+       unsigned long rate;
+       unsigned int ppl, hsw, hfp, hbp;
+       unsigned int lpp, vsw, vfp, vbp;
+       unsigned int reg;
+       int ret = 0;
+
+       drm_mode_debug_printmodeline(mode);
+
+       rate = mode->clock * 1000;
+
+       if (rate != clk_get_rate(priv->clk)) {
+               DRM_DEV_DEBUG_DRIVER(dev->dev, "Requested pixel clock is %lu Hz\n", rate);
+
+               /* hold clock domain reset; disable clocking */
+               writel(0, priv->regs + PCTR);
+
+               if (__clk_is_enabled(priv->clk))
+                       clk_disable_unprepare(priv->clk);
+               ret = clk_set_rate(priv->clk, rate);
+
+               if (ret >= 0) {
+                       clk_prepare_enable(priv->clk);
+                       if (!__clk_is_enabled(priv->clk))
+                               ret = -1;
+               }
+
+               /* release clock domain reset; enable clocking */
+               reg = readl(priv->regs + PCTR);
+               reg |= PCTR_PCR + PCTR_PCI;
+               writel(reg, priv->regs + PCTR);
+       }
+
+       if (ret < 0)
+               DRM_ERROR("Cannot set desired pixel clock (%lu Hz)\n", rate);
+
+       ppl = mode->hdisplay / 16;
+       if (priv->index == CRTC_LVDS && priv-> num_lanes == 2) {
+               hsw = mode->hsync_end - mode->hsync_start;
+               hfp = mode->hsync_start - mode->hdisplay - 1;
+       } else {
+               hsw = mode->hsync_end - mode->hsync_start - 1;
+               hfp = mode->hsync_start - mode->hdisplay;
+       }
+       hbp = mode->htotal - mode->hsync_end;
+
+       lpp = mode->vdisplay;
+       vsw = mode->vsync_end - mode->vsync_start;
+       vfp = mode->vsync_start - mode->vdisplay;
+       vbp = mode->vtotal - mode->vsync_end;
+
+       writel((HTR_HFP(hfp) & HTR_HFP_MASK) |
+                       (HTR_PPL(ppl) & HTR_PPL_MASK) |
+                       (HTR_HBP(hbp) & HTR_HBP_MASK) |
+                       (HTR_HSW(hsw) & HTR_HSW_MASK),
+                       priv->regs + HTR);
+
+       if (mode->hdisplay > 4080 || ppl * 16 != mode->hdisplay)
+               writel((HPPLOR_HPPLO(mode->hdisplay) & HPPLOR_HPPLO_MASK) | HPPLOR_HPOE,
+                               priv->regs + HPPLOR);
+
+       writel((VTR1_VSW(vsw) & VTR1_VSW_MASK) |
+                       (VTR1_VFP(vfp) & VTR1_VFP_MASK) |
+                       (VTR1_VBP(vbp) & VTR1_VBP_MASK),
+                       priv->regs + VTR1);
+
+       writel(lpp & VTR2_LPP_MASK, priv->regs + VTR2);
+
+       writel((HVTER_VSWE(vsw >> VTR1_VSW_LSB_WIDTH) & HVTER_VSWE_MASK) |
+                       (HVTER_HSWE(hsw >> HTR_HSW_LSB_WIDTH) & HVTER_HSWE_MASK) |
+                       (HVTER_VBPE(vbp >> VTR1_VBP_LSB_WIDTH) & HVTER_VBPE_MASK) |
+                       (HVTER_VFPE(vfp >> VTR1_VFP_LSB_WIDTH) & HVTER_VFPE_MASK) |
+                       (HVTER_HBPE(hbp >> HTR_HBP_LSB_WIDTH) & HVTER_HBPE_MASK) |
+                       (HVTER_HFPE(hfp >> HTR_HFP_LSB_WIDTH) & HVTER_HFPE_MASK),
+                       priv->regs + HVTER);
+
+       /* Set polarities */
+       reg = readl(priv->regs + CR1);
+       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+               reg |= CR1_HSP;
+       else
+               reg &= ~CR1_HSP;
+       reg &= ~CR1_VSP; // always set VSP to active high
+       reg |= CR1_DEP; // set DE to active high;
+       writel(reg, priv->regs + CR1);
+}
+
+static enum drm_mode_status baikal_vdu_mode_valid(struct drm_crtc *crtc,
+                       const struct drm_display_mode *mode)
+{
+       struct baikal_vdu_private *priv = crtc_to_baikal_vdu(crtc);
+       if (!priv->mode_override && (mode->hdisplay > 2560 ||
+                       mode->vdisplay > 1440))
+               return MODE_BAD;
+       else
+               return MODE_OK;
+}
+
+static void baikal_vdu_crtc_helper_enable(struct drm_crtc *crtc,
+                                         struct drm_atomic_state *old_state)
+{
+       struct baikal_vdu_private *priv = crtc_to_baikal_vdu(crtc);
+       const char *data_mapping = NULL;
+       u32 cntl, gpio;
+
+       DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "enabling pixel clock\n");
+       clk_prepare_enable(priv->clk);
+
+       /* Set 16-word input FIFO watermark */
+       /* Enable and Power Up */
+       cntl = readl(priv->regs + CR1);
+       cntl &= ~(CR1_FDW_MASK | CR1_OPS_MASK);
+       cntl |= CR1_LCE | CR1_FDW_16_WORDS;
+
+       if (priv->index == CRTC_LVDS) {
+               if (priv->bridge->of_node) {
+                       of_property_read_string(priv->bridge->of_node,
+                                               "data-mapping", &data_mapping);
+               } else {
+                       struct fwnode_handle *fwnode;
+
+                       fwnode = fwnode_find_reference(priv->bridge->dev->dev->fwnode,
+                                                      "baikal,lvds-panel", 0);
+                       if (!IS_ERR_OR_NULL(fwnode))
+                               fwnode_property_read_string(fwnode, "data-mapping",
+                                                           &data_mapping);
+               }
+
+               if (!data_mapping) {
+                       cntl |= CR1_OPS_LCD18;
+               } else if (!strncmp(data_mapping, "vesa-24", 7))
+                       cntl |= CR1_OPS_LCD24;
+               else if (!strncmp(data_mapping, "jeida-18", 8))
+                       cntl |= CR1_OPS_LCD18;
+               else {
+                       dev_warn(crtc->dev->dev, "%s data mapping is not supported, vesa-24 is set\n", data_mapping);
+                       cntl |= CR1_OPS_LCD24;
+               }
+               gpio = GPIOR_UHD_ENB;
+               if (priv->num_lanes == 4)
+                       gpio |= GPIOR_UHD_QUAD_PORT;
+               else if (priv->num_lanes == 2)
+                       gpio |= GPIOR_UHD_DUAL_PORT;
+               else
+                       gpio |= GPIOR_UHD_SNGL_PORT;
+               writel(gpio, priv->regs + GPIOR);
+       } else
+               cntl |= CR1_OPS_LCD24;
+       writel(cntl, priv->regs + CR1);
+
+       writel(0x3ffff, priv->regs + ISR);
+       writel(INTR_FER, priv->regs + IMR);
+}
+
+void baikal_vdu_crtc_helper_disable(struct drm_crtc *crtc)
+{
+       struct baikal_vdu_private *priv = crtc_to_baikal_vdu(crtc);
+
+       writel(0x3ffff, priv->regs + ISR);
+       writel(0, priv->regs + IMR);
+
+       /* Disable clock */
+       DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "disabling pixel clock\n");
+       clk_disable_unprepare(priv->clk);
+}
+
+static void baikal_vdu_crtc_helper_atomic_flush(struct drm_crtc *crtc,
+                                          struct drm_atomic_state *old_state)
+{
+       struct drm_pending_vblank_event *event = crtc->state->event;
+
+       if (event) {
+               crtc->state->event = NULL;
+
+               spin_lock_irq(&crtc->dev->event_lock);
+               if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0)
+                       drm_crtc_arm_vblank_event(crtc, event);
+               else
+                       drm_crtc_send_vblank_event(crtc, event);
+               spin_unlock_irq(&crtc->dev->event_lock);
+       }
+}
+
+const struct drm_crtc_funcs crtc_funcs = {
+       .set_config = drm_atomic_helper_set_config,
+       .page_flip = drm_atomic_helper_page_flip,
+       .reset = drm_atomic_helper_crtc_reset,
+       .destroy = drm_crtc_cleanup,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+const struct drm_crtc_helper_funcs crtc_helper_funcs = {
+       .mode_set_nofb = baikal_vdu_crtc_helper_mode_set_nofb,
+       .mode_valid = baikal_vdu_mode_valid,
+       .atomic_flush = baikal_vdu_crtc_helper_atomic_flush,
+       .disable = baikal_vdu_crtc_helper_disable,
+       .atomic_enable = baikal_vdu_crtc_helper_enable,
+};
+
+int baikal_vdu_crtc_create(struct baikal_vdu_private *priv)
+{
+       struct drm_device *dev = priv->drm;
+       struct drm_crtc *crtc = &priv->crtc;
+
+       drm_crtc_init_with_planes(dev, crtc,
+                                 &priv->primary, NULL,
+                                 &crtc_funcs, "primary");
+       drm_crtc_helper_add(crtc, &crtc_helper_funcs);
+
+       DRM_DEV_DEBUG_DRIVER(crtc->dev->dev, "enabling pixel clock\n");
+       clk_prepare_enable(priv->clk);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_debugfs.c b/drivers/gpu/drm/baikal/baikal_vdu_debugfs.c
new file mode 100644 (file)
index 0000000..1fda8c2
--- /dev/null
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+#include <linux/seq_file.h>
+#include <linux/device.h>
+#include <drm/drm_debugfs.h>
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+#define REGDEF(reg) { reg, #reg }
+static const struct {
+       u32 reg;
+       const char *name;
+} baikal_vdu_reg_defs[] = {
+       REGDEF(CR1),
+       REGDEF(HTR),
+       REGDEF(VTR1),
+       REGDEF(VTR2),
+       REGDEF(PCTR),
+       REGDEF(ISR),
+       REGDEF(IMR),
+       REGDEF(IVR),
+       REGDEF(ISCR),
+       REGDEF(DBAR),
+       REGDEF(DCAR),
+       REGDEF(DEAR),
+       REGDEF(PWMFR),
+       REGDEF(PWMDCR),
+       REGDEF(HVTER),
+       REGDEF(HPPLOR),
+       REGDEF(GPIOR),
+       REGDEF(MRR),
+};
+
+#define REGS_HDMI      "regs_hdmi"
+#define REGS_LVDS      "regs_lvds"
+
+int baikal_vdu_debugfs_regs(struct seq_file *m, void *unused)
+{
+       struct drm_info_node *node = (struct drm_info_node *)m->private;
+       struct drm_device *dev = node->minor->dev;
+       struct baikal_vdu_crossbar *crossbar = drm_to_baikal_vdu_crossbar(dev);
+       char *filename = m->file->f_path.dentry->d_iname;
+       struct baikal_vdu_private *priv = NULL;
+       int i;
+
+       if (!strcmp(REGS_HDMI, filename))
+               priv = &crossbar->hdmi;
+       if (!strcmp(REGS_LVDS, filename))
+               priv = &crossbar->lvds;
+
+       if (!priv || !priv->regs)
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(baikal_vdu_reg_defs); i++) {
+               seq_printf(m, "%s (0x%04x): 0x%08x\n",
+                          baikal_vdu_reg_defs[i].name, baikal_vdu_reg_defs[i].reg,
+                          readl(priv->regs + baikal_vdu_reg_defs[i].reg));
+       }
+       for (i = 0; i < ARRAY_SIZE(priv->counters); i++) {
+               seq_printf(m, "COUNTER[%d]: 0x%08x\n", i, priv->counters[i]);
+       }
+
+       return 0;
+}
+
+static const struct drm_info_list baikal_vdu_hdmi_debugfs_list[] = {
+       {REGS_HDMI, baikal_vdu_debugfs_regs, 0},
+};
+
+static const struct drm_info_list baikal_vdu_lvds_debugfs_list[] = {
+       {REGS_LVDS, baikal_vdu_debugfs_regs, 0},
+};
+
+void baikal_vdu_hdmi_debugfs_init(struct drm_minor *minor)
+{
+       drm_debugfs_create_files(baikal_vdu_hdmi_debugfs_list,
+                               ARRAY_SIZE(baikal_vdu_hdmi_debugfs_list),
+                               minor->debugfs_root, minor);
+}
+
+void baikal_vdu_lvds_debugfs_init(struct drm_minor *minor)
+{
+       drm_debugfs_create_files(baikal_vdu_lvds_debugfs_list,
+                               ARRAY_SIZE(baikal_vdu_lvds_debugfs_list),
+                               minor->debugfs_root, minor);
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_drm.h b/drivers/gpu/drm/baikal/baikal_vdu_drm.h
new file mode 100644 (file)
index 0000000..04d7ba6
--- /dev/null
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+#ifndef __BAIKAL_VDU_DRM_H__
+#define __BAIKAL_VDU_DRM_H__
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_gem.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include <drm/bridge/dw_hdmi.h>
+
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/backlight.h>
+
+#define CRTC_HDMI      0
+#define CRTC_LVDS      1
+
+#define crtc_to_baikal_vdu(x) \
+       container_of(x, struct baikal_vdu_private, crtc)
+#define bridge_to_baikal_lvds_bridge(x) \
+       container_of(x, struct baikal_lvds_bridge, bridge)
+#define connector_to_baikal_lvds_bridge(x) \
+       container_of(x, struct baikal_lvds_bridge, connector)
+#define drm_to_baikal_vdu_crossbar(x) \
+       container_of(x, struct baikal_vdu_crossbar, drm)
+
+#define VDU_NAME_LEN 5
+
+struct baikal_vdu_private {
+       struct drm_device *drm;
+       struct drm_crtc crtc;
+       struct drm_encoder encoder;
+       struct drm_bridge *bridge;
+       struct drm_plane primary;
+       void *regs;
+       int irq;
+       struct clk *clk;
+       spinlock_t lock;
+       u32 counters[20];
+       int mode_override;
+       int index;
+       char name[VDU_NAME_LEN];
+       char irq_name[VDU_NAME_LEN + 4];
+       char pclk_name[VDU_NAME_LEN + 5];
+       char regs_name[VDU_NAME_LEN + 5];
+       int num_lanes;
+       int data_mapping;
+       int off;
+       int ready;
+
+       /* backlight */
+       struct gpio_desc *enable_gpio;
+       struct backlight_device *bl_dev;
+       int min_brightness;
+       int brightness_step;
+       bool brightness_on;
+};
+
+struct baikal_vdu_crossbar {
+       struct drm_device drm;
+       struct baikal_vdu_private hdmi;
+       struct baikal_vdu_private lvds;
+};
+
+struct baikal_lvds_bridge {
+       struct baikal_vdu_private *vdu;
+       struct drm_bridge bridge;
+       struct drm_connector connector;
+       struct drm_panel *panel;
+       u32 connector_type;
+};
+
+struct baikal_hdmi_bridge {
+       struct dw_hdmi *hdmi;
+       struct drm_bridge *bridge;
+};
+
+/* Generic functions */
+inline void baikal_vdu_switch_on(struct baikal_vdu_private *priv);
+
+inline void baikal_vdu_switch_off(struct baikal_vdu_private *priv);
+
+/* Bridge functions */
+bool bridge_is_baikal_lvds_bridge(const struct drm_bridge *bridge);
+
+struct drm_bridge *devm_baikal_lvds_bridge_add(struct device *dev,
+                                            struct drm_panel *panel,
+                                            u32 connector_type);
+
+/* CRTC Functions */
+int baikal_vdu_crtc_create(struct baikal_vdu_private *priv);
+
+irqreturn_t baikal_vdu_irq(int irq, void *data);
+
+int baikal_vdu_primary_plane_init(struct baikal_vdu_private *priv);
+
+/* Backlight Functions */
+int baikal_vdu_backlight_create(struct drm_device *drm);
+
+/* Debugfs functions */
+void baikal_vdu_hdmi_debugfs_init(struct drm_minor *minor);
+
+void baikal_vdu_lvds_debugfs_init(struct drm_minor *minor);
+
+#endif /* __BAIKAL_VDU_DRM_H__ */
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_drv.c b/drivers/gpu/drm/baikal/baikal_vdu_drv.c
new file mode 100644 (file)
index 0000000..b1e41de
--- /dev/null
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ * Bugfixes by Alexey Sheplyakov <asheplyakov@altlinux.org>
+ *
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/version.h>
+#include <linux/shmem_fs.h>
+#include <linux/dma-buf.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_aperture.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+#define DRIVER_NAME            "baikal-vdu"
+#define DRIVER_DESC            "Baikal VDU DRM driver"
+#define DRIVER_DATE            "20230221"
+
+#define BAIKAL_SMC_LOG_DISABLE 0x82000200
+#define IFIFO_SIZE     16384
+#define UHD_FIFO_SIZE  16384
+
+int mode_override = 0;
+int hdmi_off = 0;
+int lvds_off = 0;
+
+static struct drm_mode_config_funcs mode_config_funcs = {
+       .fb_create = drm_gem_fb_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = drm_atomic_helper_commit,
+};
+
+const struct drm_encoder_funcs baikal_vdu_encoder_funcs = {
+       .destroy = drm_encoder_cleanup,
+};
+
+static struct drm_bridge *devm_baikal_get_bridge(struct device *dev,
+                                                u32 port, u32 endpoint)
+{
+       struct baikal_hdmi_bridge *priv_hdmi;
+       struct device *tmp;
+       struct drm_bridge *bridge;
+       struct drm_panel *panel;
+       struct fwnode_handle *fwnode;
+       int ret = 0;
+
+       if (is_of_node(dev->fwnode)) {
+               ret = drm_of_find_panel_or_bridge(to_of_node(dev->fwnode), port,
+                                                 endpoint, &panel, &bridge);
+       } else {
+               if (port == CRTC_HDMI) {
+                       fwnode = fwnode_find_reference(dev->fwnode,
+                                                      "baikal,hdmi-bridge", 0);
+                       if (IS_ERR_OR_NULL(fwnode))
+                               return ERR_PTR(-ENODEV);
+
+                       tmp = bus_find_device_by_fwnode(&platform_bus_type, fwnode);
+                       if (IS_ERR_OR_NULL(tmp))
+                               return ERR_PTR(-ENODEV);
+
+                       priv_hdmi = dev_get_drvdata(tmp);
+                       if (!priv_hdmi)
+                               return ERR_PTR(-EPROBE_DEFER);
+
+                       bridge = priv_hdmi->bridge;
+                       panel = NULL;
+               } else if (port == CRTC_LVDS) {
+                       fwnode = fwnode_find_reference(dev->fwnode,
+                                                      "baikal,lvds-panel", 0);
+                       if (IS_ERR_OR_NULL(fwnode))
+                               return ERR_PTR(-ENODEV);
+
+                       panel = fwnode_drm_find_panel(fwnode);
+                       if (IS_ERR(panel))
+                               return ERR_CAST(panel);
+               } else {
+                       return ERR_PTR(-ENODEV);
+               }
+       }
+
+       if (ret)
+               return ERR_PTR(ret);
+
+       if (panel) {
+               bridge = devm_baikal_lvds_bridge_add(dev, panel, DRM_MODE_CONNECTOR_LVDS);
+       }
+
+       return bridge;
+}
+
+static int baikal_vdu_remove_efifb(struct drm_device *dev)
+{
+       int err;
+       err = drm_aperture_remove_framebuffers("baikal-vdudrmfb", false);
+       if (err)
+               dev_warn(dev->dev, "failed to remove firmware framebuffer\n");
+       return err;
+}
+
+inline void baikal_vdu_switch_off(struct baikal_vdu_private *priv)
+{
+       u32 cntl = readl(priv->regs + CR1);
+       cntl &= ~CR1_LCE;
+       writel(cntl, priv->regs + CR1);
+}
+
+inline void baikal_vdu_switch_on(struct baikal_vdu_private *priv)
+{
+       u32 cntl = readl(priv->regs + CR1);
+       cntl |= CR1_LCE;
+       writel(cntl, priv->regs + CR1);
+}
+
+static int baikal_vdu_modeset_init(struct baikal_vdu_private *priv)
+{
+       struct drm_device *dev = priv->drm;
+       struct drm_encoder *encoder;
+       int ret = 0;
+       int index;
+
+       if (priv == NULL)
+               return -EINVAL;
+       index = priv->index;
+
+       ret = baikal_vdu_primary_plane_init(priv);
+       if (ret != 0) {
+               dev_err(dev->dev, "%s: failed to init primary plane\n", priv->name);
+               return ret;
+       }
+
+       ret = baikal_vdu_crtc_create(priv);
+       if (ret) {
+               dev_err(dev->dev, "%s: failed to create CRTC\n", priv->name);
+               return ret;
+       }
+
+       if (priv->bridge) {
+               encoder = &priv->encoder;
+               ret = drm_encoder_init(dev, encoder, &baikal_vdu_encoder_funcs,
+                                      DRM_MODE_ENCODER_NONE, NULL);
+               if (ret) {
+                       dev_err(dev->dev, "%s: failed to create DRM encoder\n", priv->name);
+                       return ret;
+               }
+               encoder->crtc = &priv->crtc;
+               encoder->possible_crtcs = BIT(drm_crtc_index(encoder->crtc));
+               priv->bridge->encoder = encoder;
+               ret = drm_bridge_attach(encoder, priv->bridge, NULL, 0);
+               if (ret) {
+                       dev_err(dev->dev, "%s: failed to attach DRM bridge (%d)\n", priv->name, ret);
+                       return ret;
+               }
+       } else {
+               dev_err(dev->dev, "%s: no bridge or panel attached\n", priv->name);
+               return -ENODEV;
+       }
+
+       priv->mode_override = mode_override;
+
+       return ret;
+}
+
+static int baikal_dumb_create(struct drm_file *file, struct drm_device *dev,
+                            struct drm_mode_create_dumb *args)
+{
+       args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+       args->size = args->pitch * args->height + IFIFO_SIZE + UHD_FIFO_SIZE;
+
+       return drm_gem_dma_dumb_create_internal(file, dev, args);
+}
+
+DEFINE_DRM_GEM_DMA_FOPS(baikal_drm_fops);
+
+static struct drm_driver vdu_drm_driver = {
+       .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
+       DRM_GEM_DMA_DRIVER_OPS,
+       .lastclose = drm_fb_helper_lastclose,
+       .ioctls = NULL,
+       .fops = &baikal_drm_fops,
+       .name = DRIVER_NAME,
+       .desc = DRIVER_DESC,
+       .date = DRIVER_DATE,
+       .major = 2,
+       .minor = 0,
+       .patchlevel = 0,
+       .dumb_create = baikal_dumb_create,
+       .dumb_map_offset = drm_gem_dumb_map_offset,
+       .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
+       .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
+       .gem_prime_import = drm_gem_prime_import,
+       .gem_prime_import_sg_table = drm_gem_dma_prime_import_sg_table,
+       .gem_prime_mmap = drm_gem_prime_mmap,
+};
+
+static int baikal_vdu_allocate_resources(struct platform_device *pdev,
+               struct baikal_vdu_private *priv)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *mem;
+       int ret;
+
+       mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, priv->regs_name);
+       if (!mem)
+               mem = platform_get_resource(pdev, IORESOURCE_MEM, priv->index);
+
+       if (!mem) {
+               dev_err(dev, "%s %s: no MMIO resource specified\n", __func__, priv->name);
+               return -EINVAL;
+       }
+
+       priv->regs = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(priv->regs)) {
+               dev_err(dev, "%s %s: MMIO allocation failed\n", __func__, priv->name);
+               return PTR_ERR(priv->regs);
+       }
+
+       if (priv->off) {
+               baikal_vdu_switch_off(priv);
+               return -EPERM;
+       } else {
+               ret = baikal_vdu_modeset_init(priv);
+               if (ret) {
+                       dev_err(dev, "%s %s: failed to init modeset\n", __func__, priv->name);
+                       if (ret == -ENODEV) {
+                               baikal_vdu_switch_off(priv);
+                       }
+                       return ret;
+               } else {
+                       writel(MRR_MAX_VALUE, priv->regs + MRR);
+                       spin_lock_init(&priv->lock);
+                       return 0;
+               }
+       }
+}
+
+static int baikal_vdu_allocate_irq(struct platform_device *pdev,
+              struct baikal_vdu_private *priv)
+{
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       priv->irq = fwnode_irq_get_byname(dev->fwnode, priv->irq_name);
+       if (priv->irq < 0) {
+               dev_err(dev, "%s %s: no IRQ resource specified\n", __func__, priv->name);
+               return -EINVAL;
+       }
+
+       /* turn off interrupts before requesting the irq */
+       writel(0, priv->regs + IMR);
+       ret = request_irq(priv->irq, baikal_vdu_irq, IRQF_SHARED, dev->driver->name, priv);
+       if (ret != 0)
+               dev_err(dev, "%s %s: IRQ %d allocation failed\n", __func__, priv->name, priv->irq);
+       return ret;
+}
+
+static void baikal_vdu_free_irq(struct baikal_vdu_private *priv)
+{
+       writel(0, priv->regs + IMR);
+       writel(0x3ffff, priv->regs + ISR);
+       free_irq(priv->irq, priv->drm->dev);
+}
+
+static int baikal_vdu_allocate_clk(struct baikal_vdu_private *priv)
+{
+       priv->clk = clk_get(priv->drm->dev, priv->pclk_name);
+       if (IS_ERR(priv->clk)) {
+               dev_err(priv->drm->dev, "%s: unable to get %s, err %ld\n", priv->name, priv->pclk_name, PTR_ERR(priv->clk));
+               return PTR_ERR(priv->clk);
+       } else
+               return 0;
+}
+
+static void baikal_vdu_set_name(struct baikal_vdu_private *priv, int index, const char *name)
+{
+       char *c;
+       int len = sizeof(priv->name) / sizeof(priv->name[0]) - 1;
+       strncpy(priv->name, name, len);
+       for (c = priv->name; c < priv->name + len && *c; c++) {
+               *c = toupper(*c);
+       }
+       sprintf(priv->irq_name, "%s_irq", name);
+       sprintf(priv->pclk_name, "%s_pclk", name);
+       sprintf(priv->regs_name, "%s_regs", name);
+       priv->index = index;
+}
+
+static int baikal_vdu_bridge_init(struct baikal_vdu_private *priv, struct drm_device *drm) {
+       int ret = 0;
+       struct device *dev;
+       struct drm_bridge *bridge;
+       if (!priv || !drm)
+               return -ENODEV;
+       priv->drm = drm;
+       dev = drm->dev;
+       bridge = devm_baikal_get_bridge(dev, priv->index, 0);
+       if (IS_ERR(bridge)) {
+               ret = PTR_ERR(bridge);
+               if (ret == -EPROBE_DEFER) {
+                       dev_info(dev, "%s: bridge probe deferred\n", priv->name);
+               }
+               priv->bridge = NULL;
+       } else {
+               priv->bridge = bridge;
+       }
+       return ret;
+}
+
+static int baikal_vdu_resources_init(struct platform_device *pdev, struct baikal_vdu_private *priv)
+{
+       int ret = baikal_vdu_allocate_resources(pdev, priv);
+       if (ret)
+               return 0;
+       ret = baikal_vdu_allocate_irq(pdev, priv);
+       if (ret)
+               return 0;
+       ret = baikal_vdu_allocate_clk(priv);
+       if (ret) {
+               baikal_vdu_free_irq(priv);
+               return 0;
+       } else {
+               return 1;
+       }
+}
+
+static int baikal_vdu_drm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct baikal_lvds_bridge *panel_bridge;
+       struct baikal_vdu_crossbar *crossbar;
+       struct baikal_vdu_private *hdmi;
+       struct baikal_vdu_private *lvds;
+       struct drm_device *drm;
+       struct drm_mode_config *mode_config;
+       struct arm_smccc_res res;
+       int ret;
+
+       crossbar = devm_drm_dev_alloc(dev, &vdu_drm_driver,
+                  struct baikal_vdu_crossbar, drm);
+       if (IS_ERR(crossbar))
+               return PTR_ERR(crossbar);
+
+       drm = &crossbar->drm;
+       platform_set_drvdata(pdev, drm);
+       hdmi = &crossbar->hdmi;
+       baikal_vdu_set_name(hdmi, CRTC_HDMI, "hdmi");
+       lvds = &crossbar->lvds;
+       baikal_vdu_set_name(lvds, CRTC_LVDS, "lvds");
+
+       ret = baikal_vdu_bridge_init(hdmi, drm);
+       if (ret == -EPROBE_DEFER) {
+               goto out_drm;
+       }
+
+       ret = device_property_read_u32(&pdev->dev, "lvds-lanes",
+                                      &lvds->num_lanes);
+       if (ret)
+               lvds->num_lanes = 0;
+       if (lvds->num_lanes) {
+               ret = baikal_vdu_bridge_init(lvds, drm);
+               if (ret == -EPROBE_DEFER) {
+                       goto out_drm;
+               }
+       }
+
+       drm_mode_config_init(drm);
+       mode_config = &drm->mode_config;
+       mode_config->funcs = &mode_config_funcs;
+       mode_config->min_width = 1;
+       mode_config->max_width = 8192;
+       mode_config->min_height = 1;
+       mode_config->max_height = 8192;
+
+       hdmi->off = hdmi_off;
+       hdmi->ready = baikal_vdu_resources_init(pdev, hdmi);
+       if (lvds->num_lanes) {
+               lvds->off = lvds_off;
+               lvds->ready = baikal_vdu_resources_init(pdev, lvds);
+       } else {
+               lvds->ready = 0;
+               lvds->bridge = NULL;
+               dev_info(dev, "No 'lvds-lanes' property found\n");
+       }
+       if (lvds->ready) {
+               ret = baikal_vdu_backlight_create(drm);
+               if (ret) {
+                       dev_err(dev, "LVDS: failed to create backlight\n");
+               }
+               if (bridge_is_baikal_lvds_bridge(lvds->bridge)) {
+                       panel_bridge = bridge_to_baikal_lvds_bridge(lvds->bridge);
+                       panel_bridge->vdu = lvds;
+               } else {
+               // TODO implement handling of third-party bridges
+               }
+       }
+       if (hdmi->bridge) {
+               // TODO implement functions specific to HDMI bridge
+       }
+
+       hdmi->ready = hdmi->ready & !hdmi->off;
+       lvds->ready = lvds->ready & !lvds->off;
+       dev_info(dev, "%s output %s\n", hdmi->name, hdmi->ready ? "enabled" : "disabled");
+       dev_info(dev, "%s output %s\n", lvds->name, lvds->ready ? "enabled" : "disabled");
+       baikal_vdu_remove_efifb(drm);
+
+       if (hdmi->ready || lvds->ready) {
+               /* Disable SCP debug output as it may affect VDU performance */
+               arm_smccc_smc(BAIKAL_SMC_LOG_DISABLE, 0, 0, 0, 0, 0, 0, 0, &res);
+
+               drm_mode_config_reset(drm);
+               drm_kms_helper_poll_init(drm);
+               ret = drm_dev_register(drm, 0);
+               if (ret) {
+                       dev_err(dev, "failed to register DRM device\n");
+                       goto out_config;
+               }
+               drm_fbdev_generic_setup(drm, 32);
+#if defined(CONFIG_DEBUG_FS)
+               if (hdmi->ready)
+                       baikal_vdu_hdmi_debugfs_init(drm->primary);
+               if (lvds->ready)
+                       baikal_vdu_lvds_debugfs_init(drm->primary);
+#endif
+               return 0;
+       } else {
+               dev_err(dev, "no active outputs configured\n");
+               ret = -ENODEV;
+       }
+out_config:
+       drm_mode_config_cleanup(drm);
+out_drm:
+       dev_err(dev, "failed to probe: %d\n", ret);
+       return ret;
+}
+
+static int baikal_vdu_drm_remove(struct platform_device *pdev)
+{
+       struct drm_device *drm = platform_get_drvdata(pdev);
+       struct baikal_vdu_crossbar *crossbar = drm_to_baikal_vdu_crossbar(drm);
+
+       drm_dev_unregister(drm);
+       drm_mode_config_cleanup(drm);
+       if (crossbar->hdmi.irq)
+               free_irq(crossbar->hdmi.irq, drm->dev);
+       if (crossbar->lvds.irq)
+               free_irq(crossbar->lvds.irq, drm->dev);
+
+       return 0;
+}
+
+static const struct of_device_id baikal_vdu_of_match[] = {
+       { .compatible = "baikal,vdu" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, baikal_vdu_of_match);
+
+static struct platform_driver baikal_vdu_platform_driver = {
+       .probe  = baikal_vdu_drm_probe,
+       .remove = baikal_vdu_drm_remove,
+       .driver = {
+               .name   = DRIVER_NAME,
+               .of_match_table = baikal_vdu_of_match,
+       },
+};
+
+module_param(mode_override, int, 0644);
+module_param(hdmi_off, int, 0644);
+module_param(lvds_off, int, 0644);
+
+module_platform_driver(baikal_vdu_platform_driver);
+
+MODULE_AUTHOR("Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal Electronics BE-M1000 Video Display Unit (VDU) DRM Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_SOFTDEP("pre: baikal_hdmi");
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_panel.c b/drivers/gpu/drm/baikal/baikal_vdu_panel.c
new file mode 100644 (file)
index 0000000..9983f36
--- /dev/null
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_probe_helper.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+static void baikal_lvds_connector_force(struct drm_connector *connector)
+{
+       struct baikal_lvds_bridge *bridge = connector_to_baikal_lvds_bridge(connector);
+       struct baikal_vdu_private *priv = bridge->vdu;
+       u32 cntl = readl(priv->regs + CR1);
+       if (connector->force == DRM_FORCE_OFF)
+               cntl &= ~CR1_LCE;
+       else
+               cntl |= CR1_LCE;
+       writel(cntl, priv->regs + CR1);
+}
+
+static int baikal_lvds_connector_get_modes(struct drm_connector *connector)
+{
+       struct baikal_lvds_bridge *panel_bridge =
+               connector_to_baikal_lvds_bridge(connector);
+
+       return drm_panel_get_modes(panel_bridge->panel, connector);
+}
+
+static const struct drm_connector_helper_funcs
+baikal_lvds_bridge_connector_helper_funcs = {
+       .get_modes = baikal_lvds_connector_get_modes,
+};
+
+static const struct drm_connector_funcs baikal_lvds_bridge_connector_funcs = {
+       .reset = drm_atomic_helper_connector_reset,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = drm_connector_cleanup,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+       .force = baikal_lvds_connector_force,
+};
+
+static int baikal_lvds_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)
+{
+       struct baikal_lvds_bridge *panel_bridge = bridge_to_baikal_lvds_bridge(bridge);
+       struct drm_connector *connector = &panel_bridge->connector;
+       int ret;
+
+       if (!bridge->encoder) {
+               DRM_ERROR("Missing encoder\n");
+               return -ENODEV;
+       }
+
+       drm_connector_helper_add(connector,
+                                &baikal_lvds_bridge_connector_helper_funcs);
+
+       ret = drm_connector_init(bridge->dev, connector,
+                                &baikal_lvds_bridge_connector_funcs,
+                                panel_bridge->connector_type);
+       if (ret) {
+               DRM_ERROR("Failed to initialize connector\n");
+               return ret;
+       }
+
+       ret = drm_connector_attach_encoder(&panel_bridge->connector,
+                                         bridge->encoder);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static void baikal_lvds_bridge_pre_enable(struct drm_bridge *bridge)
+{
+       struct baikal_lvds_bridge *panel_bridge = bridge_to_baikal_lvds_bridge(bridge);
+
+       baikal_vdu_switch_on(panel_bridge->vdu);
+       drm_panel_prepare(panel_bridge->panel);
+}
+
+static void baikal_lvds_bridge_enable(struct drm_bridge *bridge)
+{
+       struct baikal_lvds_bridge *panel_bridge = bridge_to_baikal_lvds_bridge(bridge);
+
+       drm_panel_enable(panel_bridge->panel);
+}
+
+static void baikal_lvds_bridge_disable(struct drm_bridge *bridge)
+{
+       struct baikal_lvds_bridge *panel_bridge = bridge_to_baikal_lvds_bridge(bridge);
+
+       drm_panel_disable(panel_bridge->panel);
+}
+
+static void baikal_lvds_bridge_post_disable(struct drm_bridge *bridge)
+{
+       struct baikal_lvds_bridge *panel_bridge = bridge_to_baikal_lvds_bridge(bridge);
+
+       drm_panel_unprepare(panel_bridge->panel);
+       baikal_vdu_switch_off(panel_bridge->vdu);
+}
+
+static const struct drm_bridge_funcs baikal_lvds_bridge_funcs = {
+       .attach = baikal_lvds_bridge_attach,
+       .pre_enable = baikal_lvds_bridge_pre_enable,
+       .enable = baikal_lvds_bridge_enable,
+       .disable = baikal_lvds_bridge_disable,
+       .post_disable = baikal_lvds_bridge_post_disable,
+};
+
+static struct drm_bridge *baikal_lvds_bridge_add(struct drm_panel *panel,
+                                       u32 connector_type)
+{
+       struct baikal_lvds_bridge *panel_bridge;
+
+       if (!panel)
+               return ERR_PTR(-EINVAL);
+
+       panel_bridge = devm_kzalloc(panel->dev, sizeof(*panel_bridge),
+                                   GFP_KERNEL);
+       if (!panel_bridge)
+               return ERR_PTR(-ENOMEM);
+
+       panel_bridge->connector_type = connector_type;
+       panel_bridge->panel = panel;
+
+       panel_bridge->bridge.funcs = &baikal_lvds_bridge_funcs;
+#ifdef CONFIG_OF
+       panel_bridge->bridge.of_node = panel->dev->of_node;
+#endif
+
+       drm_bridge_add(&panel_bridge->bridge);
+
+       return &panel_bridge->bridge;
+}
+
+static void baikal_lvds_bridge_remove(struct drm_bridge *bridge)
+{
+       struct baikal_lvds_bridge *panel_bridge;
+
+       if (!bridge)
+               return;
+
+       if (bridge->funcs != &baikal_lvds_bridge_funcs)
+               return;
+
+       panel_bridge = bridge_to_baikal_lvds_bridge(bridge);
+
+       drm_bridge_remove(bridge);
+       devm_kfree(panel_bridge->panel->dev, bridge);
+}
+
+static void devm_baikal_lvds_bridge_release(struct device *dev, void *res)
+{
+       struct drm_bridge **bridge = res;
+
+       baikal_lvds_bridge_remove(*bridge);
+}
+
+struct drm_bridge *devm_baikal_lvds_bridge_add(struct device *dev,
+                                            struct drm_panel *panel,
+                                            u32 connector_type)
+{
+       struct drm_bridge **ptr, *bridge;
+
+       ptr = devres_alloc(devm_baikal_lvds_bridge_release, sizeof(*ptr),
+                          GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       bridge = baikal_lvds_bridge_add(panel, connector_type);
+       if (!IS_ERR(bridge)) {
+               *ptr = bridge;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return bridge;
+}
+
+bool bridge_is_baikal_lvds_bridge(const struct drm_bridge *bridge)
+{
+       return bridge && (bridge->funcs == &baikal_lvds_bridge_funcs);
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_plane.c b/drivers/gpu/drm/baikal/baikal_vdu_plane.c
new file mode 100644 (file)
index 0000000..d9bdb95
--- /dev/null
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/of_graph.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_fb_dma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "baikal_vdu_drm.h"
+#include "baikal_vdu_regs.h"
+
+static void baikal_vdu_primary_plane_atomic_update(struct drm_plane *plane,
+                                             struct drm_atomic_state *old_state)
+{
+       struct baikal_vdu_private *priv;
+       struct drm_plane_state *state = plane->state;
+       struct drm_crtc *crtc = state->crtc;
+       struct drm_framebuffer *fb = state->fb;
+       uint32_t cntl;
+       uint32_t addr;
+       unsigned long flags;
+
+       if (!fb)
+               return;
+
+       priv = crtc_to_baikal_vdu(crtc);
+       addr = drm_fb_dma_get_gem_addr(fb, state, 0);
+
+       spin_lock_irqsave(&priv->lock, flags);
+       writel(addr, priv->regs + DBAR);
+       spin_unlock_irqrestore(&priv->lock, flags);
+       priv->counters[16]++;
+
+       cntl = readl(priv->regs + CR1);
+       cntl &= ~CR1_BPP_MASK;
+
+       /* Note that the the hardware's format reader takes 'r' from
+        * the low bit, while DRM formats list channels from high bit
+        * to low bit as you read left to right.
+        */
+       switch (fb->format->format) {
+       case DRM_FORMAT_BGR888:
+               cntl |= CR1_BPP24 | CR1_FBP | CR1_BGR;
+               break;
+       case DRM_FORMAT_RGB888:
+               cntl |= CR1_BPP24 | CR1_FBP;
+               break;
+       case DRM_FORMAT_ABGR8888:
+       case DRM_FORMAT_XBGR8888:
+               cntl |= CR1_BPP24 | CR1_BGR;
+               break;
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_XRGB8888:
+               cntl |= CR1_BPP24;
+               break;
+       case DRM_FORMAT_BGR565:
+               cntl |= CR1_BPP16_565 | CR1_BGR;
+               break;
+       case DRM_FORMAT_RGB565:
+               cntl |= CR1_BPP16_565;
+               break;
+       case DRM_FORMAT_ABGR1555:
+       case DRM_FORMAT_XBGR1555:
+               cntl |= CR1_BPP16_555 | CR1_BGR;
+               break;
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_XRGB1555:
+               cntl |= CR1_BPP16_555;
+               break;
+       default:
+               WARN_ONCE(true, "Unknown FB format 0x%08x, set XRGB8888 instead\n",
+                               fb->format->format);
+               cntl |= CR1_BPP24;
+               break;
+       }
+
+       writel(cntl, priv->regs + CR1);
+}
+
+static const struct drm_plane_helper_funcs baikal_vdu_primary_plane_helper_funcs = {
+       .atomic_update = baikal_vdu_primary_plane_atomic_update,
+};
+
+static const struct drm_plane_funcs baikal_vdu_primary_plane_funcs = {
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
+       .reset = drm_atomic_helper_plane_reset,
+       .destroy = drm_plane_cleanup,
+       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+int baikal_vdu_primary_plane_init(struct baikal_vdu_private *priv)
+{
+       struct drm_device *drm = priv->drm;
+       struct drm_plane *plane = &priv->primary;
+       static const u32 formats[] = {
+               DRM_FORMAT_BGR888,
+               DRM_FORMAT_RGB888,
+               DRM_FORMAT_ABGR8888,
+               DRM_FORMAT_XBGR8888,
+               DRM_FORMAT_ARGB8888,
+               DRM_FORMAT_XRGB8888,
+               DRM_FORMAT_BGR565,
+               DRM_FORMAT_RGB565,
+               DRM_FORMAT_ABGR1555,
+               DRM_FORMAT_XBGR1555,
+               DRM_FORMAT_ARGB1555,
+               DRM_FORMAT_XRGB1555,
+       };
+       int ret;
+
+       ret = drm_universal_plane_init(drm, plane, 0,
+                                      &baikal_vdu_primary_plane_funcs,
+                                      formats,
+                                      ARRAY_SIZE(formats),
+                                      NULL,
+                                      DRM_PLANE_TYPE_PRIMARY,
+                                      NULL);
+       if (ret)
+               return ret;
+
+       drm_plane_helper_add(plane, &baikal_vdu_primary_plane_helper_funcs);
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/baikal/baikal_vdu_regs.h b/drivers/gpu/drm/baikal/baikal_vdu_regs.h
new file mode 100644 (file)
index 0000000..d48ea8d
--- /dev/null
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+#ifndef __BAIKAL_VDU_REGS_H__
+#define __BAIKAL_VDU_REGS_H__
+
+#define CR1         0x000
+#define HTR         0x008
+#define VTR1        0x00C
+#define VTR2        0x010
+#define PCTR        0x014
+#define ISR         0x018
+#define IMR         0x01C
+#define IVR         0x020
+#define ISCR        0x024
+#define DBAR        0x028
+#define DCAR        0x02C
+#define DEAR        0x030
+#define PWMFR       0x034
+#define PWMDCR      0x038
+#define HVTER       0x044
+#define HPPLOR      0x048
+#define GPIOR       0x1F8
+#define MRR         0xFFC
+
+#define INTR_UFU    BIT(16)
+#define INTR_BAU    BIT(7)
+#define INTR_VCT    BIT(6)
+#define INTR_MBE    BIT(5)
+#define INTR_FER    BIT(4)
+#define INTR_IFO    BIT(3)
+#define INTR_OFU    BIT(0)
+
+#define CR1_FBP             BIT(19)
+#define CR1_FDW_MASK        GENMASK(17, 16)
+#define CR1_FDW_4_WORDS     (0 << 16)
+#define CR1_FDW_8_WORDS     (1 << 16)
+#define CR1_FDW_16_WORDS    (2 << 16)
+#define CR1_OPS_MASK        GENMASK(14, 12)
+#define CR1_OPS_LCD18       (0 << 13)
+#define CR1_OPS_LCD24       (1 << 13)
+#define CR1_OPS_565         (0 << 12)
+#define CR1_OPS_555         (1 << 12)
+#define CR1_VSP             BIT(11)
+#define CR1_HSP             BIT(10)
+#define CR1_DEP             BIT(8)
+#define CR1_BGR             BIT(5)
+#define CR1_BPP_MASK        GENMASK(4, 2)
+#define CR1_BPP1            (0 << 2)
+#define CR1_BPP2            (1 << 2)
+#define CR1_BPP4            (2 << 2)
+#define CR1_BPP8            (3 << 2)
+#define CR1_BPP16           (4 << 2)
+#define CR1_BPP18           (5 << 2)
+#define CR1_BPP24           (6 << 2)
+#define CR1_LCE             BIT(0)
+
+#define CR1_BPP16_555 ((CR1_BPP16) | (CR1_OPS_555))
+#define CR1_BPP16_565 ((CR1_BPP16) | (CR1_OPS_565))
+
+#define VTR1_VBP_MASK       GENMASK(23, 16)
+#define VTR1_VBP(x)         ((x) << 16)
+#define VTR1_VBP_LSB_WIDTH  8
+#define VTR1_VFP_MASK       GENMASK(15, 8)
+#define VTR1_VFP(x)         ((x) << 8)
+#define VTR1_VFP_LSB_WIDTH  8
+#define VTR1_VSW_MASK       GENMASK(7, 0)
+#define VTR1_VSW(x)         ((x) << 0)
+#define VTR1_VSW_LSB_WIDTH  8
+
+#define VTR2_LPP_MASK       GENMASK(11, 0)
+
+#define HTR_HSW_MASK        GENMASK(31, 24)
+#define HTR_HSW(x)          ((x) << 24)
+#define HTR_HSW_LSB_WIDTH   8
+#define HTR_HBP_MASK        GENMASK(23, 16)
+#define HTR_HBP(x)          ((x) << 16)
+#define HTR_HBP_LSB_WIDTH   8
+#define HTR_PPL_MASK        GENMASK(15, 8)
+#define HTR_PPL(x)          ((x) << 8)
+#define HTR_HFP_MASK        GENMASK(7, 0)
+#define HTR_HFP(x)          ((x) << 0)
+#define HTR_HFP_LSB_WIDTH   8
+
+#define PCTR_PCI2           BIT(11)
+#define PCTR_PCR            BIT(10)
+#define PCTR_PCI            BIT(9)
+#define PCTR_PCB            BIT(8)
+#define PCTR_PCD_MASK       GENMASK(7, 0)
+#define PCTR_MAX_PCD        128
+
+#define ISCR_VSC_OFF        0x0
+#define ISCR_VSC_VSW        0x4
+#define ISCR_VSC_VBP        0x5
+#define ISCR_VSC_VACTIVE    0x6
+#define ISCR_VSC_VFP        0x7
+
+#define PWMFR_PWMPCR        BIT(24)
+#define PWMFR_PWMFCI        BIT(23)
+#define PWMFR_PWMFCE        BIT(22)
+#define PWMFR_PWMFCD_MASK   GENMASK(21, 0)
+#define PWMFR_PWMFCD(x)     ((x) << 0)
+
+#define HVTER_VSWE_MASK     GENMASK(25, 24)
+#define HVTER_VSWE(x)       ((x) << 24)
+#define HVTER_HSWE_MASK     GENMASK(17, 16)
+#define HVTER_HSWE(x)       ((x) << 16)
+#define HVTER_VBPE_MASK     GENMASK(13, 12)
+#define HVTER_VBPE(x)       ((x) << 12)
+#define HVTER_VFPE_MASK     GENMASK(9, 8)
+#define HVTER_VFPE(x)       ((x) << 8)
+#define HVTER_HBPE_MASK     GENMASK(5, 4)
+#define HVTER_HBPE(x)       ((x) << 4)
+#define HVTER_HFPE_MASK     GENMASK(1, 0)
+#define HVTER_HFPE(x)       ((x) << 0)
+
+#define HPPLOR_HPOE         BIT(31)
+#define HPPLOR_HPPLO_MASK   GENMASK(11, 0)
+#define HPPLOR_HPPLO(x)     ((x) << 0)
+
+#define GPIOR_UHD_MASK      GENMASK(23, 16)
+#define GPIOR_UHD_SNGL_PORT (0 << 18)
+#define GPIOR_UHD_DUAL_PORT (1 << 18)
+#define GPIOR_UHD_QUAD_PORT (2 << 18)
+#define GPIOR_UHD_ENB       BIT(17)
+
+#define MRR_DEAR_MRR_MASK   GENMASK(31, 3)
+#define MRR_OUTSTND_RQ_MASK GENMASK(2, 0)
+#define MRR_OUTSTND_RQ(x)   ((x >> 1) << 0)
+#define MRR_MAX_VALUE       ((0xffffffff & MRR_DEAR_MRR_MASK) | MRR_OUTSTND_RQ(4))
+
+#endif /* __BAIKAL_VDU_REGS_H__ */
index 15fc182d05ef02e65541542610964ae028687491..61a8542708c719554f4473a634224d81f3df863c 100644 (file)
@@ -51,3 +51,19 @@ config DRM_DW_MIPI_DSI
        select DRM_KMS_HELPER
        select DRM_MIPI_DSI
        select DRM_PANEL_BRIDGE
+
+config DRM_BAIKAL_HDMI
+       tristate "Baikal-M HDMI transmitter"
+       select DRM_DW_HDMI
+       help
+         Choose this if you want to use HDMI on Baikal-M.
+
+config DRM_BAIKAL_HDMI_AHB_AUDIO
+       tristate "Baikal-M HDMI Audio interface"
+       depends on DRM_BAIKAL_HDMI && SND
+       select SND_PCM
+       select SND_PCM_ELD
+       select SND_PCM_IEC958
+       help
+         Support the AHB Audio interface which is part of the
+         Baikal-M HDMI Tx block.
index ce715562e9e522e8036d0efc6943f5c0799c2af6..b5285a4261d8b264b54960605813d463feff34c6 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
+obj-$(CONFIG_DRM_BAIKAL_HDMI_AHB_AUDIO) += baikal-hdmi-ahb-audio.o
 obj-$(CONFIG_DRM_DW_HDMI_GP_AUDIO) += dw-hdmi-gp-audio.o
 obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o
 obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
diff --git a/drivers/gpu/drm/bridge/synopsys/baikal-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/baikal-hdmi-ahb-audio.c
new file mode 100644 (file)
index 0000000..c1c71a8
--- /dev/null
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Baikal Electronics BE-M1000 DesignWare HDMI AHB audio driver
+ *
+ * Copyright (C) 2020-2022 Baikal Electronics, JSC
+ *
+ * Author: Pavel Parkhomenko <Pavel.Parkhomenko@baikalelectronics.ru>
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <drm/drm_edid.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/pcm_iec958.h>
+
+#include "dw-hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-ahb-audio"
+
+#define BAIKAL_HDMI_REG_SHIFT  2
+
+/* Provide some bits rather than bit offsets */
+enum {
+       HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+       HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+       HDMI_AHB_DMA_START_START = BIT(0),
+       HDMI_AHB_DMA_STOP_STOP = BIT(0),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_OVERRUN = BIT(6),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_OVERRUN |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+               HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+       HDMI_IH_AHBDMAAUD_STAT0_OVERRUN = BIT(6),
+       HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+       HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+       HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+       HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+       HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+       HDMI_IH_AHBDMAAUD_STAT0_ALL =
+               HDMI_IH_AHBDMAAUD_STAT0_OVERRUN |
+               HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+               HDMI_IH_AHBDMAAUD_STAT0_LOST |
+               HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+               HDMI_IH_AHBDMAAUD_STAT0_DONE |
+               HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+               HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+       HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+       HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+       HDMI_AHB_DMA_CONF0_INCR4 = 0,
+       HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+       HDMI_AHB_DMA_MASK_DONE = BIT(7),
+
+       HDMI_REVISION_ID = 0x0001,
+       HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+       HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+       HDMI_FC_AUDICONF2 = 0x1027,
+       HDMI_FC_AUDSCONF = 0x1063,
+       HDMI_FC_AUDSCONF_LAYOUT1 = 1 << 0,
+       HDMI_FC_AUDSCONF_LAYOUT0 = 0 << 0,
+       HDMI_AHB_DMA_CONF0 = 0x3600,
+       HDMI_AHB_DMA_START = 0x3601,
+       HDMI_AHB_DMA_STOP = 0x3602,
+       HDMI_AHB_DMA_THRSLD = 0x3603,
+       HDMI_AHB_DMA_STRADDR0 = 0x3604,
+       HDMI_AHB_DMA_STPADDR0 = 0x3608,
+       HDMI_AHB_DMA_MASK = 0x3614,
+       HDMI_AHB_DMA_POL = 0x3615,
+       HDMI_AHB_DMA_CONF1 = 0x3616,
+       HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct dw_hdmi_channel_conf {
+       u8 conf1;
+       u8 ca;
+};
+
+/*
+ * The default mapping of ALSA channels to HDMI channels and speaker
+ * allocation bits.  Note that we can't do channel remapping here -
+ * channels must be in the same order.
+ *
+ * Mappings for alsa-lib pcm/surround*.conf files:
+ *
+ *             Front   Sur4.0  Sur4.1  Sur5.0  Sur5.1  Sur7.1
+ * Channels    2       4       6       6       6       8
+ *
+ * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
+ *
+ *                             Number of ALSA channels
+ * ALSA Channel        2       3       4       5       6       7       8
+ * 0           FL:0    =       =       =       =       =       =
+ * 1           FR:1    =       =       =       =       =       =
+ * 2                   FC:3    RL:4    LFE:2   =       =       =
+ * 3                           RR:5    RL:4    FC:3    =       =
+ * 4                                   RR:5    RL:4    =       =
+ * 5                                           RR:5    =       =
+ * 6                                                   RC:6    =
+ * 7                                                   RLC/FRC RLC/FRC
+ */
+static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
+       { 0x03, 0x00 }, /* FL,FR */
+       { 0x0b, 0x02 }, /* FL,FR,FC */
+       { 0x33, 0x08 }, /* FL,FR,RL,RR */
+       { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
+       { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
+       { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
+       { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
+};
+
+struct snd_dw_hdmi {
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       spinlock_t lock;
+       struct dw_hdmi_audio_data data;
+       struct snd_pcm_substream *substream;
+       void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+       void *buf_src;
+       void *buf_dst;
+       dma_addr_t buf_addr;
+       unsigned buf_offset;
+       unsigned buf_period;
+       unsigned buf_size;
+       unsigned channels;
+       u8 revision;
+       u8 iec_offset;
+       u8 cs[192][8];
+};
+
+static inline void baikal_hdmi_writeb(u8 val, void __iomem *base, uint offset)
+{
+       writeb(val, base + (offset << BAIKAL_HDMI_REG_SHIFT));
+}
+
+static inline void baikal_hdmi_writeb_relaxed(u8 val, void __iomem *base, uint offset)
+{
+       writeb_relaxed(val, base + (offset << BAIKAL_HDMI_REG_SHIFT));
+}
+
+static inline u8 baikal_hdmi_readb_relaxed(void __iomem *base, uint offset)
+{
+       return readb_relaxed(base + (offset << BAIKAL_HDMI_REG_SHIFT));
+}
+
+static void baikal_hdmi_writel(u32 val, void __iomem *base, uint offset)
+{
+       writeb_relaxed(val, base + (offset << BAIKAL_HDMI_REG_SHIFT));
+       writeb_relaxed(val >> 8, base + ((offset + 1) << BAIKAL_HDMI_REG_SHIFT));
+       writeb_relaxed(val >> 16, base + ((offset + 2) << BAIKAL_HDMI_REG_SHIFT));
+       writeb_relaxed(val >> 24, base + ((offset + 3) << BAIKAL_HDMI_REG_SHIFT));
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+       size_t offset, size_t bytes)
+{
+       u32 *src = dw->buf_src + offset;
+       u32 *dst = dw->buf_dst + offset;
+       u32 *end = dw->buf_src + offset + bytes;
+
+       do {
+               u32 b, sample = *src++;
+
+               b = (sample & 8) << (28 - 3);
+
+               sample >>= 4;
+
+               *dst++ = sample | b;
+       } while (src < end);
+}
+
+static u32 parity(u32 sample)
+{
+       sample ^= sample >> 16;
+       sample ^= sample >> 8;
+       sample ^= sample >> 4;
+       sample ^= sample >> 2;
+       sample ^= sample >> 1;
+       return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+       size_t offset, size_t bytes)
+{
+       u32 *src = dw->buf_src + offset;
+       u32 *dst = dw->buf_dst + offset;
+       u32 *end = dw->buf_src + offset + bytes;
+
+       do {
+               unsigned i;
+               u8 *cs;
+
+               cs = dw->cs[dw->iec_offset++];
+               if (dw->iec_offset >= 192)
+                       dw->iec_offset = 0;
+
+               i = dw->channels;
+               do {
+                       u32 sample = *src++;
+
+                       sample &= ~0xff000000;
+                       sample |= *cs++ << 24;
+                       sample |= parity(sample & ~0xf8000000);
+
+                       *dst++ = sample;
+               } while (--i);
+       } while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+       struct snd_pcm_runtime *runtime)
+{
+       u8 cs[4];
+       unsigned ch, i, j;
+
+       snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
+
+       memset(dw->cs, 0, sizeof(dw->cs));
+
+       for (ch = 0; ch < 8; ch++) {
+               cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+               cs[2] |= (ch + 1) << 4;
+
+               for (i = 0; i < ARRAY_SIZE(cs); i++) {
+                       unsigned c = cs[i];
+
+                       for (j = 0; j < 8; j++, c >>= 1)
+                               dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+               }
+       }
+       dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+       void __iomem *base = dw->data.base;
+       unsigned offset = dw->buf_offset;
+       unsigned period = dw->buf_period;
+       u32 start, stop;
+
+       dw->reformat(dw, offset, period);
+
+       /* Clear all irqs before enabling irqs and starting DMA */
+       baikal_hdmi_writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+                      base, HDMI_IH_AHBDMAAUD_STAT0);
+
+       start = dw->buf_addr + offset;
+       stop = start + period - 1;
+
+       /* Setup the hardware start/stop addresses */
+       baikal_hdmi_writel(start, base, HDMI_AHB_DMA_STRADDR0);
+       baikal_hdmi_writel(stop, base, HDMI_AHB_DMA_STPADDR0);
+
+       baikal_hdmi_writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base, HDMI_AHB_DMA_MASK);
+       baikal_hdmi_writeb(HDMI_AHB_DMA_START_START, base, HDMI_AHB_DMA_START);
+
+       offset += period;
+       if (offset >= dw->buf_size)
+               offset = 0;
+       dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+       /* Disable interrupts before disabling DMA */
+       baikal_hdmi_writeb_relaxed(~0, dw->data.base, HDMI_AHB_DMA_MASK);
+       baikal_hdmi_writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base, HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+       struct snd_dw_hdmi *dw = data;
+       struct snd_pcm_substream *substream;
+       unsigned stat;
+
+       stat = baikal_hdmi_readb_relaxed(dw->data.base, HDMI_IH_AHBDMAAUD_STAT0);
+       if (!stat)
+               return IRQ_NONE;
+
+       baikal_hdmi_writeb_relaxed(stat, dw->data.base, HDMI_IH_AHBDMAAUD_STAT0);
+
+       substream = dw->substream;
+       if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+               snd_pcm_period_elapsed(substream);
+
+               spin_lock(&dw->lock);
+               if (dw->substream)
+                       dw_hdmi_start_dma(dw);
+               spin_unlock(&dw->lock);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID,
+       .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+                  SNDRV_PCM_FMTBIT_S24_LE,
+       .rates = SNDRV_PCM_RATE_32000 |
+                SNDRV_PCM_RATE_44100 |
+                SNDRV_PCM_RATE_48000 |
+                SNDRV_PCM_RATE_88200 |
+                SNDRV_PCM_RATE_96000 |
+                SNDRV_PCM_RATE_176400 |
+                SNDRV_PCM_RATE_192000,
+       .channels_min = 2,
+       .channels_max = 8,
+       .buffer_bytes_max = 1024 * 1024,
+       .period_bytes_min = 256,
+       .period_bytes_max = 8192,       /* ERR004323: must limit to 8k */
+       .periods_min = 2,
+       .periods_max = 16,
+       .fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_dw_hdmi *dw = substream->private_data;
+       void __iomem *base = dw->data.base;
+       u8 *eld;
+       int ret;
+
+       runtime->hw = dw_hdmi_hw;
+
+       eld = dw->data.get_eld(dw->data.hdmi);
+       if (eld) {
+               ret = snd_pcm_hw_constraint_eld(runtime, eld);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = snd_pcm_limit_hw_rates(runtime);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               return ret;
+
+       /* Limit the buffer size to the size of the preallocated buffer */
+       ret = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                                          0, substream->dma_buffer.bytes);
+       if (ret < 0)
+               return ret;
+
+       /* Clear FIFO */
+       baikal_hdmi_writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+                      base, HDMI_AHB_DMA_CONF0);
+
+       /* Configure interrupt polarities */
+       baikal_hdmi_writeb_relaxed(~0, base, HDMI_AHB_DMA_POL);
+       baikal_hdmi_writeb_relaxed(~0, base, HDMI_AHB_DMA_BUFFPOL);
+
+       /* Keep interrupts masked, and clear any pending */
+       baikal_hdmi_writeb_relaxed(~0, base, HDMI_AHB_DMA_MASK);
+       baikal_hdmi_writeb_relaxed(~0, base, HDMI_IH_AHBDMAAUD_STAT0);
+
+       ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+                         "dw-hdmi-audio", dw);
+       if (ret)
+               return ret;
+
+       /* Un-mute done interrupt */
+       baikal_hdmi_writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+                      ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+                      base, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+       return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+       struct snd_dw_hdmi *dw = substream->private_data;
+
+       /* Mute all interrupts */
+       baikal_hdmi_writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+                      dw->data.base, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+       free_irq(dw->data.irq, dw);
+
+       return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       /* Allocate the PCM runtime buffer, which is exposed to userspace. */
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_dw_hdmi *dw = substream->private_data;
+       u8 threshold, conf0, conf1, layout, ca;
+
+       /* Setup as per 3.0.5 FSL 4.1.0 BSP */
+       switch (dw->revision) {
+       case 0x0a:
+               conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+                       HDMI_AHB_DMA_CONF0_INCR4;
+               if (runtime->channels == 2)
+                       threshold = 126;
+               else
+                       threshold = 124;
+               break;
+       case 0x1a:
+               conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+                       HDMI_AHB_DMA_CONF0_INCR8;
+               threshold = 128;
+               break;
+       case 0x2a: /* this revision is used in Baikal-M SoC */
+               conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+                       HDMI_AHB_DMA_CONF0_INCR16;
+               threshold = 128;
+               break;
+       default:
+               /* NOTREACHED */
+               return -EINVAL;
+       }
+
+       dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
+
+       /* Minimum number of bytes in the fifo. */
+       runtime->hw.fifo_size = threshold * 32;
+
+       conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+       conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
+       ca = default_hdmi_channel_config[runtime->channels - 2].ca;
+
+       /*
+        * For >2 channel PCM audio, we need to select layout 1
+        * and set an appropriate channel map.
+        */
+       if (runtime->channels > 2)
+               layout = HDMI_FC_AUDSCONF_LAYOUT1;
+       else
+               layout = HDMI_FC_AUDSCONF_LAYOUT0;
+
+       baikal_hdmi_writeb_relaxed(threshold, dw->data.base, HDMI_AHB_DMA_THRSLD);
+       baikal_hdmi_writeb_relaxed(conf0, dw->data.base, HDMI_AHB_DMA_CONF0);
+       baikal_hdmi_writeb_relaxed(conf1, dw->data.base, HDMI_AHB_DMA_CONF1);
+       baikal_hdmi_writeb_relaxed(layout, dw->data.base, HDMI_FC_AUDSCONF);
+       baikal_hdmi_writeb_relaxed(ca, dw->data.base, HDMI_FC_AUDICONF2);
+
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+               dw->reformat = dw_hdmi_reformat_iec958;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               dw_hdmi_create_cs(dw, runtime);
+               dw->reformat = dw_hdmi_reformat_s24;
+               break;
+       }
+       dw->iec_offset = 0;
+       dw->channels = runtime->channels;
+       dw->buf_src  = runtime->dma_area;
+       dw->buf_dst  = substream->dma_buffer.area;
+       dw->buf_addr = substream->dma_buffer.addr;
+       dw->buf_period = snd_pcm_lib_period_bytes(substream);
+       dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+       return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_dw_hdmi *dw = substream->private_data;
+       unsigned long flags;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               spin_lock_irqsave(&dw->lock, flags);
+               dw->buf_offset = 0;
+               dw->substream = substream;
+               dw_hdmi_start_dma(dw);
+               dw_hdmi_audio_enable(dw->data.hdmi);
+               spin_unlock_irqrestore(&dw->lock, flags);
+               substream->runtime->delay = substream->runtime->period_size;
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               spin_lock_irqsave(&dw->lock, flags);
+               dw->substream = NULL;
+               dw_hdmi_stop_dma(dw);
+               dw_hdmi_audio_disable(dw->data.hdmi);
+               spin_unlock_irqrestore(&dw->lock, flags);
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_dw_hdmi *dw = substream->private_data;
+
+       /*
+        * We are unable to report the exact hardware position as
+        * reading the 32-bit DMA position using 8-bit reads is racy.
+        */
+       return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+       .open = dw_hdmi_open,
+       .close = dw_hdmi_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = dw_hdmi_hw_params,
+       .hw_free = dw_hdmi_hw_free,
+       .prepare = dw_hdmi_prepare,
+       .trigger = dw_hdmi_trigger,
+       .pointer = dw_hdmi_pointer,
+       .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+       const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+       struct device *dev = pdev->dev.parent;
+       struct snd_dw_hdmi *dw;
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       unsigned revision;
+       int ret;
+
+       baikal_hdmi_writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+                      data->base, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+       revision = baikal_hdmi_readb_relaxed(data->base, HDMI_REVISION_ID);
+       if (revision != 0x0a && revision != 0x1a && revision != 0x2a) {
+               dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+                       revision);
+               return -ENXIO;
+       }
+
+       ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+                             THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+       if (ret < 0)
+               return ret;
+
+       strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+       strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+       snprintf(card->longname, sizeof(card->longname),
+                "%s rev 0x%02x, irq %d", card->shortname, revision,
+                data->irq);
+
+       dw = card->private_data;
+       dw->card = card;
+       dw->data = *data;
+       dw->revision = revision;
+
+       spin_lock_init(&dw->lock);
+
+       ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+       if (ret < 0)
+               goto err;
+
+       dw->pcm = pcm;
+       pcm->private_data = dw;
+       strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+       /*
+        * To support 8-channel 96kHz audio reliably, we need 512k
+        * to satisfy alsa with our restricted period (ERR004323).
+        */
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                       dev, 128 * 1024, 1024 * 1024);
+
+       ret = snd_card_register(card);
+       if (ret < 0)
+               goto err;
+
+       platform_set_drvdata(pdev, dw);
+
+       return 0;
+
+err:
+       snd_card_free(card);
+       return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+       struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+       snd_card_free(dw->card);
+
+       return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
+/*
+ * This code is fine, but requires implementation in the dw_hdmi_trigger()
+ * method which is currently missing as I have no way to test this.
+ */
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+       struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+       snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+       snd_pcm_suspend_all(dw->pcm);
+
+       return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+       struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+       snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+                        snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
+static struct platform_driver snd_dw_hdmi_driver = {
+       .probe  = snd_dw_hdmi_probe,
+       .remove = snd_dw_hdmi_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .pm = PM_OPS,
+       },
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
index aa51c61a78c716bce8e833d6656c380ff9334470..212bb0707bbc66d129e7213d67a15e4362427850 100644 (file)
@@ -3403,7 +3403,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev,
        if (!plat_data->regm) {
                const struct regmap_config *reg_config;
 
-               of_property_read_u32(np, "reg-io-width", &val);
+               device_property_read_u32(dev, "reg-io-width", &val);
                switch (val) {
                case 4:
                        reg_config = &hdmi_regmap_32bit_config;
@@ -3703,6 +3703,15 @@ void dw_hdmi_resume(struct dw_hdmi *hdmi)
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_resume);
 
+struct drm_bridge *dw_hdmi_get_bridge(struct dw_hdmi *hdmi)
+{
+       if (IS_ERR_OR_NULL(hdmi))
+               return NULL;
+
+       return &hdmi->bridge;
+}
+EXPORT_SYMBOL_GPL(dw_hdmi_get_bridge);
+
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
 MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
index f634371c717a856b2e66961a58df77a7bf5012ed..e8a596a2a375b4eed3419aa11550469f7971a496 100644 (file)
@@ -223,6 +223,43 @@ int drm_panel_get_modes(struct drm_panel *panel,
 }
 EXPORT_SYMBOL(drm_panel_get_modes);
 
+/**
+ * fwnode_drm_find_panel - look up a panel using a fwnode
+ * @fwnode: fwnode of the panel
+ *
+ * Searches the set of registered panels for one that matches the given fwnode.
+ * If a matching panel is found, return a pointer to it.
+ *
+ * Return: A pointer to the panel registered for the specified fwnode or
+ * an ERR_PTR() if no panel matching the fwnode can be found.
+ *
+ * Possible error codes returned by this function:
+ *
+ * - EPROBE_DEFER: the panel device has not been probed yet, and the caller
+ *   should retry later
+ * - ENODEV: the device is not available
+ */
+struct drm_panel *fwnode_drm_find_panel(const struct fwnode_handle *fwnode)
+{
+       struct drm_panel *panel;
+
+       if (!fwnode_device_is_available(fwnode))
+               return ERR_PTR(-ENODEV);
+
+       mutex_lock(&panel_lock);
+
+       list_for_each_entry(panel, &panel_list, list) {
+               if (panel->dev->fwnode == fwnode) {
+                       mutex_unlock(&panel_lock);
+                       return panel;
+               }
+       }
+
+       mutex_unlock(&panel_lock);
+       return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(fwnode_drm_find_panel);
+
 #ifdef CONFIG_OF
 /**
  * of_drm_find_panel - look up a panel using a device tree node
index a582ddd583c242157b896d0ef83d9acc13ea39e3..f9ae53ecaecc1f7916b96d3744e1dea12d0f71e4 100644 (file)
@@ -37,6 +37,14 @@ config DRM_PANEL_ASUS_Z00T_TM5P5_NT35596
          NT35596 1080x1920 video mode panel as found in some Asus
          Zenfone 2 Laser Z00T devices.
 
+config DRM_PANEL_BAIKAL_LVDS
+       tristate "Baikal LVDS panel driver"
+       select VIDEOMODE_HELPERS
+       help
+         This driver supports LVDS panels that don't require device-specific
+         handling of power supplies or control signals. Used in Baikal SoCs
+         for ACPI.
+
 config DRM_PANEL_BOE_BF060Y8M_AJ0
        tristate "Boe BF060Y8M-AJ0 panel"
        depends on OF
index 34e717382dbb65145deaedeff8b5491a629e4b46..ef380bfaa811a8f96a3a7b9c3225115f5deb89fe 100644 (file)
@@ -2,6 +2,7 @@
 obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o
 obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o
 obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o
+obj-$(CONFIG_DRM_PANEL_BAIKAL_LVDS) += panel-baikal-lvds.o
 obj-$(CONFIG_DRM_PANEL_BOE_BF060Y8M_AJ0) += panel-boe-bf060y8m-aj0.o
 obj-$(CONFIG_DRM_PANEL_BOE_HIMAX8279D) += panel-boe-himax8279d.o
 obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o
diff --git a/drivers/gpu/drm/panel/panel-baikal-lvds.c b/drivers/gpu/drm/panel/panel-baikal-lvds.c
new file mode 100644 (file)
index 0000000..b593bcb
--- /dev/null
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Baikal LVDS panel driver
+ *
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ * Author: Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ *
+ * Implementation based on panel-lvds.c
+ */
+
+#include <linux/media-bus-format.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+
+#include <video/display_timing.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_panel.h>
+
+struct panel_lvds {
+       struct drm_panel panel;
+       struct device *dev;
+       unsigned int width;
+       unsigned int height;
+       struct drm_display_mode dmode;
+       u32 bus_flags;
+       unsigned int bus_format;
+       enum drm_panel_orientation orientation;
+};
+
+static inline struct panel_lvds *to_panel_lvds(struct drm_panel *panel)
+{
+       return container_of(panel, struct panel_lvds, panel);
+}
+
+static int panel_lvds_get_modes(struct drm_panel *panel,
+                               struct drm_connector *connector)
+{
+       struct panel_lvds *lvds = to_panel_lvds(panel);
+       struct drm_display_mode *mode;
+
+       mode = drm_mode_duplicate(connector->dev, &lvds->dmode);
+       if (!mode)
+               return 0;
+
+       mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+       drm_mode_probed_add(connector, mode);
+
+       connector->display_info.width_mm = lvds->dmode.width_mm;
+       connector->display_info.height_mm = lvds->dmode.height_mm;
+       drm_display_info_set_bus_formats(&connector->display_info,
+                                        &lvds->bus_format, 1);
+       connector->display_info.bus_flags = lvds->bus_flags;
+
+       /*
+        * TODO: Remove once all drm drivers call
+        * drm_connector_set_orientation_from_panel()
+        */
+       drm_connector_set_panel_orientation(connector, lvds->orientation);
+
+       return 1;
+}
+
+static enum drm_panel_orientation panel_lvds_get_orientation(struct drm_panel *panel)
+{
+       struct panel_lvds *lvds = to_panel_lvds(panel);
+
+       return lvds->orientation;
+}
+
+static const struct drm_panel_funcs panel_lvds_funcs = {
+       .get_modes = panel_lvds_get_modes,
+       .get_orientation = panel_lvds_get_orientation,
+};
+
+static int panel_lvds_get_data_mapping(struct device *dev)
+{
+       const char *mapping;
+       int ret;
+
+       ret = device_property_read_string(dev, "data-mapping", &mapping);
+       if (ret < 0)
+               return -ENODEV;
+
+       if (!strcmp(mapping, "jeida-18"))
+               return MEDIA_BUS_FMT_RGB666_1X7X3_SPWG;
+       if (!strcmp(mapping, "jeida-24"))
+               return MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA;
+       if (!strcmp(mapping, "vesa-24"))
+               return MEDIA_BUS_FMT_RGB888_1X7X4_SPWG;
+
+       return -EINVAL;
+}
+
+static int parse_timing_property(struct device *dev, const char *name,
+                                struct timing_entry *result)
+{
+       int count, ret;
+
+       count = device_property_count_u32(dev, name);
+       if (count == 1) {
+               ret = device_property_read_u32(dev, name, &result->typ);
+               result->min = result->typ;
+               result->max = result->typ;
+       } else if (count == 3) {
+               ret = device_property_read_u32_array(dev, name, &result->min, count);
+       } else {
+               dev_err(dev, "illegal timing specification in %s\n", name);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static int panel_lvds_get_display_timing(struct device *dev,
+                                        struct display_timing *dt)
+{
+       u32 val = 0;
+       int ret = 0;
+
+       memset(dt, 0, sizeof(*dt));
+
+       ret |= parse_timing_property(dev, "hback-porch", &dt->hback_porch);
+       ret |= parse_timing_property(dev, "hfront-porch", &dt->hfront_porch);
+       ret |= parse_timing_property(dev, "hactive", &dt->hactive);
+       ret |= parse_timing_property(dev, "hsync-len", &dt->hsync_len);
+       ret |= parse_timing_property(dev, "vback-porch", &dt->vback_porch);
+       ret |= parse_timing_property(dev, "vfront-porch", &dt->vfront_porch);
+       ret |= parse_timing_property(dev, "vactive", &dt->vactive);
+       ret |= parse_timing_property(dev, "vsync-len", &dt->vsync_len);
+       ret |= parse_timing_property(dev, "clock-frequency", &dt->pixelclock);
+
+       dt->flags = 0;
+       if (!device_property_read_u32(dev, "vsync-active", &val))
+               dt->flags |= val ? DISPLAY_FLAGS_VSYNC_HIGH :
+                               DISPLAY_FLAGS_VSYNC_LOW;
+       if (!device_property_read_u32(dev, "hsync-active", &val))
+               dt->flags |= val ? DISPLAY_FLAGS_HSYNC_HIGH :
+                               DISPLAY_FLAGS_HSYNC_LOW;
+       if (!device_property_read_u32(dev, "de-active", &val))
+               dt->flags |= val ? DISPLAY_FLAGS_DE_HIGH :
+                               DISPLAY_FLAGS_DE_LOW;
+       if (!device_property_read_u32(dev, "pixelclk-active", &val))
+               dt->flags |= val ? DISPLAY_FLAGS_PIXDATA_POSEDGE :
+                               DISPLAY_FLAGS_PIXDATA_NEGEDGE;
+
+       if (!device_property_read_u32(dev, "syncclk-active", &val))
+               dt->flags |= val ? DISPLAY_FLAGS_SYNC_POSEDGE :
+                               DISPLAY_FLAGS_SYNC_NEGEDGE;
+       else if (dt->flags & (DISPLAY_FLAGS_PIXDATA_POSEDGE |
+                             DISPLAY_FLAGS_PIXDATA_NEGEDGE))
+               dt->flags |= dt->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ?
+                               DISPLAY_FLAGS_SYNC_POSEDGE :
+                               DISPLAY_FLAGS_SYNC_NEGEDGE;
+
+       if (device_property_read_bool(dev, "interlaced"))
+               dt->flags |= DISPLAY_FLAGS_INTERLACED;
+       if (device_property_read_bool(dev, "doublescan"))
+               dt->flags |= DISPLAY_FLAGS_DOUBLESCAN;
+       if (device_property_read_bool(dev, "doubleclk"))
+               dt->flags |= DISPLAY_FLAGS_DOUBLECLK;
+
+       if (ret) {
+               dev_err(dev, "error reading timing properties\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int panel_lvds_get_display_mode(struct device *dev,
+                                      struct drm_display_mode *dmode,
+                                      u32 *bus_flags)
+{
+       u32 width_mm = 0, height_mm = 0;
+       struct display_timing timing;
+       struct videomode vm;
+       int ret;
+
+       ret = panel_lvds_get_display_timing(dev, &timing);
+       if (ret)
+               return ret;
+
+       videomode_from_timing(&timing, &vm);
+
+       memset(dmode, 0, sizeof(*dmode));
+       drm_display_mode_from_videomode(&vm, dmode);
+       if (bus_flags)
+               drm_bus_flags_from_videomode(&vm, bus_flags);
+
+       ret = device_property_read_u32(dev, "width-mm", &width_mm);
+       if (ret)
+               return ret;
+
+       ret = device_property_read_u32(dev, "height-mm", &height_mm);
+       if (ret)
+               return ret;
+
+       dmode->width_mm = width_mm;
+       dmode->height_mm = height_mm;
+
+       drm_mode_debug_printmodeline(dmode);
+
+       return 0;
+}
+
+static int panel_lvds_get_panel_orientation(struct device *dev,
+                                           enum drm_panel_orientation *orientation)
+{
+       int rotation, ret;
+
+       ret = device_property_read_u32(dev, "rotation", &rotation);
+       if (ret == -EINVAL) {
+               /* Don't return an error if there's no rotation property. */
+               *orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+               return 0;
+       }
+
+       if (ret < 0)
+               return ret;
+
+       if (rotation == 0)
+               *orientation = DRM_MODE_PANEL_ORIENTATION_NORMAL;
+       else if (rotation == 90)
+               *orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
+       else if (rotation == 180)
+               *orientation = DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
+       else if (rotation == 270)
+               *orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int panel_lvds_parse(struct panel_lvds *lvds)
+{
+       struct device *dev = lvds->dev;
+       int ret;
+
+       ret = panel_lvds_get_panel_orientation(dev, &lvds->orientation);
+       if (ret < 0) {
+               dev_err(dev, "failed to get orientation %d\n", ret);
+               return ret;
+       }
+
+       ret = panel_lvds_get_display_mode(dev, &lvds->dmode, &lvds->bus_flags);
+       if (ret < 0) {
+               dev_err(dev, "problems parsing panel-timing (%d)\n", ret);
+               return ret;
+       }
+
+       ret = panel_lvds_get_data_mapping(dev);
+       if (ret < 0) {
+               dev_err(dev, "invalid or missing data-mapping property\n");
+               return ret;
+       }
+
+       lvds->bus_format = ret;
+
+       lvds->bus_flags |= device_property_read_bool(dev, "data-mirror") ?
+                          DRM_BUS_FLAG_DATA_LSB_TO_MSB :
+                          DRM_BUS_FLAG_DATA_MSB_TO_LSB;
+
+       return 0;
+}
+
+static int panel_lvds_probe(struct platform_device *pdev)
+{
+       struct panel_lvds *lvds;
+       int ret;
+
+       lvds = devm_kzalloc(&pdev->dev, sizeof(*lvds), GFP_KERNEL);
+       if (!lvds)
+               return -ENOMEM;
+
+       lvds->dev = &pdev->dev;
+
+       ret = panel_lvds_parse(lvds);
+       if (ret < 0)
+               return ret;
+
+       /* Register the panel. */
+       drm_panel_init(&lvds->panel, lvds->dev, &panel_lvds_funcs,
+                      DRM_MODE_CONNECTOR_LVDS);
+
+       drm_panel_add(&lvds->panel);
+
+       dev_set_drvdata(lvds->dev, lvds);
+       return 0;
+}
+
+static int panel_lvds_remove(struct platform_device *pdev)
+{
+       struct panel_lvds *lvds = platform_get_drvdata(pdev);
+
+       drm_panel_remove(&lvds->panel);
+
+       drm_panel_disable(&lvds->panel);
+
+       return 0;
+}
+
+static const struct of_device_id panel_lvds_of_table[] = {
+       { .compatible = "baikal,panel-lvds", },
+       { /* Sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, panel_lvds_of_table);
+
+static struct platform_driver panel_baikal_lvds_driver = {
+       .probe          = panel_lvds_probe,
+       .remove         = panel_lvds_remove,
+       .driver         = {
+               .name   = "panel-baikal-lvds",
+               .of_match_table = panel_lvds_of_table,
+       },
+};
+
+module_platform_driver(panel_baikal_lvds_driver);
+
+MODULE_AUTHOR("Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal LVDS Panel Driver");
+MODULE_LICENSE("GPL");
index fe5f12f16a632cfa17819b86e63c78a29bd9c82a..8197647485c3fb4f670e573fc86b5c0ab39832ce 100644 (file)
@@ -7,6 +7,8 @@
 #include <linux/platform_device.h>
 #include <linux/pm_opp.h>
 
+#include "../../../opp/opp.h"
+
 #include "panfrost_device.h"
 #include "panfrost_devfreq.h"
 
@@ -82,6 +84,82 @@ static struct devfreq_dev_profile panfrost_devfreq_profile = {
        .get_dev_status = panfrost_devfreq_get_dev_status,
 };
 
+static void panfrost_devfreq_opp_remove(void *data)
+{
+       dev_pm_opp_remove_all_dynamic(data);
+}
+
+static struct dev_pm_opp *panfrost_devfreq_opp_get(struct opp_table *opp_table,
+                                                  unsigned long rate)
+{
+       struct dev_pm_opp *opp = NULL, *temp;
+
+       if (opp_table) {
+               mutex_lock(&opp_table->lock);
+               list_for_each_entry(temp, &opp_table->opp_list, node) {
+                       if (!temp->removed && temp->dynamic &&
+                           temp->rates[0] == rate) {
+                               opp = temp;
+                               break;
+                       }
+               }
+               mutex_unlock(&opp_table->lock);
+       }
+
+       return opp;
+}
+
+static int panfrost_devfreq_opp_add_table(struct device *dev)
+{
+       struct opp_table *opp_table = NULL;
+       struct dev_pm_opp *opp;
+       u64 *arr;
+       int i, count, ret;
+
+       count = device_property_count_u64(dev, "operating-points");
+       if (count < 2 || count % 2)
+               return -ENODEV;
+
+       arr = kcalloc(count, sizeof(u64), GFP_KERNEL);
+       if (!arr)
+               return -ENOMEM;
+
+       ret = device_property_read_u64_array(dev, "operating-points", arr, count);
+       if (ret)
+               goto err;
+
+       count >>= 1;
+       for (i = 0; i < count; ++i) {
+               ret = dev_pm_opp_add(dev, arr[2 * i], 0);
+               if (ret) {
+                       if (!IS_ERR_OR_NULL(opp_table)) {
+                               dev_pm_opp_put_opp_table(opp_table);
+                               dev_pm_opp_remove_all_dynamic(dev);
+                       }
+                       goto err;
+               }
+
+               if (!opp_table)
+                       opp_table = dev_pm_opp_get_opp_table(dev);
+
+               opp = panfrost_devfreq_opp_get(opp_table, i);
+               if (opp) {
+                       mutex_lock(&opp_table->lock);
+                       opp->clock_latency_ns = arr[2 * i + 1];
+                       if (opp->clock_latency_ns > opp_table->clock_latency_ns_max)
+                               opp_table->clock_latency_ns_max = opp->clock_latency_ns;
+                       mutex_unlock(&opp_table->lock);
+               }
+       }
+
+       dev_pm_opp_put_opp_table(opp_table);
+       devm_add_action_or_reset(dev, panfrost_devfreq_opp_remove, dev);
+
+err:
+       kfree(arr);
+       return ret;
+}
+
 int panfrost_devfreq_init(struct panfrost_device *pfdev)
 {
        int ret;
@@ -111,7 +189,11 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
                }
        }
 
-       ret = devm_pm_opp_of_add_table(dev);
+       if (dev->of_node)
+               ret = devm_pm_opp_of_add_table(dev);
+       else
+               ret = panfrost_devfreq_opp_add_table(dev);
+
        if (ret) {
                /* Optional, continue without devfreq */
                if (ret == -ENODEV)
@@ -160,6 +242,12 @@ int panfrost_devfreq_init(struct panfrost_device *pfdev)
        }
        pfdevfreq->devfreq = devfreq;
 
+       if (!dev->of_node) {
+               mutex_lock(&devfreq->lock);
+               update_devfreq(devfreq);
+               mutex_unlock(&devfreq->lock);
+       }
+
        cooling = devfreq_cooling_em_register(devfreq, NULL);
        if (IS_ERR(cooling))
                DRM_DEV_INFO(dev, "Failed to register cooling device\n");
index 919e6cc0498281ff8c4626f3c7998e49511f47d3..ec97c577e94c17eae2d3d10f2b5c32a9a4b277b1 100644 (file)
@@ -567,7 +567,7 @@ static int panfrost_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, pfdev);
 
-       pfdev->comp = of_device_get_match_data(&pdev->dev);
+       pfdev->comp = device_get_match_data(&pdev->dev);
        if (!pfdev->comp)
                return -ENODEV;
 
index 6452e4e900dd78f0d44807609707a8222c05ea07..294ecb8fff4f2082b897fe32d3e948f9d1b473f5 100644 (file)
@@ -381,7 +381,7 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
 
        dma_set_max_seg_size(pfdev->dev, UINT_MAX);
 
-       irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
+       irq = fwnode_irq_get_byname(pfdev->dev->fwnode, "gpu");
        if (irq <= 0)
                return -ENODEV;
 
index dbc597ab46fb9f9260a2232de22d069050bdc72e..7daf08291cd3fb307f98eb6e102ec6373dbfa67e 100644 (file)
@@ -793,7 +793,7 @@ int panfrost_job_init(struct panfrost_device *pfdev)
        INIT_WORK(&pfdev->reset.work, panfrost_reset_work);
        spin_lock_init(&js->job_lock);
 
-       js->irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "job");
+       js->irq = fwnode_irq_get_byname(pfdev->dev->fwnode, "job");
        if (js->irq <= 0)
                return -ENODEV;
 
index e961fa27702ce61f8675f6b63b6ef4dfd1496e05..683b3abed6afdda40b6744af24c929ee62ae3c59 100644 (file)
@@ -753,7 +753,7 @@ int panfrost_mmu_init(struct panfrost_device *pfdev)
 {
        int err, irq;
 
-       irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
+       irq = fwnode_irq_get_byname(pfdev->dev->fwnode, "mmu");
        if (irq <= 0)
                return -ENODEV;
 
index a5143d01b95f8e218955be33f2433800a934e3c0..b5116b9b9e438f056e871d593f1003ce96c08e3d 100644 (file)
@@ -419,7 +419,6 @@ config SENSORS_ATXP1
 
 config SENSORS_BT1_PVT
        tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
-       depends on MIPS_BAIKAL_T1 || COMPILE_TEST
        select POLYNOMIAL
        help
          If you say yes here you get support for Baikal-T1 PVT sensor
index 21ab172774ec58b27bc1420c7b91529fada02f93..8d13b2baf6b421ce6400cd15175808a1206214a9 100644 (file)
@@ -66,25 +66,49 @@ static const struct pvt_sensor_info pvt_info[] = {
  *     48380,
  * where T = [-48380, 147438] mC and N = [0, 1023].
  */
-static const struct polynomial __maybe_unused poly_temp_to_N = {
-       .total_divider = 10000,
-       .terms = {
-               {4, 18322, 10000, 10000},
-               {3, 2343, 10000, 10},
-               {2, 87018, 10000, 10},
-               {1, 39269, 1000, 1},
-               {0, 1720400, 1, 1}
+static const struct pvt_poly poly_temp_to_N[chips_number] = {
+       [bm1000] = {
+               .total_divider = 10000,
+               .terms = {
+                       {4,   18322, 10000, 10000},
+                       {3,    2343, 10000,    10},
+                       {2,   87018, 10000,    10},
+                       {1,   39269,  1000,     1},
+                       {0, 1720400,     1,     1}
+               }
+       },
+       [bs1000] = {
+               .total_divider = 10000,
+               .terms = {
+                       {4,   12569, 10000, 10000},
+                       {3,    2476, 10000,    10},
+                       {2,   66309, 10000,    10},
+                       {1,   36384,  1000,     1},
+                       {0, 2070500,     1,     1}
+               }
        }
 };
 
-static const struct polynomial poly_N_to_temp = {
-       .total_divider = 1,
-       .terms = {
-               {4, -16743, 1000, 1},
-               {3, 81542, 1000, 1},
-               {2, -182010, 1000, 1},
-               {1, 310200, 1000, 1},
-               {0, -48380, 1, 1}
+static const struct pvt_poly poly_N_to_temp[chips_number] = {
+       [bm1000] = {
+               .total_divider = 1,
+               .terms = {
+                       {4,  -16743, 1000, 1},
+                       {3,   81542, 1000, 1},
+                       {2, -182010, 1000, 1},
+                       {1,  310200, 1000, 1},
+                       {0,  -48380,    1, 1}
+               }
+       },
+       [bs1000] = {
+               .total_divider = 1,
+               .terms = {
+                       {4,   16034, 1000, 1},
+                       {3,   15608, 1000, 1},
+                       {2, -150890, 1000, 1},
+                       {1,  334080, 1000, 1},
+                       {0,  -62861,    1, 1}
+               }
        }
 };
 
@@ -98,29 +122,93 @@ static const struct polynomial poly_N_to_temp = {
  * N = (18658e-3*V - 11572) / 10,
  * V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
  */
-static const struct polynomial __maybe_unused poly_volt_to_N = {
-       .total_divider = 10,
-       .terms = {
-               {1, 18658, 1000, 1},
-               {0, -11572, 1, 1}
+static const struct pvt_poly poly_volt_to_N[chips_number] = {
+       [bm1000] = {
+               .total_divider = 10,
+               .terms = {
+                       {1,  18658, 1000, 1},
+                       {0, -11572,    1, 1}
+               }
+       },
+       [bs1000] = {
+               .total_divider = 10,
+               .terms = {
+                       {1, 16757, 1000, 1},
+                       {0, -8564,    1, 1}
+               }
        }
 };
 
-static const struct polynomial poly_N_to_volt = {
-       .total_divider = 10,
-       .terms = {
-               {1, 100000, 18658, 1},
-               {0, 115720000, 1, 18658}
+static const struct pvt_poly poly_N_to_volt[chips_number] = {
+       [bm1000] = {
+               .total_divider = 10,
+               .terms = {
+                       {1,    100000, 18658,     1},
+                       {0, 115720000,     1, 18658}
+               }
+       },
+       [bs1000] = {
+               .total_divider = 10,
+               .terms = {
+                       {1,   100000, 16757,     1},
+                       {0, 85639000,     1, 16757}
+               }
        }
 };
 
-static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
+
+/*
+ * Here is the polynomial calculation function, which performs the
+ * redistributed terms calculations. It's pretty straightforward. We walk
+ * over each degree term up to the free one, and perform the redistributed
+ * multiplication of the term coefficient, its divider (as for the rationale
+ * fraction representation), data power and the rational fraction divider
+ * leftover. Then all of this is collected in a total sum variable, which
+ * value is normalized by the total divider before being returned.
+ */
+static long pvt_calc_poly(const struct pvt_poly *poly, long data)
 {
-       u32 old;
+       const struct pvt_poly_term *term = poly->terms;
+       long tmp, ret = 0;
+       int deg;
+       do {
+               tmp = term->coef;
+               for (deg = 0; deg < term->deg; ++deg) {
+                       tmp = mult_frac(tmp, data, term->divider);
+               }
+
+               ret += tmp / term->divider_leftover;
+       } while ((term++)->deg);
+       return ret / poly->total_divider;
+}
+
+
+static uint32_t writel_pvt(uint32_t val, uint32_t pvt_base, uint32_t offset)
+{
+       struct arm_smccc_res res;
 
-       old = readl_relaxed(reg);
-       writel((old & ~mask) | (data & mask), reg);
+       arm_smccc_smc(BAIKAL_SMC_PVT_CMD, PVT_WRITE, pvt_base, offset, val,
+                     0, 0, 0, &res);
+       // pr_debug("wr arm_smccc_smc %x, %x , %x , %x \n",pvt_base, offset, val, res.a0 );
+       return res.a0;
+}
+
+static uint32_t readl_pvt(uint32_t pvt_base, uint32_t offset)
+{
+       struct arm_smccc_res res;
 
+       arm_smccc_smc(BAIKAL_SMC_PVT_CMD, PVT_READ, pvt_base, offset,
+                     0, 0, 0, 0, &res);
+       // pr_debug("rd arm_smccc_smc  %x, %x , %d  \n",pvt_base, offset, res.a0 );
+       return res.a0;
+}
+
+static inline u32 pvt_update(void __iomem *pvt_base, void __iomem *offset, u32 mask, u32 data)
+{
+       u32 old;
+       old = readl_pvt(pvt_base, offset);
+       writel_pvt((old & ~mask) | (data & mask), pvt_base, offset);
+       // pr_debug("pvt_update addr %x, %x, mask %x, data %x, %x  \n",pvt_base, offset, mask, data, old );
        return old & mask;
 }
 
@@ -136,9 +224,8 @@ static inline void pvt_set_mode(struct pvt_hwmon *pvt, u32 mode)
        u32 old;
 
        mode = FIELD_PREP(PVT_CTRL_MODE_MASK, mode);
-
-       old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
+       old = pvt_update(pvt->base, PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->base,  PVT_CTRL, PVT_CTRL_MODE_MASK | PVT_CTRL_EN,
                   mode | old);
 }
 
@@ -155,8 +242,8 @@ static inline void pvt_set_trim(struct pvt_hwmon *pvt, u32 trim)
 
        trim = FIELD_PREP(PVT_CTRL_TRIM_MASK, trim);
 
-       old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
+       old = pvt_update(pvt->regs PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->regs PVT_CTRL, PVT_CTRL_TRIM_MASK | PVT_CTRL_EN,
                   trim | old);
 }
 
@@ -164,9 +251,9 @@ static inline void pvt_set_tout(struct pvt_hwmon *pvt, u32 tout)
 {
        u32 old;
 
-       old = pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
-       writel(tout, pvt->regs + PVT_TTIMEOUT);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, old);
+       old = pvt_update(pvt->base,  PVT_CTRL, PVT_CTRL_EN, 0);
+       writel_pvt(tout, pvt->base, PVT_TTIMEOUT);
+       pvt_update(pvt->base,  PVT_CTRL, PVT_CTRL_EN, old);
 }
 
 /*
@@ -213,7 +300,7 @@ static irqreturn_t pvt_soft_isr(int irq, void *data)
         * status before the next conversion happens. Threshold events will be
         * handled a bit later.
         */
-       thres_sts = readl(pvt->regs + PVT_RAW_INTR_STAT);
+       thres_sts = readl_pvt(pvt->base, PVT_RAW_INTR_STAT);
 
        /*
         * Then lets recharge the PVT interface with the next sampling mode.
@@ -236,14 +323,14 @@ static irqreturn_t pvt_soft_isr(int irq, void *data)
         */
        mutex_lock(&pvt->iface_mtx);
 
-       old = pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+       old = pvt_update(pvt->base, PVT_INTR_MASK, PVT_INTR_DVALID,
                         PVT_INTR_DVALID);
 
-       val = readl(pvt->regs + PVT_DATA);
+       val = readl_pvt(pvt->base, PVT_DATA);
 
        pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
 
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, old);
+       pvt_update(pvt->base, PVT_INTR_MASK, PVT_INTR_DVALID, old);
 
        mutex_unlock(&pvt->iface_mtx);
 
@@ -293,16 +380,16 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
        struct pvt_cache *cache = &pvt->cache[type];
        unsigned int seq;
        u32 data;
-
+       pr_debug("pvt_read_data into val %x \n",  val );
        do {
                seq = read_seqbegin(&cache->data_seqlock);
                data = cache->data;
        } while (read_seqretry(&cache->data_seqlock, seq));
-
+       data = readl_pvt(pvt->base, 0xc);
        if (type == PVT_TEMP)
                *val = polynomial_calc(&poly_N_to_temp, data);
        else
-               *val = polynomial_calc(&poly_N_to_volt, data);
+               *val = polynomial_calc(&poly_N_to_volt[tp], data);
 
        return 0;
 }
@@ -313,17 +400,17 @@ static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
        u32 data;
 
        /* No need in serialization, since it is just read from MMIO. */
-       data = readl(pvt->regs + pvt_info[type].thres_base);
-
+       data = readl_pvt(pvt->base, pvt_info[type].thres_base);
+       int tp = pvt->type_cpu;
        if (is_low)
                data = FIELD_GET(PVT_THRES_LO_MASK, data);
        else
                data = FIELD_GET(PVT_THRES_HI_MASK, data);
 
        if (type == PVT_TEMP)
-               *val = polynomial_calc(&poly_N_to_temp, data);
+               *val = pvt_calc_poly(&poly_N_to_temp[tp], data);
        else
-               *val = polynomial_calc(&poly_N_to_volt, data);
+               *val = pvt_calc_poly(&poly_N_to_volt[tp], data);
 
        return 0;
 }
@@ -336,10 +423,10 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
 
        if (type == PVT_TEMP) {
                val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
-               data = polynomial_calc(&poly_temp_to_N, val);
+               data = pvt_calc_poly(&poly_temp_to_N[pvt->type_cpu], val);
        } else {
                val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
-               data = polynomial_calc(&poly_volt_to_N, val);
+               data = pvt_calc_poly(&poly_volt_to_N[pvt->type_cpu], val);
        }
 
        /* Serialize limit update, since a part of the register is changed. */
@@ -348,7 +435,7 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
                return ret;
 
        /* Make sure the upper and lower ranges don't intersect. */
-       limit = readl(pvt->regs + pvt_info[type].thres_base);
+       limit = readl_pvt(pvt->regs, pvt_info[type].thres_base);
        if (is_low) {
                limit = FIELD_GET(PVT_THRES_HI_MASK, limit);
                data = clamp_val(data, PVT_DATA_MIN, limit);
@@ -361,7 +448,7 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
                mask = PVT_THRES_HI_MASK;
        }
 
-       pvt_update(pvt->regs + pvt_info[type].thres_base, mask, data);
+       pvt_update(pvt->regs, pvt_info[type].thres_base, mask, data);
 
        mutex_unlock(&pvt->iface_mtx);
 
@@ -415,21 +502,27 @@ static irqreturn_t pvt_hard_isr(int irq, void *data)
         * Mask the DVALID interrupt so after exiting from the handler a
         * repeated conversion wouldn't happen.
         */
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+       pvt_update(pvt->base, PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+       pvt_update(pvt->base, PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+
+       pvt_update(pvt->base, PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->base, PVT_INTR_MASK, PVT_INTR_DVALID,
                   PVT_INTR_DVALID);
 
        /*
         * Nothing special for alarm-less driver. Just read the data, update
         * the cache and notify a waiter of this event.
         */
-       val = readl(pvt->regs + PVT_DATA);
-       if (!(val & PVT_DATA_VALID)) {
-               dev_err(pvt->dev, "Got IRQ when data isn't valid\n");
-               return IRQ_HANDLED;
-       }
+       val = readl_pvt(pvt->regs, PVT_DATA);
+       val &= PVT_DATA_DATA_MASK;
+       pr_debug("irqreturn_t pvt_hard_isr data %x %x \n",data, val );
+       //if (!(val & PVT_DATA_VALID)) {
+       //      dev_err(pvt->dev, "Got IRQ when data isn't valid\n");
+       //      return IRQ_HANDLED;
+       //}
 
        cache = &pvt->cache[pvt->sensor];
-
+       pr_debug("irqreturn_t pvt_hard_isr cache->data %x  \n",cache->data );
        WRITE_ONCE(cache->data, FIELD_GET(PVT_DATA_DATA_MASK, val));
 
        complete(&cache->conversion);
@@ -454,7 +547,7 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
 {
        struct pvt_cache *cache = &pvt->cache[type];
        unsigned long timeout;
-       u32 data;
+       long data;
        int ret;
 
        /*
@@ -474,8 +567,8 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
         * Unmask the DVALID interrupt and enable the sensors conversions.
         * Do the reverse procedure when conversion is done.
         */
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+       pvt_update(pvt->base, PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+       pvt_update(pvt->base, PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
 
        /*
         * Wait with timeout since in case if the sensor is suddenly powered
@@ -486,21 +579,23 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
        timeout = 2 * usecs_to_jiffies(ktime_to_us(pvt->timeout));
        ret = wait_for_completion_timeout(&cache->conversion, timeout);
 
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+       pvt_update(pvt->base, PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->base, PVT_INTR_MASK, PVT_INTR_DVALID,
                   PVT_INTR_DVALID);
-
-       data = READ_ONCE(cache->data);
-
+///bs No alarm  read hear TODO read from cache as for bt1
+///bs data = READ_ONCE(cache->data);
+       data = readl_pvt(pvt->base, PVT_DATA);
+       data &= PVT_DATA_DATA_MASK;
        mutex_unlock(&pvt->iface_mtx);
 
-       if (!ret)
-               return -ETIMEDOUT;
+       //bs if (!ret)
+       //bs    return -ETIMEDOUT;
+       int tp = pvt->type_cpu;
 
        if (type == PVT_TEMP)
-               *val = polynomial_calc(&poly_N_to_temp, data);
+               *val = pvt_calc_poly(&poly_N_to_temp[tp], data);
        else
-               *val = polynomial_calc(&poly_N_to_volt, data);
+               *val = pvt_calc_poly(&poly_N_to_volt[tp], data);
 
        return 0;
 }
@@ -613,7 +708,7 @@ static int pvt_read_trim(struct pvt_hwmon *pvt, long *val)
 {
        u32 data;
 
-       data = readl(pvt->regs + PVT_CTRL);
+       data = readl_pvt(pvt->base, PVT_CTRL);
        *val = FIELD_GET(PVT_CTRL_TRIM_MASK, data) * PVT_TRIM_STEP;
 
        return 0;
@@ -900,11 +995,56 @@ static int pvt_request_regs(struct pvt_hwmon *pvt)
        }
 
        pvt->regs = devm_ioremap_resource(pvt->dev, res);
+       pvt->regs = pvt->base= (int)res->start;
        if (IS_ERR(pvt->regs))
                return PTR_ERR(pvt->regs);
 
        return 0;
 }
+static int pvt_check_addr(struct pvt_hwmon *pvt)
+{
+       struct platform_device *pdev = to_platform_device(pvt->dev);
+       int hwmon_type_cpu = (enum chips)device_get_match_data(&pdev->dev);
+
+       if (hwmon_type_cpu == bm1000 &&
+               (pvt->base == MMCA57_0_PVT_BASE ||
+                pvt->base == MMCA57_1_PVT_BASE ||
+                pvt->base == MMCA57_2_PVT_BASE ||
+                pvt->base == MMCA57_3_PVT_BASE ||
+                pvt->base == MMMALI_PVT_BASE)) {
+               pvt->type_cpu = bm1000;
+       } else if (hwmon_type_cpu == bs1000 &&
+               (pvt->base == CA75_0_PVT_BASE    ||
+                pvt->base == CA75_1_PVT_BASE    ||
+                pvt->base == CA75_2_PVT_BASE    ||
+                pvt->base == CA75_3_PVT_BASE    ||
+                pvt->base == CA75_4_PVT_BASE    ||
+                pvt->base == CA75_5_PVT_BASE    ||
+                pvt->base == CA75_6_PVT_BASE    ||
+                pvt->base == CA75_7_PVT_BASE    ||
+                pvt->base == CA75_8_PVT_BASE    ||
+                pvt->base == CA75_9_PVT_BASE    ||
+                pvt->base == CA75_10_PVT_BASE ||
+                pvt->base == CA75_11_PVT_BASE ||
+                pvt->base == DDR0_PVT_BASE      ||
+                pvt->base == DDR1_PVT_BASE      ||
+                pvt->base == DDR2_PVT_BASE      ||
+                pvt->base == DDR3_PVT_BASE      ||
+                pvt->base == DDR4_PVT_BASE      ||
+                pvt->base == DDR5_PVT_BASE      ||
+                pvt->base == PCIE0_PVT_BASE     ||
+                pvt->base == PCIE1_PVT_BASE     ||
+                pvt->base == PCIE2_PVT_BASE     ||
+                pvt->base == PCIE3_PVT_BASE     ||
+                pvt->base == PCIE4_PVT_BASE)) {
+               pvt->type_cpu = bs1000;
+       } else {
+               dev_err(&pdev->dev, "invalid base address or failed to get type: 0x%x\n", pvt->base);
+               return -EINVAL;
+       }
+       pr_debug("pvt_probe pvt_check_addr. hwmon_type_cpu is %d\n",  hwmon_type_cpu);
+       return 0;
+}
 
 static void pvt_disable_clks(void *data)
 {
@@ -915,24 +1055,23 @@ static void pvt_disable_clks(void *data)
 
 static int pvt_request_clks(struct pvt_hwmon *pvt)
 {
-       int ret;
+       int ret = 0;
 
        pvt->clks[PVT_CLOCK_APB].id = "pclk";
        pvt->clks[PVT_CLOCK_REF].id = "ref";
-
-       ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks);
+       //bs ret = devm_clk_bulk_get(pvt->dev, PVT_CLOCK_NUM, pvt->clks);
        if (ret) {
                dev_err(pvt->dev, "Couldn't get PVT clocks descriptors\n");
                return ret;
        }
 
-       ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks);
+       //bs ret = clk_bulk_prepare_enable(PVT_CLOCK_NUM, pvt->clks);
        if (ret) {
                dev_err(pvt->dev, "Couldn't enable the PVT clocks\n");
                return ret;
        }
 
-       ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt);
+       //bs ret = devm_add_action_or_reset(pvt->dev, pvt_disable_clks, pvt);
        if (ret) {
                dev_err(pvt->dev, "Can't add PVT clocks disable action\n");
                return ret;
@@ -957,21 +1096,21 @@ static int pvt_check_pwr(struct pvt_hwmon *pvt)
         * conversion. In the later case alas we won't be able to detect the
         * problem.
         */
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+       pvt_update(pvt->regs, PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
+       pvt_update(pvt->regs, PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
        pvt_set_tout(pvt, 0);
-       readl(pvt->regs + PVT_DATA);
+       readl_pvt(pvt->regs, PVT_DATA);
 
        tout = PVT_TOUT_MIN / NSEC_PER_USEC;
        usleep_range(tout, 2 * tout);
 
-       data = readl(pvt->regs + PVT_DATA);
+       data = readl_pvt(pvt->regs, PVT_DATA);
        if (!(data & PVT_DATA_VALID)) {
                ret = -ENODEV;
                dev_err(pvt->dev, "Sensor is powered down\n");
        }
 
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->regs, PVT_CTRL, PVT_CTRL_EN, 0);
 
        return ret;
 }
@@ -992,10 +1131,10 @@ static int pvt_init_iface(struct pvt_hwmon *pvt)
         * accidentally have ISR executed before the driver data is fully
         * initialized. Clear the IRQ status as well.
         */
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
-       readl(pvt->regs + PVT_CLR_INTR);
-       readl(pvt->regs + PVT_DATA);
+       pvt_update(pvt->regs, PVT_INTR_MASK, PVT_INTR_ALL, PVT_INTR_ALL);
+       pvt_update(pvt->regs, PVT_CTRL, PVT_CTRL_EN, 0);
+       readl_pvt(pvt->regs, PVT_CLR_INTR);
+       readl_pvt(pvt->regs, PVT_DATA);
 
        /* Setup default sensor mode, timeout and temperature trim. */
        pvt_set_mode(pvt, pvt_info[pvt->sensor].mode);
@@ -1079,8 +1218,8 @@ static void pvt_disable_iface(void *data)
        struct pvt_hwmon *pvt = data;
 
        mutex_lock(&pvt->iface_mtx);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, 0);
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID,
+       pvt_update(pvt->regs, PVT_CTRL, PVT_CTRL_EN, 0);
+       pvt_update(pvt->regs, PVT_INTR_MASK, PVT_INTR_DVALID,
                   PVT_INTR_DVALID);
        mutex_unlock(&pvt->iface_mtx);
 }
@@ -1102,8 +1241,8 @@ static int pvt_enable_iface(struct pvt_hwmon *pvt)
         * which theoretically may cause races.
         */
        mutex_lock(&pvt->iface_mtx);
-       pvt_update(pvt->regs + PVT_INTR_MASK, PVT_INTR_DVALID, 0);
-       pvt_update(pvt->regs + PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
+       pvt_update(pvt->regs, PVT_INTR_MASK, PVT_INTR_DVALID, 0);
+       pvt_update(pvt->regs, PVT_CTRL, PVT_CTRL_EN, PVT_CTRL_EN);
        mutex_unlock(&pvt->iface_mtx);
 
        return 0;
@@ -1135,15 +1274,15 @@ static int pvt_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       ret = pvt_check_pwr(pvt);
+       //bs ret = pvt_check_pwr(pvt);
        if (ret)
                return ret;
 
-       ret = pvt_init_iface(pvt);
+       //bs  ret = pvt_init_iface(pvt);
        if (ret)
                return ret;
 
-       ret = pvt_request_irq(pvt);
+       //bs ret = pvt_request_irq(pvt);
        if (ret)
                return ret;
 
@@ -1155,11 +1294,16 @@ static int pvt_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
+       ret = pvt_check_addr(pvt);
+       if(ret)
+               return ret;
        return 0;
 }
 
 static const struct of_device_id pvt_of_match[] = {
-       { .compatible = "baikal,bt1-pvt" },
+       { .compatible = "baikal,bt1-pvt", .data = (void *)bm1000 },
+       { .compatible = "baikal,bs1000-pvt", .data = (void *)bs1000 },
+       { .compatible = "baikal,bm1000-pvt", .data = (void *)bm1000 },
        { }
 };
 MODULE_DEVICE_TABLE(of, pvt_of_match);
index 93b8dd5e7c944c31b81ef2dd9126edc77f328135..329fd5f8ea84feec01a41068937b7c79a8cbcb16 100644 (file)
 #include <linux/mutex.h>
 #include <linux/seqlock.h>
 
+#define BAIKAL_SMC_PVT_CMD     0x82000001
+#define PVT_READ               0
+#define PVT_WRITE              1
+enum chips { bt1000, bm1000, bs1000, chips_number };
+
+/* "baikal,bm1000-pvt" base addresses */
+#define MMCA57_0_PVT_BASE   0x28200000
+#define MMCA57_1_PVT_BASE   0xc200000
+#define MMCA57_2_PVT_BASE   0xa200000
+#define MMCA57_3_PVT_BASE   0x26200000
+#define MMMALI_PVT_BASE     0x2a060000
+
+/* "baikal,bs1000-pvt" base addresses */
+#define CA75_0_PVT_BASE     0x4030000
+#define CA75_1_PVT_BASE     0x8030000
+#define CA75_2_PVT_BASE     0xc030000
+#define CA75_3_PVT_BASE     0x10030000
+#define CA75_4_PVT_BASE     0x14030000
+#define CA75_5_PVT_BASE     0x18030000
+#define CA75_6_PVT_BASE     0x1c030000
+#define CA75_7_PVT_BASE     0x20030000
+#define CA75_8_PVT_BASE     0x24030000
+#define CA75_9_PVT_BASE     0x28030000
+#define CA75_10_PVT_BASE    0x2c030000
+#define CA75_11_PVT_BASE    0x30030000
+#define PCIE0_PVT_BASE      0x38030000
+#define PCIE1_PVT_BASE      0x3c030000
+#define PCIE2_PVT_BASE      0x44030000
+#define PCIE3_PVT_BASE      0x48030000
+#define PCIE4_PVT_BASE      0x4c030000
+#define DDR0_PVT_BASE       0x50030000
+#define DDR1_PVT_BASE       0x54030000
+#define DDR2_PVT_BASE       0x58030000
+#define DDR3_PVT_BASE       0x60030000
+#define DDR4_PVT_BASE       0x64030000
+#define DDR5_PVT_BASE       0x68030000
+
 /* Baikal-T1 PVT registers and their bitfields */
 #define PVT_CTRL                       0x00
 #define PVT_CTRL_EN                    BIT(0)
 #if defined(CONFIG_SENSORS_BT1_PVT_ALARMS)
 # define PVT_TOUT_DEF          60000
 #else
-# define PVT_TOUT_DEF          0
+# define PVT_TOUT_DEF          1000
 #endif
 
 /*
@@ -209,6 +246,8 @@ struct pvt_hwmon {
        struct device *hwmon;
 
        void __iomem *regs;
+    int base;
+    enum chips type_cpu;
        int irq;
 
        struct clk_bulk_data clks[PVT_CLOCK_NUM];
@@ -239,9 +278,11 @@ struct pvt_poly_term {
  * @total_divider: total data divider.
  * @terms: polynomial terms up to a free one.
  */
+#define MAX_POLY_POWER      5
+
 struct pvt_poly {
        long total_divider;
-       struct pvt_poly_term terms[];
+       struct pvt_poly_term terms[MAX_POLY_POWER];
 };
 
 #endif /* __HWMON_BT1_PVT_H__ */
index ccef04f27f12faecd0c8e236f5ba479735a01ae8..36fbe912f81d0172ad932fda52566ea724e8be39 100644 (file)
@@ -2,6 +2,7 @@
 /*
  * Copyright (c) 2019, The Linaro Limited. All rights reserved.
  */
+#include <linux/acpi.h>
 #include <linux/coresight.h>
 #include <linux/device.h>
 #include <linux/err.h>
@@ -66,6 +67,58 @@ static int of_cti_get_cpu_at_node(const struct device_node *node)
 
 #endif
 
+#ifdef CONFIG_ACPI
+
+#include <acpi/actypes.h>
+#include <acpi/processor.h>
+
+/*
+ * acpi_handle_to_logical_cpuid - Map a given acpi_handle to the
+ * logical CPU id of the corresponding CPU device.
+ *
+ * Returns the logical CPU id when found. Otherwise returns >= nr_cpus_id.
+ */
+static int
+acpi_handle_to_logical_cpuid(acpi_handle handle)
+{
+       int i;
+       struct acpi_processor *pr;
+
+       for_each_possible_cpu(i) {
+               pr = per_cpu(processors, i);
+               if (pr && pr->handle == handle)
+                       break;
+       }
+
+       return i;
+}
+
+static int acpi_cti_get_cpu_at_node(struct fwnode_handle *fwnode)
+{
+       int cpu;
+       acpi_handle cpu_handle;
+       acpi_status status;
+       struct acpi_device *adev = to_acpi_device_node(fwnode);
+
+       if (!adev)
+               return -1;
+       status = acpi_get_parent(adev->handle, &cpu_handle);
+       if (ACPI_FAILURE(status))
+               return -1;
+
+       cpu = acpi_handle_to_logical_cpuid(cpu_handle);
+       if (cpu >= nr_cpu_ids)
+               return -1;
+       return cpu;
+}
+
+#else
+static int acpi_cti_get_cpu_at_node(struct fwnode_handle *fwnode)
+{
+       return -1;
+}
+
+#endif
 /*
  * CTI can be bound to a CPU, or a system device.
  * CPU can be declared at the device top level or in a connections node
@@ -75,6 +128,8 @@ static int cti_plat_get_cpu_at_node(struct fwnode_handle *fwnode)
 {
        if (is_of_node(fwnode))
                return of_cti_get_cpu_at_node(to_of_node(fwnode));
+       else if (is_acpi_device_node(fwnode))
+               return acpi_cti_get_cpu_at_node(fwnode);
        return -1;
 }
 
@@ -82,6 +137,8 @@ const char *cti_plat_get_node_name(struct fwnode_handle *fwnode)
 {
        if (is_of_node(fwnode))
                return of_node_full_name(to_of_node(fwnode));
+       else if (is_acpi_device_node(fwnode))
+               return fwnode_get_name(fwnode);
        return "unknown";
 }
 
@@ -423,7 +480,8 @@ static int cti_plat_create_impdef_connections(struct device *dev,
                return -EINVAL;
 
        fwnode_for_each_child_node(fwnode, child) {
-               if (cti_plat_node_name_eq(child, CTI_DT_CONNS))
+               if (cti_plat_node_name_eq(child, CTI_DT_CONNS) ||
+                   is_acpi_device_node(fwnode))
                        rc = cti_plat_create_connection(dev, drvdata,
                                                        child);
                if (rc != 0)
@@ -444,7 +502,8 @@ static int cti_plat_get_hw_data(struct device *dev, struct cti_drvdata *drvdata)
        device_property_read_u32(dev, CTI_DT_CTM_ID, &cti_dev->ctm_id);
 
        /* check for a v8 architectural CTI device */
-       if (cti_plat_check_v8_arch_compatible(dev))
+       if (cti_plat_check_v8_arch_compatible(dev) ||
+           device_property_present(dev, CTI_DT_V8ARCH_COMPAT))
                rc = cti_plat_create_v8_connections(dev, drvdata);
        else
                rc = cti_plat_create_impdef_connections(dev, drvdata);
index e50f9603d189e8c0edbf1324398eb3cf2f636cb3..facd3f5bd44fd55d8ab1a6ce8b2d6ac9117377f3 100644 (file)
@@ -452,6 +452,9 @@ config I2C_AXXIA
          255 bytes in length. Any attempt to to a larger transfer will return
          an error.
 
+config I2C_BAIKAL_SMBUS
+       tristate "Baikal-M SoC SMBus interface"
+
 config I2C_BCM2835
        tristate "Broadcom BCM2835 I2C controller"
        depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST
index e73cdb1d2b5a85bf0973ee0facc92fe4b76704c4..71d4eb16382eecf71875678ace0386517c242387 100644 (file)
@@ -45,6 +45,7 @@ ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y)
 endif
 obj-$(CONFIG_I2C_AU1550)       += i2c-au1550.o
 obj-$(CONFIG_I2C_AXXIA)                += i2c-axxia.o
+obj-$(CONFIG_I2C_BAIKAL_SMBUS) += i2c-baikal-smbus.o
 obj-$(CONFIG_I2C_BCM2835)      += i2c-bcm2835.o
 obj-$(CONFIG_I2C_BCM_IPROC)    += i2c-bcm-iproc.o
 obj-$(CONFIG_I2C_CADENCE)      += i2c-cadence.o
diff --git a/drivers/i2c/busses/i2c-baikal-smbus.c b/drivers/i2c/busses/i2c-baikal-smbus.c
new file mode 100644 (file)
index 0000000..54e3cde
--- /dev/null
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SMBus controller driver for Baikal SoC
+ *
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ * Author: Georgy Vlasov <georgy.vlasov@baikalelectronics.ru>
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define BAIKAL_SMBUS_CR1       0x00
+#define BAIKAL_CR1_IRT         BIT(0)
+#define BAIKAL_CR1_TRS         BIT(1)
+#define BAIKAL_CR1_IEB         BIT(3)
+
+#define BAIKAL_SMBUS_CR2       0x04
+#define BAIKAL_CR2_FTE         BIT(1)
+
+#define BAIKAL_SMBUS_FBCR1     0x08
+#define BAIKAL_SMBUS_FIFO      0x0c
+#define BAIKAL_SMBUS_SCD1      0x10
+
+#define BAIKAL_SMBUS_SCD2      0x14
+#define BAIKAL_SCD2_MASK       0x03
+#define BAIKAL_SCD2_SHT                BIT(7)
+
+#define BAIKAL_SMBUS_ADR1      0x18
+
+#define BAIKAL_SMBUS_ISR1      0x20
+#define BAIKAL_ISR1_MASK       0x7f
+#define BAIKAL_ISR1_FER                BIT(2)
+#define BAIKAL_ISR1_RNK                BIT(3)
+#define BAIKAL_ISR1_ALD                BIT(4)
+#define BAIKAL_ISR1_TCS                BIT(6)
+
+#define BAIKAL_SMBUS_IMR1      0x24
+#define BAIKAL_SMBUS_FBCR2     0x2c
+#define BAIKAL_SMBUS_IMR2      0x48
+
+#define BAIKAL_SMBUS_FIFO_SIZE 16
+
+struct baikal_smbus_dev {
+       struct device           *dev;
+       void __iomem            *base;
+       struct clk              *smbus_clk;
+       struct i2c_adapter      adapter;
+       u32                     bus_clk_rate;
+       u64                     smbus_clk_rate;
+};
+
+static int baikal_smbus_init(struct baikal_smbus_dev *smbus)
+{
+       u32 divider = (smbus->smbus_clk_rate == 0 ?
+                       clk_get_rate(smbus->smbus_clk) :
+                       smbus->smbus_clk_rate) / smbus->bus_clk_rate - 1;
+
+       writel(BAIKAL_CR1_IRT, smbus->base + BAIKAL_SMBUS_CR1);
+       writel(0, smbus->base + BAIKAL_SMBUS_CR1);
+       writel(0, smbus->base + BAIKAL_SMBUS_CR2);
+       writel(0, smbus->base + BAIKAL_SMBUS_FBCR2);
+       writel(0, smbus->base + BAIKAL_SMBUS_IMR1);
+       writel(0, smbus->base + BAIKAL_SMBUS_IMR2);
+       writel(divider & 0xff, smbus->base + BAIKAL_SMBUS_SCD1);
+       writel(BAIKAL_SCD2_SHT | ((divider >> 8) & BAIKAL_SCD2_MASK),
+              smbus->base + BAIKAL_SMBUS_SCD2);
+
+       return 0;
+}
+
+static int baikal_smbus_xfer(struct i2c_adapter *adap, u16 addr,
+                            unsigned short flags, char read_write,
+                            u8 command, int size, union i2c_smbus_data *data)
+{
+       struct baikal_smbus_dev *const smbus = adap->dev.driver_data;
+       int ret = -EINVAL;
+
+       switch (size) {
+       case I2C_SMBUS_BYTE:
+               writel(BAIKAL_CR1_IRT, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(0, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(addr, smbus->base + BAIKAL_SMBUS_ADR1);
+               writel(1, smbus->base + BAIKAL_SMBUS_FBCR1);
+
+               if (read_write == I2C_SMBUS_WRITE) {
+                       writel(BAIKAL_CR1_TRS | BAIKAL_CR1_IEB,
+                              smbus->base + BAIKAL_SMBUS_CR1);
+                       writel(command, smbus->base + BAIKAL_SMBUS_FIFO);
+               } else {
+                       writel(BAIKAL_CR1_IEB, smbus->base + BAIKAL_SMBUS_CR1);
+               }
+
+               writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+               writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+               while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                      !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                       (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                       ;
+
+               if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                       (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                       ret = -ENXIO;
+                       goto exit;
+               }
+
+               if (read_write == I2C_SMBUS_READ) {
+                       data->byte = readl(smbus->base + BAIKAL_SMBUS_FIFO);
+
+                       if (readl(smbus->base + BAIKAL_SMBUS_ISR1) & BAIKAL_ISR1_FER) {
+                               ret = -EMSGSIZE;
+                               goto exit;
+                       }
+               }
+
+               ret = 0;
+               break;
+       case I2C_SMBUS_BYTE_DATA:
+               writel(BAIKAL_CR1_IRT, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(0, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(addr, smbus->base + BAIKAL_SMBUS_ADR1);
+               writel(BAIKAL_CR1_TRS | BAIKAL_CR1_IEB, smbus->base + BAIKAL_SMBUS_CR1);
+
+               if (read_write == I2C_SMBUS_WRITE) {
+                       writel(2, smbus->base + BAIKAL_SMBUS_FBCR1);
+                       writel(command, smbus->base + BAIKAL_SMBUS_FIFO);
+                       writel(data->byte, smbus->base + BAIKAL_SMBUS_FIFO);
+               } else {
+                       writel(1, smbus->base + BAIKAL_SMBUS_FBCR1);
+                       writel(command, smbus->base + BAIKAL_SMBUS_FIFO);
+               }
+
+               writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+               writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+               while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                      !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                       (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                       ;
+
+               if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                       (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                       ret = -ENXIO;
+                       goto exit;
+               }
+
+               if (read_write == I2C_SMBUS_READ) {
+                       writel(BAIKAL_CR1_IEB, smbus->base + BAIKAL_SMBUS_CR1);
+                       writel(1, smbus->base + BAIKAL_SMBUS_FBCR1);
+                       writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+                       writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+                       while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                              !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                               (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                               ;
+
+                       if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                               (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                               ret = -ENXIO;
+                               goto exit;
+                       }
+
+                       data->byte = readl(smbus->base + BAIKAL_SMBUS_FIFO);
+                       if (readl(smbus->base + BAIKAL_SMBUS_ISR1) & BAIKAL_ISR1_FER) {
+                               ret = -EMSGSIZE;
+                               goto exit;
+                       }
+               }
+
+               ret = 0;
+               break;
+       case I2C_SMBUS_WORD_DATA:
+               writel(BAIKAL_CR1_IRT, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(0, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(addr, smbus->base + BAIKAL_SMBUS_ADR1);
+               writel(BAIKAL_CR1_TRS | BAIKAL_CR1_IEB, smbus->base + BAIKAL_SMBUS_CR1);
+
+               if (read_write == I2C_SMBUS_WRITE) {
+                       writel(3, smbus->base + BAIKAL_SMBUS_FBCR1);
+                       writel(command, smbus->base + BAIKAL_SMBUS_FIFO);
+                       writel((data->word >> 0) & 0xff,
+                              smbus->base + BAIKAL_SMBUS_FIFO);
+                       writel((data->word >> 8) & 0xff,
+                              smbus->base + BAIKAL_SMBUS_FIFO);
+               } else {
+                       writel(1, smbus->base + BAIKAL_SMBUS_FBCR1);
+                       writel(command, smbus->base + BAIKAL_SMBUS_FIFO);
+               }
+
+               writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+               writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+               while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                      !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                       (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                       ;
+
+               if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                       (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                       ret = -ENXIO;
+                       goto exit;
+               }
+
+               if (read_write == I2C_SMBUS_READ) {
+                       writel(BAIKAL_CR1_IEB, smbus->base + BAIKAL_SMBUS_CR1);
+                       writel(2, smbus->base + BAIKAL_SMBUS_FBCR1);
+                       writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+                       writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+                       while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                              !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                               (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                               ;
+
+                       if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                               (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                               ret = -ENXIO;
+                               goto exit;
+                       }
+
+                       data->word  = readl(smbus->base + BAIKAL_SMBUS_FIFO);
+                       data->word |= readl(smbus->base + BAIKAL_SMBUS_FIFO) << 8;
+                       if (readl(smbus->base + BAIKAL_SMBUS_ISR1) & BAIKAL_ISR1_FER) {
+                               ret = -EMSGSIZE;
+                               goto exit;
+                       }
+               }
+
+               ret = 0;
+               break;
+       case I2C_SMBUS_BLOCK_DATA:
+               writel(BAIKAL_CR1_IRT, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(0, smbus->base + BAIKAL_SMBUS_CR1);
+               writel(addr, smbus->base + BAIKAL_SMBUS_ADR1);
+               writel(BAIKAL_CR1_TRS | BAIKAL_CR1_IEB, smbus->base + BAIKAL_SMBUS_CR1);
+
+               if (read_write == I2C_SMBUS_WRITE) {
+                       unsigned int txedsize = 0;
+
+                       while (txedsize < data->block[0] + 1) {
+                               unsigned int xblocknum;
+                               unsigned int xfersize;
+
+                               xfersize = data->block[0] + 1 - txedsize;
+                               if (xfersize > BAIKAL_SMBUS_FIFO_SIZE)
+                                       xfersize = BAIKAL_SMBUS_FIFO_SIZE;
+
+                               writel(xfersize, smbus->base + BAIKAL_SMBUS_FBCR1);
+                               if (!txedsize) {
+                                       /*
+                                        * The first xfer should start with
+                                        * command byte
+                                        */
+                                       writel(command,
+                                              smbus->base + BAIKAL_SMBUS_FIFO);
+                                       xblocknum = 1;
+                                       ++txedsize;
+                               } else {
+                                       xblocknum = 0;
+                               }
+
+                               while (xblocknum < xfersize) {
+                                       writel(data->block[txedsize++],
+                                              smbus->base + BAIKAL_SMBUS_FIFO);
+                                       ++xblocknum;
+                               }
+
+                               writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+                               writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+                               while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                                      !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                                       (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                                       ;
+
+                               if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                                       (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                                       ret = -ENXIO;
+                                       goto exit;
+                               }
+                       }
+               } else {
+                       unsigned int rxedsize = 0;
+
+                       writel(1, smbus->base + BAIKAL_SMBUS_FBCR1);
+                       writel(command, smbus->base + BAIKAL_SMBUS_FIFO);
+                       writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+                       writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+                       while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                              !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                               (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                               ;
+
+                       if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                               (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                               ret = -ENXIO;
+                               goto exit;
+                       }
+
+                       writel(BAIKAL_CR1_IEB, smbus->base + BAIKAL_SMBUS_CR1);
+
+                       while (rxedsize < I2C_SMBUS_BLOCK_MAX) {
+                               unsigned int i;
+
+                               writel(BAIKAL_SMBUS_FIFO_SIZE, smbus->base + BAIKAL_SMBUS_FBCR1);
+                               writel(BAIKAL_ISR1_MASK, smbus->base + BAIKAL_SMBUS_ISR1);
+                               writel(BAIKAL_CR2_FTE, smbus->base + BAIKAL_SMBUS_CR2);
+
+                               while ((readl(smbus->base + BAIKAL_SMBUS_CR2) & BAIKAL_CR2_FTE) &&
+                                      !(readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                                       (BAIKAL_ISR1_TCS | BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)))
+                                       ;
+
+                               if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                                       (BAIKAL_ISR1_ALD | BAIKAL_ISR1_RNK)) {
+                                       ret = -ENXIO;
+                                       goto exit;
+                               }
+
+                               for (i = 0; i < BAIKAL_SMBUS_FIFO_SIZE; ++i) {
+                                       data->block[1 + rxedsize++] =
+                                               readl(smbus->base + BAIKAL_SMBUS_FIFO);
+
+                                       if (readl(smbus->base + BAIKAL_SMBUS_ISR1) &
+                                                       BAIKAL_ISR1_FER) {
+                                               ret = -EMSGSIZE;
+                                               goto exit;
+                                       }
+                               }
+                       }
+
+                       data->block[0] = rxedsize;
+               }
+
+               ret = 0;
+               break;
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+exit:
+       writel(0, smbus->base + BAIKAL_SMBUS_CR1);
+       return ret;
+}
+
+static u32 baikal_functionality(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+              I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm baikal_smbus_algorithm = {
+       .smbus_xfer     = baikal_smbus_xfer,
+       .functionality  = baikal_functionality
+};
+
+static irqreturn_t baikal_smbus_isr(int irq, void *_dev)
+{
+       return IRQ_HANDLED;
+}
+
+static int baikal_smbus_probe(struct platform_device *pdev)
+{
+       struct baikal_smbus_dev *smbus;
+       void __iomem *base;
+       int irq;
+       int ret;
+
+       smbus = devm_kzalloc(&pdev->dev, sizeof(*smbus), GFP_KERNEL);
+       if (!smbus)
+               return -ENOMEM;
+
+       base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "missing interrupt resource\n");
+               return irq;
+       }
+
+       if (dev_of_node(&pdev->dev)) {
+               smbus->smbus_clk = devm_clk_get(&pdev->dev, NULL);
+               if (IS_ERR(smbus->smbus_clk)) {
+                       dev_err(&pdev->dev, "missing clock\n");
+                       return PTR_ERR(smbus->smbus_clk);
+               }
+
+               ret = clk_prepare_enable(smbus->smbus_clk);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to enable clock\n");
+                       return ret;
+               }
+
+               smbus->smbus_clk_rate = 0;
+#ifdef CONFIG_ACPI
+       } else if (to_acpi_device_node(pdev->dev.fwnode) &&
+                  acpi_evaluate_integer(to_acpi_device_node(pdev->dev.fwnode)->handle,
+                                        "CLK", NULL, &smbus->smbus_clk_rate)) {
+               dev_err(&pdev->dev, "missing clock-frequency value\n");
+               return -EINVAL;
+#endif
+       }
+
+       smbus->base = base;
+       smbus->dev = &pdev->dev;
+
+       device_property_read_u32(&pdev->dev, "clock-frequency",
+                                &smbus->bus_clk_rate);
+       if (!smbus->bus_clk_rate)
+               smbus->bus_clk_rate = 100000; /* default clock rate */
+
+       ret = devm_request_irq(&pdev->dev, irq, baikal_smbus_isr, 0,
+                              pdev->name, smbus);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to claim IRQ%d\n", irq);
+               return ret;
+       }
+
+       i2c_set_adapdata(&smbus->adapter, smbus);
+       strscpy(smbus->adapter.name, pdev->name, sizeof(smbus->adapter.name));
+       smbus->adapter.owner = THIS_MODULE;
+       smbus->adapter.algo = &baikal_smbus_algorithm;
+       smbus->adapter.dev.parent = &pdev->dev;
+       if (dev_of_node(&pdev->dev))
+               smbus->adapter.dev.of_node = pdev->dev.of_node;
+       else
+               smbus->adapter.dev.fwnode = pdev->dev.fwnode;
+
+       platform_set_drvdata(pdev, smbus);
+
+       ret = baikal_smbus_init(smbus);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to init SMBus\n");
+               goto err_clk;
+       }
+
+       ret = i2c_add_adapter(&smbus->adapter);
+       if (ret)
+               goto err_clk;
+
+       return 0;
+
+err_clk:
+       if (dev_of_node(&pdev->dev))
+               clk_disable_unprepare(smbus->smbus_clk);
+
+       return ret;
+}
+
+static int baikal_smbus_remove(struct platform_device *pdev)
+{
+       struct baikal_smbus_dev *smbus = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(smbus->smbus_clk);
+       i2c_del_adapter(&smbus->adapter);
+       return 0;
+}
+
+static const struct of_device_id baikal_smbus_match[] = {
+       { .compatible = "baikal,bm1000-smbus" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, baikal_smbus_match);
+
+static struct platform_driver baikal_smbus_driver = {
+       .driver = {
+               .name = "baikal-smbus",
+               .of_match_table = baikal_smbus_match
+       },
+       .probe  = baikal_smbus_probe,
+       .remove = baikal_smbus_remove
+};
+
+module_platform_driver(baikal_smbus_driver);
+
+MODULE_AUTHOR("Georgy Vlasov <georgy.vlasov@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal SMBus driver");
+MODULE_LICENSE("GPL v2");
index 358ad56f65245e2094960dad0e6ee6f52f0110b1..17d7bfd8ab91e3b76694b13709df5f51b2f6a99c 100644 (file)
@@ -496,6 +496,22 @@ config VCPU_STALL_DETECTOR
 
          If you do not intend to run this kernel as a guest, say N.
 
+config TP_BMC
+       tristate "T-Platforms Baikal-T(1)/M BMC"
+       depends on I2C && OF
+       select PINCTRL
+       select GENERIC_PINCONF
+       select SERIO
+       help
+         Say Y here if you want to build a driver for T-Platforms BMC devices
+         embedded into the boards with Baikal-T(1)/M processors. The device main
+         purpose is the CPU kick-starting as well as some additional side-way
+         functionality like power on/off buttons state tracing and full device
+         powering off.
+
+         If you choose to build module, its name will be tp-bt-bmc. If unsure,
+         say N here.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
index ac9b3e757ba1dfbbd44d5410ab31fd335fba90e9..93453052ed92b366d11d3e496040c75a1bea13a5 100644 (file)
@@ -62,3 +62,4 @@ obj-$(CONFIG_HI6421V600_IRQ)  += hi6421v600-irq.o
 obj-$(CONFIG_OPEN_DICE)                += open-dice.o
 obj-$(CONFIG_GP_PCI1XXXX)      += mchp_pci1xxxx/
 obj-$(CONFIG_VCPU_STALL_DETECTOR)      += vcpu_stall_detector.o
+obj-$(CONFIG_TP_BMC)           += tp_bmc.o
diff --git a/drivers/misc/tp_bmc.c b/drivers/misc/tp_bmc.c
new file mode 100644 (file)
index 0000000..268d373
--- /dev/null
@@ -0,0 +1,770 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/pm.h>
+#include <linux/rtc.h>
+#include <linux/serio.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+enum I2C_REGS {
+       R_ID1 = 0,
+       R_ID2,
+       R_ID3,
+       R_ID4,
+       R_SOFTOFF_RQ,
+       R_PWROFF_RQ,
+       R_PWRBTN_STATE,
+       R_VERSION1,
+       R_VERSION2,
+       R_BOOTREASON,
+       R_BOOTREASON_ARG,
+       R_SCRATCH1,
+       R_SCRATCH2,
+       R_SCRATCH3,
+       R_SCRATCH4,
+       R_CAP,
+       R_GPIODIR0,
+       R_GPIODIR1,
+       R_GPIODIR2,
+       R_COUNT
+};
+
+#define BMC_ID1_VAL            0x49
+#define BMC_ID2_VAL            0x54
+#define BMC_ID3_VAL            0x58
+#define BMC_ID4_VAL0           0x32
+#define BMC_ID4_VAL1           0x2
+
+#define BMC_VERSION1           0
+#define BMC_VERSION2           2
+#define BMC_VERSION2_3         3
+
+#define BMC_CAP_PWRBTN         0x1
+#define BMC_CAP_TOUCHPAD       0x2
+#define BMC_CAP_RTC            0x4
+#define BMC_CAP_FRU            0x8
+#define BMC_CAP_GPIODIR                0x10
+
+#define BMC_SERIO_BUFSIZE      7
+
+struct bmc_poll_data {
+       struct i2c_client *c;
+};
+
+static struct i2c_client       *bmc_i2c;
+static struct i2c_client       *rtc_i2c;
+static struct i2c_driver       mitx2_bmc_i2c_driver;
+static struct input_dev                *button_dev;
+static struct bmc_poll_data    poll_data;
+static struct task_struct      *polling_task;
+#ifdef CONFIG_SERIO
+static struct i2c_client       *serio_i2c;
+static struct task_struct      *touchpad_task;
+#endif
+static u8 bmc_proto_version[3];
+static u8 bmc_bootreason[2];
+static u8 bmc_scratch[4];
+static int bmc_cap;
+static const char input_name[] = "BMC input dev";
+static u8 prev_softoff_ret;
+
+static int bmc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       uint8_t rtc_buf[8];
+       struct i2c_msg msg;
+       int t;
+       int rc;
+
+       msg.addr = client->addr;
+       msg.flags = I2C_M_RD;
+       msg.len = 8;
+       msg.buf = rtc_buf;
+       rc = i2c_transfer(client->adapter, &msg, 1);
+       if (rc != 1) {
+               dev_err(dev, "rtc_read_time: i2c_transfer error %d\n", rc);
+               return rc;
+       }
+
+       tm->tm_sec  = bcd2bin(rtc_buf[0] & 0x7f);
+       tm->tm_min  = bcd2bin(rtc_buf[1] & 0x7f);
+       tm->tm_hour = bcd2bin(rtc_buf[2] & 0x3f);
+       if (rtc_buf[3] & (1 << 6)) /* PM */
+               tm->tm_hour += 12;
+
+       tm->tm_mday = bcd2bin(rtc_buf[4] & 0x3f);
+       tm->tm_mon  = bcd2bin(rtc_buf[5] & 0x1f);
+       t = rtc_buf[5] >> 5;
+       tm->tm_wday = (t == 7) ? 0 : t;
+       tm->tm_year = bcd2bin(rtc_buf[6]) + 100; /* year since 1900 */
+       tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+       tm->tm_isdst = 0;
+
+       return rtc_valid_tm(tm);
+}
+
+static int bmc_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       uint8_t rtc_buf[8];
+       struct i2c_msg msg;
+       int rc;
+       uint8_t seconds, minutes, hours, wday, mday, month, years;
+
+       seconds = bin2bcd(tm->tm_sec);
+       minutes = bin2bcd(tm->tm_min);
+       hours   = bin2bcd(tm->tm_hour);
+       wday    = tm->tm_wday ? tm->tm_wday : 0x7;
+       mday    = bin2bcd(tm->tm_mday);
+       month   = bin2bcd(tm->tm_mon);
+       years   = bin2bcd(tm->tm_year % 100);
+
+       /* TODO: need sanity check? */
+       rtc_buf[0] = seconds;
+       rtc_buf[1] = minutes;
+       rtc_buf[2] = hours;
+       rtc_buf[3] = 0;
+       rtc_buf[4] = mday;
+       rtc_buf[5] = month | (wday << 5);
+       rtc_buf[6] = years;
+       rtc_buf[7] = 0;
+
+       msg.addr = client->addr;
+       msg.flags = 0;
+       msg.len = 8;
+       msg.buf = rtc_buf;
+
+       dev_dbg(dev, "rtc_set_time: %08x-%08x\n",
+               *(uint32_t *)&rtc_buf[0],
+               *(uint32_t *)&rtc_buf[4]);
+
+       rc = i2c_transfer(client->adapter, &msg, 1);
+       if (rc != 1)
+               dev_err(dev, "i2c write: %d\n", rc);
+
+       return rc == 1 ? 0 : -EIO;
+}
+
+static const struct rtc_class_ops bmc_rtc_ops = {
+       .read_time = bmc_rtc_read_time,
+       .set_time  = bmc_rtc_set_time,
+};
+
+#ifdef CONFIG_SERIO
+/* BMC serio (PS/2 touchpad) interface */
+
+static int bmc_serio_write(struct serio *id, unsigned char val)
+{
+       struct i2c_client *client = id->port_data;
+       uint8_t buf[4];
+       struct i2c_msg msg;
+       int rc;
+
+       buf[0] = val;
+       msg.addr = client->addr;
+       msg.flags = 0;
+       msg.len = 1;
+       msg.buf = buf;
+       dev_dbg(&client->dev, "bmc_serio_write: 0x%02x\n", val);
+       rc = i2c_transfer(client->adapter, &msg, 1);
+       if (rc != 1)
+               dev_err(&client->dev, "i2c write: %d\n", rc);
+
+       return rc == 1 ? 0 : -EIO;
+}
+
+/* returns: -1 on error, +1 if more data available, 0 otherwise */
+static int bmc_serio_read(struct i2c_client *client)
+{
+       struct serio *serio = dev_get_drvdata(&client->dev);
+       int i, rc, cnt;
+       uint8_t buf[BMC_SERIO_BUFSIZE];
+       struct i2c_msg msg;
+
+       msg.addr = client->addr;
+       msg.flags = I2C_M_RD;
+       msg.len = BMC_SERIO_BUFSIZE;
+       msg.buf = buf;
+       rc = i2c_transfer(client->adapter, &msg, 1);
+       if (rc != 1) {
+               dev_err(&client->dev,
+                       "bmc_serio_read: i2c_transfer error %d\n", rc);
+               return -1;
+       }
+
+       cnt = buf[0];
+       rc = 0;
+       if (cnt > BMC_SERIO_BUFSIZE - 1) {
+               cnt = BMC_SERIO_BUFSIZE - 1;
+               rc = 1;
+       }
+
+       for (i = 0; i < cnt; i++)
+               serio_interrupt(serio, buf[i + 1], 0);
+
+       return 0;
+}
+
+int touchpad_poll_fn(void *data)
+{
+       int ret;
+
+       while (1) {
+               if (kthread_should_stop())
+                       break;
+
+               while ((ret = bmc_serio_read(serio_i2c)) > 0)
+                       ;
+
+               if (ret < 0)
+                       msleep_interruptible(10000);
+
+               msleep_interruptible(10);
+       }
+
+       return 0;
+}
+#endif /* CONFIG_SERIO */
+
+#ifdef CONFIG_PINCTRL
+static uint8_t bmc_pincf_state [3];
+#define BMC_NPINS      (sizeof(bmc_pincf_state) * 8)
+
+static struct pinctrl_pin_desc bmc_pin_desc[BMC_NPINS] = {
+       PINCTRL_PIN(0, "P0"),
+       PINCTRL_PIN(1, "P1"),
+       PINCTRL_PIN(2, "P2"),
+       PINCTRL_PIN(3, "P3"),
+       PINCTRL_PIN(4, "P4"),
+       PINCTRL_PIN(5, "P5"),
+       PINCTRL_PIN(6, "P6"),
+       PINCTRL_PIN(7, "P7"),
+       PINCTRL_PIN(8, "P8"),
+       PINCTRL_PIN(9, "P9"),
+       PINCTRL_PIN(10, "P10"),
+       PINCTRL_PIN(11, "P11"),
+       PINCTRL_PIN(12, "P12"),
+       PINCTRL_PIN(13, "P13"),
+       PINCTRL_PIN(14, "P14"),
+       PINCTRL_PIN(15, "P15"),
+       PINCTRL_PIN(16, "P16"),
+       PINCTRL_PIN(17, "P17"),
+       PINCTRL_PIN(18, "P18"),
+       PINCTRL_PIN(19, "P19"),
+       PINCTRL_PIN(20, "P20"),
+       PINCTRL_PIN(21, "P21"),
+       PINCTRL_PIN(22, "P22"),
+       PINCTRL_PIN(23, "P23"),
+};
+
+#define PCTRL_DEV      "bmc_pinctrl"
+
+static int bmc_pin_config_get(struct pinctrl_dev *pctldev,
+                             unsigned pin,
+                             unsigned long *config)
+{
+       int idx, bit;
+
+       if (pin > BMC_NPINS)
+               return -EINVAL;
+
+       idx = pin >> 3;
+       bit = pin & 7;
+
+       *config = !!(bmc_pincf_state[idx] & (1 << bit));
+       return 0;
+}
+
+static int bmc_pin_config_set(struct pinctrl_dev *pctldev,
+                             unsigned pin,
+                             unsigned long *config,
+                             unsigned nc)
+{
+       int idx, bit;
+       enum pin_config_param param;
+       int arg;
+
+       if (pin > BMC_NPINS)
+               return -EINVAL;
+
+       idx = pin >> 3;
+       bit = pin & 7;
+
+       param = pinconf_to_config_param (*config);
+       arg = pinconf_to_config_argument (*config);
+       if (param != PIN_CONFIG_OUTPUT)
+               return -EINVAL;
+
+       if (arg)
+               bmc_pincf_state[idx] |= (1 << bit);
+       else
+               bmc_pincf_state[idx] &= ~(1 << bit);
+
+       dev_dbg(&bmc_i2c->dev,
+               "bmc_pin_config_set: pin %u, dir %lu\n", pin, *config);
+
+       return i2c_smbus_write_byte_data(bmc_i2c, R_GPIODIR0 + idx,
+                                        bmc_pincf_state[idx]);
+}
+
+void pinconf_generic_dump_config(struct pinctrl_dev *pctldev,
+                                struct seq_file *s, unsigned long config);
+
+void pinctrl_utils_free_map(struct pinctrl_dev *pctldev,
+                           struct pinctrl_map *map, unsigned num_maps);
+
+static const struct pinconf_ops bmc_confops = {
+       .pin_config_get = bmc_pin_config_get,
+       .pin_config_set = bmc_pin_config_set,
+       .pin_config_config_dbg_show = pinconf_generic_dump_config,
+};
+
+static int bmc_groups_count(struct pinctrl_dev *pctldev)
+{
+       return 0;
+}
+
+static const char *bmc_group_name(struct pinctrl_dev *pctldev,
+                                 unsigned selector)
+{
+       return NULL;
+}
+
+static const struct pinctrl_ops bmc_ctrl_ops = {
+       .get_groups_count = bmc_groups_count,
+       .get_group_name = bmc_group_name,
+       .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+       .dt_free_map = pinctrl_utils_free_map,
+};
+
+static struct pinctrl_desc bmc_pincrtl_desc = {
+       .name = PCTRL_DEV,
+       .pins = bmc_pin_desc,
+       .pctlops = &bmc_ctrl_ops,
+       .npins = BMC_NPINS,
+       .confops = &bmc_confops,
+};
+
+static struct pinctrl_dev *bmc_pinctrl_dev;
+
+static int bmc_pinctrl_register(struct device *dev)
+{
+       struct pinctrl_dev *pctrl_dev;
+       struct platform_device *pbdev;
+
+       pbdev = platform_device_alloc(PCTRL_DEV, -1);
+       pbdev->dev.parent = dev;
+       pbdev->dev.of_node = of_find_node_by_name(dev->of_node, "bmc_pinctrl");
+       platform_device_add(pbdev);
+       pctrl_dev = devm_pinctrl_register(&pbdev->dev, &bmc_pincrtl_desc, NULL);
+       if (IS_ERR(pctrl_dev)) {
+               dev_err(&pbdev->dev,
+                       "Can't register pinctrl (%ld)\n", PTR_ERR(pctrl_dev));
+               return PTR_ERR(pctrl_dev);
+       } else {
+               dev_dbg(&pbdev->dev, "BMC pinctrl registered\n");
+               bmc_pinctrl_dev = pctrl_dev;
+       }
+       /* reset all pins to default state */
+       i2c_smbus_write_byte_data(to_i2c_client(dev), R_GPIODIR0, 0);
+       i2c_smbus_write_byte_data(to_i2c_client(dev), R_GPIODIR1, 0);
+       i2c_smbus_write_byte_data(to_i2c_client(dev), R_GPIODIR2, 0);
+       return 0;
+}
+
+static void bmc_pinctrl_unregister(void)
+{
+       if (bmc_pinctrl_dev)
+               devm_pinctrl_unregister(&bmc_i2c->dev, bmc_pinctrl_dev);
+}
+#endif
+
+void bmc_pwroff_rq(void)
+{
+       int ret;
+
+       dev_info(&bmc_i2c->dev, "Write reg R_PWROFF_RQ\n");
+       ret = i2c_smbus_write_byte_data(bmc_i2c, R_PWROFF_RQ, 0x01);
+       dev_info(&bmc_i2c->dev, "ret: %i\n", ret);
+}
+
+int pwroff_rq_poll_fn(void *data)
+{
+       int ret;
+
+       while (1) {
+               if (kthread_should_stop())
+                       break;
+
+               dev_dbg(&poll_data.c->dev, "Polling\n");
+               ret = i2c_smbus_read_byte_data(poll_data.c, R_SOFTOFF_RQ);
+               dev_dbg(&poll_data.c->dev, "Polling returned: %i\n", ret);
+
+               if (ret < 0) {
+                       dev_err(&poll_data.c->dev,
+                               "Could not read register 0x%x\n",
+                               R_SOFTOFF_RQ);
+                       return -EIO;
+               }
+
+               if (prev_softoff_ret != ret) {
+                       dev_info(&poll_data.c->dev, "key change [%i]\n", ret);
+                       if (ret != 0) {
+                               dev_info(&poll_data.c->dev,
+                                        "PWROFF \"irq\" detected [%i]\n", ret);
+                               input_event(button_dev, EV_KEY, KEY_POWER, 1);
+                       } else {
+                               input_event(button_dev, EV_KEY, KEY_POWER, 0);
+                       }
+
+                       input_sync(button_dev);
+                       prev_softoff_ret = ret;
+               }
+
+               msleep_interruptible(100);
+       }
+
+       do_exit(1);
+       return 0;
+}
+
+static int mitx2_bmc_validate(struct i2c_client *client)
+{
+       int ret = 0;
+       int i = 0;
+       static const u8 regs[] = {R_ID1, R_ID2, R_ID3};
+       static const u8 vals[] = {BMC_ID1_VAL, BMC_ID2_VAL, BMC_ID3_VAL};
+
+       bmc_proto_version[0] = 0;
+       bmc_proto_version[1] = 0;
+       bmc_proto_version[2] = 0;
+
+       for (i = 0; i < ARRAY_SIZE(regs); i++) {
+               ret = i2c_smbus_read_byte_data(client, regs[i]);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Could not read register 0x%x\n",
+                               regs[i]);
+                       return -EIO;
+               }
+
+               if (ret != vals[i]) {
+                       dev_err(&client->dev,
+                               "Bad value [0x%02x] in register 0x%x, should be [0x%02x]\n",
+                               ret, regs[i], vals[i]);
+                       return -ENODEV;
+               }
+       }
+
+       ret = i2c_smbus_read_byte_data(client, R_ID4);
+       if (ret < 0) {
+               dev_err(&client->dev, "Could not read register 0x%x\n", R_ID4);
+               return -EIO;
+       }
+
+       if (ret == BMC_ID4_VAL0) {
+               bmc_proto_version[0] = 0;
+       } else if (ret == BMC_ID4_VAL1) {
+               bmc_proto_version[0] = 2;
+               ret = i2c_smbus_read_byte_data(client, R_VERSION1);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Could not read register 0x%x\n",
+                               R_VERSION1);
+                       return -EIO;
+               }
+
+               bmc_proto_version[1] = ret;
+
+               ret = i2c_smbus_read_byte_data(client, R_VERSION2);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Could not read register 0x%x\n",
+                               R_VERSION2);
+                       return -EIO;
+               }
+
+               bmc_proto_version[2] = ret;
+
+               ret = i2c_smbus_read_byte_data(client, R_BOOTREASON);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Could not read register 0x%x\n",
+                               R_BOOTREASON);
+                       return -EIO;
+               }
+
+               bmc_bootreason[0] = ret;
+
+               ret = i2c_smbus_read_byte_data(client, R_BOOTREASON_ARG);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Could not read register 0x%x\n",
+                               R_BOOTREASON_ARG);
+                       return -EIO;
+               }
+
+               bmc_bootreason[1] = ret;
+
+               for (i = R_SCRATCH1; i <= R_SCRATCH4; i++) {
+                       ret = i2c_smbus_read_byte_data(client, i);
+                       if (ret < 0) {
+                               dev_err(&client->dev,
+                                       "Could not read register 0x%x\n", i);
+                               return -EIO;
+                       }
+
+                       bmc_scratch[i - R_SCRATCH1] = ret;
+               }
+
+               if (bmc_proto_version[2] >= BMC_VERSION2_3) {
+                       ret = i2c_smbus_read_byte_data(client, R_CAP);
+                       if (ret >= 0)
+                               bmc_cap = ret;
+               } else {
+                       bmc_cap = BMC_CAP_PWRBTN;
+               }
+
+               dev_info(&client->dev,
+                        "bootreason %i:%i, extcap 0x%x\n",
+                        bmc_bootreason[0],
+                        bmc_bootreason[1],
+                        bmc_cap);
+       } else {
+               dev_err(&client->dev, "Bad value [0x%02x] in register 0x%x\n",
+                       ret, R_ID4);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int bmc_create_client_devices(struct device *bmc_dev)
+{
+       int ret = 0;
+       struct rtc_device *rtc_dev;
+       struct i2c_client *client = to_i2c_client(bmc_dev);
+       int client_addr = client->addr + 1;
+
+       if (bmc_cap & BMC_CAP_TOUCHPAD) {
+#ifdef CONFIG_SERIO
+               struct serio *serio;
+               serio_i2c = i2c_new_ancillary_device(client,
+                                                    "bmc_serio", client_addr);
+               if (IS_ERR(serio_i2c)) {
+                       dev_err(&client->dev, "Can't get serio secondary\n");
+                       serio_i2c = NULL;
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               serio = devm_kzalloc(&serio_i2c->dev,
+                                    sizeof(struct serio), GFP_KERNEL);
+               if (!serio) {
+                       dev_err(&serio_i2c->dev, "Can't allocate serio\n");
+                       ret = -ENOMEM;
+                       i2c_unregister_device(serio_i2c);
+                       serio_i2c = NULL;
+                       goto skip_tp;
+               }
+
+               serio->write = bmc_serio_write;
+               serio->port_data = serio_i2c;
+               serio->id.type = SERIO_PS_PSTHRU;
+               serio_register_port(serio);
+               dev_set_drvdata(&serio_i2c->dev, serio);
+               touchpad_task = kthread_run(touchpad_poll_fn, NULL,
+                                           "BMC serio poll task");
+skip_tp:
+#endif
+               client_addr++;
+       }
+
+       if (bmc_cap & BMC_CAP_RTC) {
+               rtc_i2c = i2c_new_ancillary_device(client,
+                                                  "bmc_rtc", client_addr);
+               if (IS_ERR(rtc_i2c)) {
+                       dev_err(&client->dev, "Can't get RTC secondary\n");
+                       rtc_i2c = NULL;
+                       ret = -ENOMEM;
+                       goto fail;
+               }
+
+               rtc_dev = devm_rtc_device_register(&rtc_i2c->dev, "bmc_rtc",
+                                                  &bmc_rtc_ops, THIS_MODULE);
+               if (IS_ERR(rtc_dev)) {
+                       ret = PTR_ERR(rtc_dev);
+                       dev_err(&client->dev,
+                               "Failed to register RTC device: %d\n", ret);
+                       i2c_unregister_device(rtc_i2c);
+                       rtc_i2c = NULL;
+               }
+fail:
+               client_addr++;
+       }
+#ifdef CONFIG_PINCTRL
+       if (bmc_cap & BMC_CAP_GPIODIR || 1 /*vvv*/)
+               bmc_pinctrl_register(bmc_dev);
+#endif
+       return ret;
+}
+
+static int mitx2_bmc_i2c_probe(struct i2c_client *client,
+                              const struct i2c_device_id *id)
+{
+       int err = 0;
+       int i = 0;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+               return -ENODEV;
+
+       for (i = 0; i < 10; i++) {
+               err = mitx2_bmc_validate(client);
+               if (!err)
+                       break;
+
+               msleep_interruptible(20);
+       }
+
+       if (err)
+               return err;
+
+       if (bmc_cap & BMC_CAP_PWRBTN) {
+               button_dev = input_allocate_device();
+               if (!button_dev) {
+                       dev_err(&client->dev, "Not enough memory\n");
+                       return -ENOMEM;
+               }
+
+               button_dev->id.bustype = BUS_I2C;
+               button_dev->dev.parent = &client->dev;
+               button_dev->name = input_name;
+               button_dev->phys = "bmc-input0";
+               button_dev->evbit[0] = BIT_MASK(EV_KEY);
+               button_dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+
+               err = input_register_device(button_dev);
+               if (err) {
+                       dev_err(&client->dev, "Failed to register device\n");
+                       input_free_device(button_dev);
+                       return err;
+               }
+
+               dev_dbg(&client->dev, "Starting polling thread\n");
+               poll_data.c = client;
+               polling_task = kthread_run(pwroff_rq_poll_fn, NULL,
+                                          "BMC poll task");
+       }
+
+       if (bmc_cap || 1 /*vvv*/)
+               err = bmc_create_client_devices(&client->dev);
+
+       bmc_i2c = client;
+       /* register as poweroff handler */
+       pm_power_off = bmc_pwroff_rq;
+
+       return 0;
+}
+
+static void mitx2_bmc_i2c_remove(struct i2c_client *client)
+{
+#ifdef CONFIG_SERIO
+       struct serio *serio;
+#endif
+       if (button_dev) {
+               kthread_stop(polling_task);
+               input_unregister_device(button_dev);
+       }
+#ifdef CONFIG_SERIO
+       if (serio_i2c) {
+               kthread_stop(touchpad_task);
+               serio = dev_get_drvdata(&serio_i2c->dev);
+               serio_unregister_port(serio);
+               i2c_unregister_device(serio_i2c);
+       }
+#endif
+       if (rtc_i2c)
+               i2c_unregister_device(rtc_i2c);
+#ifdef CONFIG_PINCTRL
+       bmc_pinctrl_unregister();
+#endif
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mitx2_bmc_of_match[] = {
+       { .compatible = "tp,mitx2-bmc" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mitx2_bmc_of_match);
+#endif
+
+static const struct i2c_device_id mitx2_bmc_i2c_id[] = {
+       { "mitx2_bmc", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, mitx2_bmc_i2c_id);
+
+static ssize_t version_show(struct kobject *kobj,
+                           struct kobj_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%i.%i.%i\n", bmc_proto_version[0],
+                                         bmc_proto_version[1],
+                                         bmc_proto_version[2]);
+}
+
+static struct kobj_attribute version_attribute =
+       __ATTR(version, 0664, version_show, NULL);
+
+static ssize_t bootreason_show(struct kobject *kobj,
+                              struct kobj_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%i\n", bmc_bootreason[0] |
+                                  (bmc_bootreason[1] << 8));
+}
+
+static struct kobj_attribute bootreason_attribute =
+       __ATTR(bootreason, 0664, bootreason_show, NULL);
+
+static ssize_t scratch_show(struct kobject *kobj,
+                           struct kobj_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%i\n", bmc_scratch[0] |
+                                  (bmc_scratch[1] <<  8) |
+                                  (bmc_scratch[2] << 16) |
+                                  (bmc_scratch[3] << 24));
+}
+
+static struct kobj_attribute scratch_attribute =
+       __ATTR(scratch, 0664, scratch_show, NULL);
+
+static struct attribute *bmc_attrs[] = {
+       &version_attribute.attr,
+       &bootreason_attribute.attr,
+       &scratch_attribute.attr,
+       NULL,
+};
+
+ATTRIBUTE_GROUPS(bmc);
+
+static struct i2c_driver mitx2_bmc_i2c_driver = {
+       .driver   = {
+               .name = "mitx2-bmc",
+               .of_match_table = of_match_ptr(mitx2_bmc_of_match),
+               .groups = bmc_groups,
+       },
+       .probe    = mitx2_bmc_i2c_probe,
+       .remove   = mitx2_bmc_i2c_remove,
+       .id_table = mitx2_bmc_i2c_id,
+};
+module_i2c_driver(mitx2_bmc_i2c_driver);
+
+MODULE_AUTHOR("Konstantin Kirik");
+MODULE_DESCRIPTION("mITX2 BMC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("serial:bmc");
index a7343d4bc50e75788313217142c8c1d70342c808..5530db114a6f2515277995a7884c7ab63f0318f9 100644 (file)
@@ -325,6 +325,36 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
        sdhci_reset(host, mask);
 }
 
+static void baikal_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       clk_set_rate(pltfm_host->clk, clock * 2);
+       host->mmc->actual_clock = clk_get_rate(pltfm_host->clk) / 2;
+       sdhci_enable_clk(host, 0);
+}
+
+static unsigned int baikal_get_max_clock (struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       return clk_round_rate(pltfm_host->clk, ULONG_MAX) / 2;
+}
+
+static unsigned int baikal_get_min_clock (struct sdhci_host *host)
+{
+       struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+       return clk_round_rate(pltfm_host->clk, 1) / 2;
+}
+
+static const struct sdhci_ops sdhci_dwcmshc_baikal_ops = {
+       .set_clock              = baikal_set_clock,
+       .get_max_clock          = baikal_get_max_clock,
+       .get_min_clock          = baikal_get_min_clock,
+       .set_bus_width          = sdhci_set_bus_width,
+       .set_uhs_signaling      = dwcmshc_set_uhs_signaling,
+       .reset                  = sdhci_reset,
+       .adma_write_desc        = dwcmshc_adma_write_desc,
+};
+
 static const struct sdhci_ops sdhci_dwcmshc_ops = {
        .set_clock              = sdhci_set_clock,
        .set_bus_width          = sdhci_set_bus_width,
@@ -343,6 +373,13 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
        .adma_write_desc        = dwcmshc_adma_write_desc,
 };
 
+static const struct sdhci_pltfm_data sdhci_dwcmshc_baikal_pdata = {
+       .ops = &sdhci_dwcmshc_baikal_ops,
+       .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+       .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+                  SDHCI_QUIRK2_BROKEN_64_BIT_DMA_MASK,
+};
+
 static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
        .ops = &sdhci_dwcmshc_ops,
        .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
@@ -422,6 +459,10 @@ static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv
 }
 
 static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
+       {
+               .compatible = "baikal,dwcmshc-sdhci",
+               .data = &sdhci_dwcmshc_baikal_pdata,
+       },
        {
                .compatible = "rockchip,rk3588-dwcmshc",
                .data = &sdhci_dwcmshc_rk35xx_pdata,
@@ -444,6 +485,10 @@ static const struct acpi_device_id sdhci_dwcmshc_acpi_ids[] = {
                .id = "MLNXBF30",
                .driver_data = (kernel_ulong_t)&sdhci_dwcmshc_bf3_pdata,
        },
+       {
+               .id = "BKLE0004",
+               .driver_data = (kernel_ulong_t)&sdhci_dwcmshc_baikal_pdata,
+       },
        {}
 };
 #endif
@@ -481,7 +526,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
        pltfm_host = sdhci_priv(host);
        priv = sdhci_pltfm_priv(pltfm_host);
 
-       if (dev->of_node) {
+       if (dev->of_node || pltfm_data == &sdhci_dwcmshc_baikal_pdata) {
                pltfm_host->clk = devm_clk_get(dev, "core");
                if (IS_ERR(pltfm_host->clk)) {
                        err = PTR_ERR(pltfm_host->clk);
index 632341911b6e7807ae59edca74a77d393f87c2ae..eb870f517d31eb6b860b7282854b3e31c338bd6b 100644 (file)
@@ -4098,7 +4098,8 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
                host->flags &= ~SDHCI_USE_64_BIT_DMA;
 
        /* Try 64-bit mask if hardware is capable  of it */
-       if (host->flags & SDHCI_USE_64_BIT_DMA) {
+       if (host->flags & SDHCI_USE_64_BIT_DMA &&
+         !(host->quirks2 & SDHCI_QUIRK2_BROKEN_64_BIT_DMA_MASK)) {
                ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
                if (ret) {
                        pr_warn("%s: Failed to set 64-bit DMA mask.\n",
index 5ce7cdcc192fd24a1c3725c1fe839e74f7daa413..18310a27a51bac5e1890835e892c3162bef5558e 100644 (file)
@@ -480,6 +480,7 @@ struct sdhci_host {
 #define SDHCI_QUIRK2_USE_32BIT_BLK_CNT                 (1<<18)
 /* Issue CMD and DATA reset together */
 #define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER      (1<<19)
+#define SDHCI_QUIRK2_BROKEN_64_BIT_DMA_MASK            (1<<31)
 
        int irq;                /* Device IRQ */
        void __iomem *ioaddr;   /* Mapped address */
index aaa06050c9bc94596f890607a36699aaa9788524..5fe98b2d0757f36424c1c176b28a3214298f5f26 100644 (file)
@@ -205,3 +205,10 @@ config MTD_SERCOMM_PARTS
          partition map. This partition table contains real partition
          offsets, which may differ from device to device depending on the
          number and location of bad blocks on NAND.
+
+config MTD_BAIKAL_PARTS
+       bool "Baikal flash partition parser for ACPI"
+       depends on ARCH_BAIKAL && ACPI
+       help
+         This provides flash partition parser which derives the partition map
+         from the flash ACPI node.
index 23fa4de4016f1a6f323ff09470de6e6c0cf8b861..106afa8a3357166f157262d48a3b7a4213d6b39a 100644 (file)
@@ -15,3 +15,4 @@ obj-$(CONFIG_MTD_SERCOMM_PARTS)               += scpart.o
 obj-$(CONFIG_MTD_SHARPSL_PARTS)                += sharpslpart.o
 obj-$(CONFIG_MTD_REDBOOT_PARTS)                += redboot.o
 obj-$(CONFIG_MTD_QCOMSMEM_PARTS)       += qcomsmempart.o
+obj-$(CONFIG_MTD_BAIKAL_PARTS)         += baikal.o
diff --git a/drivers/mtd/parsers/baikal.c b/drivers/mtd/parsers/baikal.c
new file mode 100644 (file)
index 0000000..a9ae0ca
--- /dev/null
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Baikal flash partition driver for ACPI
+ *
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ * Author: Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include <linux/device/bus.h>
+#include <linux/mtd/partitions.h>
+#include <linux/spi/flash.h>
+#include <linux/spi/spi.h>
+
+#include <acpi/actypes.h>
+
+static int baikal_add_mtd_partitions(struct device *dev, void *unused)
+{
+       struct acpi_device *adev;
+       const union acpi_object *obj;
+       struct flash_platform_data *data;
+       struct mtd_partition *parts;
+       int nr_parts, i, ret;
+
+       if (dev) {
+               adev = ACPI_COMPANION(dev);
+               if (!adev)
+                       return 0;
+
+               if (!acpi_dev_get_property(adev, "baikal,partitions",
+                                          ACPI_TYPE_PACKAGE, &obj)) {
+                       if (!obj->package.count || obj->package.count % 3)
+                               return 0;
+                       nr_parts = obj->package.count / 3;
+
+                       data = kzalloc(sizeof(*data) + nr_parts * sizeof(*parts),
+                                      GFP_KERNEL);
+                       if (!data)
+                               return -ENOMEM;
+                       parts = (void *)data + sizeof(*data);
+
+                       for (i = 0; i < nr_parts; ++i) {
+                               if (obj->package.elements[3 * i].type != ACPI_TYPE_STRING ||
+                                   obj->package.elements[3 * i + 1].type != ACPI_TYPE_INTEGER ||
+                                   obj->package.elements[3 * i + 2].type != ACPI_TYPE_INTEGER) {
+                                       kfree(data);
+                                       return 0;
+                               }
+
+                               parts[i].name = kmemdup_nul(obj->package.elements[3 * i].string.pointer,
+                                                           obj->package.elements[3 * i].string.length,
+                                                           GFP_KERNEL);
+                               if (!parts[i].name) {
+                                       kfree(data);
+                                       return -ENOMEM;
+                               }
+
+                               parts[i].offset =
+                                       obj->package.elements[3 * i + 1].integer.value;
+                               parts[i].size =
+                                       obj->package.elements[3 * i + 2].integer.value;
+                       }
+
+                       data->parts = parts;
+                       data->nr_parts = nr_parts;
+                       dev->platform_data = data;
+
+                       ret = driver_set_override(dev, &to_spi_device(dev)->driver_override,
+                                                 "spi-nor", strlen("spi-nor"));
+                       if (ret) {
+                               dev->platform_data = NULL;
+                               kfree(data);
+                               return ret;
+                       }
+
+                       ret = device_reprobe(dev);
+                       if (ret) {
+                               dev->platform_data = NULL;
+                               kfree(data);
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int __init baikal_spi_partitions_init(void)
+{
+       return bus_for_each_dev(&spi_bus_type, NULL, NULL,
+                               baikal_add_mtd_partitions);
+}
+
+late_initcall(baikal_spi_partitions_init);
index ffaa2405525989b9ebde133a5f287a859bc217a7..f4bdf46aea2b08a584eb6988d00844e766ada265 100644 (file)
@@ -127,6 +127,10 @@ static const struct flash_info winbond_nor_parts[] = {
                .fixups = &w25q256_fixups },
        { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512)
                PARSE_SFDP },
+       { "w25q512jv", INFO(0xef7020, 0, 64 * 1024, 1024)
+               FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+               NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+                             SPI_NOR_QUAD_READ) },
        { "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512)
                NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
                              SPI_NOR_QUAD_READ) },
index e5c571fd232cc2f220d84e72339443c5176b272e..4ecb67d4fcff7b6301d180975f354a394ead5d85 100644 (file)
@@ -58,4 +58,13 @@ config MUX_MMIO
          To compile the driver as a module, choose M here: the module will
          be called mux-mmio.
 
+config MUX_BAIKAL
+       tristate "Baikal-S SoC low speed peripheral IO Multiplexer"
+       help
+         The driver allows to control three switches of the multiplexer
+         independently:
+         - 8 GPIOs or UART/SMBus/SMBus;
+         - 8 GPIOs or SPI;
+         - 16 GPIOs or eSPI.
+
 endmenu
index 6e9fa47daf56634d0a5ca505afab8cc5f1832f8e..af1454c869def320dd74f9c0311fcef0095bdf54 100644 (file)
@@ -8,9 +8,11 @@ mux-adg792a-objs               := adg792a.o
 mux-adgs1408-objs              := adgs1408.o
 mux-gpio-objs                  := gpio.o
 mux-mmio-objs                  := mmio.o
+mux-baikal-objs                        := baikal.o
 
 obj-$(CONFIG_MULTIPLEXER)      += mux-core.o
 obj-$(CONFIG_MUX_ADG792A)      += mux-adg792a.o
 obj-$(CONFIG_MUX_ADGS1408)     += mux-adgs1408.o
 obj-$(CONFIG_MUX_GPIO)         += mux-gpio.o
 obj-$(CONFIG_MUX_MMIO)         += mux-mmio.o
+obj-$(CONFIG_MUX_BAIKAL)       += mux-baikal.o
diff --git a/drivers/mux/baikal.c b/drivers/mux/baikal.c
new file mode 100644 (file)
index 0000000..b459e53
--- /dev/null
@@ -0,0 +1,703 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Multiplexer driver for Baikal-S SoC low speed peripheral devices
+ *
+ * Copyright (C) 2022-2023 Baikal Electronics, JSC
+ * Author: Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/mux/consumer.h>
+#include <linux/mux/driver.h>
+#include <linux/platform_device.h>
+
+#define BAIKAL_SMC_LSP_MUX             0x82000600
+#define BAIKAL_LSP_MUX_COUNT           3
+#define BAIKAL_LSP_MUX_STATE_COUNT     2
+#define BAIKAL_LSP_MUX_AS_IS           2
+
+static u8 uart_smbus = BAIKAL_LSP_MUX_AS_IS;
+module_param(uart_smbus, byte, 0);
+MODULE_PARM_DESC(uart_smbus, "UART/SMBus/SMBus. 0=Disable, 1=Enable");
+
+static u8 qspi = BAIKAL_LSP_MUX_AS_IS;
+module_param(qspi, byte, 0);
+MODULE_PARM_DESC(qspi, "QSPI. 0=Disable, 1=Enable");
+
+static u8 espi = BAIKAL_LSP_MUX_AS_IS;
+module_param(espi, byte, 0);
+MODULE_PARM_DESC(espi, "eSPI. 0=Disable, 1=Enable");
+
+static const u8 * const baikal_lsp_mux_init_value[3] = {
+       &uart_smbus,
+       &qspi,
+       &espi
+};
+
+struct baikal_lsp_mux_priv {
+       struct fwnode_handle **fwnode[BAIKAL_LSP_MUX_STATE_COUNT];
+       struct platform_device **plat_dev[BAIKAL_LSP_MUX_STATE_COUNT];
+       u8 count[BAIKAL_LSP_MUX_STATE_COUNT];
+       bool acpi_node_enabled[BAIKAL_LSP_MUX_STATE_COUNT];
+       struct mutex lock;
+       struct mux_control *mux;
+       struct platform_device *pdev;
+};
+
+static char *baikal_lsp_mux_property(unsigned int state)
+{
+       if (acpi_disabled)
+               return state ? "mux-state1" : "mux-state0";
+       else
+               return state ? "DEV1" : "DEV0";
+}
+
+static struct platform_device *
+baikal_lsp_mux_channel_create_device(struct fwnode_handle *fwnode,
+                                    struct device *parent)
+{
+       if (acpi_disabled) {
+               return of_platform_device_create(to_of_node(fwnode), NULL,
+                                                parent);
+       } else {
+               return acpi_create_platform_device(to_acpi_device_node(fwnode),
+                                                  NULL);
+       }
+}
+
+static void baikal_lsp_mux_channel_remove_device(struct platform_device *pdev)
+{
+       if (acpi_disabled) {
+               of_platform_device_destroy(&pdev->dev, NULL);
+       } else {
+               platform_device_unregister(pdev);
+       }
+}
+
+#ifdef CONFIG_ACPI
+static int baikal_lsp_mux_channel_enable_nodes_acpi(struct baikal_lsp_mux_priv *priv,
+                                                   unsigned int state)
+{
+       union acpi_object val = {
+               .type = ACPI_TYPE_INTEGER,
+               .integer.type = ACPI_TYPE_INTEGER,
+               .integer.value = state
+       };
+       struct acpi_object_list args = {
+               .count = 1,
+               .pointer = &val
+       };
+       struct acpi_device *adev =
+               to_acpi_device_node(priv->pdev->dev.fwnode);
+       struct device *ref_dev;
+       struct platform_device **pdev;
+       struct fwnode_handle **fwnode;
+       u64 is_enabled;
+       acpi_status status;
+       int i;
+
+       status = acpi_evaluate_integer(adev->handle,
+                                      state ? "STA1" : "STA0",
+                                      NULL,
+                                      &is_enabled);
+       if (ACPI_FAILURE(status)) {
+               dev_err(&adev->dev, "failed to get state for %s\n",
+                       baikal_lsp_mux_property(i));
+               return -ENODEV;
+       }
+
+       if (is_enabled) {
+               priv->acpi_node_enabled[state] = true;
+               return 0;
+       }
+
+       status = acpi_evaluate_object(adev->handle, "INIT", &args,
+                                     NULL);
+       if (ACPI_FAILURE(status)) {
+               dev_err(&adev->dev, "failed to enable %s devices\n",
+                       baikal_lsp_mux_property(i));
+               return -ENODEV;
+       }
+
+       priv->acpi_node_enabled[state] = true;
+
+       for (i = 0; i < priv->count[state]; ++i) {
+               fwnode = &priv->fwnode[state][i];
+               pdev = &priv->plat_dev[state][i];
+
+               if (!*fwnode)
+                       continue;
+
+               adev = to_acpi_device_node(*fwnode);
+               acpi_scan_lock_acquire();
+               if (adev)
+                       acpi_bus_scan(adev->handle);
+               acpi_scan_lock_release();
+
+               ref_dev = bus_find_device_by_fwnode(&platform_bus_type,
+                                                   *fwnode);
+               if (ref_dev)
+                       *pdev = to_platform_device(ref_dev);
+       }
+
+       /* Device nodes were created by this function */
+       return 1;
+}
+#else
+static inline int baikal_lsp_mux_channel_enable_nodes_acpi(struct baikal_lsp_mux_priv *priv,
+                                                          unsigned int state)
+{
+       return 0;
+}
+#endif
+
+static void baikal_lsp_mux_channel_remove_devices(struct baikal_lsp_mux_priv *priv,
+                                                 unsigned int state)
+{
+       struct platform_device **pdev;
+       unsigned int i;
+
+       if (state >= BAIKAL_LSP_MUX_STATE_COUNT)
+               return;
+
+       for (i = 0; i < priv->count[state]; ++i) {
+               pdev = &priv->plat_dev[state][i];
+
+               if (*pdev) {
+                       baikal_lsp_mux_channel_remove_device(*pdev);
+                       *pdev = NULL;
+               }
+       }
+}
+
+static int baikal_lsp_mux_channel_switch_devices(struct baikal_lsp_mux_priv *priv,
+                                                unsigned int state)
+{
+       struct device *parent = priv->mux->chip->dev.parent;
+       struct platform_device **pdev;
+       struct fwnode_handle **fwnode;
+       int i, ret = 0;
+
+       state = !!state;
+
+       mutex_lock(&priv->lock);
+
+       if (priv->mux->cached_state == state)
+               goto mux_unlock;
+
+       if (priv->mux->cached_state == MUX_IDLE_AS_IS) {
+               for (i = 0; i < BAIKAL_LSP_MUX_STATE_COUNT; ++i)
+                       baikal_lsp_mux_channel_remove_devices(priv, i);
+       } else {
+               baikal_lsp_mux_channel_remove_devices(priv, !state);
+       }
+
+       ret = mux_control_select(priv->mux, state);
+       if (ret)
+               goto mux_unlock;
+
+       ret = mux_control_deselect(priv->mux);
+       if (ret)
+               goto mux_unlock;
+
+       if (priv->mux->cached_state == MUX_IDLE_AS_IS) {
+               baikal_lsp_mux_channel_remove_devices(priv, state);
+               ret = -EPROTO;
+               goto mux_unlock;
+       }
+
+       if (!acpi_disabled && !priv->acpi_node_enabled[state]) {
+               ret = baikal_lsp_mux_channel_enable_nodes_acpi(priv, state);
+
+               /* Check if device nodes were created */
+               if (ret == 1) {
+                       ret = 0;
+                       goto mux_unlock;
+               }
+       }
+
+       if (!ret) {
+               for (i = 0; i < priv->count[state]; ++i) {
+                       fwnode = &priv->fwnode[state][i];
+                       pdev = &priv->plat_dev[state][i];
+
+                       if (!*fwnode)
+                               continue;
+
+                       if (!*pdev) {
+                               *pdev = baikal_lsp_mux_channel_create_device(*fwnode,
+                                                                            parent);
+                               if (IS_ERR_OR_NULL(*pdev)) {
+                                       *pdev = NULL;
+                               }
+                       } else {
+                               baikal_lsp_mux_channel_remove_device(*pdev);
+                       }
+               }
+       }
+
+mux_unlock:
+       mutex_unlock(&priv->lock);
+       return ret;
+}
+
+static ssize_t baikal_lsp_mux_channel_store(struct device *dev,
+                                           struct device_attribute *attr,
+                                           const char *buf, size_t count)
+{
+       struct baikal_lsp_mux_priv *priv = dev_get_drvdata(dev);
+       unsigned int n = 0;
+       int ret;
+
+       ret = kstrtouint(buf, 10, &n);
+       if (ret)
+               return ret;
+
+       ret = baikal_lsp_mux_channel_switch_devices(priv, n);
+
+       ret = ret ? ret : count;
+
+       return ret;
+}
+
+static ssize_t baikal_lsp_mux_channel_show(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       struct baikal_lsp_mux_priv *priv = dev_get_drvdata(dev);
+       return sprintf(buf, "%i\n", priv->mux->cached_state);
+}
+
+static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, baikal_lsp_mux_channel_show,
+                  baikal_lsp_mux_channel_store);
+
+static struct attribute *baikal_lsp_mux_channel_attrs[] = {
+       &dev_attr_enabled.attr,
+       NULL
+};
+
+static const struct attribute_group baikal_lsp_mux_channel_attr_group = {
+       .attrs = baikal_lsp_mux_channel_attrs
+};
+
+static struct baikal_lsp_mux_priv *
+baikal_lsp_mux_channel_get_of_data(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *mux_dev;
+       struct baikal_lsp_mux_priv *priv;
+       int count[BAIKAL_LSP_MUX_STATE_COUNT];
+       int i, j;
+
+       if (!np)
+               return ERR_PTR(-ENODEV);
+
+       for (i = 0; i < BAIKAL_LSP_MUX_STATE_COUNT; ++i) {
+               count[i] = of_count_phandle_with_args(np,
+                                             baikal_lsp_mux_property(i), NULL);
+               if (count[i] < 1) {
+                       dev_err(dev, "invalid number of phandles in '%s' property\n",
+                               baikal_lsp_mux_property(i));
+                       return ERR_PTR(-EINVAL);
+               }
+       }
+
+       priv = devm_kzalloc(dev, sizeof(*priv) +
+                                2 * (count[0] + count[1]) * sizeof(void *),
+                           GFP_KERNEL);
+       if (!priv)
+               return ERR_PTR(-ENOMEM);
+
+       priv->count[0] = count[0];
+       priv->count[1] = count[1];
+       priv->fwnode[0] = (struct fwnode_handle **)(priv + 1);
+       priv->fwnode[1] = priv->fwnode[0] + priv->count[0];
+       priv->plat_dev[0] = (struct platform_device **)(priv->fwnode[1] +
+                           priv->count[1]);
+       priv->plat_dev[1] = priv->plat_dev[0] + priv->count[0];
+
+       priv->mux = mux_control_get(dev, NULL);
+       if (IS_ERR(priv->mux))
+               return ERR_CAST(priv->mux);
+
+       for (i = 0; i < BAIKAL_LSP_MUX_STATE_COUNT; ++i) {
+               for (j = 0; j < priv->count[i]; ++j) {
+                       mux_dev = of_parse_phandle(np,
+                                                  baikal_lsp_mux_property(i),
+                                                  j);
+                       if (!mux_dev) {
+                               dev_err(dev, "invalid phandle%i in '%s' property\n",
+                                       j, baikal_lsp_mux_property(i));
+                               return ERR_PTR(-EINVAL);
+                       }
+
+                       priv->fwnode[i][j] = of_fwnode_handle(mux_dev);
+               }
+       }
+
+       return priv;
+}
+
+#ifdef CONFIG_ACPI
+static struct mux_control *
+baikal_lsp_mux_channel_get_mux_acpi(struct acpi_device *adev)
+{
+       struct device *dev = &adev->dev, *mux_chip_dev;
+       struct acpi_device *mux;
+       struct mux_chip *mux_chip;
+       union acpi_object *package = NULL;
+       union acpi_object *element = NULL;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       acpi_status status = AE_OK;
+       unsigned int id;
+       int ret;
+
+       status = acpi_evaluate_object_typed(adev->handle, "MUX", NULL,
+                                           &buffer, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "failed to get MUX data\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       package = buffer.pointer;
+       if (package->package.count != 2) {
+               dev_err(dev, "invalid MUX data\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       element = &(package->package.elements[0]);
+       if (element->type != ACPI_TYPE_LOCAL_REFERENCE ||
+           !element->reference.handle) {
+               dev_err(dev, "invalid MUX reference\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       mux = acpi_fetch_acpi_dev(element->reference.handle);
+       if (!mux) {
+               dev_err(dev, "failed to process MUX reference\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       element = &(package->package.elements[1]);
+       if (element->type != ACPI_TYPE_INTEGER) {
+               dev_err(dev, "failed to get MUX index\n");
+               ret = -EINVAL;
+               goto err;
+       }
+       id = element->integer.value;
+
+       mux_chip_dev = bus_find_device_by_fwnode(&platform_bus_type,
+                                                acpi_fwnode_handle(mux));
+       if (!mux_chip_dev) {
+               dev_err(dev, "failed to get mux chip device\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       mux_chip = dev_get_drvdata(mux_chip_dev);
+       if (!mux_chip) {
+               dev_err(dev, "failed to get mux chip\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       if (id >= mux_chip->controllers) {
+               dev_err(dev, "bad mux controller %u\n", id);
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ret = 0;
+
+err:
+       acpi_os_free(buffer.pointer);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return &mux_chip->mux[id];
+}
+
+static struct baikal_lsp_mux_priv *
+baikal_lsp_mux_channel_get_acpi_data(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct acpi_device *adev = ACPI_COMPANION(dev);
+       struct acpi_device *mux_dev;
+       struct baikal_lsp_mux_priv *priv;
+       int count[BAIKAL_LSP_MUX_STATE_COUNT];
+       struct acpi_buffer buffer[BAIKAL_LSP_MUX_STATE_COUNT];
+       union acpi_object *package = NULL;
+       union acpi_object *element = NULL;
+       acpi_status status = AE_OK;
+       int i, j, ret;
+
+       if (!adev)
+               return ERR_PTR(-ENODEV);
+
+       for (i = 0; i < BAIKAL_LSP_MUX_STATE_COUNT; ++i) {
+               buffer[i].length = ACPI_ALLOCATE_BUFFER;
+               buffer[i].pointer = NULL;
+               status = acpi_evaluate_object_typed(adev->handle,
+                                                   baikal_lsp_mux_property(i),
+                                                   NULL,
+                                                   &buffer[i],
+                                                   ACPI_TYPE_PACKAGE);
+               if (ACPI_FAILURE(status)) {
+                       dev_err(dev, "failed to get %s data\n",
+                               baikal_lsp_mux_property(i));
+                       return ERR_PTR(-ENODEV);
+               }
+
+               package = buffer[i].pointer;
+               count[i] = package->package.count;
+               if (count[i] < 1) {
+                       dev_err(dev, "invalid number of references in %s\n",
+                               baikal_lsp_mux_property(i));
+                       ret = -EINVAL;
+                       goto err;
+               }
+       }
+
+       priv = devm_kzalloc(dev, sizeof(*priv) +
+                                2 * (count[0] + count[1]) * sizeof(void *),
+                           GFP_KERNEL);
+       if (!priv) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       priv->count[0] = count[0];
+       priv->count[1] = count[1];
+       priv->fwnode[0] = (struct fwnode_handle **)(priv + 1);
+       priv->fwnode[1] = priv->fwnode[0] + priv->count[0];
+       priv->plat_dev[0] = (struct platform_device **)(priv->fwnode[1] +
+                           priv->count[1]);
+       priv->plat_dev[1] = priv->plat_dev[0] + priv->count[0];
+
+       priv->mux = baikal_lsp_mux_channel_get_mux_acpi(adev);
+       if (IS_ERR(priv->mux)) {
+               ret = PTR_ERR(priv->mux);
+               goto err;
+       }
+
+       for (i = 0; i < BAIKAL_LSP_MUX_STATE_COUNT; ++i) {
+               package = buffer[i].pointer;
+               for (j = 0; j < priv->count[i]; ++j) {
+                       element = &(package->package.elements[j]);
+
+                       if (element->type != ACPI_TYPE_LOCAL_REFERENCE ||
+                           !element->reference.handle) {
+                               dev_err(dev, "invalid reference%i in %s\n", j,
+                                       baikal_lsp_mux_property(i));
+                               ret = -EINVAL;
+                               goto err;
+                       }
+
+                       mux_dev = acpi_fetch_acpi_dev(element->reference.handle);
+                       if (!mux_dev) {
+                               dev_err(dev, "failed to process reference%i in %s\n",
+                                       j, baikal_lsp_mux_property(i));
+                               ret = -EINVAL;
+                               goto err;
+                       }
+
+                       priv->fwnode[i][j] = acpi_fwnode_handle(mux_dev);
+               }
+       }
+
+       ret = 0;
+
+err:
+       for (i = 0; i < BAIKAL_LSP_MUX_STATE_COUNT; ++i)
+               acpi_os_free(buffer[i].pointer);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return priv;
+}
+#else /* CONFIG_ACPI */
+static inline struct baikal_lsp_mux_priv *
+baikal_lsp_mux_channel_get_acpi_data(struct platform_device *pdev)
+{
+       return ERR_PTR(-ENODEV);
+}
+#endif
+
+static int baikal_lsp_mux_channel_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct baikal_lsp_mux_priv *priv;
+       unsigned int id;
+       int ret;
+
+       if (acpi_disabled)
+               priv = baikal_lsp_mux_channel_get_of_data(pdev);
+       else
+               priv = baikal_lsp_mux_channel_get_acpi_data(pdev);
+
+       if (IS_ERR(priv)) {
+               dev_err(dev, "failed to get mux data\n");
+               return PTR_ERR(priv);
+       }
+
+       priv->pdev = pdev;
+       mutex_init(&priv->lock);
+       platform_set_drvdata(pdev, priv);
+
+       id = mux_control_get_index(priv->mux);
+       ret = baikal_lsp_mux_channel_switch_devices(priv,
+                                               *baikal_lsp_mux_init_value[id]);
+       if (ret) {
+               dev_err(dev, "failed to initialize mux devices: %i\n", ret);
+               return ret;
+       }
+
+       ret = sysfs_create_group(&dev->kobj, &baikal_lsp_mux_channel_attr_group);
+       if (ret) {
+               dev_err(dev, "failed to create sysfs group: %i\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id baikal_lsp_mux_channel_of_match[] = {
+       { .compatible = "baikal,bs1000-lsp-mux-channel" },
+       {}
+};
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id baikal_lsp_mux_channel_acpi_match[] = {
+       { "BKLE0003" },
+       {}
+};
+
+MODULE_DEVICE_TABLE(acpi, baikal_lsp_mux_channel_acpi_match);
+#endif
+
+static struct platform_driver baikal_lsp_mux_channel_driver = {
+       .driver = {
+               .name = "baikal-lsp-mux-channel",
+               .of_match_table = baikal_lsp_mux_channel_of_match,
+               .acpi_match_table = ACPI_PTR(baikal_lsp_mux_channel_acpi_match),
+               .suppress_bind_attrs = true
+       },
+       .probe = baikal_lsp_mux_channel_probe
+};
+
+static DEFINE_MUTEX(baikal_lsp_mux_lock);
+
+static int baikal_lsp_mux_set(struct mux_control *mux, int state)
+{
+       struct arm_smccc_res res;
+
+       mutex_lock(&baikal_lsp_mux_lock);
+       arm_smccc_smc(BAIKAL_SMC_LSP_MUX,
+                     state << mux_control_get_index(mux) & 0x7,
+                     0, 0, 0, 0, 0, 0, &res);
+       mutex_unlock(&baikal_lsp_mux_lock);
+
+       return res.a0;
+}
+
+static const struct mux_control_ops baikal_lsp_mux_ops = {
+       .set = baikal_lsp_mux_set
+};
+
+static int baikal_lsp_mux_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct mux_chip *mux_chip;
+       int i, ret;
+
+       mux_chip = devm_mux_chip_alloc(dev, BAIKAL_LSP_MUX_COUNT, 0);
+       if (IS_ERR(mux_chip)) {
+               dev_err(dev, "failed to allocate mux chip\n");
+               return PTR_ERR(mux_chip);
+       }
+
+       mux_chip->ops = &baikal_lsp_mux_ops;
+
+       if (uart_smbus == BAIKAL_LSP_MUX_AS_IS) {
+               uart_smbus = !!device_property_present(dev, "uart-smbus-enabled");
+       }
+       baikal_lsp_mux_set(&mux_chip->mux[0], uart_smbus);
+
+       if (qspi == BAIKAL_LSP_MUX_AS_IS) {
+               qspi = !!device_property_present(dev, "qspi-enabled");
+       }
+       baikal_lsp_mux_set(&mux_chip->mux[1], qspi);
+
+       if (espi == BAIKAL_LSP_MUX_AS_IS) {
+               espi = !!device_property_present(dev, "espi-enabled");
+       }
+       baikal_lsp_mux_set(&mux_chip->mux[2], espi);
+
+       for (i = 0; i < mux_chip->controllers; ++i)
+               mux_chip->mux[i].states = BAIKAL_LSP_MUX_STATE_COUNT;
+
+       ret = devm_mux_chip_register(dev, mux_chip);
+       if (ret) {
+               dev_err(dev, "failed to register mux chip\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, mux_chip);
+
+       return 0;
+}
+
+static const struct of_device_id baikal_lsp_mux_of_match[] = {
+       { .compatible = "baikal,bs1000-lsp-mux" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, baikal_lsp_mux_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id baikal_lsp_mux_acpi_match[] = {
+       { "BKLE0002" },
+       {}
+};
+
+MODULE_DEVICE_TABLE(acpi, baikal_lsp_mux_acpi_match);
+#endif
+
+static struct platform_driver baikal_lsp_mux_driver = {
+       .driver = {
+               .name = "baikal-lsp-mux",
+               .of_match_table = baikal_lsp_mux_of_match,
+               .acpi_match_table = ACPI_PTR(baikal_lsp_mux_acpi_match),
+               .suppress_bind_attrs = true
+       },
+       .probe = baikal_lsp_mux_probe
+};
+
+static struct platform_driver * const baikal_lsp_mux_drivers[] = {
+       &baikal_lsp_mux_driver,
+       &baikal_lsp_mux_channel_driver
+};
+
+static int __init baikal_lsp_mux_init(void)
+{
+       return platform_register_drivers(baikal_lsp_mux_drivers,
+                                        ARRAY_SIZE(baikal_lsp_mux_drivers));
+}
+module_init(baikal_lsp_mux_init);
+
+static void __exit baikal_lsp_mux_exit(void)
+{
+       platform_unregister_drivers(baikal_lsp_mux_drivers,
+                                   ARRAY_SIZE(baikal_lsp_mux_drivers));
+}
+module_exit(baikal_lsp_mux_exit);
+
+MODULE_AUTHOR("Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-S SoC low speed peripheral mux driver");
+MODULE_LICENSE("GPL v2");
index 31ff35174034297ab68cc6eeae6b9d063812f1f4..1161bf74bdf88abc999e19a678731519a5a0cbb8 100644 (file)
@@ -66,6 +66,14 @@ config DWMAC_ANARION
 
          This selects the Anarion SoC glue layer support for the stmmac driver.
 
+config DWMAC_BAIKAL
+       tristate "Baikal Electronics DWMAC support"
+       depends on OF
+       help
+         Support for Baikal Electronics DWMAC Ethernet.
+
+         This selects the Baikal SoC glue layer support for the stmmac driver.
+
 config DWMAC_INGENIC
        tristate "Ingenic MAC support"
        default MACH_INGENIC
index d4e12e9ace4ffe3ab45267fc610148e877a9a9c2..ad138062e199e1d1142a431a753c16bc75f54941 100644 (file)
@@ -14,6 +14,7 @@ stmmac-$(CONFIG_STMMAC_SELFTESTS) += stmmac_selftests.o
 # Ordering matters. Generic driver must be last.
 obj-$(CONFIG_STMMAC_PLATFORM)  += stmmac-platform.o
 obj-$(CONFIG_DWMAC_ANARION)    += dwmac-anarion.o
+obj-$(CONFIG_DWMAC_BAIKAL)     += dwmac-baikal.o
 obj-$(CONFIG_DWMAC_INGENIC)    += dwmac-ingenic.o
 obj-$(CONFIG_DWMAC_IPQ806X)    += dwmac-ipq806x.o
 obj-$(CONFIG_DWMAC_LPC18XX)    += dwmac-lpc18xx.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-baikal.c
new file mode 100644 (file)
index 0000000..b823281
--- /dev/null
@@ -0,0 +1,532 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Baikal Electronics DWMAC specific glue layer
+ *
+ * Copyright (C) 2015-2022 Baikal Electronics, JSC
+ * Authors: Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru>
+ *          Alexey Sheplyakov <asheplyakov@altlinux.org>
+ */
+
+#include <linux/acpi.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "stmmac.h"
+#include "stmmac_platform.h"
+#include "common.h"
+#include "dwmac_dma.h"
+#include "dwmac1000_dma.h"
+
+#define MAC_GPIO       0x00e0          /* GPIO register */
+#define MAC_GPIO_GPO   (1 << 8)        /* Output port */
+
+#define BAIKAL_SMC_GMAC_DIV2_ENABLE    0x82000500
+#define BAIKAL_SMC_GMAC_DIV2_DISABLE   0x82000501
+
+struct baikal_gmac {
+       struct device   *dev;
+       uint64_t        base;
+       struct clk      *axi_clk;
+       struct clk      *tx2_clk;
+       int             has_aux_div2;
+};
+
+static int baikal_gmac_dma_reset(void __iomem *ioaddr)
+{
+       int err;
+       u32 value;
+
+       /* DMA SW reset */
+       value = readl(ioaddr + DMA_BUS_MODE);
+       value |= DMA_BUS_MODE_SFT_RESET;
+       writel(value, ioaddr + DMA_BUS_MODE);
+
+       /* Software DMA reset also resets MAC, so GP_OUT is set to zero.
+        * Which resets PHY as a side effect (if GP_OUT is connected directly
+        * to PHY reset).
+        * TODO: read the PHY reset duration from the device tree.
+        * Meanwhile use 100 milliseconds which seems to be enough for
+        * most PHYs
+        */
+       usleep_range(100000, 120000);
+
+       /* Clear PHY reset */
+       value = readl(ioaddr + MAC_GPIO);
+       value |= MAC_GPIO_GPO;
+       writel(value, ioaddr + MAC_GPIO);
+
+       /* Many PHYs need ~100 milliseconds to calm down after PHY reset
+        * has been cleared. And check for DMA reset below might return
+        * much earlier (i.e. in ~20 milliseconds). As a result reading
+        * PHY registers (after this function returns) might return garbage.
+        * Wait a bit to avoid the problem.
+        */
+       usleep_range(100000, 150000);
+
+       err = readl_poll_timeout(ioaddr + DMA_BUS_MODE, value,
+                                !(value & DMA_BUS_MODE_SFT_RESET),
+                                10000, 1000000);
+       if (err) {
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static const struct stmmac_dma_ops baikal_gmac_dma_ops = {
+       .reset = baikal_gmac_dma_reset,
+       .init = dwmac1000_dma_init,
+       .init_rx_chan = dwmac1000_dma_init_rx,
+       .init_tx_chan = dwmac1000_dma_init_tx,
+       .axi = dwmac1000_dma_axi,
+       .dump_regs = dwmac1000_dump_dma_regs,
+       .dma_rx_mode = dwmac1000_dma_operation_mode_rx,
+       .dma_tx_mode = dwmac1000_dma_operation_mode_tx,
+       .enable_dma_transmission = dwmac_enable_dma_transmission,
+       .enable_dma_irq = dwmac_enable_dma_irq,
+       .disable_dma_irq = dwmac_disable_dma_irq,
+       .start_tx = dwmac_dma_start_tx,
+       .stop_tx = dwmac_dma_stop_tx,
+       .start_rx = dwmac_dma_start_rx,
+       .stop_rx = dwmac_dma_stop_rx,
+       .dma_interrupt = dwmac_dma_interrupt,
+       .get_hw_feature = dwmac1000_get_hw_feature,
+       .rx_watchdog = dwmac1000_rx_watchdog
+};
+
+static struct mac_device_info *baikal_gmac_setup(void *ppriv)
+{
+       struct mac_device_info *mac, *old_mac;
+       struct stmmac_priv *priv = ppriv;
+       struct gpio_desc *reset_gpio;
+       int err;
+       u32 value;
+
+       mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
+       if (!mac) {
+               return NULL;
+       }
+
+       /* Clear PHY reset */
+       value = readl(priv->ioaddr + MAC_GPIO);
+       value |= MAC_GPIO_GPO;
+       writel(value, priv->ioaddr + MAC_GPIO);
+       reset_gpio = devm_gpiod_get_optional(priv->device,
+                                            "snps,reset",
+                                            GPIOD_OUT_LOW);
+
+       err = readl_poll_timeout(priv->ioaddr + DMA_BUS_MODE, value,
+                                !(value & DMA_BUS_MODE_SFT_RESET),
+                                10000, 1000000);
+
+       if (reset_gpio != NULL) {
+               devm_gpiod_put(priv->device, reset_gpio);
+       }
+
+       if (err) {
+               dev_err(priv->device, "SW reset is not cleared: error %d", err);
+               return NULL;
+       }
+
+       mac->dma = &baikal_gmac_dma_ops;
+       old_mac = priv->hw;
+       priv->hw = mac;
+       err = dwmac1000_setup(priv);
+       priv->hw = old_mac;
+       if (err) {
+               dev_err(priv->device,
+                       "%s: dwmac1000_setup failed with error %d",
+                       __func__, err);
+               return NULL;
+       }
+
+       return mac;
+}
+
+static void baikal_gmac_fix_mac_speed(void *priv, unsigned int speed)
+{
+       struct arm_smccc_res res;
+       struct baikal_gmac *gmac = priv;
+       unsigned long tx2_clk_freq = 0;
+
+       switch (speed) {
+       case SPEED_1000:
+               tx2_clk_freq = 250000000;
+               if (gmac->has_aux_div2) {
+                       arm_smccc_smc(BAIKAL_SMC_GMAC_DIV2_DISABLE,
+                                     gmac->base, 0, 0, 0, 0, 0, 0, &res);
+               }
+               break;
+       case SPEED_100:
+               tx2_clk_freq = 50000000;
+               if (gmac->has_aux_div2) {
+                       arm_smccc_smc(BAIKAL_SMC_GMAC_DIV2_DISABLE,
+                                     gmac->base, 0, 0, 0, 0, 0, 0, &res);
+               }
+               break;
+       case SPEED_10:
+               tx2_clk_freq = 5000000;
+               if (gmac->has_aux_div2) {
+                       tx2_clk_freq *= 2;
+                       arm_smccc_smc(BAIKAL_SMC_GMAC_DIV2_ENABLE,
+                                     gmac->base, 0, 0, 0, 0, 0, 0, &res);
+               }
+               break;
+       }
+
+       if (gmac->tx2_clk != NULL && tx2_clk_freq) {
+               clk_set_rate(gmac->tx2_clk, tx2_clk_freq);
+       }
+}
+
+#ifdef CONFIG_ACPI
+static struct plat_stmmacenet_data *baikal_stmmac_probe_config(struct device *dev,
+                                                               const char **mac)
+{
+       struct plat_stmmacenet_data *plat_dat;
+       u8 nvmem_mac[ETH_ALEN];
+       int ret;
+       bool is_fixed_clk = false;
+
+       plat_dat = devm_kzalloc(dev, sizeof(*plat_dat), GFP_KERNEL);
+       if (!plat_dat) {
+               return ERR_PTR(-ENOMEM);
+       }
+
+       ret = nvmem_get_mac_address(dev, &nvmem_mac);
+       if (ret) {
+               if (ret == -EPROBE_DEFER) {
+                       return ERR_PTR(ret);
+               }
+
+               *mac = NULL;
+       } else {
+               *mac = devm_kmemdup(dev, nvmem_mac, ETH_ALEN, GFP_KERNEL);
+       }
+
+       plat_dat->phy_interface = device_get_phy_mode(dev);
+       if (plat_dat->phy_interface < 0) {
+               return NULL;
+       }
+
+       plat_dat->interface = plat_dat->phy_interface;
+
+       if (device_property_read_u32(dev, "max-speed", &plat_dat->max_speed)) {
+               plat_dat->max_speed = -1;
+       }
+
+       plat_dat->bus_id = ACPI_COMPANION(dev)->pnp.instance_no;
+
+       ret = device_property_read_u32(dev, "reg", &plat_dat->phy_addr);
+       if (ret) {
+               dev_err(dev, "couldn't get reg property\n");
+               return ERR_PTR(ret);
+       }
+
+       if (plat_dat->phy_addr >= PHY_MAX_ADDR) {
+               dev_err(dev, "PHY address %i is too large\n",
+                               plat_dat->phy_addr);
+               return ERR_PTR(-EINVAL);
+       }
+
+       plat_dat->mdio_bus_data = devm_kzalloc(dev,
+                                               sizeof(*plat_dat->mdio_bus_data),
+                                               GFP_KERNEL);
+       if (!plat_dat->mdio_bus_data) {
+               return ERR_PTR(-ENOMEM);
+       }
+
+       plat_dat->mdio_bus_data->needs_reset = true;
+       plat_dat->maxmtu = JUMBO_LEN;
+       plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
+       plat_dat->unicast_filter_entries = 1;
+       plat_dat->bugged_jumbo = 1; /* TODO: is it really required? */
+
+       plat_dat->dma_cfg = devm_kzalloc(dev,
+                                       sizeof(*plat_dat->dma_cfg),
+                                       GFP_KERNEL);
+       if (!plat_dat->dma_cfg) {
+               return ERR_PTR(-ENOMEM);
+       }
+
+       plat_dat->dma_cfg->pbl = DEFAULT_DMA_PBL;
+       device_property_read_u32(dev, "snps,txpbl", &plat_dat->dma_cfg->txpbl);
+       device_property_read_u32(dev, "snps,rxpbl", &plat_dat->dma_cfg->rxpbl);
+       plat_dat->dma_cfg->fixed_burst = device_property_read_bool(dev,
+                                                       "snps,fixed-burst");
+
+       plat_dat->axi = devm_kzalloc(dev, sizeof(*plat_dat->axi), GFP_KERNEL);
+       if (!plat_dat->axi) {
+               return ERR_PTR(-ENOMEM);
+       }
+
+       device_property_read_u32_array(dev, "snps,blen",
+                                       plat_dat->axi->axi_blen, AXI_BLEN);
+
+       plat_dat->rx_queues_to_use = 1;
+       plat_dat->tx_queues_to_use = 1;
+       plat_dat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB;
+       plat_dat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB;
+
+       if (device_property_read_u32(dev, "stmmac-clk", &plat_dat->clk_ptp_rate)) {
+               plat_dat->clk_ptp_rate = 50000000;
+       }
+
+       plat_dat->stmmac_clk = devm_clk_get(dev, STMMAC_RESOURCE_NAME);
+       if (IS_ERR(plat_dat->stmmac_clk)) {
+               if (!plat_dat->clk_ptp_rate) {
+                       dev_err(dev, "stmmaceth clock and 'stmmac-clk' property are missed simultaneously\n");
+                       return ERR_PTR(-EINVAL);
+               }
+
+               plat_dat->stmmac_clk = clk_register_fixed_rate(NULL,
+                                                       dev_name(dev), NULL, 0,
+                                                       plat_dat->clk_ptp_rate);
+               if (IS_ERR(plat_dat->stmmac_clk))
+                       return ERR_CAST(plat_dat->stmmac_clk);
+
+               is_fixed_clk = true;
+       } else {
+               if (!plat_dat->clk_ptp_rate)
+                       plat_dat->clk_ptp_rate = clk_get_rate(plat_dat->stmmac_clk);
+       }
+
+       plat_dat->clk_ptp_ref = devm_clk_get(dev, "ptp_ref");
+       if (IS_ERR(plat_dat->clk_ptp_ref)) {
+               plat_dat->clk_ptp_ref = NULL;
+       } else {
+               plat_dat->clk_ptp_rate = clk_get_rate(plat_dat->clk_ptp_ref);
+       }
+
+       clk_prepare_enable(plat_dat->stmmac_clk);
+
+       plat_dat->stmmac_rst = devm_reset_control_get(dev,
+                                                     STMMAC_RESOURCE_NAME);
+       if (IS_ERR(plat_dat->stmmac_rst))
+               plat_dat->stmmac_rst = NULL;
+
+       plat_dat->mdio_bus_data->phy_mask = ~0;
+
+       if (device_get_child_node_count(dev) != 1) {
+               clk_disable_unprepare(plat_dat->stmmac_clk);
+               if (is_fixed_clk)
+                       clk_unregister_fixed_rate(plat_dat->stmmac_clk);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return plat_dat;
+}
+
+static int baikal_add_mdio_phy(struct device *dev)
+{
+       struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(dev));
+       struct fwnode_handle *fwnode = device_get_next_child_node(dev, NULL);
+       struct phy_device *phy;
+       int ret;
+
+       phy = get_phy_device(priv->mii, priv->plat->phy_addr, 0);
+       if (IS_ERR(phy)) {
+               return PTR_ERR(phy);
+       }
+
+       phy->irq = priv->mii->irq[priv->plat->phy_addr];
+       phy->mdio.dev.fwnode = fwnode;
+
+       ret = phy_device_register(phy);
+       if (ret) {
+               phy_device_free(phy);
+               return ret;
+       }
+
+       return 0;
+}
+#else
+static struct plat_stmmacenet_data *baikal_stmmac_probe_config(struct device *dev,
+                                                              const char **mac)
+{
+       return NULL;
+}
+
+static int baikal_add_mdio_phy(struct device *dev)
+{
+       return 0;
+}
+#endif
+
+static int baikal_gmac_probe(struct platform_device *pdev)
+{
+       struct plat_stmmacenet_data *plat_dat;
+       struct stmmac_resources stmmac_res;
+       struct resource *res;
+       struct baikal_gmac *gmac;
+       struct device_node *dn = NULL;
+       const char *str = NULL;
+       int ret;
+
+       if (acpi_disabled) {
+               ret = stmmac_get_platform_resources(pdev, &stmmac_res);
+               if (ret) {
+                       return ret;
+               }
+       } else {
+               memset(&stmmac_res, 0, sizeof(stmmac_res));
+               stmmac_res.irq = platform_get_irq(pdev, 0);
+               if (stmmac_res.irq < 0) {
+                       return stmmac_res.irq;
+               }
+
+               stmmac_res.wol_irq = stmmac_res.irq;
+               stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0);
+               if (IS_ERR(stmmac_res.addr)) {
+                       return PTR_ERR(stmmac_res.addr);
+               }
+       }
+
+       ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+       if (ret) {
+               dev_warn(&pdev->dev, "no suitable DMA available\n");
+               return ret;
+       }
+
+       if (pdev->dev.of_node) {
+               plat_dat = stmmac_probe_config_dt(pdev, (u8 *)&stmmac_res.mac);
+               if (IS_ERR(plat_dat)) {
+                       dev_err(&pdev->dev, "dt configuration failed\n");
+                       return PTR_ERR(plat_dat);
+               }
+       } else if (!acpi_disabled) {
+               plat_dat = baikal_stmmac_probe_config(&pdev->dev,
+                                                       (const char **)&stmmac_res.mac);
+               if (IS_ERR(plat_dat)) {
+                       dev_err(&pdev->dev, "acpi configuration failed\n");
+                       return PTR_ERR(plat_dat);
+               }
+
+               dn = kzalloc(sizeof(struct device_node), GFP_KERNEL);
+               if (!dn) {
+                       ret = -ENOMEM;
+                       goto err_remove_config_dt;
+               }
+
+               plat_dat->phy_node = dn;
+       } else {
+               plat_dat = dev_get_platdata(&pdev->dev);
+               if (!plat_dat) {
+                       dev_err(&pdev->dev, "no platform data provided\n");
+                       return -EINVAL;
+               }
+
+               /* Set default value for multicast hash bins */
+               plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
+
+               /* Set default value for unicast filter entries */
+               plat_dat->unicast_filter_entries = 1;
+       }
+
+       gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL);
+       if (!gmac) {
+               ret = -ENOMEM;
+               goto err_remove_config_dt;
+       }
+
+       gmac->dev = &pdev->dev;
+       gmac->tx2_clk = devm_clk_get(gmac->dev, "tx2_clk");
+       if (IS_ERR(gmac->tx2_clk)) {
+               dev_warn(&pdev->dev, "couldn't get TX2 clock\n");
+               gmac->tx2_clk = NULL;
+       }
+
+       gmac->axi_clk = devm_clk_get(gmac->dev, "axi_clk");
+       if (IS_ERR(gmac->axi_clk)) {
+               dev_warn(&pdev->dev, "couldn't get AXI clock\n");
+               gmac->axi_clk = NULL;
+       } else {
+               clk_set_rate(gmac->axi_clk, 300000000);
+       }
+
+       if (!acpi_disabled) {
+               device_property_read_string(&pdev->dev, "compatible", &str);
+       }
+
+       if ((gmac->dev->of_node &&
+            of_device_is_compatible(gmac->dev->of_node, "baikal,bs1000-gmac"))
+           || (str && strcasecmp(str, "baikal,bs1000-gmac") == 0)) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               gmac->base = res->start;
+               gmac->has_aux_div2 = 1;
+       } else {
+               gmac->has_aux_div2 = 0;
+       }
+
+       plat_dat->fix_mac_speed = baikal_gmac_fix_mac_speed;
+       plat_dat->bsp_priv = gmac;
+       plat_dat->has_gmac = 1;
+       plat_dat->bugged_jumbo = 1; /* TODO: is it really required? */
+       plat_dat->setup = baikal_gmac_setup;
+
+       ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+       if (ret) {
+               goto err_remove_config_dt;
+       }
+
+       if (!acpi_disabled) {
+               ret = baikal_add_mdio_phy(&pdev->dev);
+               if (ret) {
+                       goto err_remove_config_dt;
+               }
+       }
+
+       kfree(dn);
+       return 0;
+
+err_remove_config_dt:
+       stmmac_remove_config_dt(pdev, plat_dat);
+
+       if (!acpi_disabled) {
+               clk_disable_unprepare(plat_dat->stmmac_clk);
+               clk_unregister_fixed_rate(plat_dat->stmmac_clk);
+       }
+
+       kfree(dn);
+       return ret;
+}
+
+static int baikal_gmac_remove(struct platform_device *pdev)
+{
+       struct stmmac_priv *priv = netdev_priv(dev_get_drvdata(&pdev->dev));
+
+       if (!acpi_disabled) {
+               clk_disable_unprepare(priv->plat->stmmac_clk);
+               clk_unregister_fixed_rate(priv->plat->stmmac_clk);
+               priv->plat->stmmac_clk = NULL;
+       }
+
+       return stmmac_pltfr_remove(pdev);
+}
+
+static const struct of_device_id baikal_gmac_dwmac_match[] = {
+       { .compatible = "baikal,bm1000-gmac" },
+       { .compatible = "baikal,bs1000-gmac" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, baikal_gmac_dwmac_match);
+
+static struct platform_driver baikal_gmac_dwmac_driver = {
+       .probe  = baikal_gmac_probe,
+       .remove = baikal_gmac_remove,
+       .driver = {
+               .name = "baikal-gmac-dwmac",
+               .pm = &stmmac_pltfr_pm_ops,
+               .of_match_table = of_match_ptr(baikal_gmac_dwmac_match)
+       }
+};
+module_platform_driver(baikal_gmac_dwmac_driver);
+
+MODULE_DESCRIPTION("Baikal DWMAC specific glue driver");
+MODULE_LICENSE("GPL");
index f5581db0ba9bacb040d5328866ab2bc47b2029e0..0a32081c4fb0788d6629b934de263ea508827e0e 100644 (file)
@@ -16,7 +16,7 @@
 #include "dwmac1000.h"
 #include "dwmac_dma.h"
 
-static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
+void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
 {
        u32 value = readl(ioaddr + DMA_AXI_BUS_MODE);
        int i;
@@ -69,9 +69,10 @@ static void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi)
 
        writel(value, ioaddr + DMA_AXI_BUS_MODE);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_dma_axi);
 
-static void dwmac1000_dma_init(void __iomem *ioaddr,
-                              struct stmmac_dma_cfg *dma_cfg, int atds)
+void dwmac1000_dma_init(void __iomem *ioaddr,
+                       struct stmmac_dma_cfg *dma_cfg, int atds)
 {
        u32 value = readl(ioaddr + DMA_BUS_MODE);
        int txpbl = dma_cfg->txpbl ?: dma_cfg->pbl;
@@ -109,22 +110,25 @@ static void dwmac1000_dma_init(void __iomem *ioaddr,
        /* Mask interrupts by writing to CSR7 */
        writel(DMA_INTR_DEFAULT_MASK, ioaddr + DMA_INTR_ENA);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_dma_init);
 
-static void dwmac1000_dma_init_rx(void __iomem *ioaddr,
-                                 struct stmmac_dma_cfg *dma_cfg,
-                                 dma_addr_t dma_rx_phy, u32 chan)
+void dwmac1000_dma_init_rx(void __iomem *ioaddr,
+                          struct stmmac_dma_cfg *dma_cfg,
+                          dma_addr_t dma_rx_phy, u32 chan)
 {
        /* RX descriptor base address list must be written into DMA CSR3 */
        writel(lower_32_bits(dma_rx_phy), ioaddr + DMA_RCV_BASE_ADDR);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_dma_init_rx);
 
-static void dwmac1000_dma_init_tx(void __iomem *ioaddr,
-                                 struct stmmac_dma_cfg *dma_cfg,
-                                 dma_addr_t dma_tx_phy, u32 chan)
+void dwmac1000_dma_init_tx(void __iomem *ioaddr,
+                          struct stmmac_dma_cfg *dma_cfg,
+                          dma_addr_t dma_tx_phy, u32 chan)
 {
        /* TX descriptor base address list must be written into DMA CSR4 */
        writel(lower_32_bits(dma_tx_phy), ioaddr + DMA_TX_BASE_ADDR);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_dma_init_tx);
 
 static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
 {
@@ -147,8 +151,8 @@ static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
        return csr6;
 }
 
-static void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
-                                           u32 channel, int fifosz, u8 qmode)
+void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
+                                    u32 channel, int fifosz, u8 qmode)
 {
        u32 csr6 = readl(ioaddr + DMA_CONTROL);
 
@@ -174,9 +178,10 @@ static void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
 
        writel(csr6, ioaddr + DMA_CONTROL);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_dma_operation_mode_rx);
 
-static void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
-                                           u32 channel, int fifosz, u8 qmode)
+void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
+                                    u32 channel, int fifosz, u8 qmode)
 {
        u32 csr6 = readl(ioaddr + DMA_CONTROL);
 
@@ -207,8 +212,9 @@ static void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
 
        writel(csr6, ioaddr + DMA_CONTROL);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_dma_operation_mode_tx);
 
-static void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space)
+void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space)
 {
        int i;
 
@@ -217,8 +223,9 @@ static void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space)
                        reg_space[DMA_BUS_MODE / 4 + i] =
                                readl(ioaddr + DMA_BUS_MODE + i * 4);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_dump_dma_regs);
 
-static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
+int dwmac1000_get_hw_feature(void __iomem *ioaddr,
                                    struct dma_features *dma_cap)
 {
        u32 hw_cap = readl(ioaddr + DMA_HW_FEATURE);
@@ -262,12 +269,14 @@ static int dwmac1000_get_hw_feature(void __iomem *ioaddr,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(dwmac1000_get_hw_feature);
 
-static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt,
-                                 u32 queue)
+void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt,
+                          u32 number_chan)
 {
        writel(riwt, ioaddr + DMA_RX_WATCHDOG);
 }
+EXPORT_SYMBOL_GPL(dwmac1000_rx_watchdog);
 
 const struct stmmac_dma_ops dwmac1000_dma_ops = {
        .reset = dwmac_dma_reset,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.h
new file mode 100644 (file)
index 0000000..df82d4a
--- /dev/null
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __DWMAC1000_DMA_H__
+#define __DWMAC1000_DMA_H__
+#include "dwmac1000.h"
+
+void dwmac1000_dma_axi(void __iomem *ioaddr, struct stmmac_axi *axi);
+void dwmac1000_dma_init(void __iomem *ioaddr,
+                       struct stmmac_dma_cfg *dma_cfg, int atds);
+void dwmac1000_dma_init_rx(void __iomem *ioaddr,
+                          struct stmmac_dma_cfg *dma_cfg,
+                          dma_addr_t dma_rx_phy, u32 chan);
+void dwmac1000_dma_init_tx(void __iomem *ioaddr,
+                          struct stmmac_dma_cfg *dma_cfg,
+                          dma_addr_t dma_tx_phy, u32 chan);
+void dwmac1000_dma_operation_mode_rx(void __iomem *ioaddr, int mode,
+                                    u32 channel, int fifosz, u8 qmode);
+void dwmac1000_dma_operation_mode_tx(void __iomem *ioaddr, int mode,
+                                    u32 channel, int fifosz, u8 qmode);
+void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space);
+
+int dwmac1000_get_hw_feature(void __iomem *ioaddr,
+                             struct dma_features *dma_cap);
+
+void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt, u32 number_chan);
+#endif /* __DWMAC1000_DMA_H__ */
index 5f177ea807258c35aa9edb835e976dfbbbd109ca..6c07c9bd72a655ec832d91836b0248606a47aa5e 100644 (file)
@@ -10,6 +10,7 @@
   Maintainer: Giuseppe Cavallaro <peppe.cavallaro@st.com>
 *******************************************************************************/
 
+#include <linux/acpi.h>
 #include <linux/gpio/consumer.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
@@ -350,8 +351,8 @@ int stmmac_mdio_reset(struct mii_bus *bus)
        struct stmmac_priv *priv = netdev_priv(ndev);
        unsigned int mii_address = priv->hw->mii.addr;
 
-#ifdef CONFIG_OF
-       if (priv->device->of_node) {
+       if (priv->device->of_node ||
+           is_acpi_device_node(priv->device->fwnode)) {
                struct gpio_desc *reset_gpio;
                u32 delays[3] = { 0, 0, 0 };
 
@@ -376,7 +377,6 @@ int stmmac_mdio_reset(struct mii_bus *bus)
                if (delays[2])
                        msleep(DIV_ROUND_UP(delays[2], 1000));
        }
-#endif
 
        /* This is a workaround for problems with the STE101P PHY.
         * It doesn't complete its reset until at least one clock cycle
index ae9d083c406f85e9d3b4d9c2bc791c3ed98b2970..966a26a85dac041d837ec65ec4667b4eaad5da4d 100644 (file)
@@ -222,6 +222,38 @@ config PCIE_ARTPEC6_EP
          Enables support for the PCIe controller in the ARTPEC-6 SoC to work in
          endpoint mode. This uses the DesignWare core.
 
+config PCIE_BAIKAL
+       bool
+
+config PCIE_BAIKAL_HOST
+       bool "Baikal SoCs PCIe controller - Host Mode"
+       depends on ARCH_BAIKAL || COMPILE_TEST
+       depends on OF || (ACPI && PCI_QUIRKS)
+       depends on PCI_MSI_IRQ_DOMAIN
+       select PCIE_DW_HOST
+       select PCIE_BAIKAL
+       help
+         Enables support for the PCIe controller in Baikal SoCs to work in
+         host mode. There are two instances of PCIe controller in Baikal SoCs.
+         This controller can work either as EP or RC. In order to enable
+         host-specific features PCIE_BAIKAL_HOST must be selected and in order
+         to enable device-specific features PCIE_BAIKAL_EP must be selected.
+         This uses the DesignWare core.
+
+config PCIE_BAIKAL_EP
+       bool "Baikal SoCs PCIe controller - Endpoint Mode"
+       depends on ARCH_BAIKAL || COMPILE_TEST
+       depends on PCI_ENDPOINT
+       select PCIE_DW_EP
+       select PCIE_BAIKAL
+       help
+         Enables support for the PCIe controller in Baikal SoCs to work in
+         host mode. There are two instances of PCIe controller in Baikal SoCs.
+         This controller can work either as EP or RC. In order to enable
+         host-specific features PCIE_BAIKAL_HOST must be selected and in order
+         to enable device-specific features PCIE_BAIKAL_EP must be selected.
+         This uses the DesignWare core.
+
 config PCIE_ROCKCHIP_DW_HOST
        bool "Rockchip DesignWare PCIe controller"
        select PCIE_DW
index 8ba7b67f5e50a9ab268728cf8563c5143e4f1f55..b38abfaa749e230e052e2c70beeac107e58bb974 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
 obj-$(CONFIG_PCIE_QCOM_EP) += pcie-qcom-ep.o
 obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
 obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
+obj-$(CONFIG_PCIE_BAIKAL) += pcie-baikal.o
 obj-$(CONFIG_PCIE_ROCKCHIP_DW_HOST) += pcie-dw-rockchip.o
 obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o
 obj-$(CONFIG_PCIE_KEEMBAY) += pcie-keembay.o
@@ -26,6 +27,9 @@ obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
 obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o
 obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o
 
+pcie-baikal-objs := pcie-baikal-core.o \
+                    pcie-baikal-tune.o
+
 # The following drivers are for devices that use the generic ACPI
 # pci_root.c driver but don't support standard ECAM config access.
 # They contain MCFG quirks to replace the generic ECAM accessors with
@@ -44,5 +48,6 @@ ifdef CONFIG_PCI_QUIRKS
 obj-$(CONFIG_ARM64) += pcie-al.o
 obj-$(CONFIG_ARM64) += pcie-hisi.o
 obj-$(CONFIG_ARM64) += pcie-tegra194-acpi.o
+obj-$(CONFIG_ARM64) += pcie-baikal-acpi.o
 endif
 endif
diff --git a/drivers/pci/controller/dwc/pcie-baikal-acpi.c b/drivers/pci/controller/dwc/pcie-baikal-acpi.c
new file mode 100644 (file)
index 0000000..85da9f8
--- /dev/null
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021-2023 Baikal Electronics, JSC
+ * Author: Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ */
+
+#include <linux/pci-ecam.h>
+
+#ifdef CONFIG_PCIE_BAIKAL
+extern const struct pci_ecam_ops baikal_m_pcie_ecam_ops;
+extern const struct pci_ecam_ops baikal_s_pcie_ecam_ops;
+#else
+const struct pci_ecam_ops baikal_m_pcie_ecam_ops = {
+       .bus_shift      = 20,
+       .pci_ops        = {
+               .map_bus        = pci_ecam_map_bus,
+               .read           = pci_generic_config_read,
+               .write          = pci_generic_config_write
+       }
+};
+
+const struct pci_ecam_ops baikal_s_pcie_ecam_ops = baikal_m_pcie_ecam_ops;
+#endif
diff --git a/drivers/pci/controller/dwc/pcie-baikal-core.c b/drivers/pci/controller/dwc/pcie-baikal-core.c
new file mode 100644 (file)
index 0000000..311ea9f
--- /dev/null
@@ -0,0 +1,2129 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe controller driver for Baikal Electronics SoCs
+ *
+ * Copyright (C) 2019-2023 Baikal Electronics, JSC
+ * Authors: Pavel Parkhomenko <pavel.parkhomenko@baikalelectronics.ru>
+ *          Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ */
+
+#include <linux/acpi.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/pci-ecam.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <acpi/acrestyp.h>
+
+#include "pcie-designware.h"
+#include "pcie-baikal.h"
+
+#define BAIKAL_PCIE_LTSSM_MASK         0x3f
+#define BAIKAL_PCIE_LTSSM_STATE_L0     0x11
+
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+struct baikal_pcie_acpi_data {
+       u64             mem_base;
+       phys_addr_t     mem_bus_addr;
+       u32             mem_size;
+};
+
+static const struct baikal_pcie_of_data bm1000_pcie_rc_of_data;
+static const struct baikal_pcie_of_data bs1000_pcie_rc_of_data;
+#else
+static inline u16 bm1000_pcie_get_segment_acpi(struct device *dev)
+{
+       return 0;
+}
+#endif
+
+struct baikal_pcie_of_data {
+       enum dw_pcie_device_mode        mode;
+       struct dw_pcie_ops              dw_pcie_ops;
+       int (*get_resources)(struct platform_device *pdev,
+                            struct dw_pcie *pci,
+                            const struct baikal_pcie_of_data *data);
+       int (*add_pcie_port)(struct platform_device *pdev);
+};
+
+static const char *baikal_pcie_link_speed_str(const unsigned speed)
+{
+       static const char *speed_str[] = {"2.5", "5.0", "8.0", "16.0"};
+
+       if (speed > 0 && speed <= ARRAY_SIZE(speed_str))
+               return speed_str[speed - 1];
+
+       return "???";
+}
+
+static void baikal_pcie_link_print_status(struct dw_pcie *pci)
+{
+       struct device *dev = pci->dev;
+       u16 exp_cap_off;
+       u32 reg;
+
+       if (!pci->ops->link_up(pci)) {
+               dev_info(dev, "link is down\n");
+               return;
+       }
+
+       exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKSTA);
+
+       dev_info(dev, "current link is %s GT/s%s, x%u\n",
+                baikal_pcie_link_speed_str(FIELD_GET(PCI_EXP_LNKSTA_CLS, reg)),
+                FIELD_GET(PCI_EXP_LNKSTA_LT, reg) ? " (training)" : "",
+                FIELD_GET(PCI_EXP_LNKSTA_NLW, reg));
+}
+
+static bool baikal_pcie_link_is_training(struct dw_pcie *pci)
+{
+       u16 exp_cap_off;
+       u32 reg;
+
+       exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKSTA);
+       return FIELD_GET(PCI_EXP_LNKSTA_LT, reg);
+}
+
+static bool baikal_pcie_link_wait_training_done(struct dw_pcie *pci)
+{
+       struct device *dev = pci->dev;
+       unsigned long start_jiffies = jiffies;
+
+       while (baikal_pcie_link_is_training(pci)) {
+               if (time_after(jiffies, start_jiffies + HZ)) {
+                       dev_err(dev, "link training timeout occured\n");
+                       return false;
+               }
+
+               udelay(100);
+       }
+
+       return true;
+}
+
+#define BM1000_PCIE_GPR_RESET_BASE             0x00
+#define BM1000_PCIE_GPR_RESET(x)               ((x * 0x20) + BM1000_PCIE_GPR_RESET_BASE)
+#define BM1000_PCIE_PHY_RST                    BIT(0)
+#define BM1000_PCIE_PIPE_RST                   BIT(4) /* x4 controllers only */
+#define BM1000_PCIE_PIPE0_RST                  BIT(4) /* x8 controller only */
+#define BM1000_PCIE_PIPE1_RST                  BIT(5) /* x8 controller only */
+#define BM1000_PCIE_CORE_RST                   BIT(8)
+#define BM1000_PCIE_PWR_RST                    BIT(9)
+#define BM1000_PCIE_STICKY_RST                 BIT(10)
+#define BM1000_PCIE_NONSTICKY_RST              BIT(11)
+#define BM1000_PCIE_HOT_RST                    BIT(12)
+#define BM1000_PCIE_ADB_PWRDWN                 BIT(13)
+
+#define BM1000_PCIE_GPR_STATUS_BASE            0x04
+#define BM1000_PCIE_GPR_STATUS(x)              ((x * 0x20) + BM1000_PCIE_GPR_STATUS_BASE)
+
+#define BM1000_PCIE_GPR_GENCTL_BASE            0x08
+#define BM1000_PCIE_GPR_GENCTL(x)              ((x * 0x20) + BM1000_PCIE_GPR_GENCTL_BASE)
+#define BM1000_PCIE_LTSSM_ENABLE               BIT(1)
+#define BM1000_PCIE_DBI2_MODE                  BIT(2)
+#define BM1000_PCIE_PHY_MGMT_ENABLE            BIT(3)
+
+#define BM1000_PCIE_GPR_MSI_TRANS_CTL2         0xf8
+#define BM1000_PCIE_MSI_TRANS_EN(x)            BIT(9 + (x))
+#define BM1000_PCIE_MSI_TRANS_RCNUM(x)         ((x) << (2 * (x)))
+#define BM1000_PCIE_MSI_TRANS_RCNUM_MASK(x)    ((3) << (2 * (x)))
+
+#define BM1000_PCIE0_DBI_BASE                  0x02200000
+#define BM1000_PCIE1_DBI_BASE                  0x02210000
+#define BM1000_PCIE2_DBI_BASE                  0x02220000
+
+struct bm1000_pcie {
+       struct dw_pcie  *pci;
+       unsigned int    num;
+       struct regmap   *gpr;
+       union {
+               struct gpio_desc *reset_gpio;
+               struct {
+                       u8 num : 5;
+                       u8 polarity : 1;
+                       u8 is_set : 1;
+               } gpio[2];
+       };
+       bool            reset_active_low;
+       char            reset_name[32];
+       bool            retrained;
+};
+
+void bm1000_pcie_phy_enable(struct dw_pcie *pci)
+{
+       struct bm1000_pcie *bm = dev_get_drvdata(pci->dev);
+       u32 reg;
+
+       regmap_read(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), &reg);
+       reg |= BM1000_PCIE_PHY_MGMT_ENABLE | BM1000_PCIE_DBI2_MODE;
+       regmap_write(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), reg);
+}
+
+void bm1000_pcie_phy_disable(struct dw_pcie *pci)
+{
+       struct bm1000_pcie *bm = dev_get_drvdata(pci->dev);
+       u32 reg;
+
+       regmap_read(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), &reg);
+       reg &= ~(BM1000_PCIE_PHY_MGMT_ENABLE | BM1000_PCIE_DBI2_MODE);
+       regmap_write(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), reg);
+}
+
+static int bm1000_get_resources(struct platform_device *pdev,
+                               struct dw_pcie *pci,
+                               const struct baikal_pcie_of_data *data)
+{
+       struct bm1000_pcie *bm = platform_get_drvdata(pdev);
+       struct device *dev = pci->dev;
+       struct resource *res;
+       int reset_gpio;
+       enum of_gpio_flags gpio_flags;
+
+       bm->pci = pci;
+       bm->gpr = syscon_regmap_lookup_by_compatible("baikal,bm1000-pcie-gpr");
+       if (IS_ERR(bm->gpr)) {
+               dev_err(dev, "failed to find PCIe GPR registers\n");
+               return PTR_ERR(bm->gpr);
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+       if (!res) {
+               dev_err(dev, "failed to find \"dbi\" region\n");
+               return -EINVAL;
+       }
+
+       pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);
+       if (IS_ERR(pci->dbi_base))
+               return PTR_ERR(pci->dbi_base);
+
+       if (res->start == BM1000_PCIE0_DBI_BASE) {
+               bm->num = 0;
+       } else if (res->start == BM1000_PCIE1_DBI_BASE) {
+               bm->num = 1;
+       } else if (res->start == BM1000_PCIE2_DBI_BASE) {
+               bm->num = 2;
+       } else {
+               dev_err(dev, "incorrect \"dbi\" base\n");
+               return -EINVAL;
+       }
+
+       reset_gpio = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0,
+                                            &gpio_flags);
+       if (gpio_is_valid(reset_gpio)) {
+               bm->reset_gpio = gpio_to_desc(reset_gpio);
+               bm->reset_active_low = gpio_flags & OF_GPIO_ACTIVE_LOW;
+               snprintf(bm->reset_name, sizeof(bm->reset_name), "pcie%u-reset",
+                        bm->num);
+       } else {
+               bm->reset_gpio = NULL;
+       }
+
+       return 0;
+}
+
+static int bm1000_pcie_link_up(struct dw_pcie *pci)
+{
+       struct bm1000_pcie *bm = dev_get_drvdata(pci->dev);
+       u32 reg;
+
+       regmap_read(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), &reg);
+       if (!(reg & BM1000_PCIE_LTSSM_ENABLE))
+               return 0;
+
+       regmap_read(bm->gpr, BM1000_PCIE_GPR_STATUS(bm->num), &reg);
+       return (reg & BAIKAL_PCIE_LTSSM_MASK) == BAIKAL_PCIE_LTSSM_STATE_L0;
+}
+
+static int bm1000_pcie_start_link(struct dw_pcie *pci)
+{
+       struct bm1000_pcie *bm = dev_get_drvdata(pci->dev);
+       u32 reg;
+
+       regmap_read(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), &reg);
+       reg |= BM1000_PCIE_LTSSM_ENABLE;
+       regmap_write(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), reg);
+       return 0;
+}
+
+static void bm1000_pcie_link_speed_fixup(struct bm1000_pcie *bm,
+                                        struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+       struct dw_pcie *pci = bm->pci;
+       unsigned dev_lnkcap_speed;
+       unsigned dev_lnkcap_width;
+       unsigned rc_lnkcap_speed;
+       unsigned rc_lnksta_speed;
+       unsigned rc_target_speed;
+       u16 exp_cap_off;
+       u32 reg;
+
+       /* Return if the bus has already been retrained */
+       if (bm->retrained)
+               return;
+
+       list_for_each_entry(dev, &bus->devices, bus_list)
+               if (dev->subordinate)
+                       bm1000_pcie_link_speed_fixup(bm, dev->subordinate);
+
+       /* Skip root bridge and devices not directly attached to the RC */
+       if (pci_is_root_bus(bus) || !pci_is_root_bus(bus->parent))
+               return;
+
+       dev = list_first_entry_or_null(&bus->devices, struct pci_dev, bus_list);
+       if (dev == NULL)
+               return;
+
+       exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+
+       reg = dw_pcie_readl_dbi(pci, exp_cap_off + PCI_EXP_LNKCAP);
+       rc_lnkcap_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, reg);
+
+       reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKSTA);
+       rc_lnksta_speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, reg);
+
+       if (acpi_disabled) {
+               pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &reg);
+       } else {
+               struct pci_config_window *cfg;
+               int where = pci_pcie_cap(dev) + PCI_EXP_LNKCAP;
+
+               cfg = dev->bus->sysdata;
+               if (dev->bus->number == cfg->busr.start) {
+                       if (PCI_SLOT(dev->devfn) > 0)
+                               reg = 0;
+                       else
+                               reg = readl(pci->dbi_base + where);
+               } else {
+                       unsigned int busn = dev->bus->number - cfg->busr.start;
+                       unsigned int devfn_shift = cfg->ops->bus_shift - 8;
+
+                       reg = readl(cfg->win +
+                                   (busn << cfg->ops->bus_shift) +
+                                   (dev->devfn << devfn_shift) +
+                                   where);
+               }
+       }
+
+       dev_lnkcap_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, reg);
+       dev_lnkcap_width = FIELD_GET(PCI_EXP_LNKCAP_MLW, reg);
+
+       baikal_pcie_link_print_status(pci);
+
+       /*
+        * [2.5 -> 8.0 GT/s] is suitable way of retraining.
+        * [2.5 -> 5.0 GT/s] is used when 8.0 GT/s could not be reached.
+        * [5.0 -> 8.0 GT/s] causes system freezing sometimes.
+        */
+       if (rc_lnkcap_speed < dev_lnkcap_speed)
+               rc_target_speed = rc_lnkcap_speed;
+       else
+               rc_target_speed = dev_lnkcap_speed;
+
+       while (rc_lnksta_speed < rc_target_speed) {
+               unsigned long start_jiffies;
+
+               /* Try to change link speed */
+               dev_info(pci->dev, "retrain link to %s GT/s\n",
+                        baikal_pcie_link_speed_str(rc_target_speed));
+
+               /* If link is already training wait for training to complete */
+               baikal_pcie_link_wait_training_done(pci);
+
+               /* Set desired speed */
+               reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKCTL2);
+               reg &= ~PCI_EXP_LNKCTL2_TLS;
+               reg |= rc_target_speed;
+               dw_pcie_writew_dbi(pci, exp_cap_off + PCI_EXP_LNKCTL2, reg);
+
+               /* Deassert and assert PORT_LOGIC_SPEED_CHANGE bit */
+               reg = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+               reg &= ~PORT_LOGIC_SPEED_CHANGE;
+               dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, reg);
+               reg |=  PORT_LOGIC_SPEED_CHANGE;
+               dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, reg);
+
+               /* Wait for link training */
+               start_jiffies = jiffies;
+               for (;;) {
+                       /* Wait for link training begin */
+                       if (!baikal_pcie_link_is_training(pci)) {
+                               if (time_after(jiffies, start_jiffies + HZ)) {
+                                       dev_err(pci->dev,
+                                            "link training has not started\n");
+                                       /*
+                                        * Don't wait for training_done()
+                                        * if it hasn't started.
+                                        */
+                                       break;
+                               }
+
+                               udelay(100);
+                               continue;
+                       }
+
+                       /* Wait for link training end */
+                       if (!baikal_pcie_link_wait_training_done(pci))
+                               break;
+
+                       if (!dw_pcie_wait_for_link(pci)) {
+                               /* Wait if link switched to config/recovery */
+                               baikal_pcie_link_wait_training_done(pci);
+                               baikal_pcie_link_print_status(pci);
+                       }
+
+                       break;
+               }
+
+               /* Check if the link is down after retrain */
+               if (!bm1000_pcie_link_up(pci)) {
+                       /*
+                        * Check if the link has already been down and
+                        * the link is unable to re-establish at 2.5 GT/s
+                        */
+                       if (rc_lnksta_speed == 0 &&
+                           rc_target_speed == PCI_EXP_LNKCTL2_TLS_2_5GT)
+                               break;
+
+                       rc_lnksta_speed = 0;
+                       if (rc_target_speed > PCI_EXP_LNKCTL2_TLS_2_5GT) {
+                               /* Try to use lower speed */
+                               --rc_target_speed;
+                       }
+
+                       continue;
+               }
+
+               reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKSTA);
+               rc_lnksta_speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, reg);
+
+               /* Check if the targeted speed has not been reached */
+               if (rc_lnksta_speed < rc_target_speed &&
+                   rc_target_speed > PCI_EXP_LNKCTL2_TLS_2_5GT) {
+                       /* Try to use lower speed */
+                       --rc_target_speed;
+               }
+       }
+
+       bm->retrained = true;
+}
+
+static u16 bm1000_pcie_get_segment_acpi(struct device *dev);
+
+static void bm1000_pcie_set_gpio(u8 num, bool enable)
+{
+       void __iomem *addr;
+       u32 val;
+
+       if (num > 31)
+               return;
+
+       addr = ioremap(0x20200000, 8);
+       if (addr) {
+               val = readl(addr);
+               if (enable)
+                       val |= 1 << num;
+               else
+                       val &= ~BIT(num);
+               writel(val, addr);
+
+               val = readl(addr + 4);
+               val |= 1 << num;
+               writel(val, addr + 4);
+
+               iounmap(addr);
+       }
+}
+
+static int bm1000_pcie_host_init(struct dw_pcie_rp *pp)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+       struct bm1000_pcie *bm = dev_get_drvdata(pci->dev);
+       struct device *dev = pci->dev;
+       int err;
+       int linkup;
+       u16 exp_cap_off;
+       u16 ext_cap_err_off;
+       u32 reg;
+
+       /* Disable access to PHY registers and DBI2 mode */
+       bm1000_pcie_phy_disable(pci);
+
+       bm->retrained = false;
+       linkup = bm1000_pcie_link_up(pci);
+
+       /* If link is not established yet, reset the RC */
+       if (!linkup) {
+               /* Disable link training */
+               regmap_read(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), &reg);
+               reg &= ~BM1000_PCIE_LTSSM_ENABLE;
+               regmap_write(bm->gpr, BM1000_PCIE_GPR_GENCTL(bm->num), reg);
+
+               /* Assert PERST pin */
+               if (acpi_disabled && bm->reset_gpio) {
+                       unsigned long gpio_flags;
+
+                       if (bm->reset_active_low) {
+                               gpio_flags = GPIOF_ACTIVE_LOW |
+                                            GPIOF_OUT_INIT_LOW;
+                       } else {
+                               gpio_flags = GPIOF_OUT_INIT_HIGH;
+                       }
+
+                       err = devm_gpio_request_one(dev,
+                                                  desc_to_gpio(bm->reset_gpio),
+                                                  gpio_flags, bm->reset_name);
+                       if (err) {
+                               dev_err(dev, "request GPIO failed (%d)\n", err);
+                               return err;
+                       }
+               } else if (!acpi_disabled && bm->gpio[0].is_set) {
+                       bm1000_pcie_set_gpio(bm->gpio[0].num, ~bm->gpio[0].polarity);
+               }
+
+               /* Reset the RC */
+               regmap_read(bm->gpr, BM1000_PCIE_GPR_RESET(bm->num), &reg);
+               reg |= BM1000_PCIE_NONSTICKY_RST |
+                      BM1000_PCIE_STICKY_RST    |
+                      BM1000_PCIE_PWR_RST       |
+                      BM1000_PCIE_CORE_RST      |
+                      BM1000_PCIE_PHY_RST;
+
+               /* If the RC is PCIe x8, reset PIPE0 and PIPE1 */
+               if (bm->num == 2) {
+                       reg |= BM1000_PCIE_PIPE0_RST |
+                              BM1000_PCIE_PIPE1_RST;
+               } else {
+                       reg |= BM1000_PCIE_PIPE_RST;
+               }
+
+               regmap_write(bm->gpr, BM1000_PCIE_GPR_RESET(bm->num), reg);
+
+               if (!acpi_disabled && bm->num == 2 && bm->gpio[1].is_set) {
+                       /* Assert PRSNT pin */
+                       bm1000_pcie_set_gpio(bm->gpio[1].num, ~bm->gpio[1].polarity);
+               }
+
+               usleep_range(20000, 30000);
+
+               /* Deassert PERST pin */
+               if (acpi_disabled && bm->reset_gpio)
+                       gpiod_set_value_cansleep(bm->reset_gpio, 0);
+               else if (!acpi_disabled && bm->gpio[0].is_set)
+                       bm1000_pcie_set_gpio(bm->gpio[0].num, bm->gpio[0].polarity);
+
+               /* Deassert PHY reset */
+               regmap_read(bm->gpr, BM1000_PCIE_GPR_RESET(bm->num), &reg);
+               reg &= ~BM1000_PCIE_PHY_RST;
+               regmap_write(bm->gpr, BM1000_PCIE_GPR_RESET(bm->num), reg);
+
+               /* Deassert all software controlled resets */
+               regmap_read(bm->gpr, BM1000_PCIE_GPR_RESET(bm->num), &reg);
+               reg &= ~(BM1000_PCIE_ADB_PWRDWN    |
+                        BM1000_PCIE_HOT_RST       |
+                        BM1000_PCIE_NONSTICKY_RST |
+                        BM1000_PCIE_STICKY_RST    |
+                        BM1000_PCIE_PWR_RST       |
+                        BM1000_PCIE_CORE_RST      |
+                        BM1000_PCIE_PHY_RST);
+
+               if (bm->num == 2) {
+                       reg &= ~(BM1000_PCIE_PIPE0_RST |
+                                BM1000_PCIE_PIPE1_RST);
+               } else {
+                       reg &= ~BM1000_PCIE_PIPE_RST;
+               }
+
+               regmap_write(bm->gpr, BM1000_PCIE_GPR_RESET(bm->num), reg);
+       }
+
+       /* Enable error reporting */
+       ext_cap_err_off = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_ERR);
+       reg = dw_pcie_readl_dbi(pci, ext_cap_err_off + PCI_ERR_ROOT_COMMAND);
+       reg |= PCI_ERR_ROOT_CMD_COR_EN      |
+              PCI_ERR_ROOT_CMD_NONFATAL_EN |
+              PCI_ERR_ROOT_CMD_FATAL_EN;
+       dw_pcie_writel_dbi(pci, ext_cap_err_off + PCI_ERR_ROOT_COMMAND, reg);
+
+       exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_DEVCTL);
+       reg |= PCI_EXP_DEVCTL_CERE  |
+              PCI_EXP_DEVCTL_NFERE |
+              PCI_EXP_DEVCTL_FERE  |
+              PCI_EXP_DEVCTL_URRE;
+       dw_pcie_writew_dbi(pci, exp_cap_off + PCI_EXP_DEVCTL, reg);
+
+       reg = dw_pcie_readl_dbi(pci, exp_cap_off + PCI_EXP_RTCTL);
+       reg |= PCI_EXP_RTCTL_SECEE  |
+              PCI_EXP_RTCTL_SENFEE |
+              PCI_EXP_RTCTL_SEFEE  |
+              PCI_EXP_RTCTL_PMEIE;
+       dw_pcie_writel_dbi(pci, exp_cap_off + PCI_EXP_RTCTL, reg);
+
+       if (linkup) {
+               dev_info(dev, "link is already up\n");
+       } else {
+               /* Use 2.5 GT/s rate for link establishing */
+               reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKCTL2);
+               reg &= ~PCI_EXP_LNKCTL2_TLS;
+               reg |=  PCI_EXP_LNKCTL2_TLS_2_5GT;
+               dw_pcie_writew_dbi(pci, exp_cap_off + PCI_EXP_LNKCTL2, reg);
+       }
+
+       regmap_read(bm->gpr, BM1000_PCIE_GPR_MSI_TRANS_CTL2, &reg);
+       reg &= ~BM1000_PCIE_MSI_TRANS_RCNUM_MASK(bm->num);
+       if (acpi_disabled)
+               reg |= BM1000_PCIE_MSI_TRANS_RCNUM(bm->num);
+       else
+               reg |= (bm1000_pcie_get_segment_acpi(dev) + 1) << (2 * bm->num);
+       reg |=  BM1000_PCIE_MSI_TRANS_EN(bm->num);
+       regmap_write(bm->gpr, BM1000_PCIE_GPR_MSI_TRANS_CTL2, reg);
+
+       /* RX/TX equalizers fine tune */
+       bm1000_pcie_tune(pci);
+
+       return 0;
+}
+
+static irqreturn_t bm1000_pcie_aer_irq_handler(int irq, void *arg)
+{
+       struct bm1000_pcie *bm = arg;
+       struct dw_pcie *pci = bm->pci;
+       struct device *dev = pci->dev;
+       u16 exp_cap_off;
+       u16 ext_cap_err_off;
+       u16 dev_sta;
+       u32 cor_err;
+       u32 root_err;
+       u32 uncor_err;
+
+       exp_cap_off     = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       ext_cap_err_off = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_ERR);
+
+       uncor_err       = dw_pcie_readl_dbi(pci,
+                                       ext_cap_err_off + PCI_ERR_UNCOR_STATUS);
+       cor_err         = dw_pcie_readl_dbi(pci,
+                                       ext_cap_err_off + PCI_ERR_COR_STATUS);
+       root_err        = dw_pcie_readl_dbi(pci,
+                                       ext_cap_err_off + PCI_ERR_ROOT_STATUS);
+       dev_sta         = dw_pcie_readw_dbi(pci,
+                                       exp_cap_off + PCI_EXP_DEVSTA);
+
+       dw_pcie_writel_dbi(pci,
+                          ext_cap_err_off + PCI_ERR_UNCOR_STATUS, uncor_err);
+       dw_pcie_writel_dbi(pci,
+                          ext_cap_err_off + PCI_ERR_COR_STATUS, cor_err);
+       dw_pcie_writel_dbi(pci,
+                          ext_cap_err_off + PCI_ERR_ROOT_STATUS, root_err);
+       dw_pcie_writew_dbi(pci,
+                          exp_cap_off + PCI_EXP_DEVSTA, dev_sta);
+
+       dev_err(dev,
+               "DevSta:0x%04x RootErr:0x%x UncorErr:0x%x CorErr:0x%x\n",
+               dev_sta, root_err, uncor_err, cor_err);
+
+       return IRQ_HANDLED;
+}
+
+static const struct dw_pcie_host_ops bm1000_pcie_host_ops = {
+       .host_init = bm1000_pcie_host_init,
+};
+
+static int bm1000_add_pcie_port(struct platform_device *pdev)
+{
+       struct bm1000_pcie *bm = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       struct dw_pcie *pci = bm->pci;
+       struct dw_pcie_rp *pp = &pci->pp;
+       int ret;
+
+       pp->irq = platform_get_irq_byname(pdev, "aer");
+       if (pp->irq < 0) {
+               dev_err(dev, "failed to get \"aer\" IRQ\n");
+               return pp->irq;
+       }
+
+       ret = devm_request_irq(dev, pp->irq, bm1000_pcie_aer_irq_handler,
+                              IRQF_SHARED, "bm1000-pcie-aer", bm);
+       if (ret) {
+               dev_err(dev, "failed to request IRQ %d\n", pp->irq);
+               return ret;
+       }
+
+       pp->num_vectors = MAX_MSI_IRQS;
+       pp->ops = &bm1000_pcie_host_ops;
+       ret = dw_pcie_host_init(pp);
+       if (ret) {
+               dev_err(dev, "failed to initialize host\n");
+               return ret;
+       }
+
+       bm1000_pcie_link_speed_fixup(bm, pp->bridge->bus);
+       return 0;
+}
+
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+static u16 bm1000_pcie_get_segment_acpi(struct device *dev)
+{
+       struct acpi_pci_root *root = acpi_driver_data(to_acpi_device(dev));
+       return root->segment;
+}
+
+static void bm1000_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
+                                         int type, u64 cpu_addr, u64 pci_addr,
+                                         u32 size, u32 flags)
+{
+       u32 retries, val;
+
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
+                          PCIE_ATU_REGION_DIR_OB | index);
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT_BASE + PCIE_ATU_LOWER_BASE,
+                          lower_32_bits(cpu_addr));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT_BASE + PCIE_ATU_UPPER_BASE,
+                          upper_32_bits(cpu_addr));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT_BASE + PCIE_ATU_LIMIT,
+                          lower_32_bits(cpu_addr + size - 1));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT_BASE + PCIE_ATU_LOWER_TARGET,
+                          lower_32_bits(pci_addr));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT_BASE + PCIE_ATU_UPPER_TARGET,
+                          upper_32_bits(pci_addr));
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT_BASE, type);
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT_BASE + PCIE_ATU_REGION_CTRL2,
+                          PCIE_ATU_ENABLE | flags);
+
+       /*
+        * Make sure ATU enable takes effect before any subsequent config
+        * and I/O accesses.
+        */
+       for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; ++retries) {
+               val = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT_BASE +
+                                            PCIE_ATU_REGION_CTRL2);
+               if (val & PCIE_ATU_ENABLE)
+                       return;
+
+               mdelay(LINK_WAIT_IATU);
+       }
+       dev_err(pci->dev, "Outbound iATU is not being enabled\n");
+}
+
+#define PCIE_IATU_REGION_CTRL_2_REG_SHIFT_MODE BIT(28)
+
+static void bm1000_pcie_setup_rc_acpi(struct dw_pcie_rp *pp,
+                                     const struct baikal_pcie_acpi_data *mem_data)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+       acpi_status status;
+       u64 lanes;
+       u32 val;
+       int i;
+
+       /*
+        * Enable DBI read-only registers for writing/updating configuration.
+        * Write permission gets disabled towards the end of this function.
+        */
+       dw_pcie_dbi_ro_wr_en(pci);
+
+       val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
+       val &= ~PORT_LINK_FAST_LINK_MODE;
+       val |= PORT_LINK_DLL_LINK_EN;
+       dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
+
+       status = acpi_evaluate_integer(to_acpi_device(pci->dev)->handle,
+                                      "NUML", NULL, &lanes);
+       if (ACPI_FAILURE(status)) {
+               dev_dbg(pci->dev, "failed to get num-lanes\n");
+       } else {
+               pci->num_lanes = lanes;
+
+               /* Set the number of lanes */
+               val &= ~PORT_LINK_FAST_LINK_MODE;
+               val &= ~PORT_LINK_MODE_MASK;
+               switch (pci->num_lanes) {
+               case 1:
+                       val |= PORT_LINK_MODE_1_LANES;
+                       break;
+               case 2:
+                       val |= PORT_LINK_MODE_2_LANES;
+                       break;
+               case 4:
+                       val |= PORT_LINK_MODE_4_LANES;
+                       break;
+               case 8:
+                       val |= PORT_LINK_MODE_8_LANES;
+                       break;
+               default:
+                       dev_err(pci->dev, "NUML %u: invalid value\n", pci->num_lanes);
+                       goto skip_lanes;
+               }
+               dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
+
+               /* Set link width speed control register */
+               val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+               val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+               switch (pci->num_lanes) {
+               case 1:
+                       val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+                       break;
+               case 2:
+                       val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+                       break;
+               case 4:
+                       val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
+                       break;
+               case 8:
+                       val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
+                       break;
+               }
+               dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+       }
+
+skip_lanes:
+       /* Setup RC BARs */
+       dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);
+       dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
+
+       /* Setup interrupt pins */
+       val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
+       val &= 0xffff00ff;
+       val |= 0x00000100;
+       dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
+
+       /* Setup bus numbers */
+       val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
+       val &= 0xff000000;
+       val |= 0x00ff0100;
+       dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val);
+
+       /* Setup command register */
+       val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+       val &= 0xffff0000;
+       val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+               PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+       dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+
+       pci->atu_base = pci->dbi_base + PCIE_ATU_VIEWPORT_BASE;
+       pci->atu_size = PCIE_ATU_VIEWPORT_SIZE;
+       pci->num_ob_windows = 4;
+       pci->num_ib_windows = 0;
+
+       for (i = 0; i < pci->num_ob_windows; ++i)
+               dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
+
+       /* Program ATU */
+       bm1000_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_CFG0,
+                                     pp->cfg0_base, 0,
+                                     pp->cfg0_size,
+                                     PCIE_IATU_REGION_CTRL_2_REG_SHIFT_MODE);
+       bm1000_pcie_prog_outbound_atu(pci, 1, PCIE_ATU_TYPE_CFG1,
+                                     pp->cfg0_base, 0,
+                                     pp->cfg0_size,
+                                     PCIE_IATU_REGION_CTRL_2_REG_SHIFT_MODE);
+       bm1000_pcie_prog_outbound_atu(pci, 2, PCIE_ATU_TYPE_MEM,
+                                     mem_data->mem_base, mem_data->mem_bus_addr,
+                                     mem_data->mem_size, 0);
+       bm1000_pcie_prog_outbound_atu(pci, 3, PCIE_ATU_TYPE_IO,
+                                     pp->io_base, pp->io_bus_addr,
+                                     pp->io_size, 0);
+
+       dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
+
+       /* Program correct class for RC */
+       dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
+
+       val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+       val |= PORT_LOGIC_SPEED_CHANGE;
+       dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+
+       dw_pcie_dbi_ro_wr_dis(pci);
+}
+
+struct bm1000_pcie_acpi_gpio {
+       struct list_head node;
+       struct acpi_resource_gpio *ares;
+};
+
+static int bm1000_pcie_acpi_dev_filter_gpio(struct acpi_resource *ares, void *data)
+{
+       struct list_head *list = data;
+       struct bm1000_pcie_acpi_gpio *gpio;
+
+       if (ares->type == ACPI_RESOURCE_TYPE_GPIO) {
+               gpio = kzalloc(sizeof(*gpio), GFP_KERNEL);
+               if (gpio) {
+                       INIT_LIST_HEAD(&gpio->node);
+                       gpio->ares = &ares->data.gpio;
+                       list_add_tail(&gpio->node, list);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static int bm1000_pcie_get_res_acpi(struct acpi_device *adev,
+                                   struct acpi_device **res_dev,
+                                   struct bm1000_pcie *bm,
+                                   struct baikal_pcie_acpi_data *mem_data)
+{
+       struct device *dev = &adev->dev;
+       struct dw_pcie_rp *pp = &bm->pci->pp;
+       struct resource_entry *entry;
+       struct list_head list, *pos;
+       struct fwnode_handle *fwnode;
+       struct bm1000_pcie_acpi_gpio *gpio, *tmp;
+       int ret;
+       unsigned long flags = IORESOURCE_MEM;
+
+       fwnode = fwnode_get_named_child_node(&adev->fwnode, "RES0");
+       if (!fwnode) {
+               dev_err(dev, "failed to get RES0 subdevice\n");
+               return -EINVAL;
+       }
+
+       *res_dev = to_acpi_device_node(fwnode);
+       if (!*res_dev) {
+               dev_err(dev, "RES0 is not an acpi device node\n");
+               return -EINVAL;
+       }
+
+       INIT_LIST_HEAD(&list);
+       ret = acpi_dev_get_resources(*res_dev, &list,
+                                    acpi_dev_filter_resource_type_cb,
+                                    (void *)flags);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse RES0._CRS method, error code %d\n", ret);
+               return ret;
+       }
+
+       if (ret != 2) {
+               dev_err(dev, "invalid number of MEM resources present in RES0._CRS (%i, need 2)\n", ret);
+               return -EINVAL;
+       }
+
+       /* ECAM */
+       pos = list.next;
+       entry = list_entry(pos, struct resource_entry, node);
+       pp->cfg0_size = resource_size(entry->res);
+       pp->cfg0_base = entry->res->start;
+
+       /* DBI */
+       pos = pos->next;
+       entry = list_entry(pos, struct resource_entry, node);
+       if (entry->res->start == BM1000_PCIE0_DBI_BASE) {
+               bm->num = 0;
+       } else if (entry->res->start == BM1000_PCIE1_DBI_BASE) {
+               bm->num = 1;
+       } else if (entry->res->start == BM1000_PCIE2_DBI_BASE) {
+               bm->num = 2;
+       } else {
+               dev_err(dev, "incorrect \"dbi\" base\n");
+               return -EINVAL;
+       }
+       bm->pci->dbi_base = devm_ioremap_resource(dev, entry->res);
+       if (IS_ERR(bm->pci->dbi_base)) {
+               dev_err(dev, "error with dbi ioremap\n");
+               ret = PTR_ERR(bm->pci->dbi_base);
+               return ret;
+       }
+
+       acpi_dev_free_resource_list(&list);
+
+       /* Non-prefetchable memory */
+       INIT_LIST_HEAD(&list);
+       flags = IORESOURCE_MEM;
+       ret = acpi_dev_get_resources(adev, &list,
+                                    acpi_dev_filter_resource_type_cb,
+                                    (void *)flags);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse _CRS method, error code %d\n", ret);
+               return ret;
+       }
+
+       if (ret != 1) {
+               dev_err(dev, "invalid number of MEM resources present in _CRS (%i, need 1)\n", ret);
+               return -EINVAL;
+       }
+
+       pos = list.next;
+       entry = list_entry(pos, struct resource_entry, node);
+       mem_data->mem_base = entry->res->start;
+       mem_data->mem_size = resource_size(entry->res);
+       mem_data->mem_bus_addr = entry->res->start - entry->offset;
+
+       acpi_dev_free_resource_list(&list);
+
+       /* I/O */
+       INIT_LIST_HEAD(&list);
+       flags = IORESOURCE_IO;
+       ret = acpi_dev_get_resources(adev, &list,
+                                    acpi_dev_filter_resource_type_cb,
+                                    (void *)flags);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse _CRS method, error code %d\n", ret);
+               return ret;
+       }
+
+       if (ret != 1) {
+               dev_err(dev, "invalid number of IO resources present in _CRS (%i, need 1)\n", ret);
+               return -EINVAL;
+       }
+
+       pos = list.next;
+       entry = list_entry(pos, struct resource_entry, node);
+       pp->io_base = entry->res->start;
+       pp->io_size = resource_size(entry->res);
+       pp->io_bus_addr = entry->res->start - entry->offset;
+
+       acpi_dev_free_resource_list(&list);
+
+       /* GPIO */
+       INIT_LIST_HEAD(&list);
+       ret = acpi_dev_get_resources(*res_dev, &list,
+                                    bm1000_pcie_acpi_dev_filter_gpio,
+                                    &list);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse RES0._CRS method, error code %d\n", ret);
+               return ret;
+       }
+
+       if (!ret) {
+               u8 count = 0;
+               u8 i;
+
+               list_for_each_entry(gpio, &list, node) {
+                       for (i = 0; i < gpio->ares->pin_table_length &&
+                            count < ARRAY_SIZE(bm->gpio); ++i) {
+                               bm->gpio[count].num = gpio->ares->pin_table[i] & 0x1f;
+                               bm->gpio[count].polarity = 1;
+                               bm->gpio[count].is_set = 1;
+                               ++count;
+                       }
+
+                       if (count == ARRAY_SIZE(bm->gpio))
+                               break;
+               }
+       }
+
+       list_for_each_entry_safe(gpio, tmp, &list, node) {
+               list_del(&gpio->node);
+               kfree(gpio);
+       }
+
+       return 0;
+}
+
+static int bm1000_pcie_get_irq_acpi(struct device *dev,
+                                   struct acpi_device *res_dev,
+                                   struct bm1000_pcie *bm)
+{
+       struct dw_pcie_rp *pp = &bm->pci->pp;
+       struct resource res;
+       int ret;
+
+       memset(&res, 0, sizeof(res));
+
+       ret = acpi_irq_get(res_dev->handle, 0, &res);
+       if (ret) {
+               dev_err(dev, "failed to get irq %d\n", 0);
+               return ret;
+       }
+
+       if (res.flags & IORESOURCE_BITS) {
+               struct irq_data *irqd;
+
+               irqd = irq_get_irq_data(res.start);
+               if (!irqd)
+                       return -ENXIO;
+
+               irqd_set_trigger_type(irqd, res.flags & IORESOURCE_BITS);
+       }
+
+       pp->irq = res.start;
+
+       ret = devm_request_irq(dev, pp->irq, bm1000_pcie_aer_irq_handler,
+                              IRQF_SHARED, "bm1000-pcie-aer", bm);
+       if (ret) {
+               dev_err(dev, "failed to request irq %d\n", pp->irq);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct regmap *bm1000_regmap;
+
+static const struct regmap_config bm1000_pcie_syscon_regmap_config = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4
+};
+
+static struct regmap *bm1000_pcie_get_gpr_acpi(struct bm1000_pcie *bm)
+{
+       struct device *dev;
+       struct acpi_device *adev;
+       acpi_handle handle;
+       acpi_status status = AE_OK;
+       struct list_head list, *pos;
+       struct resource *res;
+       void __iomem *base;
+       struct regmap *regmap = NULL;
+       struct regmap_config config = bm1000_pcie_syscon_regmap_config;
+       unsigned long flags = IORESOURCE_MEM;
+       int ret;
+
+       if (bm1000_regmap)
+               return bm1000_regmap;
+
+       status = acpi_get_handle(NULL, "\\_SB.PGPR", &handle);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "failed to get PCIe GPR device\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       adev = acpi_fetch_acpi_dev(handle);
+       if (!adev) {
+               dev_err(dev, "failed to process PCIe GPR handle\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       dev = &adev->dev;
+       INIT_LIST_HEAD(&list);
+       ret = acpi_dev_get_resources(adev, &list,
+                                    acpi_dev_filter_resource_type_cb,
+                                    (void *)flags);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse _CRS method, error code %d\n", ret);
+               return NULL;
+       }
+
+       if (ret != 1) {
+               dev_err(dev, "invalid number of MEM resources present in _CRS (%i, need 1)\n", ret);
+               goto ret;
+       }
+
+       pos = list.next;
+       res = list_entry(pos, struct resource_entry, node)->res;
+
+       base = devm_ioremap(dev, res->start, resource_size(res));
+       if (!base) {
+               dev_err(dev, "error with ioremap\n");
+               goto ret;
+       }
+
+       config.max_register = resource_size(res) - 4;
+
+       regmap = devm_regmap_init_mmio(dev, base, &config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "regmap init failed\n");
+               devm_iounmap(dev, base);
+               goto ret;
+       }
+
+       dev_dbg(dev, "regmap %pR registered\n", res);
+
+       bm1000_regmap = regmap;
+
+ret:
+       acpi_dev_free_resource_list(&list);
+       return regmap;
+}
+
+static int bm1000_get_acpi_data(struct device *dev, struct bm1000_pcie *bm,
+                               struct baikal_pcie_acpi_data *mem_data)
+{
+       struct acpi_device *adev = to_acpi_device(dev), *res_dev;
+       int ret;
+
+       bm->gpr = bm1000_pcie_get_gpr_acpi(bm);
+       if (IS_ERR_OR_NULL(bm->gpr)) {
+               dev_err(dev, "No PCIe GPR specified\n");
+               return -EINVAL;
+       }
+
+       ret = bm1000_pcie_get_res_acpi(adev, &res_dev, bm, mem_data);
+       if (ret) {
+               dev_err(dev, "failed to get resource info\n");
+               return ret;
+       }
+
+       ret = bm1000_pcie_get_irq_acpi(dev, res_dev, bm);
+       if (ret) {
+               dev_err(dev, "failed to get irq info\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bm1000_pcie_init(struct pci_config_window *cfg)
+{
+       struct device *dev = cfg->parent;
+       struct bm1000_pcie *bm;
+       struct dw_pcie *pci;
+       struct dw_pcie_rp *pp;
+       struct baikal_pcie_acpi_data mem_data = {};
+       int ret;
+
+       pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+       if (!pci)
+               return -ENOMEM;
+
+       pci->dev = dev;
+       pci->ops = &bm1000_pcie_rc_of_data.dw_pcie_ops;
+
+       bm = devm_kzalloc(dev, sizeof(*bm), GFP_KERNEL);
+       if (!bm)
+               return -ENOMEM;
+
+       cfg->priv = bm;
+       bm->pci = pci;
+       dev_set_drvdata(dev, bm);
+
+       ret = bm1000_get_acpi_data(dev, bm, &mem_data);
+       if (ret) {
+               dev_err(dev, "failed to get data from ACPI\n");
+               return ret;
+       }
+
+       pp = &pci->pp;
+       raw_spin_lock_init(&pp->lock);
+       pp->ops = &bm1000_pcie_host_ops;
+       pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, pp->cfg0_base,
+                                                  pp->cfg0_size);
+       if (!pp->va_cfg0_base) {
+               dev_err(dev, "error with ioremap\n");
+               return -ENOMEM;
+       }
+
+       ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
+       if (ret) {
+               dev_err(dev, "failed to enable DMA\n");
+               return ret;
+       }
+
+       ret = bm1000_pcie_host_init(pp);
+       if (ret) {
+               dev_err(dev, "Failed to initialize host\n");
+               return ret;
+       }
+
+       dw_pcie_version_detect(pci);
+       bm1000_pcie_setup_rc_acpi(pp, &mem_data);
+
+       if (!bm1000_pcie_link_up(bm->pci))
+               bm1000_pcie_start_link(bm->pci);
+
+       /* Link will be retrained by 'bm1000_pcie_map_bus()' */
+       bm->retrained = false;
+       return 0;
+}
+
+static void __iomem *bm1000_pcie_map_bus(struct pci_bus *bus,
+                                        unsigned int devfn, int where)
+{
+       struct pci_config_window *cfg = bus->sysdata;
+       struct bm1000_pcie *bm = cfg->priv;
+       unsigned int devfn_shift = cfg->ops->bus_shift - 8;
+       unsigned int busn = bus->number;
+       void __iomem *base;
+
+       if (!bm->retrained) {
+               struct pci_host_bridge **root_bridge = &bm->pci->pp.bridge;
+
+               if (*root_bridge == NULL) {
+                       *root_bridge = to_pci_host_bridge(bus->bridge);
+               } else if ((*root_bridge)->bus->is_added) {
+                       struct acpi_device *adev = to_acpi_device(cfg->parent);
+                       struct acpi_pci_root *root = adev->driver_data;
+
+                       if (root->bus == (*root_bridge)->bus) {
+                               pci_bus_add_devices(root->bus);
+                               bm1000_pcie_link_speed_fixup(bm, root->bus);
+                               bm->retrained = true;
+                       }
+               }
+       }
+
+       if (bus->number != cfg->busr.start && !bm1000_pcie_link_up(bm->pci))
+               return NULL;
+
+       if (bus->number == cfg->busr.start) {
+               /*
+                * The DW PCIe core doesn't filter out transactions to other
+                * devices/functions on the root bus num, so we do this here.
+                */
+               if (PCI_SLOT(devfn) > 0)
+                       return NULL;
+               else
+                       return bm->pci->dbi_base + where;
+       }
+
+       if (busn < cfg->busr.start || busn > cfg->busr.end)
+               return NULL;
+
+       busn -= cfg->busr.start;
+       base = cfg->win + (busn << cfg->ops->bus_shift);
+       return base + (devfn << devfn_shift) + where;
+}
+
+const struct pci_ecam_ops baikal_m_pcie_ecam_ops = {
+       .bus_shift      = 20,
+       .init           = bm1000_pcie_init,
+       .pci_ops        = {
+               .map_bus        = bm1000_pcie_map_bus,
+               .read           = pci_generic_config_read,
+               .write          = pci_generic_config_write
+       }
+};
+#endif
+
+#define BS1000_PCIE_APB_PE_GEN_CTRL3                   0x58
+#define BS1000_PCIE_APB_PE_GEN_CTRL3_LTSSM_EN          BIT(0)
+
+#define BS1000_PCIE_APB_PE_LINK_DBG2                   0xb4
+#define BS1000_PCIE_APB_PE_LINK_DBG2_SMLH_LINK_UP      BIT(6)
+#define BS1000_PCIE_APB_PE_LINK_DBG2_RDLH_LINK_UP      BIT(7)
+
+#define BS1000_PCIE_APB_PE_ERR_STS                     0xe0
+#define BS1000_PCIE_APB_PE_INT_STS                     0xe8
+
+#define BS1000_PCIE0_P0_DBI_BASE                       0x39000000
+#define BS1000_PCIE0_P1_DBI_BASE                       0x39400000
+#define BS1000_PCIE1_P0_DBI_BASE                       0x3d000000
+#define BS1000_PCIE1_P1_DBI_BASE                       0x3d400000
+#define BS1000_PCIE2_P0_DBI_BASE                       0x45000000
+#define BS1000_PCIE2_P1_DBI_BASE                       0x45400000
+
+struct bs1000_pcie {
+       struct dw_pcie          *pci;
+       void __iomem            *apb_base;
+       u64                     cpu_addr_mask;
+};
+
+static void bs1000_pcie_ep_init(struct dw_pcie_ep *ep)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+       enum pci_barno bar;
+
+       for (bar = BAR_0; bar <= BAR_5; bar++)
+               dw_pcie_ep_reset_bar(pci, bar);
+}
+
+static int bs1000_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
+                                   enum pci_epc_irq_type type,
+                                   u16 interrupt_num)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+
+       switch (type) {
+       case PCI_EPC_IRQ_LEGACY:
+               return dw_pcie_ep_raise_legacy_irq(ep, func_no);
+       case PCI_EPC_IRQ_MSI:
+               return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
+       default:
+               dev_err(pci->dev, "UNKNOWN IRQ type\n");
+               return -EINVAL;
+       }
+}
+
+static const struct pci_epc_features bs1000_pcie_epc_features = {
+       .linkup_notifier = false,
+       .msi_capable = true,
+       .msix_capable = false,
+       .reserved_bar = BIT(BAR_3) | BIT(BAR_5),
+};
+
+static const struct pci_epc_features*
+bs1000_pcie_ep_get_features(struct dw_pcie_ep *ep)
+{
+       return &bs1000_pcie_epc_features;
+}
+
+static const struct dw_pcie_ep_ops bs1000_pcie_ep_ops = {
+       .ep_init        = bs1000_pcie_ep_init,
+       .raise_irq      = bs1000_pcie_ep_raise_irq,
+       .get_features   = bs1000_pcie_ep_get_features,
+};
+
+static int bs1000_get_resources(struct platform_device *pdev,
+                               struct dw_pcie *pci,
+                               const struct baikal_pcie_of_data *data)
+{
+       struct bs1000_pcie *bs = platform_get_drvdata(pdev);
+       struct device *dev = pci->dev;
+       struct resource *res;
+
+       bs->pci = pci;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
+       if (!res) {
+               dev_err(dev, "failed to find \"dbi\" region\n");
+               return -EINVAL;
+       }
+
+       pci->dbi_base = devm_pci_remap_cfg_resource(dev, res);
+       if (IS_ERR(pci->dbi_base))
+               return PTR_ERR(pci->dbi_base);
+
+       if (res->start == BS1000_PCIE0_P0_DBI_BASE ||
+           res->start == BS1000_PCIE0_P1_DBI_BASE ||
+           res->start == BS1000_PCIE1_P0_DBI_BASE ||
+           res->start == BS1000_PCIE1_P1_DBI_BASE ||
+           res->start == BS1000_PCIE2_P0_DBI_BASE ||
+           res->start == BS1000_PCIE2_P1_DBI_BASE) {
+               bs->cpu_addr_mask = 0x7fffffffff;
+       } else {
+               bs->cpu_addr_mask = 0xffffffffff;
+       }
+
+       bs->apb_base = devm_platform_ioremap_resource_byname(pdev, "apb");
+       if (IS_ERR(bs->apb_base))
+               return PTR_ERR(bs->apb_base);
+
+       if (data->mode == DW_PCIE_EP_TYPE) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
+               if (!res)
+                       return -EINVAL;
+
+               pci->ep.phys_base = res->start;
+               pci->ep.addr_size = resource_size(res);
+               pci->ep.ops = &bs1000_pcie_ep_ops;
+               pci->dbi_base2 = pci->dbi_base + 0x100000;
+       }
+
+       return 0;
+}
+
+static u64 bs1000_pcie_cpu_addr_fixup(struct dw_pcie *pci, u64 cpu_addr)
+{
+       struct bs1000_pcie *bs = dev_get_drvdata(pci->dev);
+
+       return cpu_addr & bs->cpu_addr_mask;
+}
+
+static int bs1000_pcie_link_up(struct dw_pcie *pci)
+{
+       struct bs1000_pcie *bs = dev_get_drvdata(pci->dev);
+       u32 reg;
+
+       reg = readl(bs->apb_base + BS1000_PCIE_APB_PE_LINK_DBG2);
+       return ((reg & BAIKAL_PCIE_LTSSM_MASK) == BAIKAL_PCIE_LTSSM_STATE_L0) &&
+               (reg & BS1000_PCIE_APB_PE_LINK_DBG2_SMLH_LINK_UP) &&
+               (reg & BS1000_PCIE_APB_PE_LINK_DBG2_RDLH_LINK_UP);
+}
+
+static int bs1000_pcie_start_link(struct dw_pcie *pci)
+{
+       struct bs1000_pcie *bs = dev_get_drvdata(pci->dev);
+       u32 reg;
+
+       reg = readl(bs->apb_base + BS1000_PCIE_APB_PE_GEN_CTRL3);
+       reg |= BS1000_PCIE_APB_PE_GEN_CTRL3_LTSSM_EN;
+       writel(reg, bs->apb_base + BS1000_PCIE_APB_PE_GEN_CTRL3);
+       return 0;
+}
+
+static int bs1000_pcie_host_init(struct dw_pcie_rp *pp)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+       u16 exp_cap_off;
+       u16 ext_cap_err_off;
+       u32 reg;
+
+       /* Enable error reporting */
+       ext_cap_err_off = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_ERR);
+       reg = dw_pcie_readl_dbi(pci, ext_cap_err_off + PCI_ERR_ROOT_COMMAND);
+       reg |= PCI_ERR_ROOT_CMD_COR_EN      |
+              PCI_ERR_ROOT_CMD_NONFATAL_EN |
+              PCI_ERR_ROOT_CMD_FATAL_EN;
+       dw_pcie_writel_dbi(pci, ext_cap_err_off + PCI_ERR_ROOT_COMMAND, reg);
+
+       exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       reg = dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_DEVCTL);
+       reg |= PCI_EXP_DEVCTL_CERE  |
+              PCI_EXP_DEVCTL_NFERE |
+              PCI_EXP_DEVCTL_FERE  |
+              PCI_EXP_DEVCTL_URRE;
+       dw_pcie_writew_dbi(pci, exp_cap_off + PCI_EXP_DEVCTL, reg);
+
+       reg = dw_pcie_readl_dbi(pci, exp_cap_off + PCI_EXP_RTCTL);
+       reg |= PCI_EXP_RTCTL_SECEE  |
+              PCI_EXP_RTCTL_SENFEE |
+              PCI_EXP_RTCTL_SEFEE  |
+              PCI_EXP_RTCTL_PMEIE;
+       dw_pcie_writel_dbi(pci, exp_cap_off + PCI_EXP_RTCTL, reg);
+
+       return 0;
+}
+
+static irqreturn_t bs1000_pcie_intr_irq_handler(int irq, void *arg)
+{
+       struct bs1000_pcie *bs = arg;
+       struct dw_pcie *pci = bs->pci;
+       struct device *dev = pci->dev;
+       u16 exp_cap_off;
+       u16 ext_cap_err_off;
+       u16 dev_sta;
+       u32 cor_err;
+       u32 root_err;
+       u32 uncor_err;
+       u32 apb_pe_err;
+       u32 apb_pe_int;
+
+       apb_pe_err = readl(bs->apb_base + BS1000_PCIE_APB_PE_ERR_STS);
+       apb_pe_int = readl(bs->apb_base + BS1000_PCIE_APB_PE_INT_STS);
+
+       exp_cap_off     = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       ext_cap_err_off = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_ERR);
+
+       uncor_err       = dw_pcie_readl_dbi(pci,
+                                       ext_cap_err_off + PCI_ERR_UNCOR_STATUS);
+       cor_err         = dw_pcie_readl_dbi(pci,
+                                       ext_cap_err_off + PCI_ERR_COR_STATUS);
+       root_err        = dw_pcie_readl_dbi(pci,
+                                       ext_cap_err_off + PCI_ERR_ROOT_STATUS);
+       dev_sta         = dw_pcie_readw_dbi(pci,
+                                       exp_cap_off + PCI_EXP_DEVSTA);
+
+       writel(apb_pe_err, bs->apb_base + BS1000_PCIE_APB_PE_ERR_STS);
+       writel(apb_pe_int, bs->apb_base + BS1000_PCIE_APB_PE_INT_STS);
+
+       dw_pcie_writel_dbi(pci,
+                          ext_cap_err_off + PCI_ERR_UNCOR_STATUS, uncor_err);
+       dw_pcie_writel_dbi(pci,
+                          ext_cap_err_off + PCI_ERR_COR_STATUS, cor_err);
+       dw_pcie_writel_dbi(pci,
+                          ext_cap_err_off + PCI_ERR_ROOT_STATUS, root_err);
+       dw_pcie_writew_dbi(pci,
+                          exp_cap_off + PCI_EXP_DEVSTA, dev_sta);
+
+       dev_err(dev,
+               "DevSta:0x%04x RootErr:0x%x UncorErr:0x%x CorErr:0x%x ApbErr:0x%x ApbInt:0x%x\n",
+               dev_sta, root_err, uncor_err, cor_err, apb_pe_err, apb_pe_int);
+
+       return IRQ_HANDLED;
+}
+
+static const struct dw_pcie_host_ops bs1000_pcie_host_ops = {
+       .host_init = bs1000_pcie_host_init,
+};
+
+static int bs1000_add_pcie_port(struct platform_device *pdev)
+{
+       struct bs1000_pcie *bs = platform_get_drvdata(pdev);
+       struct device *dev = &pdev->dev;
+       struct dw_pcie *pci = bs->pci;
+       struct dw_pcie_rp *pp = &pci->pp;
+       int ret;
+
+       pp->irq = platform_get_irq_byname(pdev, "intr");
+       if (pp->irq < 0) {
+               dev_err(dev, "failed to get \"intr\" IRQ\n");
+               return pp->irq;
+       }
+
+       ret = devm_request_irq(dev, pp->irq, bs1000_pcie_intr_irq_handler,
+                              IRQF_SHARED, "bs1000-pcie-intr", bs);
+       if (ret) {
+               dev_err(dev, "failed to request IRQ %d\n", pp->irq);
+               return ret;
+       }
+
+       pp->num_vectors = MAX_MSI_IRQS;
+       pp->ops = &bs1000_pcie_host_ops;
+       ret = dw_pcie_host_init(pp);
+       if (ret) {
+               dev_err(dev, "failed to initialize host\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)
+static void dw_pcie_writel_ob_unroll(struct dw_pcie *pci, u32 index, u32 reg,
+                                    u32 val)
+{
+       dw_pcie_write(pci->atu_base +
+                     PCIE_ATU_UNROLL_BASE(PCIE_ATU_REGION_DIR_OB, index) +
+                     reg, 0x4, val);
+}
+
+static void bs1000_pcie_prog_outbound_atu(struct dw_pcie *pci, int index,
+                                         int type, u64 cpu_addr, u64 pci_addr,
+                                         u32 size, u32 flags)
+{
+       u32 retries, val;
+
+       cpu_addr = bs1000_pcie_cpu_addr_fixup(pci, cpu_addr);
+
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE,
+                                lower_32_bits(cpu_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE,
+                                upper_32_bits(cpu_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_LIMIT,
+                                lower_32_bits(cpu_addr + size - 1));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET,
+                                lower_32_bits(pci_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
+                                upper_32_bits(pci_addr));
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, type);
+       dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2,
+                                PCIE_ATU_ENABLE | flags);
+
+       /*
+        * Make sure ATU enable takes effect before any subsequent config
+        * and I/O accesses.
+        */
+       for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; ++retries) {
+               dw_pcie_read(pci->atu_base +
+                            PCIE_ATU_UNROLL_BASE(index, PCIE_ATU_REGION_DIR_OB) +
+                            PCIE_ATU_UNR_REGION_CTRL2, 0x4, &val);
+               if (val & PCIE_ATU_ENABLE)
+                       return;
+
+               mdelay(LINK_WAIT_IATU);
+       }
+       dev_err(pci->dev, "Outbound iATU is not being enabled\n");
+}
+
+static void bs1000_pcie_setup_rc_acpi(struct dw_pcie_rp *pp,
+                                     const struct baikal_pcie_acpi_data *mem_data)
+{
+       struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
+       acpi_status status;
+       u64 lanes;
+       u32 val;
+       int i;
+
+       /*
+        * Enable DBI read-only registers for writing/updating configuration.
+        * Write permission gets disabled towards the end of this function.
+        */
+       dw_pcie_dbi_ro_wr_en(pci);
+
+       val = dw_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
+       val &= ~PORT_LINK_FAST_LINK_MODE;
+       val |= PORT_LINK_DLL_LINK_EN;
+       dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
+
+       status = acpi_evaluate_integer(to_acpi_device(pci->dev)->handle,
+                                      "NUML", NULL, &lanes);
+       if (ACPI_FAILURE(status)) {
+               dev_dbg(pci->dev, "failed to get num-lanes\n");
+       } else {
+               pci->num_lanes = lanes;
+
+               /* Set the number of lanes */
+               val &= ~PORT_LINK_FAST_LINK_MODE;
+               val &= ~PORT_LINK_MODE_MASK;
+               switch (pci->num_lanes) {
+               case 1:
+                       val |= PORT_LINK_MODE_1_LANES;
+                       break;
+               case 2:
+                       val |= PORT_LINK_MODE_2_LANES;
+                       break;
+               case 4:
+                       val |= PORT_LINK_MODE_4_LANES;
+                       break;
+               case 8:
+                       val |= PORT_LINK_MODE_8_LANES;
+                       break;
+               default:
+                       dev_err(pci->dev, "NUML %u: invalid value\n", pci->num_lanes);
+                       goto skip_lanes;
+               }
+               dw_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
+
+               /* Set link width speed control register */
+               val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+               val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+               switch (pci->num_lanes) {
+               case 1:
+                       val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+                       break;
+               case 2:
+                       val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+                       break;
+               case 4:
+                       val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
+                       break;
+               case 8:
+                       val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
+                       break;
+               }
+               dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+       }
+
+skip_lanes:
+       /* Setup RC BARs */
+       dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x00000004);
+       dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x00000000);
+
+       /* Setup interrupt pins */
+       val = dw_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
+       val &= 0xffff00ff;
+       val |= 0x00000100;
+       dw_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
+
+       /* Setup bus numbers */
+       val = dw_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
+       val &= 0xff000000;
+       val |= 0x00ff0100;
+       dw_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val);
+
+       /* Setup command register */
+       val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
+       val &= 0xffff0000;
+       val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+               PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+       dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
+
+       pci->iatu_unroll_enabled = 1;
+       pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
+       pci->atu_size = SZ_4K;
+       pci->num_ob_windows = 4;
+       pci->num_ib_windows = 0;
+
+       for (i = 0; i < pci->num_ob_windows; ++i)
+               dw_pcie_disable_atu(pci, PCIE_ATU_REGION_DIR_OB, i);
+
+       /* Program ATU */
+       bs1000_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_CFG0,
+                                     pp->cfg0_base, 0,
+                                     pp->cfg0_size,
+                                     PCIE_IATU_REGION_CTRL_2_REG_SHIFT_MODE);
+       bs1000_pcie_prog_outbound_atu(pci, 1, PCIE_ATU_TYPE_CFG1,
+                                     pp->cfg0_base, 0,
+                                     pp->cfg0_size,
+                                     PCIE_IATU_REGION_CTRL_2_REG_SHIFT_MODE);
+       bs1000_pcie_prog_outbound_atu(pci, 2, PCIE_ATU_TYPE_MEM,
+                                     mem_data->mem_base, mem_data->mem_bus_addr,
+                                     mem_data->mem_size, 0);
+       bs1000_pcie_prog_outbound_atu(pci, 3, PCIE_ATU_TYPE_IO,
+                                     pp->io_base, pp->io_bus_addr,
+                                     pp->io_size, 0);
+
+       dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
+
+       /* Program correct class for RC */
+       dw_pcie_writew_dbi(pci, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI);
+
+       val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
+       val |= PORT_LOGIC_SPEED_CHANGE;
+       dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
+
+       dw_pcie_dbi_ro_wr_dis(pci);
+}
+
+static int bs1000_pcie_get_res_acpi(struct acpi_device *adev,
+                                   struct acpi_device **res_dev,
+                                   struct bs1000_pcie *bs,
+                                   struct baikal_pcie_acpi_data *mem_data)
+{
+       struct device *dev = &adev->dev;
+       struct dw_pcie_rp *pp = &bs->pci->pp;
+       struct resource_entry *entry;
+       struct list_head list, *pos;
+       struct fwnode_handle *fwnode;
+       int ret;
+       unsigned long flags = IORESOURCE_MEM;
+
+       fwnode = fwnode_get_named_child_node(&adev->fwnode, "RES0");
+       if (!fwnode) {
+               dev_err(dev, "failed to get RES0 subdevice\n");
+               return -EINVAL;
+       }
+
+       *res_dev = to_acpi_device_node(fwnode);
+       if (!*res_dev) {
+               dev_err(dev, "RES0 is not an acpi device node\n");
+               return -EINVAL;
+       }
+
+       INIT_LIST_HEAD(&list);
+       ret = acpi_dev_get_resources(*res_dev, &list,
+                                    acpi_dev_filter_resource_type_cb,
+                                    (void *)flags);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse RES0._CRS method, error code %d\n", ret);
+               return ret;
+       }
+
+       if (ret != 3) {
+               dev_err(dev, "invalid number of MEM resources present in RES0._CRS (%i, need 3)\n", ret);
+               return -EINVAL;
+       }
+
+       /* ECAM */
+       pos = list.next;
+       entry = list_entry(pos, struct resource_entry, node);
+       pp->cfg0_size = resource_size(entry->res);
+       pp->cfg0_base = entry->res->start;
+
+       /* DBI */
+       pos = pos->next;
+       entry = list_entry(pos, struct resource_entry, node);
+       if (entry->res->start == BS1000_PCIE0_P0_DBI_BASE ||
+           entry->res->start == BS1000_PCIE0_P1_DBI_BASE ||
+           entry->res->start == BS1000_PCIE1_P0_DBI_BASE ||
+           entry->res->start == BS1000_PCIE1_P1_DBI_BASE ||
+           entry->res->start == BS1000_PCIE2_P0_DBI_BASE ||
+           entry->res->start == BS1000_PCIE2_P1_DBI_BASE) {
+               bs->cpu_addr_mask = 0x7fffffffff;
+       } else {
+               bs->cpu_addr_mask = 0xffffffffff;
+       }
+       bs->pci->dbi_base = devm_ioremap_resource(dev, entry->res);
+       if (IS_ERR(bs->pci->dbi_base)) {
+               dev_err(dev, "error with dbi ioremap\n");
+               ret = PTR_ERR(bs->pci->dbi_base);
+               return ret;
+       }
+
+       /* APB */
+       pos = pos->next;
+       entry = list_entry(pos, struct resource_entry, node);
+       bs->apb_base = devm_ioremap_resource(dev, entry->res);
+       if (IS_ERR(bs->apb_base)) {
+               dev_err(dev, "error with apb ioremap\n");
+               ret = PTR_ERR(bs->apb_base);
+               return ret;
+       }
+
+       acpi_dev_free_resource_list(&list);
+
+       /* Non-prefetchable memory */
+       INIT_LIST_HEAD(&list);
+       flags = IORESOURCE_MEM;
+       ret = acpi_dev_get_resources(adev, &list,
+                                    acpi_dev_filter_resource_type_cb,
+                                    (void *)flags);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse _CRS method, error code %d\n", ret);
+               return ret;
+       }
+
+       if (ret != 1) {
+               dev_err(dev, "invalid number of MEM resources present in _CRS (%i, need 1)\n", ret);
+               return -EINVAL;
+       }
+
+       pos = list.next;
+       entry = list_entry(pos, struct resource_entry, node);
+       mem_data->mem_base = entry->res->start;
+       mem_data->mem_size = resource_size(entry->res);
+       mem_data->mem_bus_addr = entry->res->start - entry->offset;
+
+       acpi_dev_free_resource_list(&list);
+
+       /* I/O */
+       INIT_LIST_HEAD(&list);
+       flags = IORESOURCE_IO;
+       ret = acpi_dev_get_resources(adev, &list,
+                                    acpi_dev_filter_resource_type_cb,
+                                    (void *)flags);
+       if (ret < 0) {
+               dev_err(dev, "failed to parse _CRS method, error code %d\n", ret);
+               return ret;
+       }
+
+       if (ret != 1) {
+               dev_err(dev, "invalid number of IO resources present in _CRS (%i, need 1)\n", ret);
+               return -EINVAL;
+       }
+
+       pos = list.next;
+       entry = list_entry(pos, struct resource_entry, node);
+       pp->io_base = entry->res->start;
+       pp->io_size = resource_size(entry->res);
+       pp->io_bus_addr = entry->res->start - entry->offset;
+
+       acpi_dev_free_resource_list(&list);
+       return 0;
+}
+
+static int bs1000_pcie_get_irq_acpi(struct device *dev,
+                                   struct acpi_device *res_dev,
+                                   struct bs1000_pcie *bs)
+{
+       struct dw_pcie_rp *pp = &bs->pci->pp;
+       struct resource res;
+       int ret;
+
+       memset(&res, 0, sizeof(res));
+
+       ret = acpi_irq_get(res_dev->handle, 0, &res);
+       if (ret) {
+               dev_err(dev, "failed to get irq %d\n", 0);
+               return ret;
+       }
+
+       if (res.flags & IORESOURCE_BITS) {
+               struct irq_data *irqd;
+
+               irqd = irq_get_irq_data(res.start);
+               if (!irqd) {
+                       return -ENXIO;
+               }
+
+               irqd_set_trigger_type(irqd, res.flags & IORESOURCE_BITS);
+       }
+
+       pp->irq = res.start;
+
+       ret = devm_request_irq(dev, pp->irq, bs1000_pcie_intr_irq_handler,
+                              IRQF_SHARED, "bs1000-pcie-intr", bs);
+       if (ret) {
+               dev_err(dev, "failed to request IRQ %d\n", pp->irq);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bs1000_get_acpi_data(struct device *dev, struct bs1000_pcie *bs,
+                               struct baikal_pcie_acpi_data *mem_data)
+{
+       struct acpi_device *adev = to_acpi_device(dev), *res_dev;
+       int ret;
+
+       ret = bs1000_pcie_get_res_acpi(adev, &res_dev, bs, mem_data);
+       if (ret) {
+               dev_err(dev, "failed to get resource info\n");
+               return ret;
+       }
+
+       ret = bs1000_pcie_get_irq_acpi(dev, res_dev, bs);
+       if (ret) {
+               dev_err(dev, "failed to get irq info\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int bs1000_pcie_init(struct pci_config_window *cfg)
+{
+       struct device *dev = cfg->parent;
+       struct bs1000_pcie *bs;
+       struct dw_pcie *pci;
+       struct dw_pcie_rp *pp;
+       struct baikal_pcie_acpi_data mem_data = {};
+       int ret;
+
+       pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+       if (!pci)
+               return -ENOMEM;
+
+       pci->dev = dev;
+       pci->ops = &bs1000_pcie_rc_of_data.dw_pcie_ops;
+
+       bs = devm_kzalloc(dev, sizeof(*bs), GFP_KERNEL);
+       if (!bs)
+               return -ENOMEM;
+
+       cfg->priv = bs;
+       bs->pci = pci;
+       dev_set_drvdata(dev, bs);
+
+       ret = bs1000_get_acpi_data(dev, bs, &mem_data);
+       if (ret) {
+               dev_err(dev, "failed to get data from ACPI\n");
+               return ret;
+       }
+
+       pp = &pci->pp;
+       raw_spin_lock_init(&pp->lock);
+       pp->ops = &bs1000_pcie_host_ops;
+       pp->va_cfg0_base = devm_pci_remap_cfgspace(dev, pp->cfg0_base,
+                                                  pp->cfg0_size);
+       if (!pp->va_cfg0_base) {
+               dev_err(dev, "error with ioremap\n");
+               return -ENOMEM;
+       }
+
+       ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64));
+       if (ret) {
+               dev_err(dev, "failed to enable DMA\n");
+               return ret;
+       }
+
+       ret = bs1000_pcie_host_init(pp);
+       if (ret) {
+               dev_err(dev, "failed to initialize host\n");
+               return ret;
+       }
+
+       dw_pcie_version_detect(pci);
+       bs1000_pcie_setup_rc_acpi(pp, &mem_data);
+
+       return 0;
+}
+
+static void __iomem *bs1000_pcie_map_bus(struct pci_bus *bus,
+                                        unsigned int devfn, int where)
+{
+       struct pci_config_window *cfg = bus->sysdata;
+       struct bs1000_pcie *bs = cfg->priv;
+       unsigned int devfn_shift = cfg->ops->bus_shift - 8;
+       unsigned int busn = bus->number;
+       void __iomem *base;
+
+       if (bs->pci->pp.bridge == NULL)
+               bs->pci->pp.bridge = to_pci_host_bridge(bus->bridge);
+
+       if (bus->number != cfg->busr.start && !bs1000_pcie_link_up(bs->pci))
+               return NULL;
+
+       if (bus->number == cfg->busr.start) {
+               /*
+                * The DW PCIe core doesn't filter out transactions to other
+                * devices/functions on the root bus num, so we do this here.
+                */
+               if (PCI_SLOT(devfn) > 0)
+                       return NULL;
+               else
+                       return bs->pci->dbi_base + where;
+       }
+
+       if (busn < cfg->busr.start || busn > cfg->busr.end)
+               return NULL;
+
+       busn -= cfg->busr.start;
+       base = cfg->win + (busn << cfg->ops->bus_shift);
+       return base + (devfn << devfn_shift) + where;
+}
+
+const struct pci_ecam_ops baikal_s_pcie_ecam_ops = {
+       .bus_shift      = 20,
+       .init           = bs1000_pcie_init,
+       .pci_ops        = {
+               .map_bus        = bs1000_pcie_map_bus,
+               .read           = pci_generic_config_read,
+               .write          = pci_generic_config_write
+       }
+};
+#endif
+
+union baikal_pcie {
+       struct bm1000_pcie      bm1000_pcie;
+       struct bs1000_pcie      bs1000_pcie;
+};
+
+static int baikal_pcie_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct dw_pcie *pci;
+       union baikal_pcie *bp;
+       const struct baikal_pcie_of_data *data;
+       int ret;
+
+       data = of_device_get_match_data(dev);
+       if (!data)
+               return -EINVAL;
+
+       bp = devm_kzalloc(dev, sizeof(*bp), GFP_KERNEL);
+       if (!bp)
+               return -ENOMEM;
+
+       pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
+       if (!pci)
+               return -ENOMEM;
+
+       pci->dev = dev;
+       pci->ops = &data->dw_pcie_ops;
+
+       platform_set_drvdata(pdev, bp);
+       ret = data->get_resources(pdev, pci, data);
+       if (ret)
+               return ret;
+
+       switch (data->mode) {
+       case DW_PCIE_RC_TYPE:
+               ret = data->add_pcie_port(pdev);
+               if (ret)
+                       return ret;
+
+               break;
+       case DW_PCIE_EP_TYPE:
+               ret = dw_pcie_ep_init(&pci->ep);
+               if (ret) {
+                       dev_err(dev, "failed to initialize endpoint\n");
+                       return ret;
+               }
+
+               break;
+       default:
+               dev_err(dev, "INVALID device type %d\n", data->mode);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct baikal_pcie_of_data bm1000_pcie_rc_of_data = {
+       .mode = DW_PCIE_RC_TYPE,
+       .dw_pcie_ops = {
+               .link_up        = bm1000_pcie_link_up,
+               .start_link     = bm1000_pcie_start_link,
+       },
+       .get_resources = bm1000_get_resources,
+       .add_pcie_port = bm1000_add_pcie_port,
+};
+
+static const struct baikal_pcie_of_data bs1000_pcie_rc_of_data = {
+       .mode = DW_PCIE_RC_TYPE,
+       .dw_pcie_ops = {
+               .cpu_addr_fixup = bs1000_pcie_cpu_addr_fixup,
+               .link_up        = bs1000_pcie_link_up,
+               .start_link     = bs1000_pcie_start_link,
+       },
+       .get_resources = bs1000_get_resources,
+       .add_pcie_port = bs1000_add_pcie_port,
+};
+
+static const struct baikal_pcie_of_data bs1000_pcie_ep_of_data = {
+       .mode = DW_PCIE_EP_TYPE,
+       .dw_pcie_ops = {
+               .cpu_addr_fixup = bs1000_pcie_cpu_addr_fixup,
+               .link_up        = bs1000_pcie_link_up,
+               .start_link     = bs1000_pcie_start_link,
+       },
+       .get_resources = bs1000_get_resources,
+};
+
+static const struct of_device_id baikal_pcie_of_match[] = {
+       {
+               .compatible = "baikal,bm1000-pcie",
+               .data = &bm1000_pcie_rc_of_data,
+       },
+       {
+               .compatible = "baikal,bs1000-pcie",
+               .data = &bs1000_pcie_rc_of_data,
+       },
+       {
+               .compatible = "baikal,bs1000-pcie-ep",
+               .data = &bs1000_pcie_ep_of_data,
+       },
+       { },
+};
+
+static struct platform_driver baikal_pcie_driver = {
+       .driver = {
+               .name = "baikal-pcie",
+               .of_match_table = baikal_pcie_of_match,
+               .suppress_bind_attrs = true,
+       },
+       .probe = baikal_pcie_probe,
+};
+builtin_platform_driver(baikal_pcie_driver);
diff --git a/drivers/pci/controller/dwc/pcie-baikal-tune.c b/drivers/pci/controller/dwc/pcie-baikal-tune.c
new file mode 100644 (file)
index 0000000..80e6f74
--- /dev/null
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BM1000 PCIe Controller & Phy Gen3 equalization parameters fine tune.
+ *
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/property.h>
+#include <linux/debugfs.h>
+
+#include "pcie-designware.h"
+#include "pcie-baikal.h"
+
+static int gen3_eq_fb_mode = -1;
+module_param(gen3_eq_fb_mode, int, 0444);
+MODULE_PARM_DESC(gen3_eq_fb_mode, "feedback mode (0 - FOM + DIR, 1 - FOM only)");
+static int gen3_eq_psets = -1;
+module_param(gen3_eq_psets, int, 0444);
+MODULE_PARM_DESC(gen3_eq_psets, "initial presets");
+static int phy_rx_agc = -1;
+module_param(phy_rx_agc, int, 0444);
+MODULE_PARM_DESC(phy_rx_agc,
+       "Phy RX AGC gain ([7:4] - Pre-CTLE gain, [3:0] - Post-CTLE gain)");
+static int phy_rx_ctle = -1;
+module_param(phy_rx_ctle, int, 0444);
+MODULE_PARM_DESC(phy_rx_ctle, "Phy RX CTLE control ([7:4] - zero, [3:0] - pole)");
+static int phy_rx_dfe = -1;
+module_param(phy_rx_dfe, int, 0444);
+MODULE_PARM_DESC(phy_rx_dfe, "Phy RX DFE control (0 - disable, 1 - enable)");
+static int phy_tx_gain = -1;
+module_param(phy_tx_gain, int, 0444);
+MODULE_PARM_DESC(phy_tx_gain, "Phy TX gain value");
+static int phy_tx_turbo = -1;
+module_param(phy_tx_turbo, int, 0444);
+MODULE_PARM_DESC(phy_tx_turbo, "Phy TX turbo mode (0 - disable, 1 - enable)");
+static int phy_rx_ctle_pole = -1;
+module_param(phy_rx_ctle_pole, int, 0444);
+MODULE_PARM_DESC(phy_rx_ctle_pole, "Phy RX CTLE pole range for VMA adaptation "
+                                  "([7:4] - max, [3:0] - min)");
+static bool debugfs = false;
+module_param(debugfs, bool, 0444);
+MODULE_PARM_DESC(debugfs, "Phy debugfs monitor enable");
+static bool notune = false;
+module_param(notune, bool, 0444);
+MODULE_PARM_DESC(notune, "Gen3 equalization fine tune disable");
+
+static void bm1000_pcie_tune_debugfs_populate(struct dw_pcie *pci);
+
+/* Baikal-M PCIe PHY registers access */
+#define BM1000_PCIE_AXI2MGM_LINENUM                    0xd04
+#define BM1000_PCIE_AXI2MGM_LINENUM_LANE_SEL_MASK      GENMASK(7, 0)
+#define BM1000_PCIE_AXI2MGM_ADDRCTL                    0xd08
+#define BM1000_PCIE_AXI2MGM_ADDRCTL_BUSY               BIT(31)
+#define BM1000_PCIE_AXI2MGM_ADDRCTL_DONE               BIT(30)
+#define BM1000_PCIE_AXI2MGM_ADDRCTL_RW_FLAG            BIT(29)
+#define BM1000_PCIE_AXI2MGM_ADDRCTL_PHY_ADDR_MASK      GENMASK(20, 0)
+#define BM1000_PCIE_AXI2MGM_WRITEDATA                  0xd0c
+#define BM1000_PCIE_AXI2MGM_WRITEDATA_DATA_MASK                GENMASK(15, 0)
+#define BM1000_PCIE_AXI2MGM_READDATA                   0xd10
+#define BM1000_PCIE_AXI2MGM_READDATA_DATA_MASK         GENMASK(15, 0)
+
+#define BM1000_PCIE_PHY_REG_RETRIES            10
+#define BM1000_PCIE_PHY_REG_RETRY_TIMEOUT      100
+
+static int bm1000_pcie_phy_done(struct dw_pcie *pci)
+{
+       u32 reg;
+       int retries;
+
+       for (retries = 0; retries < BM1000_PCIE_PHY_REG_RETRIES; ++retries) {
+               reg = dw_pcie_readl_dbi(pci, BM1000_PCIE_AXI2MGM_ADDRCTL);
+               if (reg & BM1000_PCIE_AXI2MGM_ADDRCTL_DONE)
+                       return 0;
+               udelay(BM1000_PCIE_PHY_REG_RETRY_TIMEOUT);
+       }
+       return -ETIMEDOUT;
+}
+
+static int bm1000_pcie_phy_read(struct dw_pcie *pci, u8 lane,
+                               u32 addr, u16 *value)
+{
+       int ret;
+
+       bm1000_pcie_phy_enable(pci);
+       dw_pcie_writel_dbi(pci, BM1000_PCIE_AXI2MGM_LINENUM, lane);
+       dw_pcie_writel_dbi(pci, BM1000_PCIE_AXI2MGM_ADDRCTL,
+                          addr & BM1000_PCIE_AXI2MGM_ADDRCTL_PHY_ADDR_MASK);
+       ret = bm1000_pcie_phy_done(pci);
+       if (ret == 0)
+               *value = dw_pcie_readl_dbi(pci, BM1000_PCIE_AXI2MGM_READDATA) &
+                        BM1000_PCIE_AXI2MGM_READDATA_DATA_MASK;
+       bm1000_pcie_phy_disable(pci);
+       return ret;
+}
+
+static int bm1000_pcie_phy_write(struct dw_pcie *pci, u8 lanes,
+                                u32 addr, u16 value)
+{
+       int ret;
+
+       bm1000_pcie_phy_enable(pci);
+       dw_pcie_writel_dbi(pci, BM1000_PCIE_AXI2MGM_LINENUM, lanes);
+       dw_pcie_writel_dbi(pci, BM1000_PCIE_AXI2MGM_WRITEDATA, value);
+       dw_pcie_writel_dbi(pci, BM1000_PCIE_AXI2MGM_ADDRCTL,
+                          (addr & BM1000_PCIE_AXI2MGM_ADDRCTL_PHY_ADDR_MASK) |
+                          BM1000_PCIE_AXI2MGM_ADDRCTL_RW_FLAG);
+       ret = bm1000_pcie_phy_done(pci);
+       bm1000_pcie_phy_disable(pci);
+       return ret;
+}
+
+/* Baikal-M PCIe RX/TX equalizers fine tune */
+#define BM1000_PCIE_GEN3_EQ_CONTROL                    0x8a8
+#define BM1000_PCIE_GEN3_EQ_FOM_INC_INITIAL_EVAL       BIT(24)
+#define BM1000_PCIE_GEN3_EQ_PSET_REQ_VEC_MASK          GENMASK(23, 8)
+#define BM1000_PCIE_GEN3_EQ_FB_MODE_MASK               GENMASK(3, 0)
+
+#define BM1000_PCIE_PHY_RX_CFG_2                                       0x18002
+#define BM1000_PCIE_PHY_RX_CFG_2_PCS_SDS_RX_AGC_MVAL                   GENMASK(9, 0)
+#define BM1000_PCIE_PHY_RX_CFG_5                                       0x18005
+#define BM1000_PCIE_PHY_RX_CFG_5_RX_AGC_MEN_OVRRD_EN                   BIT(4)
+#define BM1000_PCIE_PHY_RX_CFG_5_RX_AGC_MEN_OVRRD_VAL                  BIT(3)
+#define BM1000_PCIE_PHY_RX_LOOP_CTRL                                   0x18009
+#define BM1000_PCIE_PHY_RX_LOOP_CTRL_CFG_RX_LCTRL_LCTRL_MEN            BIT(8)
+#define BM1000_PCIE_PHY_RX_CTLE_CTRL                                   0x1800b
+#define BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_ZERO_MASK         GENMASK(13, 10)
+#define BM1000_PCIE_PHY_RX_CTLE_CTRL_RX_CTLE_POLE_OVRRD_EN             BIT(9)
+#define BM1000_PCIE_PHY_RX_CTLE_CTRL_RX_CTLE_POLE_OVRRD_VAL_MASK       GENMASK(8, 5)
+#define BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_MAX_MASK     GENMASK(4, 3)
+#define BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_MIN_MASK     GENMASK(2, 1)
+#define BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_STEP         BIT(0)
+#define BM1000_PCIE_PHY_TX_CFG_1                                       0x18016
+#define BM1000_PCIE_PHY_TX_CFG_1_TX_VBOOST_EN_OVRRD_EN                 BIT(11)
+#define BM1000_PCIE_PHY_TX_CFG_1_TX_TURBO_EN_OVRRD_EN                  BIT(10)
+#define BM1000_PCIE_PHY_TX_CFG_3                                       0x18018
+#define BM1000_PCIE_PHY_TX_CFG_3_CFG_TX_VBOOST_EN                      BIT(14)
+#define BM1000_PCIE_PHY_TX_CFG_3_PCS_SDS_TX_GAIN_MASK                  GENMASK(6, 4)
+#define BM1000_PCIE_PHY_TX_CFG_3_CFG_TX_TURBO_EN                       BIT(0)
+#define BM1000_PCIE_PHY_RX_PWR_MON_1                                   0x1802a
+#define BM1000_PCIE_PHY_RX_PWR_MON_1_RX_PWRSM_LANE_PWR_OFF             BIT(4)
+#define BM1000_PCIE_PHY_TX_PWR_MON_0                                   0x1802c
+#define BM1000_PCIE_PHY_TX_PWR_MON_0_TX_PWRSM_LANE_PWR_OFF             BIT(15)
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_0                                        0x18048
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_1                                        0x18049
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_2                                        0x1804a
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_OVRD_EN                 BIT(5)
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C5_MEN_OVRD_VAL         BIT(4)
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C4_MEN_OVRD_VAL         BIT(3)
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C3_MEN_OVRD_VAL         BIT(2)
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C2_MEN_OVRD_VAL         BIT(1)
+#define BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C1_MEN_OVRD_VAL         BIT(0)
+
+void bm1000_pcie_tune(struct dw_pcie *pci)
+{
+       struct device *dev = pci->dev;
+       u16 exp_cap_off;
+       u8 lane = 0, lanes = 0;
+       int override;
+       int i, n_lanes;
+       int ret;
+
+       if (notune)
+               return;
+
+       /* Search for active lanes */
+       exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       n_lanes = FIELD_GET(PCI_EXP_LNKCAP_MLW,
+                           dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKCAP));
+       for (i = 0; i < n_lanes; i++) {
+               u16 reg;
+               u8 mask = 1 << i;
+
+               ret = bm1000_pcie_phy_read(pci, mask,
+                                          BM1000_PCIE_PHY_RX_PWR_MON_1, &reg);
+               if (ret != 0 ||
+                   FIELD_GET(BM1000_PCIE_PHY_RX_PWR_MON_1_RX_PWRSM_LANE_PWR_OFF,
+                             reg) == 1)
+                       continue;
+               ret = bm1000_pcie_phy_read(pci, mask,
+                                          BM1000_PCIE_PHY_TX_PWR_MON_0, &reg);
+               if (ret != 0 ||
+                   FIELD_GET(BM1000_PCIE_PHY_TX_PWR_MON_0_TX_PWRSM_LANE_PWR_OFF,
+                             reg) == 1)
+                       continue;
+               lanes |= mask;
+               if (lane == 0)
+                       lane = mask;
+       }
+
+       /* Feedback mode */
+       override = gen3_eq_fb_mode;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,gen3-eq-fb-mode", &override);
+       if (override >= 0) {
+               u32 reg;
+
+               dev_dbg(dev, "Gen3 fb_mode = %d\n", override);
+               reg = dw_pcie_readl_dbi(pci, BM1000_PCIE_GEN3_EQ_CONTROL);
+               reg &= ~BM1000_PCIE_GEN3_EQ_FB_MODE_MASK;
+               reg |= FIELD_PREP(BM1000_PCIE_GEN3_EQ_FB_MODE_MASK, override);
+               dw_pcie_writel_dbi(pci, BM1000_PCIE_GEN3_EQ_CONTROL, reg);
+       }
+       /* Initial presets */
+       override = gen3_eq_psets;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,gen3-eq-psets", &override);
+       if (override >= 0) {
+               u32 reg;
+
+               reg = dw_pcie_readl_dbi(pci, BM1000_PCIE_GEN3_EQ_CONTROL);
+               dev_dbg(dev, "Gen3 initial presets = 0x%x\n", override);
+               reg &= ~(BM1000_PCIE_GEN3_EQ_PSET_REQ_VEC_MASK |
+                        BM1000_PCIE_GEN3_EQ_FOM_INC_INITIAL_EVAL);
+               reg |= FIELD_PREP(BM1000_PCIE_GEN3_EQ_PSET_REQ_VEC_MASK,
+                                 override);
+               dw_pcie_writel_dbi(pci, BM1000_PCIE_GEN3_EQ_CONTROL, reg);
+       }
+       /* Phy RX AGC */
+       override = phy_rx_agc;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,phy-rx-agc", &override);
+       if (override >= 0) {
+               u16 reg;
+
+               ret = bm1000_pcie_phy_read(pci, lane, BM1000_PCIE_PHY_RX_CFG_2,
+                                          &reg);
+               if (ret == 0) {
+                       reg &= ~BM1000_PCIE_PHY_RX_CFG_2_PCS_SDS_RX_AGC_MVAL;
+                       reg |= FIELD_PREP(
+                               BM1000_PCIE_PHY_RX_CFG_2_PCS_SDS_RX_AGC_MVAL,
+                               override);
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_RX_CFG_2,
+                                                   reg);
+               }
+               if (ret == 0)
+                       ret = bm1000_pcie_phy_read(pci, lane,
+                                                  BM1000_PCIE_PHY_RX_CFG_5,
+                                                  &reg);
+               if (ret == 0) {
+                       reg |= BM1000_PCIE_PHY_RX_CFG_5_RX_AGC_MEN_OVRRD_EN |
+                              BM1000_PCIE_PHY_RX_CFG_5_RX_AGC_MEN_OVRRD_VAL;
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_RX_CFG_5,
+                                                   reg);
+               }
+               dev_dbg(dev, "Phy RX AGC = 0x%04x (%d)\n", override, ret);
+       }
+       /* Rhy RX CTLE */
+       override = phy_rx_ctle;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,phy-rx-ctle", &override);
+       if (override >= 0) {
+               u16 reg;
+
+               ret = bm1000_pcie_phy_read(pci, lane,
+                                          BM1000_PCIE_PHY_RX_CTLE_CTRL, &reg);
+               if (ret == 0) {
+                       reg &= ~(BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_ZERO_MASK |
+                                BM1000_PCIE_PHY_RX_CTLE_CTRL_RX_CTLE_POLE_OVRRD_VAL_MASK);
+                       reg |= BM1000_PCIE_PHY_RX_CTLE_CTRL_RX_CTLE_POLE_OVRRD_EN |
+                              FIELD_PREP(BM1000_PCIE_PHY_RX_CTLE_CTRL_RX_CTLE_POLE_OVRRD_VAL_MASK,
+                                         override & 0xf) |
+                              FIELD_PREP(BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_ZERO_MASK,
+                                         override >> 4);
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_RX_CTLE_CTRL,
+                                                   reg);
+               }
+               if (ret == 0)
+                       ret = bm1000_pcie_phy_read(pci, lane,
+                                                  BM1000_PCIE_PHY_RX_LOOP_CTRL,
+                                                  &reg);
+               if (ret == 0) {
+                       reg |= BM1000_PCIE_PHY_RX_LOOP_CTRL_CFG_RX_LCTRL_LCTRL_MEN;
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_RX_LOOP_CTRL,
+                                                   reg);
+               }
+               dev_dbg(dev, "Phy RX CTLE = 0x%04x (%d)\n", override, ret);
+       }
+       /* Phy RX DFE */
+       override = phy_rx_dfe;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,phy-rx-dfe", &override);
+       if (override == 0) { /* enabled by default - disable only */
+               u16 reg;
+
+               reg = BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_OVRD_EN;
+               reg |= BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C1_MEN_OVRD_VAL |
+                      BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C2_MEN_OVRD_VAL |
+                      BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C3_MEN_OVRD_VAL |
+                      BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C4_MEN_OVRD_VAL |
+                      BM1000_PCIE_PHY_RX_AEQ_VALBBD_2_RX_DFE_C5_MEN_OVRD_VAL;
+               ret = bm1000_pcie_phy_write(pci, lanes,
+                                           BM1000_PCIE_PHY_RX_AEQ_VALBBD_2,
+                                           reg);
+               if (ret == 0)
+                       ret = bm1000_pcie_phy_read(pci, lane,
+                                                  BM1000_PCIE_PHY_RX_LOOP_CTRL,
+                                                  &reg);
+               if (ret == 0) {
+                       reg |= BM1000_PCIE_PHY_RX_LOOP_CTRL_CFG_RX_LCTRL_LCTRL_MEN;
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_RX_LOOP_CTRL,
+                                                   reg);
+               }
+               if (ret == 0) {
+                       reg = 0;
+                       bm1000_pcie_phy_write(pci, lanes,
+                                             BM1000_PCIE_PHY_RX_AEQ_VALBBD_0,
+                                             reg);
+                       bm1000_pcie_phy_write(pci, lanes,
+                                             BM1000_PCIE_PHY_RX_AEQ_VALBBD_1,
+                                             reg);
+               }
+               dev_dbg(dev, "Phy RX DFE = %d (%d)\n", override, ret);
+       }
+       /* Phy TX gain */
+       override = phy_tx_gain;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,phy-tx-gain", &override);
+       if (override >= 0) {
+               u16 reg;
+
+               ret = bm1000_pcie_phy_read(pci, lane, BM1000_PCIE_PHY_TX_CFG_3,
+                                          &reg);
+               if (ret == 0) {
+                       reg &= ~BM1000_PCIE_PHY_TX_CFG_3_PCS_SDS_TX_GAIN_MASK;
+                       reg |= BM1000_PCIE_PHY_TX_CFG_3_CFG_TX_VBOOST_EN;
+                       reg |= FIELD_PREP(BM1000_PCIE_PHY_TX_CFG_3_PCS_SDS_TX_GAIN_MASK,
+                                         override);
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_TX_CFG_3,
+                                                   reg);
+               }
+               if (ret == 0)
+                       ret = bm1000_pcie_phy_read(pci, lane,
+                                                  BM1000_PCIE_PHY_TX_CFG_1,
+                                                  &reg);
+               if (ret == 0) {
+                       reg |= BM1000_PCIE_PHY_TX_CFG_1_TX_VBOOST_EN_OVRRD_EN;
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_TX_CFG_1,
+                                                   reg);
+               }
+               dev_dbg(dev, "Phy TX gain = 0x%x (%d)\n", override, ret);
+       }
+       /* Phy TX turbo */
+       override = phy_tx_turbo;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,phy-tx-turbo", &override);
+       if (override >= 0) {
+               u16 reg;
+
+               ret = bm1000_pcie_phy_read(pci, lane, BM1000_PCIE_PHY_TX_CFG_3,
+                                          &reg);
+               if (ret == 0) {
+                       if (override == 0)
+                               reg &= ~BM1000_PCIE_PHY_TX_CFG_3_CFG_TX_TURBO_EN;
+                       else
+                               reg |= BM1000_PCIE_PHY_TX_CFG_3_CFG_TX_TURBO_EN;
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_TX_CFG_3,
+                                                   reg);
+               }
+               if (ret == 0)
+                       ret = bm1000_pcie_phy_read(pci, lane,
+                                                  BM1000_PCIE_PHY_TX_CFG_1,
+                                                  &reg);
+               if (ret == 0) {
+                       reg |= BM1000_PCIE_PHY_TX_CFG_1_TX_TURBO_EN_OVRRD_EN;
+                       ret = bm1000_pcie_phy_write(pci, lanes,
+                                                   BM1000_PCIE_PHY_TX_CFG_1,
+                                                   reg);
+               }
+               dev_dbg(dev, "Phy TX turbo = %d (%d)\n", override, ret);
+       }
+       /* Phy RX CTLE pole range */
+       override = phy_rx_ctle_pole;
+       if (override == -1)
+               device_property_read_u32(dev, "bm1000,phy-rx-ctle-pole", &override);
+       if (override >= 0) {
+               u16 reg;
+               u8 pole_max = (override >> 4) & 0xf, pole_min = override & 0xf;
+
+               ret = bm1000_pcie_phy_read(pci, lane, BM1000_PCIE_PHY_RX_CTLE_CTRL,
+                                          &reg);
+               if (ret == 0) {
+                       reg &= ~(BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_MAX_MASK |
+                                BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_MIN_MASK);
+                       reg |= FIELD_PREP(BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_MAX_MASK,
+                                         pole_max);
+                       reg |= FIELD_PREP(BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_MIN_MASK,
+                                         pole_min);
+                       if (pole_max == pole_min)
+                               reg &= ~BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_STEP;
+                       else
+                               reg |= BM1000_PCIE_PHY_RX_CTLE_CTRL_PCS_SDS_RX_CTLE_POLE_STEP;
+                       ret = bm1000_pcie_phy_write(pci, lanes, BM1000_PCIE_PHY_RX_CTLE_CTRL,
+                                                   reg);
+               }
+               dev_dbg(dev, "Phy RX CTLE pole = 0x%04x (%d)\n", override, ret);
+       }
+
+       /* debugfs populate */
+       if (debugfs)
+               bm1000_pcie_tune_debugfs_populate(pci);
+}
+
+#ifdef CONFIG_DEBUG_FS
+
+#define BM1000_PCIE_PHY_SDS_PIN_MON_1                          0x18027
+#define BM1000_PCIE_PHY_SDS_PIN_MON_1_PCS_SDS_TX_SWING         GENMASK(5, 1)
+#define BM1000_PCIE_PHY_SDS_PIN_MON_2                          0x18028
+#define BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_VBOOST_EN     BIT(10)
+#define BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_TURBO_EN      BIT(9)
+#define BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_POST_CURSOR   GENMASK(8, 4)
+#define BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_PRE_CURSOR    GENMASK(3, 0)
+#define BM1000_PCIE_PHY_RX_PWR_MON_0                           0x18029
+#define BM1000_PCIE_PHY_RX_PWR_MON_0_RX_PWRSM_AGC_EN           BIT(10)
+#define BM1000_PCIE_PHY_RX_PWR_MON_0_RX_PWRSM_DFE_EN           BIT(9)
+#define BM1000_PCIE_PHY_RX_PWR_MON_0_RX_PWRSM_CDR_EN           BIT(8)
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_0                           0x18050
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_0_DFE_TAP5                  GENMASK(9, 5)
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_0_DFE_TAP4                  GENMASK(4, 0)
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_1                           0x18051
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_1_DFE_TAP3                  GENMASK(14, 10)
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_1_DFE_TAP2                  GENMASK(9, 5)
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_1_DFE_TAP1                  GENMASK(4, 0)
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_2                           0x18052
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_2_PRE_CTLE_GAIN             GENMASK(7, 4)
+#define BM1000_PCIE_PHY_RX_AEQ_OUT_2_POST_CTLE_GAIN            GENMASK(3, 0)
+#define BM1000_PCIE_PHY_RX_VMA_STATUS_0                                0x18057
+#define BM1000_PCIE_PHY_RX_VMA_STATUS_0_CTLE_PEAK              GENMASK(5, 2)
+#define BM1000_PCIE_PHY_RX_VMA_STATUS_0_CTLE_POLE              GENMASK(1, 0)
+
+static void print_for_all_lanes(struct dw_pcie *pci, struct seq_file *s,
+                               u8 n_lanes, u32 addr, u16 mask)
+{
+       int i;
+
+       for (i = 0; i < n_lanes; i++) {
+               u16 reg;
+               u8 lane = 1 << i;
+
+               if (bm1000_pcie_phy_read(pci, lane, addr, &reg) == 0)
+                       seq_put_hex_ll(s, " ", (reg & mask) >> __bf_shf(mask), 0);
+               else
+                       seq_puts(s, " ?");
+       }
+}
+
+static int bm1000_pcie_dbgfs_phy_mon_show(struct seq_file *s, void *data)
+{
+       struct dw_pcie *pci = s->private;
+       u8 n_lanes;
+       u16 exp_cap_off;
+
+       exp_cap_off = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
+       n_lanes = FIELD_GET(PCI_EXP_LNKCAP_MLW,
+                           dw_pcie_readw_dbi(pci, exp_cap_off + PCI_EXP_LNKCAP));
+
+       seq_puts(s, "sds_pin_mon:\n");
+       seq_puts(s, " tx_swing:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_SDS_PIN_MON_1,
+                           BM1000_PCIE_PHY_SDS_PIN_MON_1_PCS_SDS_TX_SWING);
+       seq_puts(s, "\n");
+       seq_puts(s, " pre_cursor:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_SDS_PIN_MON_2,
+                           BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_PRE_CURSOR);
+       seq_puts(s, "\n");
+       seq_puts(s, " post_cursor:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_SDS_PIN_MON_2,
+                           BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_POST_CURSOR);
+       seq_puts(s, "\n");
+       seq_puts(s, " vboost_en:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_SDS_PIN_MON_2,
+                           BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_VBOOST_EN);
+       seq_puts(s, "\n");
+       seq_puts(s, " turbo_en:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_SDS_PIN_MON_2,
+                           BM1000_PCIE_PHY_SDS_PIN_MON_2_PCS_SDS_TX_TURBO_EN);
+       seq_puts(s, "\n");
+       seq_puts(s, "rx_vma_status:\n");
+       seq_puts(s, " ctle_peak:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_VMA_STATUS_0,
+                           BM1000_PCIE_PHY_RX_VMA_STATUS_0_CTLE_PEAK);
+       seq_puts(s, "\n");
+       seq_puts(s, " ctle_pole:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_VMA_STATUS_0,
+                           BM1000_PCIE_PHY_RX_VMA_STATUS_0_CTLE_POLE);
+       seq_puts(s, "\n");
+       seq_puts(s, "rx_aeq_out:\n");
+       seq_puts(s, " pre_ctle_gain:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_AEQ_OUT_2,
+                           BM1000_PCIE_PHY_RX_AEQ_OUT_2_PRE_CTLE_GAIN);
+       seq_puts(s, "\n");
+       seq_puts(s, " post_ctle_gain:");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_AEQ_OUT_2,
+                           BM1000_PCIE_PHY_RX_AEQ_OUT_2_POST_CTLE_GAIN);
+       seq_puts(s, "\n");
+       seq_puts(s, " dfe_tap1:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_AEQ_OUT_1,
+                           BM1000_PCIE_PHY_RX_AEQ_OUT_1_DFE_TAP1);
+       seq_puts(s, "\n");
+       seq_puts(s, " dfe_tap2:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_AEQ_OUT_1,
+                           BM1000_PCIE_PHY_RX_AEQ_OUT_1_DFE_TAP2);
+       seq_puts(s, "\n");
+       seq_puts(s, " dfe_tap3:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_AEQ_OUT_1,
+                           BM1000_PCIE_PHY_RX_AEQ_OUT_1_DFE_TAP3);
+       seq_puts(s, "\n");
+       seq_puts(s, " dfe_tap4:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_AEQ_OUT_0,
+                           BM1000_PCIE_PHY_RX_AEQ_OUT_0_DFE_TAP4);
+       seq_puts(s, "\n");
+       seq_puts(s, " dfe_tap5:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_AEQ_OUT_0,
+                           BM1000_PCIE_PHY_RX_AEQ_OUT_0_DFE_TAP5);
+       seq_puts(s, "\n");
+       seq_puts(s, "pwr_mon:\n");
+       seq_puts(s, " tx_pwr_off:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_TX_PWR_MON_0,
+                           BM1000_PCIE_PHY_TX_PWR_MON_0_TX_PWRSM_LANE_PWR_OFF);
+       seq_puts(s, "\n");
+       seq_puts(s, " rx_pwr_off:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_PWR_MON_1,
+                           BM1000_PCIE_PHY_RX_PWR_MON_1_RX_PWRSM_LANE_PWR_OFF);
+       seq_puts(s, "\n");
+       seq_puts(s, " agc_en:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_PWR_MON_0,
+                           BM1000_PCIE_PHY_RX_PWR_MON_0_RX_PWRSM_AGC_EN);
+       seq_puts(s, "\n");
+       seq_puts(s, " dfe_en:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_PWR_MON_0,
+                           BM1000_PCIE_PHY_RX_PWR_MON_0_RX_PWRSM_DFE_EN);
+       seq_puts(s, "\n");
+       seq_puts(s, " cdr_en:\t");
+       print_for_all_lanes(pci, s, n_lanes, BM1000_PCIE_PHY_RX_PWR_MON_0,
+                           BM1000_PCIE_PHY_RX_PWR_MON_0_RX_PWRSM_CDR_EN);
+       seq_puts(s, "\n");
+       return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(bm1000_pcie_dbgfs_phy_mon);
+
+static void bm1000_pcie_tune_debugfs_populate(struct dw_pcie *pci)
+{
+       struct dentry *root_dir;
+
+       root_dir = debugfs_create_dir(dev_name(pci->dev), NULL);
+       if (!root_dir) {
+               dev_warn(pci->dev, "%s: failed to create debugfs dir\n",
+                        __func__);
+               return;
+       }
+       if (!debugfs_create_file("phy_mon", S_IRUGO, root_dir, pci,
+                                &bm1000_pcie_dbgfs_phy_mon_fops))
+               dev_warn(pci->dev, "%s: failed to create phy_mon debugfs file\n",
+                        __func__);
+}
+
+#else
+
+static void bm1000_pcie_tune_debugfs_populate(struct dw_pcie *pci)
+{
+}
+
+#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/pci/controller/dwc/pcie-baikal.h b/drivers/pci/controller/dwc/pcie-baikal.h
new file mode 100644 (file)
index 0000000..57834b6
--- /dev/null
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe controller driver for Baikal Electronics SoCs
+ *
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ */
+
+#ifndef _PCIE_BAIKAL_H
+#define _PCIE_BAIKAL_H
+
+void bm1000_pcie_phy_enable(struct dw_pcie *pci);
+void bm1000_pcie_phy_disable(struct dw_pcie *pci);
+
+void bm1000_pcie_tune(struct dw_pcie *pci);
+
+#endif /* _PCIE_BAIKAL_H */
index 7bd00a11d074b4c2b04ee1756119d89c3d83cb21..c5171e265ffeda66bdd7310b0417238eb0f85050 100644 (file)
@@ -96,5 +96,6 @@ source "drivers/phy/tegra/Kconfig"
 source "drivers/phy/ti/Kconfig"
 source "drivers/phy/intel/Kconfig"
 source "drivers/phy/xilinx/Kconfig"
+source "drivers/phy/baikal/Kconfig"
 
 endmenu
index 54f312c10a408aa81fc4251fe75dd90e54d0ae73..88d1e2d90cfd44e6ecf5526703799690e2241b0d 100644 (file)
@@ -34,4 +34,5 @@ obj-y                                 += allwinner/   \
                                           sunplus/     \
                                           tegra/       \
                                           ti/          \
+                                          baikal/      \
                                           xilinx/
diff --git a/drivers/phy/baikal/Kconfig b/drivers/phy/baikal/Kconfig
new file mode 100644 (file)
index 0000000..b8b598d
--- /dev/null
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config USB_PHY_BAIKAL
+       tristate "Baikal USB PHY driver"
+       depends on USB_SUPPORT
+       select GENERIC_PHY
+       select USB_PHY
+       help
+         Enable this to support the USB PHY on Baikal SoCs.
+         This driver controls both the USB2 PHY and the USB3 PHY.
\ No newline at end of file
diff --git a/drivers/phy/baikal/Makefile b/drivers/phy/baikal/Makefile
new file mode 100644 (file)
index 0000000..54fcd11
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_USB_PHY_BAIKAL)           += baikal-usb-phy.o
diff --git a/drivers/phy/baikal/baikal-usb-phy.c b/drivers/phy/baikal/baikal-usb-phy.c
new file mode 100644 (file)
index 0000000..72e7b74
--- /dev/null
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Baikal USB PHY driver
+ *
+ * Copyright (C) 2022-2023 Baikal Electronics, JSC
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <dt-bindings/phy/phy.h>
+
+struct phy_baikal_desc {
+       struct phy              *phy;
+       int                     index;
+       bool                    enable;
+};
+
+struct phy_baikal_priv {
+       struct phy_baikal_desc  **phys;
+       int                     nphys;
+       struct clk_bulk_data    *clocks;
+       unsigned int            nclocks;
+};
+
+#ifdef CONFIG_ACPI
+static int phy_baikal_acpi_get_info(struct fwnode_handle *fwnode,
+                                   bool *is_usb3, const char **ref_name)
+{
+       struct acpi_device *adev = to_acpi_device_node(fwnode);
+       struct acpi_device *ref_adev;
+       struct device *ref_dev;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object *obj;
+       acpi_status status;
+       int ret = 0;
+
+       *is_usb3 = fwnode_property_read_bool(fwnode, "usb3");
+       status = acpi_evaluate_object_typed(adev->handle, "CTRL", NULL,
+                                           &buffer, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               dev_err(&adev->dev, "failed to get CTRL data\n");
+               return -ENODEV;
+       }
+
+       obj = buffer.pointer;
+       if (obj->package.count != 1) {
+               dev_err(&adev->dev, "invalid CTRL data\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       obj = &obj->package.elements[0];
+       if (obj->type != ACPI_TYPE_LOCAL_REFERENCE || !obj->reference.handle) {
+               dev_err(&adev->dev, "invalid CTRL reference\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
+       ref_adev = acpi_fetch_acpi_dev(obj->reference.handle);
+       if (!ref_adev) {
+               dev_err(&adev->dev, "failed to process CTRL reference\n");
+               ret = -ENODEV;
+               goto err;
+       }
+
+       ref_dev = bus_find_device_by_fwnode(&platform_bus_type,
+                                           acpi_fwnode_handle(ref_adev));
+       if (!ref_dev) {
+               dev_err(&adev->dev, "failed to get referenced device\n");
+               ret = -ENODEV;
+               goto err;
+       }
+       *ref_name = dev_name(ref_dev);
+
+err:
+       acpi_os_free(buffer.pointer);
+       return ret;
+}
+
+static int phy_baikal_acpi_add(struct device *dev)
+{
+       struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+       struct fwnode_handle *child;
+       const char **ref_name;
+       bool *is_usb3;
+       int count = 0, i, ret;
+
+       ref_name = kcalloc(priv->nphys, sizeof(*ref_name), GFP_KERNEL);
+       if (!ref_name)
+               return -ENOMEM;
+
+       is_usb3 = kcalloc(priv->nphys, sizeof(*is_usb3), GFP_KERNEL);
+       if (!is_usb3) {
+               kfree(ref_name);
+               return -ENOMEM;
+       }
+
+       device_for_each_child_node(dev, child) {
+               ret = phy_baikal_acpi_get_info(child, &is_usb3[count],
+                                              &ref_name[count]);
+               if (ret)
+                       goto err;
+
+               ret = phy_create_lookup(priv->phys[count]->phy,
+                                       is_usb3[count] ? "usb3-phy" : "usb2-phy",
+                                       ref_name[count]);
+               if (ret)
+                       goto err;
+
+               ++count;
+       }
+
+err:
+       if (ret) {
+               for (i = 0; i < count; ++i)
+                       phy_remove_lookup(priv->phys[i]->phy,
+                                         is_usb3[i] ? "usb3-phy" : "usb2-phy",
+                                         ref_name[i]);
+       }
+
+       kfree(ref_name);
+       kfree(is_usb3);
+       return ret;
+}
+
+static void phy_baikal_acpi_remove(struct device *dev)
+{
+       struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+       struct fwnode_handle *child;
+       const char *ref_name;
+       bool is_usb3;
+       int i, ret;
+
+       device_for_each_child_node(dev, child) {
+               ret = phy_baikal_acpi_get_info(child, &is_usb3, &ref_name);
+               if (ret) {
+                       ++i;
+                       continue;
+               }
+
+               phy_remove_lookup(priv->phys[i++]->phy,
+                                 is_usb3 ? "usb3-phy" : "usb2-phy",
+                                 ref_name);
+       }
+}
+
+static void phy_baikal_acpi_init_clk(struct phy_baikal_desc *desc)
+{
+       struct device *dev = desc->phy->dev.parent;
+       struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+       struct fwnode_handle *child;
+       int i = 0;
+
+       device_for_each_child_node(dev, child) {
+               if (i++ == desc->index) {
+                       priv->clocks[2 * desc->index].clk =
+                               devm_clk_get_optional(&to_acpi_device_node(child)->dev, "phy0_clk");
+                       priv->clocks[2 * desc->index + 1].clk =
+                               devm_clk_get_optional(&to_acpi_device_node(child)->dev, "phy1_clk");
+                       break;
+               }
+       }
+}
+#else /* CONFIG_ACPI */
+static inline int phy_baikal_acpi_add(struct device *dev)
+{
+       return 0;
+}
+
+static inline void phy_baikal_acpi_remove(struct device *dev)
+{
+}
+
+static inline void phy_baikal_acpi_init_clk(struct phy_baikal_desc *desc)
+{
+}
+#endif
+
+static int phy_baikal_init(struct phy *phy)
+{
+       struct phy_baikal_priv *priv = dev_get_drvdata(phy->dev.parent);
+       struct phy_baikal_desc *desc = phy_get_drvdata(phy);
+       int n = 2 * desc->index;
+
+       if (!acpi_disabled)
+               phy_baikal_acpi_init_clk(desc);
+
+       if (desc->enable) {
+               clk_prepare_enable(priv->clocks[n + 0].clk);
+               clk_prepare_enable(priv->clocks[n + 1].clk);
+               return 0;
+       } else {
+               clk_disable_unprepare(priv->clocks[n + 0].clk);
+               clk_disable_unprepare(priv->clocks[n + 1].clk);
+               return -1;
+       }
+}
+
+static const struct phy_ops phy_baikal_ops = {
+       .init   = phy_baikal_init,
+       .owner  = THIS_MODULE,
+};
+
+static struct phy *phy_baikal_xlate(struct device *dev, struct of_phandle_args *args)
+{
+       int i;
+       struct phy_baikal_priv *priv = dev_get_drvdata(dev);
+
+       for (i = 0; i < priv->nphys; i++) {
+               if (priv->phys[i]->index == args->args[0])
+                       break;
+       }
+
+       return priv->phys[i]->phy;
+}
+
+static int phy_baikal_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct fwnode_handle *child;
+       struct phy *phy;
+       struct phy_baikal_priv *priv;
+       struct phy_baikal_desc *phy_desc;
+       int index;
+       int i = 0;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->nphys = device_get_child_node_count(dev);
+       priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys), GFP_KERNEL);
+       if (!priv->phys)
+               return -ENOMEM;
+
+       if (acpi_disabled) {
+               priv->nclocks = devm_clk_bulk_get_all(dev, &priv->clocks);
+       } else {
+               priv->nclocks = 2 * priv->nphys;
+               priv->clocks = devm_kcalloc(dev, priv->nclocks,
+                                           sizeof(*priv->clocks), GFP_KERNEL);
+               if (!priv->clocks)
+                       return -ENOMEM;
+       }
+
+       dev_set_drvdata(dev, priv);
+       device_for_each_child_node(dev, child) {
+               fwnode_property_read_u32(child, "reg", &index);
+               phy = devm_phy_create(dev, NULL, &phy_baikal_ops);
+               if (!phy)
+                       return -ENOMEM;
+
+               phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
+               if (!phy_desc)
+                       return -ENOMEM;
+
+               phy_desc->phy = phy;
+               phy_desc->index = index;
+               phy_desc->enable = fwnode_property_read_bool(child, "enable");
+               priv->phys[i++] = phy_desc;
+               phy_set_drvdata(phy, phy_desc);
+       }
+
+       if (acpi_disabled)
+               return PTR_ERR_OR_ZERO(devm_of_phy_provider_register(dev, phy_baikal_xlate));
+       else
+               return phy_baikal_acpi_add(dev);
+}
+
+static int phy_baikal_remove(struct platform_device *pdev)
+{
+       if (!acpi_disabled)
+               phy_baikal_acpi_remove(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id phy_baikal_table[] = {
+       { .compatible = "baikal,bm1000-usb-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, phy_baikal_table);
+
+static struct platform_driver phy_baikal_driver = {
+       .probe = phy_baikal_probe,
+       .remove = phy_baikal_remove,
+       .driver = {
+               .name = "baikal,bm1000-usb-phy",
+               .of_match_table = phy_baikal_table,
+       },
+};
+module_platform_driver(phy_baikal_driver);
+
+MODULE_DESCRIPTION("Baikal USB PHY driver");
+MODULE_LICENSE("GPL v2");
index d4b969e68c314b62e1f4d932515b7b8fd1ead99f..8fff38702c18a2ca3188a85f2fa50c40e27bd186 100644 (file)
@@ -153,6 +153,9 @@ config SPI_AXI_SPI_ENGINE
          It is part of the SPI Engine framework that is used in some Analog Devices
          reference designs for FPGAs.
 
+config SPI_BAIKAL_ESPI
+       tristate "Baikal eSPI controller"
+
 config SPI_BCM2835
        tristate "BCM2835 SPI controller"
        depends on GPIOLIB
index 4b34e855c84125782d310da1adb6cbc61591fa60..5668b16fd1e1f87a83be6cd65a318b9c136ac3e2 100644 (file)
@@ -26,6 +26,7 @@ obj-$(CONFIG_SPI_AT91_USART)          += spi-at91-usart.o
 obj-$(CONFIG_SPI_ATH79)                        += spi-ath79.o
 obj-$(CONFIG_SPI_AU1550)               += spi-au1550.o
 obj-$(CONFIG_SPI_AXI_SPI_ENGINE)       += spi-axi-spi-engine.o
+obj-$(CONFIG_SPI_BAIKAL_ESPI)          += spi-baikal-espi.o
 obj-$(CONFIG_SPI_BCM2835)              += spi-bcm2835.o
 obj-$(CONFIG_SPI_BCM2835AUX)           += spi-bcm2835aux.o
 obj-$(CONFIG_SPI_BCM63XX)              += spi-bcm63xx.o
diff --git a/drivers/spi/spi-baikal-espi.c b/drivers/spi/spi-baikal-espi.c
new file mode 100644 (file)
index 0000000..0abd910
--- /dev/null
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Baikal Electronics eSPI controller driver
+ * Copyright (C) 2020-2022 Baikal Electronics, JSC
+ */
+
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/property.h>
+#include <linux/io.h>
+
+#define ESPI_FIFO_LEN  256
+#define ESPI_DUMMY_DATA        0xff
+
+struct espi_data {
+       struct spi_master       *master;
+       /* hardware */
+       int                     irq;
+       void __iomem            *regs;
+       struct clk              *clk;
+       /* transfer */
+       uint32_t                rxlen;
+       uint32_t                txlen;
+       uint8_t                 *tx;
+       uint8_t                 *rx;
+};
+
+#define ESPI_CR1       0x00 /* control 1 */
+#define ESPI_CR2       0x04 /* control 2 */
+#define ESPI_TX_FIFO   0x0c /* tx fifo */
+#define ESPI_TX_FBCAR  0x14 /* tx byte count */
+#define ESPI_TX_FAETR  0x18 /* tx almost empty threshold */
+#define ESPI_RX_FIFO   0x1c /* rx fifo */
+#define ESPI_RX_FBCAR  0x24 /* rx byte count */
+#define ESPI_RX_FAFTR  0x28 /* rx almost full threshold */
+#define ESPI_ISR       0x30 /* irq status */
+#define ESPI_IMR       0x34 /* irq mask */
+#define ESPI_IVR       0x38 /* irq vector */
+
+typedef union {
+       uint32_t val;
+       struct {
+               uint32_t scr :1; /* reset */
+               uint32_t sce :1; /* enable */
+               uint32_t mss :1; /* select - master/slave */
+               uint32_t cph :1; /* clock phase */
+               uint32_t cpo :1; /* clock polarity */
+       } bits;
+} espi_cr1_t;
+
+#define ESPI_CR1_SCR_RESET             0
+#define ESPI_CR1_SCR_NORESET           1
+#define ESPI_CR1_SCE_CORE_DISABLE      0
+#define ESPI_CR1_SCE_CORE_ENABLE       1
+#define ESPI_CR1_MSS_MODE_MASTER       0
+#define ESPI_CR1_MSS_MODE_SLAVE                1
+#define ESPI_CR1_CPH_CLKPHASE_ODD      0
+#define ESPI_CR1_CPH_CLKPHASE_EVEN     1
+#define ESPI_CR1_CPO_CLKPOLAR_LOW      0
+#define ESPI_CR1_CPO_CLKPOLAR_HIGH     1
+
+typedef union {
+       uint32_t val;
+       struct {
+               uint32_t sso :3; /* slave select (ss0-ss7) */
+               uint32_t srd :1; /* read disable */
+               uint32_t sri :1; /* read first byte ignore */
+               uint32_t mlb :1; /* least sign git first */
+               uint32_t mte :1; /* master transfer enable */
+       } bits;
+} espi_cr2_t;
+
+#define ESPI_CR2_SRD_RX_DISABLE                0
+#define ESPI_CR2_SRD_RX_ENABLE         1
+#define ESPI_CR2_SRI_FIRST_RESIEV      0
+#define ESPI_CR2_SRI_FIRST_IGNORE      1
+#define ESPI_CR2_MLB_MSB               0
+#define ESPI_CR2_MLB_LSB               1
+#define ESPI_CR2_MTE_TX_DISABLE                0
+#define ESPI_CR2_MTE_TX_ENABLE         1 /* self clear */
+
+#define ESPI_RX_FAFTR_DISABLE          0xff
+#define ESPI_TX_FAETR_DISABLE          0x0
+#define ESPI_ISR_CLEAR_ALL             0xff /* clear by writing "1" */
+#define ESPI_IMR_DISABLE_ALL           0x0
+#define ESPI_IMR_ENABLE_ALL            0xff
+
+/* isr, imr, ivr */
+typedef union {
+       uint32_t val;
+       struct {
+               uint32_t tx_underrun     :1;
+               uint32_t tx_overrun      :1;
+               uint32_t rx_underrun     :1;
+               uint32_t rx_overrun      :1;
+               uint32_t tx_almost_empty :1;
+               uint32_t rx_almost_full  :1;
+               uint32_t tx_done_master  :1;
+               uint32_t tx_done_slave   :1;
+               uint32_t alert           :8;
+               uint32_t rx_crc_error    :1;
+       } bits;
+} espi_irq_t;
+
+#define ESPI_IRQ_DISABLE       0
+#define ESPI_IRQ_ENABLE                1
+
+typedef union {
+       uint32_t val;
+       struct {
+               uint32_t ese :1; /* enable */
+               uint32_t rms :1; /* reset# direction */
+               uint32_t raa :1; /* reset# assertion active */
+               uint32_t cre :1; /* crc receive enable */
+               uint32_t crf :1; /* crc receive byte placed in fifo */
+               uint32_t wsr :1; /* wait state response */
+               uint32_t sdl :2; /* spi data lines (single, dual, quad) */
+       } bits;
+} espi_cr3_t;
+
+static inline uint32_t espi_readl(struct espi_data *priv, uint32_t offset)
+{
+       return __raw_readl(priv->regs + offset);
+}
+static inline void espi_writel(struct espi_data *priv, uint32_t offset, uint32_t val)
+{
+       __raw_writel(val, priv->regs + offset);
+}
+
+static void espi_set_cs          (struct spi_device *spi, bool enable);
+static int  espi_setup   (struct spi_device *spi);
+static void espi_writer          (struct espi_data *priv);
+static void espi_reader          (struct espi_data *priv);
+static void espi_reset   (struct espi_data *priv);
+static void espi_enable_tx(struct espi_data *priv);
+static void espi_enable_rx(struct espi_data *priv);
+
+static void espi_reset(struct espi_data *priv)
+{
+       espi_cr1_t cr1;
+       cr1.val = espi_readl(priv, ESPI_CR1);
+       cr1.bits.scr = ESPI_CR1_SCR_RESET;
+       espi_writel(priv, ESPI_CR1, cr1.val);
+}
+
+static void espi_enable_rx(struct espi_data *priv)
+{
+       espi_cr2_t cr2;
+       cr2.val = espi_readl(priv, ESPI_CR2);
+       cr2.bits.srd = ESPI_CR2_SRD_RX_ENABLE;
+       espi_writel(priv, ESPI_CR2, cr2.val);
+}
+
+static void espi_enable_tx(struct espi_data *priv)
+{
+       espi_cr2_t cr2;
+       int cnt = espi_readl(priv, ESPI_TX_FBCAR);
+       cr2.val = espi_readl(priv, ESPI_CR2);
+
+       if (cnt && cr2.bits.mte != ESPI_CR2_MTE_TX_ENABLE) {
+               cr2.bits.mte = ESPI_CR2_MTE_TX_ENABLE;
+               espi_writel(priv, ESPI_CR2, cr2.val);
+       }
+}
+
+static void espi_set_cs(struct spi_device *spi, bool enable)
+{
+       struct espi_data *priv;
+       espi_cr2_t cr2;
+       priv = spi_master_get_devdata(spi->master);
+       cr2.val = espi_readl(priv, ESPI_CR2);
+       cr2.bits.sso = spi->chip_select;
+       espi_writel(priv, ESPI_CR2, cr2.val);
+}
+
+static void espi_writer(struct espi_data *priv)
+{
+       volatile uint8_t data;
+       uint32_t have_tx = espi_readl(priv, ESPI_TX_FBCAR);
+       uint32_t have_rx = espi_readl(priv, ESPI_RX_FBCAR);
+       uint32_t free = ESPI_FIFO_LEN - have_tx - have_rx;
+       uint32_t cnt = min(free, priv->txlen);
+
+       if (!cnt) {
+               return;
+       }
+
+       priv->txlen -= cnt;
+
+       /* fifo */
+       while (cnt--) {
+               if (priv->tx) {
+                       data = *priv->tx++;
+               } else {
+                       data = ESPI_DUMMY_DATA;
+               }
+               espi_writel(priv, ESPI_TX_FIFO, data);
+       }
+
+       espi_enable_tx(priv);
+}
+
+static void espi_reader(struct espi_data *priv)
+{
+       volatile uint8_t data;
+       uint32_t have = espi_readl(priv, ESPI_RX_FBCAR);
+       uint32_t cnt = min(have, priv->rxlen);
+
+       if (!cnt) {
+               return;
+       }
+
+       priv->rxlen -= cnt;
+
+       /* fifo */
+       while (cnt--) {
+               data = espi_readl(priv, ESPI_RX_FIFO);
+               if (priv->rx) {
+                       *priv->rx++ = data;
+               }
+       }
+}
+
+static irqreturn_t espi_irq_handler(int irq, void *dev)
+{
+       espi_irq_t status;
+       struct spi_master *master = dev;
+       struct espi_data *priv = spi_master_get_devdata(master);
+       int ret = -1;
+
+       status.val = espi_readl(priv, ESPI_IVR);
+       espi_writel(priv, ESPI_ISR, status.val);
+
+       if (!status.val) {
+               dev_err(&master->dev, "irq_none\n");
+               return IRQ_NONE;
+       }
+
+       /* errors */
+       if (status.bits.tx_underrun) {
+               dev_err(&master->dev, "tx_underrun\n");
+               goto exit;
+       }
+       if (status.bits.tx_overrun) {
+               dev_err(&master->dev, "tx_overrun\n");
+               goto exit;
+       }
+       if (status.bits.rx_underrun) {
+               dev_err(&master->dev, "rx_underrun\n");
+               goto exit;
+       }
+       if (status.bits.rx_overrun) {
+               dev_err(&master->dev, "rx_overrun\n");
+               goto exit;
+       }
+       if (status.bits.rx_crc_error) {
+               dev_err(&master->dev, "rx_crc_error\n");
+               goto exit;
+       }
+       if (status.bits.alert) {
+               dev_err(&master->dev, "alert\n");
+               goto exit;
+       }
+
+       /* transfer */
+       espi_reader(priv);
+       espi_writer(priv);
+       ret = 0;
+
+exit:
+       if (ret) {
+               master->cur_msg->status = -EIO;
+               priv->txlen = 0;
+               priv->rxlen = 0;
+               espi_reset(priv);
+       }
+
+       /* tx done */
+       if (priv->txlen == 0) {
+               espi_irq_t mask;
+               mask.val = espi_readl(priv, ESPI_IMR);
+               mask.bits.tx_almost_empty = ESPI_IRQ_DISABLE;
+               espi_writel(priv, ESPI_IMR, mask.val);
+       }
+
+       /* rx_done */
+       if ((priv->txlen == 0) && (priv->rxlen == 0)) {
+               espi_writel(priv, ESPI_IMR, ESPI_IMR_DISABLE_ALL);
+               espi_writel(priv, ESPI_ISR, ESPI_ISR_CLEAR_ALL);
+               spi_finalize_current_transfer(master);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int espi_transfer_one(struct spi_master *master,
+                            struct spi_device *spi,
+                            struct spi_transfer *transfer)
+{
+       struct espi_data *priv = spi_master_get_devdata(spi->master);
+
+       priv->tx = (void*)transfer->tx_buf;
+       priv->rx = (void*)transfer->rx_buf;
+       priv->txlen = transfer->len;
+       priv->rxlen = transfer->len;
+
+       /* clk */
+       if (transfer->speed_hz && (transfer->speed_hz != spi->max_speed_hz)) {
+               clk_set_rate(priv->clk, transfer->speed_hz);
+       }
+
+       /* transfer */
+       espi_writer(priv);
+       espi_writel(priv, ESPI_IMR, ESPI_IMR_ENABLE_ALL);
+
+       return 1;
+}
+
+static int espi_setup(struct spi_device *spi)
+{
+       struct espi_data *priv = spi_master_get_devdata(spi->master);
+       espi_cr1_t cr1;
+       espi_cr2_t cr2;
+
+       espi_reset(priv);
+
+       /* irq */
+       espi_writel(priv, ESPI_IMR, ESPI_IMR_DISABLE_ALL);
+       espi_writel(priv, ESPI_ISR, ESPI_ISR_CLEAR_ALL);
+
+       /* control 1 */
+       cr1.val = espi_readl(priv, ESPI_CR1);
+       cr1.bits.scr = ESPI_CR1_SCR_NORESET;
+       cr1.bits.sce = ESPI_CR1_SCE_CORE_ENABLE;
+       cr1.bits.mss = ESPI_CR1_MSS_MODE_MASTER;
+       cr1.bits.cph = spi->mode & SPI_CPHA ? ESPI_CR1_CPH_CLKPHASE_EVEN :
+                                             ESPI_CR1_CPH_CLKPHASE_ODD;
+       cr1.bits.cpo = spi->mode & SPI_CPOL ? ESPI_CR1_CPO_CLKPOLAR_HIGH :
+                                             ESPI_CR1_CPO_CLKPOLAR_LOW;
+       espi_writel(priv, ESPI_CR1, cr1.val);
+
+       /* control 2 */
+       cr2.val = espi_readl(priv, ESPI_CR2);
+       cr2.bits.srd = ESPI_CR2_SRD_RX_DISABLE;
+       cr2.bits.mte = ESPI_CR2_MTE_TX_DISABLE;
+       cr2.bits.sri = ESPI_CR2_SRI_FIRST_RESIEV;
+       cr2.bits.mlb = spi->mode & SPI_LSB_FIRST ? ESPI_CR2_MLB_LSB :
+                                                  ESPI_CR2_MLB_MSB;
+       espi_writel(priv, ESPI_CR2, cr2.val);
+
+       /* threshold */
+       espi_writel(priv, ESPI_TX_FAETR, ESPI_FIFO_LEN / 2);
+       espi_writel(priv, ESPI_RX_FAFTR, ESPI_FIFO_LEN / 2);
+
+       /* full duplex */
+       espi_enable_rx(priv);
+
+       /* clk */
+       if (spi->max_speed_hz != clk_get_rate(priv->clk)) {
+               clk_set_rate(priv->clk, spi->max_speed_hz);
+               spi->max_speed_hz = clk_get_rate(priv->clk);
+       }
+
+       return 0;
+}
+
+static int espi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &(pdev->dev);
+       struct espi_data *priv;
+       struct resource *mem;
+       int err;
+       struct spi_master *master;
+
+       /* init */
+       priv = devm_kzalloc(dev, sizeof(struct espi_data), GFP_KERNEL);
+       if (!priv) {
+               dev_err(dev, "no memory\n");
+               return -ENOMEM;
+       }
+       platform_set_drvdata(pdev, priv);
+
+       /* reg */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               return -EINVAL;
+       }
+
+       priv->regs = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(priv->regs)) {
+               dev_err(dev, "region map failed\n");
+               return PTR_ERR(priv->regs);
+       }
+
+       /* irq */
+       priv->irq = platform_get_irq(pdev, 0);
+       if (priv->irq < 0) {
+               dev_err(&pdev->dev, "no irq resource\n");
+               return priv->irq;
+       }
+
+       /* clk */
+       priv->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               dev_err(dev, "could not get spi clock\n");
+               return PTR_ERR(priv->clk);
+       }
+       err = clk_prepare_enable(priv->clk);
+       if (err) {
+               dev_err(dev, "could not prepare spi clock\n");
+               return err;
+       }
+
+       /* master */
+       master = spi_alloc_master(dev, 0);
+       if (!master) {
+               return -ENOMEM;
+       }
+       priv->master = master;
+       master->num_chipselect = 8;
+       master->use_gpio_descriptors = true;
+       master->mode_bits = SPI_CPHA | SPI_CPOL;
+       master->dev.of_node = dev->of_node;
+       master->dev.fwnode = dev->fwnode;
+       master->transfer_one = espi_transfer_one;
+       master->set_cs = espi_set_cs;
+       master->setup = espi_setup;
+       master->bus_num = of_alias_get_id(master->dev.of_node, "spi");
+       master->dev.of_node = dev->of_node;
+       master->dev.fwnode = dev->fwnode;
+       master->flags = SPI_MASTER_GPIO_SS;
+
+       err = devm_request_irq(dev, priv->irq, espi_irq_handler,
+               IRQF_SHARED, pdev->name, master);
+       if (err) {
+               dev_err(&pdev->dev, "unable to request irq %d\n", priv->irq);
+               return err;
+       }
+
+       spi_master_set_devdata(master, priv);
+
+       err = devm_spi_register_master(dev, master);
+       if (err) {
+               dev_err(&master->dev, "problem registering spi master\n");
+               spi_master_put(master);
+               return err;
+       }
+
+       return 0;
+}
+
+static int espi_remove(struct platform_device *pdev)
+{
+       struct espi_data *priv = platform_get_drvdata(pdev);
+       espi_reset(priv);
+       clk_disable_unprepare(priv->clk);
+       return 0;
+}
+
+static const struct of_device_id baikal_espi_of_match[] = {
+       { .compatible = "baikal,bm1000-espi" },
+       { .compatible = "baikal,bs1000-espi" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, baikal_espi_of_match);
+
+static struct platform_driver baikal_espi_driver = {
+       .probe          = espi_probe,
+       .remove         = espi_remove,
+       .driver         = {
+               .name   = "baikal-espi",
+               .of_match_table = baikal_espi_of_match
+       }
+};
+module_platform_driver(baikal_espi_driver);
+
+MODULE_DESCRIPTION("Baikal eSPI controller driver");
+MODULE_LICENSE("GPL v2");
index 7db51781289edb0c5aee1ee60048840b810b793e..cc5520f01649679809b5818faecee2f8f68ba8ef 100644 (file)
@@ -772,8 +772,14 @@ static const struct dw8250_platform_data dw8250_starfive_jh7100_data = {
        .quirks = DW_UART_QUIRK_SKIP_SET_RATE,
 };
 
+static const struct dw8250_platform_data dw8250_baikal_bm1000_data = {
+       .usr_reg = DW_UART_USR,
+       .quirks = DW_UART_QUIRK_SKIP_SET_RATE,
+};
+
 static const struct of_device_id dw8250_of_match[] = {
        { .compatible = "snps,dw-apb-uart", .data = &dw8250_dw_apb },
+       { .compatible = "baikal,bm1000-uart", .data = &dw8250_baikal_bm1000_data },
        { .compatible = "cavium,octeon-3860-uart", .data = &dw8250_octeon_3860_data },
        { .compatible = "marvell,armada-38x-uart", .data = &dw8250_armada_38x_data },
        { .compatible = "renesas,rzn1-uart", .data = &dw8250_renesas_rzn1_data },
index f668e75fbabe6191e7fb33dd032df6321b5b8679..5270f456411572d9bba1c234b83f954e23f2ace2 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <sound/hdmi-codec.h>
 
+struct drm_bridge;
 struct drm_display_info;
 struct drm_display_mode;
 struct drm_encoder;
@@ -174,6 +175,8 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
 
 void dw_hdmi_resume(struct dw_hdmi *hdmi);
 
+struct drm_bridge *dw_hdmi_get_bridge(struct dw_hdmi *hdmi);
+
 void dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense);
 
 int dw_hdmi_set_plugged_cb(struct dw_hdmi *hdmi, hdmi_codec_plugged_cb fn,
index 994bfcdd84c559f88d57496b1117490ec9f3b31e..0c5f07caf7f2c78ac723bffd280193cb3e5e9ddd 100644 (file)
@@ -205,6 +205,8 @@ int drm_panel_disable(struct drm_panel *panel);
 
 int drm_panel_get_modes(struct drm_panel *panel, struct drm_connector *connector);
 
+struct drm_panel *fwnode_drm_find_panel(const struct fwnode_handle *fwnode);
+
 #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL)
 struct drm_panel *of_drm_find_panel(const struct device_node *np);
 int of_drm_get_panel_orientation(const struct device_node *np,
index 9752f3745f764293990ea6f40677ca0ef2980840..0f4e1f02c060fe1a4aba3572f42f0ea76251e87b 100644 (file)
@@ -30,7 +30,8 @@ struct dw_dma;
 struct dw_dma_chip {
        struct device   *dev;
        int             id;
-       int             irq;
+       int             *irq;
+       int             irq_num;
        void __iomem    *regs;
        struct clk      *clk;
        struct dw_dma   *dw;
index 6b1301e2498e9e163c6e9a0525e49afa226bffea..d2126b26e878a77e88ef5a92c794fa4cba6b338a 100644 (file)
@@ -88,6 +88,8 @@ extern const struct pci_ecam_ops xgene_v2_pcie_ecam_ops; /* APM X-Gene PCIe v2.x
 extern const struct pci_ecam_ops al_pcie_ops;  /* Amazon Annapurna Labs PCIe */
 extern const struct pci_ecam_ops tegra194_pcie_ops; /* Tegra194 PCIe */
 extern const struct pci_ecam_ops loongson_pci_ecam_ops; /* Loongson PCIe */
+extern const struct pci_ecam_ops baikal_m_pcie_ecam_ops; /* Baikal-M Synopsys DesignWare PCIe */
+extern const struct pci_ecam_ops baikal_s_pcie_ecam_ops; /* Baikal-S Synopsys DesignWare PCIe */
 #endif
 
 #if IS_ENABLED(CONFIG_PCI_HOST_COMMON)
index 9a60bfdb39bac564b07a6e2a19206d224b1160a8..ba147bdd382a7a91760dccd133a7601bfee7145e 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/kernel.h>
 #include <linux/delay.h>
 #include <linux/export.h>
+#include <linux/property.h>
 #include <sound/core.h>
 #include <sound/hdaudio.h>
 #include <sound/hda_register.h>
@@ -78,8 +79,16 @@ void snd_hdac_bus_init_cmd_io(struct hdac_bus *bus)
        snd_hdac_chip_writew(bus, RIRBWP, AZX_RIRBWP_RST);
        /* set N=1, get RIRB response interrupt for new entry */
        snd_hdac_chip_writew(bus, RINTCNT, 1);
-       /* enable rirb dma and response irq */
-       snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+
+       if (bus->polling_mode &&
+           device_property_read_bool(bus->dev, "broken-response-irq"))
+               /* enable rirb dma without response irq */
+               snd_hdac_chip_writeb(bus, RIRBCTL, AZX_RBCTL_DMA_EN);
+       else
+               /* enable rirb dma and response irq */
+               snd_hdac_chip_writeb(bus, RIRBCTL,
+                                    AZX_RBCTL_DMA_EN | AZX_RBCTL_IRQ_EN);
+
        /* Accept unsolicited responses */
        snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL);
        spin_unlock_irq(&bus->reg_lock);
@@ -146,6 +155,9 @@ int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val)
 
        spin_lock_irq(&bus->reg_lock);
 
+       if (device_property_read_bool(bus->dev, "increment-codec-address"))
+               val = val + 0x10000000;
+
        bus->last_cmd[azx_command_addr(val)] = val;
 
        /* add command to corb */
index d29d8372a3c0455d89da0d23f037ad94dca11fd2..39e021d99b0da06546c32a1ac2614ecd6f2f6d8e 100644 (file)
@@ -42,6 +42,18 @@ config SND_HDA_TEGRA
          To compile this driver as a module, choose M here: the module
          will be called snd-hda-tegra.
 
+config SND_HDA_BAIKAL
+       tristate "Baikal HD Audio"
+       depends on ARCH_BAIKAL
+       select SND_HDA
+       select SND_HDA_ALIGNED_MMIO
+       help
+         Say Y here to support the HDA controller present in Baikal
+         SoCs
+
+         This options enables support for the HD Audio controller
+         present in Baikal SoCs.
+
 if SND_HDA
 
 config SND_HDA_HWDEP
index 00d3061044842d50fe3b63a703cddcc5fc9f875c..0f58b35cafb1bec3cefad6322698cad383651508 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 snd-hda-intel-objs := hda_intel.o
 snd-hda-tegra-objs := hda_tegra.o
+snd-hda-baikal-objs := hda_baikal.o
 
 snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o
 snd-hda-codec-y += hda_controller.o
@@ -62,3 +63,4 @@ obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
 # when built in kernel
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-intel.o
 obj-$(CONFIG_SND_HDA_TEGRA) += snd-hda-tegra.o
+obj-$(CONFIG_SND_HDA_BAIKAL) += snd-hda-baikal.o
diff --git a/sound/pci/hda/hda_baikal.c b/sound/pci/hda/hda_baikal.c
new file mode 100644 (file)
index 0000000..2c578c3
--- /dev/null
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+* Implementation of primary ALSA driver code base for Baikal-M HDA controller.
+*
+* /sound/pci/hda/hda_baikal.c
+*
+* Copyright (C) 2021-2022 Baikal Electronics, JSC
+* Author: Danila Sharikov <Danila.Sharikov@baikalelectronics.com>
+*
+* Based on:
+*       hda_intel.c, Copyright(c) 2004 Intel Corporation. All rights reserved.
+*        hda_tegra.c
+*/
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/hda_codec.h>
+
+#include "hda_controller.h"
+
+#ifdef CONFIG_PM
+static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT;
+module_param(power_save, bint, 0644);
+MODULE_PARM_DESC(power_save,
+                "Automatic power-saving timeout (in seconds, 0 = disable).");
+#else
+#define power_save     0
+#endif
+
+/* max number of SDs */
+#define NUM_CAPTURE_SD 4
+#define NUM_PLAYBACK_SD        4
+
+struct hda_baikal {
+       struct azx chip;
+       struct device *dev;
+       void __iomem *regs;
+       struct work_struct probe_work;
+       struct work_struct irq_pending_work;
+       unsigned int irq_pending_warned:1;
+};
+
+static void hda_baikal_probe_work(struct work_struct *work);
+
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev);
+static const struct hda_controller_ops hda_baikal_ops;
+
+/* calculate runtime delay from LPIB */
+static int azx_get_delay_from_lpib(struct azx *chip, struct azx_dev *azx_dev,
+                                  unsigned int pos)
+{
+       struct snd_pcm_substream *substream = azx_dev->core.substream;
+       int stream = substream->stream;
+       unsigned int lpib_pos = azx_get_pos_lpib(chip, azx_dev);
+       int delay;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               delay = pos - lpib_pos;
+       else
+               delay = lpib_pos - pos;
+       if (delay < 0) {
+               if (delay >= azx_dev->core.delay_negative_threshold)
+                       delay = 0;
+               else
+                       delay += azx_dev->core.bufsize;
+       }
+
+       if (delay >= azx_dev->core.period_bytes) {
+               dev_info(chip->card->dev,
+                        "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
+                        delay, azx_dev->core.period_bytes);
+               delay = 0;
+               chip->driver_caps &= ~AZX_DCAPS_COUNT_LPIB_DELAY;
+               chip->get_delay[stream] = NULL;
+       }
+
+       return bytes_to_frames(substream->runtime, delay);
+}
+
+/* called from IRQ */
+static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev)
+{
+       struct hda_baikal *hda = container_of(chip, struct hda_baikal, chip);
+       int ok;
+
+       ok = azx_position_ok(chip, azx_dev);
+
+       if (ok == 1) {
+               azx_dev->irq_pending = 0;
+               return ok;
+       } else if (ok == 0) {
+               /* bogus IRQ, process it later */
+               azx_dev->irq_pending = 1;
+               schedule_work(&hda->irq_pending_work);
+       }
+       return 0;
+}
+
+/*
+ * Check whether the current DMA position is acceptable for updating
+ * periods.  Returns non-zero if it's OK.
+ *
+ * Many HD-audio controllers appear pretty inaccurate about
+ * the update-IRQ timing.  The IRQ is issued before actually the
+ * data is processed.  So, we need to process it afterwords in a
+ * workqueue.
+ */
+static int azx_position_ok(struct azx *chip, struct azx_dev *azx_dev)
+{
+       struct snd_pcm_substream *substream = azx_dev->core.substream;
+       int stream = substream->stream;
+       u32 wallclk;
+       unsigned int pos;
+
+       wallclk = azx_readl(chip, WALLCLK) - azx_dev->core.start_wallclk;
+       if (wallclk < (azx_dev->core.period_wallclk * 2) / 3)
+               return -1;      /* bogus (too early) interrupt */
+
+       if (chip->get_position[stream])
+               pos = chip->get_position[stream](chip, azx_dev);
+       else { /* use the position buffer as default */
+               pos = azx_get_pos_posbuf(chip, azx_dev);
+               if (!pos || pos == (u32)-1) {
+                       dev_info(chip->card->dev,
+                                "Invalid position buffer, using LPIB read method instead.\n");
+                       chip->get_position[stream] = azx_get_pos_lpib;
+                       if (chip->get_position[0] == azx_get_pos_lpib &&
+                           chip->get_position[1] == azx_get_pos_lpib)
+                               azx_bus(chip)->use_posbuf = false;
+                       pos = azx_get_pos_lpib(chip, azx_dev);
+                       chip->get_delay[stream] = NULL;
+               } else {
+                       chip->get_position[stream] = azx_get_pos_posbuf;
+                       if (chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)
+                               chip->get_delay[stream] = azx_get_delay_from_lpib;
+               }
+       }
+
+       if (pos >= azx_dev->core.bufsize)
+               pos = 0;
+
+       if (WARN_ONCE(!azx_dev->core.period_bytes,
+                     "hda-baikal: zero azx_dev->period_bytes"))
+               return -1; /* this shouldn't happen! */
+       if (wallclk < (azx_dev->core.period_wallclk * 5) / 4 &&
+           pos % azx_dev->core.period_bytes > azx_dev->core.period_bytes / 2)
+               /* NG - it's below the first next period boundary */
+               return chip->bdl_pos_adj ? 0 : -1;
+       azx_dev->core.start_wallclk += wallclk;
+       return 1; /* OK, it's fine */
+}
+
+/* The work for pending PCM period updates */
+static void azx_irq_pending_work(struct work_struct *work)
+{
+       struct hda_baikal *hda = container_of(work, struct hda_baikal, irq_pending_work);
+       struct azx *chip = &hda->chip;
+       struct hdac_bus *bus = azx_bus(chip);
+       struct hdac_stream *s;
+       int pending, ok;
+
+       if (!hda->irq_pending_warned) {
+               dev_info(chip->card->dev,
+                        "IRQ timing workaround is activated for card #%d. Suggest a bigger bdl_pos_adj.\n",
+                        chip->card->number);
+               hda->irq_pending_warned = 1;
+       }
+
+       for (;;) {
+               pending = 0;
+               spin_lock_irq(&bus->reg_lock);
+               list_for_each_entry(s, &bus->stream_list, list) {
+                       struct azx_dev *azx_dev = stream_to_azx_dev(s);
+                       if (!azx_dev->irq_pending ||
+                           !s->substream ||
+                           !s->running)
+                               continue;
+                       ok = azx_position_ok(chip, azx_dev);
+                       if (ok > 0) {
+                               azx_dev->irq_pending = 0;
+                               spin_unlock(&bus->reg_lock);
+                               snd_pcm_period_elapsed(s->substream);
+                               spin_lock(&bus->reg_lock);
+                       } else if (ok < 0) {
+                               pending = 0; /* too early */
+                       } else
+                               pending++;
+               }
+               spin_unlock_irq(&bus->reg_lock);
+               if (!pending)
+                       return;
+               msleep(1);
+       }
+}
+
+/* clear irq_pending flags and assure no on-going workq */
+static void azx_clear_irq_pending(struct azx *chip)
+{
+       struct hdac_bus *bus = azx_bus(chip);
+       struct hdac_stream *s;
+
+       spin_lock_irq(&bus->reg_lock);
+       list_for_each_entry(s, &bus->stream_list, list) {
+               struct azx_dev *azx_dev = stream_to_azx_dev(s);
+               azx_dev->irq_pending = 0;
+       }
+       spin_unlock_irq(&bus->reg_lock);
+}
+
+static int hda_baikal_dev_disconnect(struct snd_device *device)
+{
+       struct azx *chip = device->device_data;
+
+       chip->bus.shutdown = 1;
+       return 0;
+}
+
+static int hda_baikal_dev_free(struct snd_device *device)
+{
+       struct azx *chip = device->device_data;
+       struct hda_baikal *hda = container_of(chip, struct hda_baikal, chip);
+
+       cancel_work_sync(&hda->probe_work);
+       if (azx_bus(chip)->chip_init) {
+               azx_clear_irq_pending(chip);
+               azx_stop_all_streams(chip);
+               azx_stop_chip(chip);
+       }
+
+       azx_free_stream_pages(chip);
+       azx_free_streams(chip);
+       snd_hdac_bus_exit(azx_bus(chip));
+
+       return 0;
+}
+
+static int hda_baikal_init_chip(struct azx *chip, struct platform_device *pdev)
+{
+       struct hda_baikal *hda = container_of(chip, struct hda_baikal, chip);
+       struct hdac_bus *bus = azx_bus(chip);
+       struct device *dev = hda->dev;
+       struct resource *res;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       hda->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(hda->regs))
+               return PTR_ERR(hda->regs);
+
+       bus->remap_addr = hda->regs;
+       bus->addr = res->start;
+
+       return 0;
+}
+
+static int hda_baikal_first_init(struct azx *chip, struct platform_device *pdev)
+{
+       struct hdac_bus *bus = azx_bus(chip);
+       struct snd_card *card = chip->card;
+       int err;
+       unsigned short gcap;
+       int irq_id = platform_get_irq(pdev, 0);
+       const char *sname, *drv_name = "Baikal-HDA";
+
+       err = hda_baikal_init_chip(chip, pdev);
+       if (err)
+               return err;
+
+       err = devm_request_irq(chip->card->dev, irq_id, azx_interrupt,
+                            IRQF_SHARED, KBUILD_MODNAME, chip);
+       if (err) {
+               dev_err(chip->card->dev,
+                       "unable to request IRQ %d, disabling device\n",
+                       irq_id);
+               return err;
+       }
+       bus->irq = irq_id;
+
+       synchronize_irq(bus->irq);
+
+       gcap = azx_readw(chip, GCAP);
+       dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+
+       /*
+        * Read number of streams from GCAP register instead of using
+        * hardcoded value
+        */
+       chip->capture_streams = (gcap >> 8) & 0x0f;
+       chip->playback_streams = (gcap >> 12) & 0x0f;
+       if (!chip->playback_streams && !chip->capture_streams) {
+               /* gcap didn't give any info, switching to old method */
+               chip->playback_streams = NUM_PLAYBACK_SD;
+               chip->capture_streams = NUM_CAPTURE_SD;
+       }
+       chip->capture_index_offset = 0;
+       chip->playback_index_offset = chip->capture_streams;
+       chip->num_streams = chip->playback_streams + chip->capture_streams;
+
+       /* initialize streams */
+       err = azx_init_streams(chip);
+       if (err < 0) {
+               dev_err(card->dev, "failed to initialize streams: %d\n", err);
+               return err;
+       }
+
+       err = azx_alloc_stream_pages(chip);
+       if (err < 0) {
+               dev_err(card->dev, "failed to allocate stream pages: %d\n",
+                       err);
+               return err;
+       }
+
+       /* initialize chip */
+       azx_init_chip(chip, 1);
+
+       /* codec detection */
+       if (!bus->codec_mask) {
+               dev_err(card->dev, "no codecs found!\n");
+               return -ENODEV;
+       }
+
+       /* driver name */
+       strncpy(card->driver, drv_name, sizeof(card->driver));
+       /* shortname for card */
+       if (device_property_read_string(&pdev->dev, "baikal,model", &sname))
+               sname = drv_name;
+       if (strlen(sname) > sizeof(card->shortname))
+               dev_info(card->dev, "truncating shortname for card\n");
+       strncpy(card->shortname, sname, sizeof(card->shortname));
+
+       /* longname for card */
+       snprintf(card->longname, sizeof(card->longname),
+                "%s at 0x%lx irq %i",
+                card->shortname, bus->addr, bus->irq);
+
+       return 0;
+}
+
+static int hda_baikal_create(struct snd_card *card,
+                           unsigned int driver_caps,
+                           struct hda_baikal *hda)
+{
+       static struct snd_device_ops ops = {
+               .dev_disconnect = hda_baikal_dev_disconnect,
+               .dev_free = hda_baikal_dev_free,
+       };
+       struct azx *chip;
+       int err;
+
+       chip = &hda->chip;
+
+       mutex_init(&chip->open_mutex);
+       chip->card = card;
+       chip->ops = &hda_baikal_ops;
+       chip->driver_caps = driver_caps;
+       chip->driver_type = driver_caps & 0xff;
+       chip->dev_index = 0;
+       INIT_LIST_HEAD(&chip->pcm_list);
+       INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work);
+
+       chip->codec_probe_mask = 0x3; /* two codecs */
+
+       chip->single_cmd = false;
+       chip->snoop = true;
+
+       chip->get_position[0] = chip->get_position[1] = azx_get_pos_lpib;
+       chip->get_delay[0] = chip->get_delay[1] = azx_get_delay_from_lpib;
+
+       INIT_WORK(&hda->probe_work, hda_baikal_probe_work);
+
+       err = azx_bus_init(chip, NULL);
+       if (err < 0)
+               return err;
+
+       //chip->bus.needs_damn_long_delay = 1;
+       chip->bus.core.aligned_mmio = 1;
+
+       /* force polling mode, because RIRB interrupts don't working */
+       if (device_property_read_bool(hda->dev, "force-polling-mode"))
+               chip->bus.core.polling_mode = 1;
+       else
+               chip->bus.core.polling_mode = 0;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+       if (err < 0) {
+               dev_err(card->dev, "Error creating device\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int hda_baikal_probe(struct platform_device *pdev)
+{
+       const unsigned int driver_flags = AZX_DCAPS_PM_RUNTIME |
+                                         AZX_DCAPS_NO_64BIT |
+                                         AZX_DCAPS_4K_BDLE_BOUNDARY |
+                                         AZX_DCAPS_COUNT_LPIB_DELAY;
+       struct snd_card *card;
+       struct azx *chip;
+       struct hda_baikal *hda;
+       int err;
+
+       hda = devm_kzalloc(&pdev->dev, sizeof(*hda), GFP_KERNEL);
+       if (!hda)
+               return -ENOMEM;
+       hda->dev = &pdev->dev;
+       chip = &hda->chip;
+
+       err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+                          THIS_MODULE, 0, &card);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Error creating card!\n");
+               return err;
+       }
+
+       err = hda_baikal_create(card, driver_flags, hda);
+       if (err < 0)
+               goto out_free;
+       card->private_data = chip;
+
+       dev_set_drvdata(&pdev->dev, card);
+
+       pm_runtime_enable(hda->dev);
+       if (!azx_has_pm_runtime(chip))
+               pm_runtime_forbid(hda->dev);
+
+       schedule_work(&hda->probe_work);
+
+       return 0;
+
+out_free:
+       snd_card_free(card);
+       return err;
+}
+
+/* Probe the given codec address */
+static int baikal_probe_codec(struct azx *chip, int addr)
+{
+       unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+               (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+       struct hdac_bus *bus = azx_bus(chip);
+       int err;
+       unsigned int res = -1;
+
+       mutex_lock(&bus->cmd_mutex);
+       chip->probing = 1;
+       bus->ops->command(bus, cmd);
+       err = bus->ops->get_response(bus, addr, &res);
+       chip->probing = 0;
+       mutex_unlock(&bus->cmd_mutex);
+       if (err < 0 || res == -1)
+               return -EIO;
+       dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr);
+       return 0;
+}
+
+/* Probe codecs */
+static int azx_baikal_probe_codecs(struct azx *chip, unsigned int max_slots)
+{
+       struct hdac_bus *bus = azx_bus(chip);
+       int c, codecs, err;
+
+       int probe_retry;
+
+       codecs = 0;
+       if (!max_slots)
+               max_slots = AZX_DEFAULT_CODECS;
+
+       for (c = 0; c < max_slots; c++) {
+               if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+                       for (probe_retry = 0; probe_retry < 100; probe_retry++) {
+                               if (baikal_probe_codec(chip, c) < 0) {
+                                       azx_stop_chip(chip);
+                                       azx_init_chip(chip, true);
+                                       continue;
+                               } else {
+                                       dev_warn(chip->card->dev,
+                                               "Codec #%d probe success; retry count = %d\n",
+                                               c, probe_retry);
+                                       break;
+                               }
+                               bus->codec_mask &= ~(1 << c);
+                               dev_warn(chip->card->dev,
+                                       "Codec #%d probe error; disabling it...\n", c);
+                       }
+               }
+       }
+
+       /* Then create codec instances */
+       for (c = 0; c < max_slots; c++) {
+               if ((bus->codec_mask & (1 << c)) & chip->codec_probe_mask) {
+                       struct hda_codec *codec;
+                       err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec);
+                       if (err < 0)
+                               continue;
+                       codec->jackpoll_interval = chip->jackpoll_interval;
+                       codec->beep_mode = chip->beep_mode;
+                       codecs++;
+               }
+       }
+       if (!codecs) {
+               dev_err(chip->card->dev, "no codecs initialized\n");
+               return -ENXIO;
+       }
+       return 0;
+}
+
+static void hda_baikal_probe_work(struct work_struct *work)
+{
+       struct hda_baikal *hda = container_of(work, struct hda_baikal,
+                                       probe_work);
+       struct azx *chip = &hda->chip;
+       struct hdac_bus *bus = azx_bus(chip);
+       struct platform_device *pdev = to_platform_device(hda->dev);
+       int max_slots;
+       int err;
+
+       pm_runtime_get_sync(hda->dev);
+       err = hda_baikal_first_init(chip, pdev);
+       if (err < 0)
+               goto out_free;
+
+       switch (bus->codec_mask) {
+       case 0x1:
+               max_slots = 1;
+               break;
+       case 0x2:
+               max_slots = 2;
+               break;
+       case 0x3:
+               max_slots = 2;
+               break;
+       }
+
+       /* create codec instances */
+       if (device_property_read_bool(bus->dev, "cyclic-codec-probe"))
+               err = azx_baikal_probe_codecs(chip, max_slots);
+       else
+               err = azx_probe_codecs(chip, max_slots);
+
+       if (err < 0)
+               goto out_free;
+
+       err = azx_codec_configure(chip);
+       if (err < 0)
+               goto out_free;
+
+       err = snd_card_register(chip->card);
+       if (err < 0)
+               goto out_free;
+
+       chip->running = 1;
+
+       snd_hda_set_power_save(&chip->bus, power_save * 1000);
+
+ out_free:
+       pm_runtime_put(hda->dev);
+       return; /* no error return from async probe */
+}
+
+static int hda_baikal_remove(struct platform_device *pdev)
+{
+       int ret;
+
+       ret = snd_card_free(dev_get_drvdata(&pdev->dev));
+       pm_runtime_disable(&pdev->dev);
+
+       return ret;
+}
+
+static void hda_baikal_shutdown(struct platform_device *pdev)
+{
+       struct snd_card *card = dev_get_drvdata(&pdev->dev);
+       struct azx *chip;
+
+       if (!card)
+               return;
+       chip = card->private_data;
+       if (chip && chip->running)
+               azx_stop_chip(chip);
+}
+
+static const struct hda_controller_ops hda_baikal_ops = {
+       .position_check = azx_position_check,
+};
+
+static const struct of_device_id hda_baikal_match[] = {
+       { .compatible = "baikal,bm1000-hda" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, hda_baikal_match);
+
+static struct platform_driver baikal_platform_hda = {
+       .driver = {
+               .name = "baikal-hda",
+               .of_match_table = hda_baikal_match,
+       },
+       .probe = hda_baikal_probe,
+       .remove = hda_baikal_remove,
+       .shutdown = hda_baikal_shutdown,
+};
+module_platform_driver(baikal_platform_hda);
+
+MODULE_DESCRIPTION("Baikal HDA bus driver");
+MODULE_LICENSE("GPL");
index 848fbae26c3bf665aa52b41886de40d33d50742c..54c314bc4d6c66f8e26a29ffb2914e15c49db4e3 100644 (file)
@@ -71,6 +71,7 @@ source "sound/soc/amd/Kconfig"
 source "sound/soc/apple/Kconfig"
 source "sound/soc/atmel/Kconfig"
 source "sound/soc/au1x/Kconfig"
+source "sound/soc/baikal/Kconfig"
 source "sound/soc/bcm/Kconfig"
 source "sound/soc/cirrus/Kconfig"
 source "sound/soc/dwc/Kconfig"
index 507eaed1d6a139c8baaae18da94d436af54132f8..a24c8174bdf91ff0feccf4054cebca4974ab2d47 100644 (file)
@@ -39,6 +39,7 @@ obj-$(CONFIG_SND_SOC) += adi/
 obj-$(CONFIG_SND_SOC)  += amd/
 obj-$(CONFIG_SND_SOC)  += atmel/
 obj-$(CONFIG_SND_SOC)  += au1x/
+obj-$(CONFIG_SND_SOC)  += baikal/
 obj-$(CONFIG_SND_SOC)  += bcm/
 obj-$(CONFIG_SND_SOC)  += cirrus/
 obj-$(CONFIG_SND_SOC)  += dwc/
diff --git a/sound/soc/baikal/Kconfig b/sound/soc/baikal/Kconfig
new file mode 100644 (file)
index 0000000..550e49a
--- /dev/null
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SND_BAIKAL_I2S
+       tristate "Baikal SoC I2S Device Driver"
+       depends on HAVE_CLK
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       select SND_SIMPLE_CARD_UTILS if ACPI
+       help
+        Say Y or M if you want to add support for I2S driver for
+        Baikal SoC I2S device. The device supports up to
+        a maximum of 8 channels each for play and record.
+
+config SND_BAIKAL_PIO_PCM
+       bool "PCM PIO extension for Baikal SoC I2S driver"
+       depends on SND_BAIKAL_I2S
+       help
+        Say Y or N if you want to add a custom ALSA extension that registers
+        a PCM and uses PIO to transfer data.
+
+        This functionality is specially suited for I2S devices that don't have
+        DMA support.
+
diff --git a/sound/soc/baikal/Makefile b/sound/soc/baikal/Makefile
new file mode 100644 (file)
index 0000000..d4e3b72
--- /dev/null
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SND_BAIKAL_I2S) += baikal_i2s.o
+obj-$(CONFIG_SND_BAIKAL_I2S) += baikal_snd.o
+
+baikal_i2s-y := baikal-i2s.o
+baikal_snd-y := baikal-snd.o
+
+ifneq ($(CONFIG_ACPI),)
+baikal_snd-objs += baikal-snd-simple-acpi.o
+endif
+
+baikal_i2s-$(CONFIG_SND_BAIKAL_PIO_PCM) += baikal-pio-pcm.o
diff --git a/sound/soc/baikal/baikal-i2s.c b/sound/soc/baikal/baikal-i2s.c
new file mode 100644 (file)
index 0000000..de85446
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+ * ALSA SoC Synopsys I2S Audio Layer
+ *
+ * sound/soc/baikal/baikal-i2s.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar <rajeevkumar.linux@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <sound/designware_i2s.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+#include "local.h"
+
+static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
+{
+       writel(val, io_base + reg);
+}
+
+static inline u32 i2s_read_reg(void __iomem *io_base, int reg)
+{
+       return readl(io_base + reg);
+}
+
+static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream)
+{
+       u32 i = 0;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < 4; i++)
+                       i2s_write_reg(dev->i2s_base, TER(i), 0);
+       } else {
+               for (i = 0; i < 4; i++)
+                       i2s_write_reg(dev->i2s_base, RER(i), 0);
+       }
+}
+
+static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
+{
+       u32 i = 0;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < 4; i++)
+                       i2s_read_reg(dev->i2s_base, TOR(i));
+       } else {
+               for (i = 0; i < 4; i++)
+                       i2s_read_reg(dev->i2s_base, ROR(i));
+       }
+}
+
+static inline void i2s_disable_irqs(struct dw_i2s_dev *dev, u32 stream,
+                                   int chan_nr)
+{
+       u32 i, irq;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
+               }
+       } else {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
+               }
+       }
+}
+
+static inline void i2s_enable_irqs(struct dw_i2s_dev *dev, u32 stream,
+                                  int chan_nr)
+{
+       u32 i, irq;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x30);
+               }
+       } else {
+               for (i = 0; i < (chan_nr / 2); i++) {
+                       irq = i2s_read_reg(dev->i2s_base, IMR(i));
+                       i2s_write_reg(dev->i2s_base, IMR(i), irq & ~0x03);
+               }
+       }
+}
+
+static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
+{
+       struct dw_i2s_dev *dev = dev_id;
+       bool irq_valid = false;
+       u32 isr[4];
+       int i;
+
+       for (i = 0; i < 4; i++)
+               isr[i] = i2s_read_reg(dev->i2s_base, ISR(i));
+
+       i2s_clear_irqs(dev, SNDRV_PCM_STREAM_PLAYBACK);
+       i2s_clear_irqs(dev, SNDRV_PCM_STREAM_CAPTURE);
+
+       for (i = 0; i < 4; i++) {
+               /*
+                * Check if TX fifo is empty. If empty fill FIFO with samples
+                * NOTE: Only two channels supported
+                */
+               if ((isr[i] & ISR_TXFE) && (i == 0) && dev->use_pio) {
+                       dw_pcm_push_tx(dev);
+                       irq_valid = true;
+               }
+
+               /*
+                * Data available. Retrieve samples from FIFO
+                * NOTE: Only two channels supported
+                */
+               if ((isr[i] & ISR_RXDA) && (i == 0) && dev->use_pio) {
+                       dw_pcm_pop_rx(dev);
+                       irq_valid = true;
+               }
+
+               /* Error Handling: TX */
+               if (isr[i] & ISR_TXFO) {
+                       dev_err(dev->dev, "TX overrun (ch_id=%d)\n", i);
+                       irq_valid = true;
+               }
+
+               /* Error Handling: RX
+               if (isr[i] & ISR_RXFO) {
+                       dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i);
+                       irq_valid = true;
+               }
+               */
+       }
+
+       if (irq_valid)
+               return IRQ_HANDLED;
+       else
+               return IRQ_NONE;
+}
+
+static void i2s_start(struct dw_i2s_dev *dev,
+                     struct snd_pcm_substream *substream)
+{
+       struct i2s_clk_config_data *config = &dev->config;
+
+       i2s_write_reg(dev->i2s_base, IER, 1);
+       i2s_enable_irqs(dev, substream->stream, config->chan_nr);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               i2s_write_reg(dev->i2s_base, ITER, 1);
+       else
+               i2s_write_reg(dev->i2s_base, IRER, 1);
+
+       i2s_write_reg(dev->i2s_base, CER, 1);
+}
+
+static void i2s_stop(struct dw_i2s_dev *dev,
+               struct snd_pcm_substream *substream)
+{
+
+       i2s_clear_irqs(dev, substream->stream);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               i2s_write_reg(dev->i2s_base, ITER, 0);
+       else
+               i2s_write_reg(dev->i2s_base, IRER, 0);
+
+       i2s_disable_irqs(dev, substream->stream, 8);
+
+       if (!dev->active) {
+               i2s_write_reg(dev->i2s_base, CER, 0);
+               i2s_write_reg(dev->i2s_base, IER, 0);
+       }
+}
+
+static int dw_i2s_startup(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *cpu_dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+       union dw_i2s_snd_dma_data *dma_data = NULL;
+
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+       if (!(dev->capability & DWC_I2S_RECORD) &&
+                       (substream->stream == SNDRV_PCM_STREAM_CAPTURE))
+               return -EINVAL;
+
+       if (!(dev->capability & DWC_I2S_PLAY) &&
+                       (substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
+               return -EINVAL;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dma_data = &dev->play_dma_data;
+       else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               dma_data = &dev->capture_dma_data;
+
+       snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
+       snd_soc_dai_set_sysclk(codec_dai, 0, dev->sysclk, SND_SOC_CLOCK_IN);
+
+       return 0;
+}
+
+static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+{
+       u32 ch_reg;
+       struct i2s_clk_config_data *config = &dev->config;
+
+       i2s_disable_channels(dev, stream);
+
+       for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       i2s_write_reg(dev->i2s_base, TCR(ch_reg),
+                                     dev->xfer_resolution);
+                       i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
+                                     dev->fifo_th - 1);
+                       i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
+               } else {
+                       i2s_write_reg(dev->i2s_base, RCR(ch_reg),
+                                     dev->xfer_resolution);
+                       i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
+                                     dev->fifo_th - 1);
+                       i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
+               }
+
+       }
+}
+
+static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+       struct i2s_clk_config_data *config = &dev->config;
+
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+
+       int ret;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               config->data_width = 16;
+               dev->ccr = 0x00;
+               dev->xfer_resolution = 0x02;
+               break;
+
+       case SNDRV_PCM_FORMAT_S24_LE:
+               config->data_width = 24;
+               dev->ccr = 0x08;
+               dev->xfer_resolution = 0x04;
+               break;
+
+       case SNDRV_PCM_FORMAT_S32_LE:
+               config->data_width = 32;
+               dev->ccr = 0x10;
+               dev->xfer_resolution = 0x05;
+               break;
+
+       default:
+               dev_err(dev->dev, "designware-i2s: unsupported PCM fmt");
+               return -EINVAL;
+       }
+
+       config->chan_nr = params_channels(params);
+
+       switch (config->chan_nr) {
+       case EIGHT_CHANNEL_SUPPORT:
+       case SIX_CHANNEL_SUPPORT:
+       case FOUR_CHANNEL_SUPPORT:
+       case TWO_CHANNEL_SUPPORT:
+               break;
+       default:
+               dev_err(dev->dev, "channel not supported\n");
+               return -EINVAL;
+       }
+
+       dw_i2s_config(dev, substream->stream);
+
+       i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
+
+       config->sample_rate = params_rate(params);
+
+       if (dev->capability & DW_I2S_MASTER) {
+               if (dev->i2s_clk_cfg) {
+                       ret = dev->i2s_clk_cfg(config);
+                       if (ret < 0) {
+                               dev_err(dev->dev, "runtime audio clk config fail\n");
+                               return ret;
+                       }
+               } else {
+                       u32 bitclk = config->sample_rate *
+                                       config->data_width * 2;
+
+                       ret = clk_set_rate(dev->clk, bitclk);
+                       if (ret) {
+                               dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
+                                       ret);
+                               return ret;
+                       }
+               }
+       }
+
+       snd_soc_dai_set_pll(codec_dai, 0, 0, dev->sysclk, params_rate(params) * 256);
+
+       return 0;
+}
+
+static void dw_i2s_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int dw_i2s_prepare(struct snd_pcm_substream *substream,
+                         struct snd_soc_dai *dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               i2s_write_reg(dev->i2s_base, TXFFR, 1);
+       else
+               i2s_write_reg(dev->i2s_base, RXFFR, 1);
+
+       return 0;
+}
+
+static int dw_i2s_trigger(struct snd_pcm_substream *substream,
+               int cmd, struct snd_soc_dai *dai)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               dev->active++;
+               i2s_start(dev, substream);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               dev->active--;
+               i2s_stop(dev, substream);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+       int ret = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+       case SND_SOC_DAIFMT_BC_FC:
+               if (dev->capability & DW_I2S_SLAVE)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_BP_FP:
+               if (dev->capability & DW_I2S_MASTER)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_BC_FP:
+       case SND_SOC_DAIFMT_BP_FC:
+               ret = -EINVAL;
+               break;
+       default:
+               dev_dbg(dev->dev, "dwc : Invalid clock provider format\n");
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
+       .startup        = dw_i2s_startup,
+       .shutdown       = dw_i2s_shutdown,
+       .hw_params      = dw_i2s_hw_params,
+       .prepare        = dw_i2s_prepare,
+       .trigger        = dw_i2s_trigger,
+       .set_fmt        = dw_i2s_set_fmt,
+};
+
+#ifdef CONFIG_PM
+static int dw_i2s_runtime_suspend(struct device *dev)
+{
+       struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+
+       if (dw_dev->capability & DW_I2S_MASTER)
+               clk_disable(dw_dev->clk);
+       return 0;
+}
+
+static int dw_i2s_runtime_resume(struct device *dev)
+{
+       struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
+       int ret;
+
+       if (dw_dev->capability & DW_I2S_MASTER) {
+               ret = clk_enable(dw_dev->clk);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static int dw_i2s_suspend(struct snd_soc_component *component)
+{
+       struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
+
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable(dev->clk);
+       return 0;
+}
+
+static int dw_i2s_resume(struct snd_soc_component *component)
+{
+       struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
+       struct snd_soc_dai *dai;
+       int stream, ret;
+
+       if (dev->capability & DW_I2S_MASTER) {
+               ret = clk_enable(dev->clk);
+               if (ret)
+                       return ret;
+       }
+
+       for_each_component_dais(component, dai) {
+               for_each_pcm_streams(stream)
+                       if (snd_soc_dai_stream_active(dai, stream))
+                               dw_i2s_config(dev, stream);
+       }
+
+       return 0;
+}
+
+#else
+#define dw_i2s_suspend NULL
+#define dw_i2s_resume  NULL
+#endif
+
+static const struct snd_soc_component_driver dw_i2s_component = {
+       .name                   = "dw-i2s",
+       .suspend                = dw_i2s_suspend,
+       .resume                 = dw_i2s_resume,
+       .legacy_dai_naming      = 1,
+};
+
+/*
+ * The following tables allow a direct lookup of various parameters
+ * defined in the I2S block's configuration in terms of sound system
+ * parameters.  Each table is sized to the number of entries possible
+ * according to the number of configuration bits describing an I2S
+ * block parameter.
+ */
+
+/* Maximum bit resolution of a channel - not uniformly spaced */
+static const u32 fifo_width[COMP_MAX_WORDSIZE] = {
+       12, 16, 20, 24, 32, 0, 0, 0
+};
+
+/* Width of (DMA) bus */
+static const u32 bus_widths[COMP_MAX_DATA_WIDTH] = {
+       DMA_SLAVE_BUSWIDTH_1_BYTE,
+       DMA_SLAVE_BUSWIDTH_2_BYTES,
+       DMA_SLAVE_BUSWIDTH_4_BYTES,
+       DMA_SLAVE_BUSWIDTH_UNDEFINED
+};
+
+/* PCM format to support channel resolution */
+static const u32 formats[COMP_MAX_WORDSIZE] = {
+       SNDRV_PCM_FMTBIT_S16_LE,
+       SNDRV_PCM_FMTBIT_S16_LE,
+       SNDRV_PCM_FMTBIT_S24_LE,
+       SNDRV_PCM_FMTBIT_S24_LE,
+       SNDRV_PCM_FMTBIT_S32_LE,
+       0,
+       0,
+       0
+};
+
+static int dw_configure_dai(struct dw_i2s_dev *dev,
+                                  struct snd_soc_dai_driver *dw_i2s_dai,
+                                  unsigned int rates)
+{
+       /*
+        * Read component parameter registers to extract
+        * the I2S block's configuration.
+        */
+       u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
+       u32 comp2 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp2);
+       u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+       u32 idx;
+
+       if (dev->capability & DWC_I2S_RECORD &&
+                       dev->quirks & DW_I2S_QUIRK_COMP_PARAM1)
+               comp1 = comp1 & ~BIT(5);
+
+       if (dev->capability & DWC_I2S_PLAY &&
+                       dev->quirks & DW_I2S_QUIRK_COMP_PARAM1)
+               comp1 = comp1 & ~BIT(6);
+
+       if (COMP1_TX_ENABLED(comp1)) {
+               dev_dbg(dev->dev, " designware: play supported\n");
+               idx = COMP1_TX_WORDSIZE_0(comp1);
+               if (WARN_ON(idx >= ARRAY_SIZE(formats)))
+                       return -EINVAL;
+               if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+                       idx = 1;
+               dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
+               dw_i2s_dai->playback.channels_max =
+                               1 << (COMP1_TX_CHANNELS(comp1) + 1);
+               dw_i2s_dai->playback.formats = formats[idx];
+               dw_i2s_dai->playback.rates = rates;
+       }
+
+       if (COMP1_RX_ENABLED(comp1)) {
+               dev_dbg(dev->dev, "designware: record supported\n");
+               idx = COMP2_RX_WORDSIZE_0(comp2);
+               if (WARN_ON(idx >= ARRAY_SIZE(formats)))
+                       return -EINVAL;
+               if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+                       idx = 1;
+               dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
+               dw_i2s_dai->capture.channels_max =
+                               1 << (COMP1_RX_CHANNELS(comp1) + 1);
+               dw_i2s_dai->capture.formats = formats[idx];
+               dw_i2s_dai->capture.rates = rates;
+       }
+
+       if (COMP1_MODE_EN(comp1)) {
+               dev_dbg(dev->dev, "designware: i2s master mode supported\n");
+               dev->capability |= DW_I2S_MASTER;
+       } else {
+               dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
+               dev->capability |= DW_I2S_SLAVE;
+       }
+
+       dev->fifo_th = fifo_depth / 2;
+       return 0;
+}
+
+static int dw_configure_dai_by_pd(struct dw_i2s_dev *dev,
+                                  struct snd_soc_dai_driver *dw_i2s_dai,
+                                  struct resource *res,
+                                  const struct i2s_platform_data *pdata)
+{
+       u32 comp1 = i2s_read_reg(dev->i2s_base, dev->i2s_reg_comp1);
+       u32 idx = COMP1_APB_DATA_WIDTH(comp1);
+       int ret;
+
+       if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
+               return -EINVAL;
+
+       ret = dw_configure_dai(dev, dw_i2s_dai, pdata->snd_rates);
+       if (ret < 0)
+               return ret;
+
+       if (dev->quirks & DW_I2S_QUIRK_16BIT_IDX_OVERRIDE)
+               idx = 1;
+       /* Set DMA slaves info */
+       dev->play_dma_data.pd.data = pdata->play_dma_data;
+       dev->capture_dma_data.pd.data = pdata->capture_dma_data;
+       dev->play_dma_data.pd.addr = res->start + I2S_TXDMA;
+       dev->capture_dma_data.pd.addr = res->start + I2S_RXDMA;
+       dev->play_dma_data.pd.max_burst = 16;
+       dev->capture_dma_data.pd.max_burst = 16;
+       dev->play_dma_data.pd.addr_width = bus_widths[idx];
+       dev->capture_dma_data.pd.addr_width = bus_widths[idx];
+       dev->play_dma_data.pd.filter = pdata->filter;
+       dev->capture_dma_data.pd.filter = pdata->filter;
+
+       return 0;
+}
+
+static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
+                                  struct snd_soc_dai_driver *dw_i2s_dai,
+                                  struct resource *res)
+{
+       u32 comp1 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_1);
+       u32 comp2 = i2s_read_reg(dev->i2s_base, I2S_COMP_PARAM_2);
+       u32 fifo_depth = 1 << (1 + COMP1_FIFO_DEPTH_GLOBAL(comp1));
+       u32 idx = COMP1_APB_DATA_WIDTH(comp1);
+       u32 idx2;
+       int ret;
+
+       if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
+               return -EINVAL;
+
+       ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000);
+       if (ret < 0)
+               return ret;
+
+       if (COMP1_TX_ENABLED(comp1)) {
+               idx2 = COMP1_TX_WORDSIZE_0(comp1);
+
+               dev->capability |= DWC_I2S_PLAY;
+               dev->play_dma_data.dt.addr = res->start + I2S_TXDMA;
+               dev->play_dma_data.dt.addr_width = bus_widths[idx];
+               dev->play_dma_data.dt.fifo_size = fifo_depth *
+                       (fifo_width[idx2]) >> 8;
+               dev->play_dma_data.dt.maxburst = 16;
+       }
+       if (COMP1_RX_ENABLED(comp1)) {
+               idx2 = COMP2_RX_WORDSIZE_0(comp2);
+
+               dev->capability |= DWC_I2S_RECORD;
+               dev->capture_dma_data.dt.addr = res->start + I2S_RXDMA;
+               dev->capture_dma_data.dt.addr_width = bus_widths[idx];
+               dev->capture_dma_data.dt.fifo_size = fifo_depth *
+                       (fifo_width[idx2] >> 8);
+               dev->capture_dma_data.dt.maxburst = 16;
+       }
+
+       return 0;
+
+}
+
+static int dw_i2s_probe(struct platform_device *pdev)
+{
+       const struct i2s_platform_data *pdata = pdev->dev.platform_data;
+       struct dw_i2s_dev *dev;
+       struct resource *res;
+       int ret, irq, irq_num;
+       struct snd_soc_dai_driver *dw_i2s_dai;
+       const char *clk_id;
+
+       dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return -ENOMEM;
+
+       dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+       if (!dw_i2s_dai)
+               return -ENOMEM;
+
+       dw_i2s_dai->ops = &dw_i2s_dai_ops;
+
+       dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+       if (IS_ERR(dev->i2s_base))
+               return PTR_ERR(dev->i2s_base);
+
+       dev->dev = &pdev->dev;
+
+       irq_num = platform_irq_count(pdev);
+       if (irq_num < 0)
+               return irq_num; /* -EPROBE_DEFER */
+
+       if (!irq_num)
+               dev_err(&pdev->dev, "No irq found on device\n");
+
+       irq_num--;
+       do {
+               irq = platform_get_irq(pdev, irq_num);
+               if (irq >= 0) {
+                       printk(KERN_INFO "%s Registered IRQ number %d %d\n",
+                               __func__, irq, irq_num);
+                       ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler,
+                               0, pdev->name, dev);
+                       if (ret < 0) {
+                               dev_err(&pdev->dev, "failed to request irq\n");
+                               return ret;
+                       }
+               }
+       } while (irq_num--);
+
+       dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
+       dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
+       if (pdata) {
+               dev->capability = pdata->cap;
+               clk_id = NULL;
+               dev->quirks = pdata->quirks;
+               if (dev->quirks & DW_I2S_QUIRK_COMP_REG_OFFSET) {
+                       dev->i2s_reg_comp1 = pdata->i2s_reg_comp1;
+                       dev->i2s_reg_comp2 = pdata->i2s_reg_comp2;
+               }
+               ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
+       } else {
+               clk_id = "i2sclk";
+               ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+       }
+       if (ret < 0)
+               return ret;
+
+       if (dev->capability & DW_I2S_MASTER) {
+               if (pdata) {
+                       dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+                       if (!dev->i2s_clk_cfg) {
+                               dev_err(&pdev->dev, "no clock configure method\n");
+                               return -ENODEV;
+                       }
+               }
+               dev->clk = devm_clk_get(&pdev->dev, clk_id);
+
+               if (IS_ERR(dev->clk))
+                       return PTR_ERR(dev->clk);
+
+               ret = clk_prepare_enable(dev->clk);
+               if (ret < 0)
+                       return ret;
+       }
+
+       dev_set_drvdata(&pdev->dev, dev);
+       ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
+                                        dw_i2s_dai, 1);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "not able to register dai\n");
+               goto err_clk_disable;
+       }
+
+       if (!pdata) {
+               if (irq >= 0) {
+                       ret = dw_pcm_register(pdev);
+                       dev->use_pio = true;
+               } else {
+                       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
+                                       0);
+                       dev->use_pio = false;
+               }
+
+               if (ret) {
+                       dev_err(&pdev->dev, "could not register pcm: %d\n",
+                                       ret);
+                       goto err_clk_disable;
+               }
+       }
+
+       ret = device_property_read_u32(&pdev->dev, "system-clock-frequency",
+                                      &dev->sysclk);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "'system-clock-frequency': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       return 0;
+
+err_clk_disable:
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable_unprepare(dev->clk);
+       return ret;
+}
+
+static int dw_i2s_remove(struct platform_device *pdev)
+{
+       struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
+
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable_unprepare(dev->clk);
+
+       pm_runtime_disable(&pdev->dev);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id dw_i2s_of_match[] = {
+       { .compatible = "snps,designware-i2s",   },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, dw_i2s_of_match);
+#endif
+
+static const struct dev_pm_ops dwc_pm_ops = {
+       SET_RUNTIME_PM_OPS(dw_i2s_runtime_suspend, dw_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver dw_i2s_driver = {
+       .probe          = dw_i2s_probe,
+       .remove         = dw_i2s_remove,
+       .driver         = {
+               .name   = "designware-i2s",
+               .of_match_table = of_match_ptr(dw_i2s_of_match),
+               .pm = &dwc_pm_ops,
+       },
+};
+
+module_platform_driver(dw_i2s_driver);
+
+MODULE_AUTHOR("Rajeev Kumar <rajeevkumar.linux@gmail.com>");
+MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:designware_i2s");
diff --git a/sound/soc/baikal/baikal-pio-pcm.c b/sound/soc/baikal/baikal-pio-pcm.c
new file mode 100644 (file)
index 0000000..de4ab3b
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * ALSA SoC Synopsys PIO PCM for I2S driver
+ *
+ * sound/soc/baikal/baikal-pio-pcm.c
+ *
+ * Copyright (C) 2016 Synopsys
+ * Jose Abreu <joabreu@synopsys.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/rcupdate.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "local.h"
+
+#define BUFFER_BYTES_MAX       (3 * 2 * 8 * PERIOD_BYTES_MIN)
+#define PERIOD_BYTES_MIN       4096
+#define PERIODS_MIN            2
+
+#define dw_pcm_tx_fn(sample_bits) \
+static unsigned int dw_pcm_tx_##sample_bits(struct dw_i2s_dev *dev, \
+               struct snd_pcm_runtime *runtime, unsigned int tx_ptr, \
+               bool *period_elapsed) \
+{ \
+       const u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
+       unsigned int period_pos = tx_ptr % runtime->period_size; \
+       int i; \
+\
+       for (i = 0; i < dev->fifo_th; i++) { \
+               iowrite32(p[tx_ptr][0], dev->i2s_base + LRBR_LTHR(0)); \
+               iowrite32(p[tx_ptr][1], dev->i2s_base + RRBR_RTHR(0)); \
+               period_pos++; \
+               if (++tx_ptr >= runtime->buffer_size) \
+                       tx_ptr = 0; \
+       } \
+       *period_elapsed = period_pos >= runtime->period_size; \
+       return tx_ptr; \
+}
+
+#define dw_pcm_rx_fn(sample_bits) \
+static unsigned int dw_pcm_rx_##sample_bits(struct dw_i2s_dev *dev, \
+               struct snd_pcm_runtime *runtime, unsigned int rx_ptr, \
+               bool *period_elapsed) \
+{ \
+       u##sample_bits (*p)[2] = (void *)runtime->dma_area; \
+       unsigned int period_pos = rx_ptr % runtime->period_size; \
+       int i; \
+\
+       for (i = 0; i < dev->fifo_th; i++) { \
+               p[rx_ptr][0] = ioread32(dev->i2s_base + LRBR_LTHR(0)); \
+               p[rx_ptr][1] = ioread32(dev->i2s_base + RRBR_RTHR(0)); \
+               period_pos++; \
+               if (++rx_ptr >= runtime->buffer_size) \
+                       rx_ptr = 0; \
+       } \
+       *period_elapsed = period_pos >= runtime->period_size; \
+       return rx_ptr; \
+}
+
+dw_pcm_tx_fn(16);
+dw_pcm_tx_fn(32);
+dw_pcm_rx_fn(16);
+dw_pcm_rx_fn(32);
+
+#undef dw_pcm_tx_fn
+#undef dw_pcm_rx_fn
+
+static const struct snd_pcm_hardware dw_pcm_hardware = {
+       .info = SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER,
+       .rates = SNDRV_PCM_RATE_32000 |
+               SNDRV_PCM_RATE_44100 |
+               SNDRV_PCM_RATE_48000,
+       .rate_min = 32000,
+       .rate_max = 48000,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+               SNDRV_PCM_FMTBIT_S24_LE |
+               SNDRV_PCM_FMTBIT_S32_LE,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = BUFFER_BYTES_MAX,
+       .period_bytes_min = PERIOD_BYTES_MIN,
+       .period_bytes_max = BUFFER_BYTES_MAX / PERIODS_MIN,
+       .periods_min = PERIODS_MIN,
+       .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
+       .fifo_size = 16,
+};
+
+static void dw_pcm_transfer(struct dw_i2s_dev *dev, bool push)
+{
+       struct snd_pcm_substream *substream;
+       bool active, period_elapsed;
+
+       rcu_read_lock();
+       if (push)
+               substream = rcu_dereference(dev->tx_substream);
+       else
+               substream = rcu_dereference(dev->rx_substream);
+       active = substream && snd_pcm_running(substream);
+       if (active) {
+               unsigned int ptr;
+               unsigned int new_ptr;
+
+               if (push) {
+                       ptr = READ_ONCE(dev->tx_ptr);
+                       new_ptr = dev->tx_fn(dev, substream->runtime, ptr,
+                                       &period_elapsed);
+                       cmpxchg(&dev->tx_ptr, ptr, new_ptr);
+               } else {
+                       ptr = READ_ONCE(dev->rx_ptr);
+                       new_ptr = dev->rx_fn(dev, substream->runtime, ptr,
+                                       &period_elapsed);
+                       cmpxchg(&dev->rx_ptr, ptr, new_ptr);
+               }
+
+               if (period_elapsed)
+                       snd_pcm_period_elapsed(substream);
+       }
+       rcu_read_unlock();
+}
+
+void dw_pcm_push_tx(struct dw_i2s_dev *dev)
+{
+       dw_pcm_transfer(dev, true);
+}
+
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
+{
+       dw_pcm_transfer(dev, false);
+}
+
+static int dw_pcm_open(struct snd_soc_component *component,
+                      struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
+
+       snd_soc_set_runtime_hwparams(substream, &dw_pcm_hardware);
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       runtime->private_data = dev;
+
+       return 0;
+}
+
+static int dw_pcm_close(struct snd_soc_component *component,
+                       struct snd_pcm_substream *substream)
+{
+       synchronize_rcu();
+       return 0;
+}
+
+static int dw_pcm_hw_params(struct snd_soc_component *component,
+                           struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct dw_i2s_dev *dev = runtime->private_data;
+
+       switch (params_channels(hw_params)) {
+       case 2:
+               break;
+       default:
+               dev_err(dev->dev, "invalid channels number\n");
+               return -EINVAL;
+       }
+
+       switch (params_format(hw_params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               dev->tx_fn = dw_pcm_tx_16;
+               dev->rx_fn = dw_pcm_rx_16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+       case SNDRV_PCM_FORMAT_S32_LE:
+               dev->tx_fn = dw_pcm_tx_32;
+               dev->rx_fn = dw_pcm_rx_32;
+               break;
+       default:
+               dev_err(dev->dev, "invalid format\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int dw_pcm_trigger(struct snd_soc_component *component,
+                         struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct dw_i2s_dev *dev = runtime->private_data;
+       int ret = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       WRITE_ONCE(dev->tx_ptr, 0);
+                       rcu_assign_pointer(dev->tx_substream, substream);
+               } else {
+                       WRITE_ONCE(dev->rx_ptr, 0);
+                       rcu_assign_pointer(dev->rx_substream, substream);
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       rcu_assign_pointer(dev->tx_substream, NULL);
+               else
+                       rcu_assign_pointer(dev->rx_substream, NULL);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t dw_pcm_pointer(struct snd_soc_component *component,
+                                       struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct dw_i2s_dev *dev = runtime->private_data;
+       snd_pcm_uframes_t pos;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               pos = READ_ONCE(dev->tx_ptr);
+       else
+               pos = READ_ONCE(dev->rx_ptr);
+
+       return pos < runtime->buffer_size ? pos : 0;
+}
+
+static int dw_pcm_new(struct snd_soc_component *component,
+                     struct snd_soc_pcm_runtime *rtd)
+{
+       size_t size = dw_pcm_hardware.buffer_bytes_max;
+
+       snd_pcm_set_managed_buffer_all(rtd->pcm,
+                       SNDRV_DMA_TYPE_CONTINUOUS,
+                       NULL, size, size);
+       return 0;
+}
+
+static const struct snd_soc_component_driver dw_pcm_component = {
+       .open           = dw_pcm_open,
+       .close          = dw_pcm_close,
+       .hw_params      = dw_pcm_hw_params,
+       .trigger        = dw_pcm_trigger,
+       .pointer        = dw_pcm_pointer,
+       .pcm_construct  = dw_pcm_new,
+};
+
+int dw_pcm_register(struct platform_device *pdev)
+{
+       return devm_snd_soc_register_component(&pdev->dev, &dw_pcm_component,
+                                              NULL, 0);
+}
diff --git a/sound/soc/baikal/baikal-snd-simple-acpi.c b/sound/soc/baikal/baikal-snd-simple-acpi.c
new file mode 100644 (file)
index 0000000..ae0ce1d
--- /dev/null
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Baikal ASoC simple sound card support for ACPI
+ *
+ * Copyright (C) 2023 Baikal Electronics, JSC
+ * Author: Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/simple_card_utils.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dapm_widget baikal_widgets[] = {
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+       SND_SOC_DAPM_HP("Headphones", NULL),
+       SND_SOC_DAPM_SPK("AUX Out", NULL)
+};
+
+static struct snd_soc_dapm_route baikal_routes[] = {
+       { .sink = "Headphones", .source = "RHP" },
+       { .sink = "Headphones", .source = "LHP" },
+       { .sink = "AUX Out",    .source = "AUXOUT1" },
+       { .sink = "AUX Out",    .source = "AUXOUT2" },
+       { .sink = "L2",         .source = "Mic Jack" },
+       { .sink = "R2",         .source = "Mic Jack" },
+       { .sink = "LAUX",       .source = "Line In" },
+       { .sink = "RAUX",       .source = "Line In" }
+};
+
+static int baikal_snd_soc_card_probe(struct snd_soc_card *card)
+{
+       struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
+       int ret;
+
+       ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL);
+       if (ret)
+               return ret;
+
+       return asoc_simple_init_mic(card, &priv->mic_jack, NULL);
+}
+
+static const struct snd_soc_ops baikal_snd_soc_ops = {
+       .startup        = asoc_simple_startup,
+       .shutdown       = asoc_simple_shutdown,
+       .hw_params      = asoc_simple_hw_params
+};
+
+static int baikal_simple_card_parse(struct asoc_simple_priv *priv)
+{
+       struct device *dev = priv->snd_card.dev, *tmp;
+       struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
+       struct fwnode_reference_args args;
+       int ret;
+
+       ret = fwnode_property_get_reference_args(dev->fwnode, "baikal,cpu-dai",
+                                                NULL, 0, 0, &args);
+       if (ret) {
+               dev_err(dev, "'baikal,cpu': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       tmp = bus_find_device_by_fwnode(&platform_bus_type, args.fwnode);
+       if (IS_ERR_OR_NULL(tmp))
+               return -ENODEV;
+       dai_link->cpus->name = dev_name(tmp);
+       dai_link->platforms->name = dev_name(tmp);
+       priv->dai_props->cpu_dai->sysclk = 100000000;
+
+       ret = fwnode_property_get_reference_args(dev->fwnode, "baikal,audio-codec",
+                                                NULL, 0, 0, &args);
+       if (ret) {
+               dev_err(dev, "'baikal,codec': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       tmp = bus_find_device_by_fwnode(&i2c_bus_type, args.fwnode);
+       if (IS_ERR_OR_NULL(tmp))
+               return -ENODEV;
+       dai_link->codecs->name = dev_name(tmp);
+
+       ret = device_property_read_string(dev, "baikal,codec-name",
+                                         &dai_link->codecs->dai_name);
+       if (ret) {
+               dev_err(dev, "'baikal,codec-name': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       dai_link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM;
+       dai_link->init = asoc_simple_dai_init;
+       dai_link->ops = &baikal_snd_soc_ops;
+
+       return asoc_simple_set_dailink_name(dev, dai_link, "%s-%s",
+                                           dai_link->cpus->name,
+                                           dai_link->codecs->dai_name);
+}
+
+static int baikal_simple_card_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct asoc_simple_priv *priv;
+       struct snd_soc_card *card;
+       struct link_info *li;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       card = &priv->snd_card;
+       card->owner = THIS_MODULE;
+       card->dev = dev;
+       card->driver_name = "baikal-simple-card";
+       card->name = "MITX-Sound-Card";
+       card->dapm_widgets = baikal_widgets;
+       card->num_dapm_widgets = ARRAY_SIZE(baikal_widgets);
+       card->dapm_routes = baikal_routes;
+       card->num_dapm_routes = ARRAY_SIZE(baikal_routes);
+       card->probe = baikal_snd_soc_card_probe;
+
+       li = kzalloc(sizeof(*li), GFP_KERNEL);
+       if (!li)
+               return -ENOMEM;
+       li->num[0].cpus = 1;
+       li->num[0].codecs = 1;
+       li->num[0].platforms = 1;
+       li->link = 1;
+
+       ret = asoc_simple_init_priv(priv, li);
+       kfree(li);
+       if (ret)
+               return ret;
+
+       ret = baikal_simple_card_parse(priv);
+       if (ret) {
+               dev_err_probe(dev, ret, "parse error\n");
+               return ret;
+       }
+
+       snd_soc_card_set_drvdata(card, priv);
+       asoc_simple_debug_info(priv);
+
+       return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct of_device_id baikal_simple_card_of_match[] = {
+       { .compatible = "baikal,simple-audio-card" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, baikal_simple_card_of_match);
+
+static struct platform_driver baikal_simple_card_driver = {
+       .driver = {
+               .name = "baikal-asoc-simple-card",
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = baikal_simple_card_of_match
+       },
+       .probe = baikal_simple_card_probe
+};
+
+module_platform_driver(baikal_simple_card_driver);
+
+MODULE_ALIAS("platform:baikal-asoc-simple-card");
+MODULE_AUTHOR("Aleksandr Efimov <alexander.efimov@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal ASoC Simple Sound Card");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/baikal/baikal-snd.c b/sound/soc/baikal/baikal-snd.c
new file mode 100644 (file)
index 0000000..5a07a8f
--- /dev/null
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2022 Baikal Electronics, JSC
+ * Author: Danila Sharikov <Danila.Sharikov@baikalelectronics.ru>
+ * Author: Ekaterina Skachko <Ekaterina.Skachko@baikalelectronics.ru>
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#include "local.h"
+
+static struct snd_soc_dai_link snd_baikal_dai = {
+       .dai_fmt = SND_SOC_DAIFMT_I2S   |
+                  SND_SOC_DAIFMT_NB_NF |
+                  SND_SOC_DAIFMT_CBM_CFM,
+};
+
+static struct snd_soc_card snd_baikal_card = {
+       .name           = "Baikal-Snd-Card",
+       .owner          = THIS_MODULE,
+       .dai_link       = &snd_baikal_dai,
+       .num_links      = 1,
+};
+
+static int snd_soc_baikal_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct fwnode_handle *codec_node, *baikal_node;
+       struct snd_soc_dai_link_component *comp;
+       int err;
+
+       comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
+       if (!comp)
+               return -ENOMEM;
+
+       baikal_node = fwnode_find_reference(dev->fwnode, "baikal,cpu-dai", 0);
+       if (IS_ERR(baikal_node)) {
+               dev_err(dev,
+                       "'baikal,cpu-dai': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       codec_node = fwnode_find_reference(dev->fwnode, "baikal,audio-codec", 0);
+       if (IS_ERR(codec_node)) {
+               dev_err(dev,
+                       "'baikal,audio-codec': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       snd_baikal_card.dev = dev;
+       snd_baikal_dai.cpus = &comp[0];
+       snd_baikal_dai.codecs = &comp[1];
+       snd_baikal_dai.platforms = &comp[2];
+
+       err = device_property_read_string(dev, "baikal,dai-name",
+                                         &snd_baikal_dai.name);
+       if (err) {
+               dev_err(dev,
+                       "'baikal,dai-name': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       err = device_property_read_string(dev, "baikal,stream-name",
+                                         &snd_baikal_dai.stream_name);
+       if (err) {
+               dev_err(dev,
+                       "'baikal,stream-name': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       err = device_property_read_string(dev, "baikal,codec-name",
+                                         &snd_baikal_dai.codecs->dai_name);
+       if (err) {
+               dev_err(dev,
+                       "'baikal,codec-name': missing or invalid!\n");
+               return -EINVAL;
+       }
+
+       snd_baikal_dai.num_cpus = 1;
+       snd_baikal_dai.num_codecs = 1;
+       snd_baikal_dai.num_platforms = 1;
+
+       if (is_of_node(baikal_node) && is_of_node(codec_node)) {
+               snd_baikal_dai.cpus->of_node = to_of_node(baikal_node);
+               snd_baikal_dai.codecs->of_node = to_of_node(codec_node);
+               snd_baikal_dai.platforms->of_node = to_of_node(baikal_node);
+               snd_soc_of_parse_card_name(&snd_baikal_card, "baikal,card-name");
+       } else {
+               struct device *tmp;
+
+               tmp = bus_find_device_by_fwnode(&platform_bus_type, baikal_node);
+               if (IS_ERR_OR_NULL(tmp))
+                       return -ENODEV;
+               snd_baikal_dai.cpus->name = dev_name(tmp);
+               snd_baikal_dai.platforms->name = dev_name(tmp);
+
+               tmp = bus_find_device_by_fwnode(&i2c_bus_type, codec_node);
+               if (IS_ERR_OR_NULL(tmp))
+                       return -ENODEV;
+               snd_baikal_dai.codecs->name = dev_name(tmp);
+       }
+
+       platform_set_drvdata(pdev, &snd_baikal_card);
+       snd_soc_card_set_drvdata(&snd_baikal_card, NULL);
+
+       err = snd_soc_register_card(&snd_baikal_card);
+       if (err)
+               dev_err(dev,
+                       "snd_soc_register_card failed (%d)!\n", err);
+
+       return err;
+}
+
+static int snd_soc_baikal_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *snd_baikal_card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(snd_baikal_card);
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id snd_soc_baikal_of_match[] = {
+       { .compatible = "baikal,snd-soc-baikal", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, snd_soc_baikal_of_match);
+#endif
+
+
+static struct platform_driver snd_soc_baikal_driver = {
+       .probe          = snd_soc_baikal_probe,
+       .remove         = snd_soc_baikal_remove,
+       .driver         = {
+               .name   = "snd-soc-baikal",
+               .of_match_table = of_match_ptr(snd_soc_baikal_of_match),
+       },
+};
+
+module_platform_driver(snd_soc_baikal_driver);
+
+MODULE_AUTHOR("Danila Sharikov <Danila.Sharikov@baikalelectronics.ru>");
+MODULE_DESCRIPTION("Baikal-M SoC Sound Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/baikal/local.h b/sound/soc/baikal/local.h
new file mode 100644 (file)
index 0000000..56382e9
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (ST) 2012 Rajeev Kumar (rajeevkumar.linux@gmail.com)
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __DESIGNWARE_LOCAL_H
+#define __DESIGNWARE_LOCAL_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/designware_i2s.h>
+
+/* common register for all channel */
+#define IER            0x000
+#define IRER           0x004
+#define ITER           0x008
+#define CER            0x00C
+#define CCR            0x010
+#define RXFFR          0x014
+#define TXFFR          0x018
+
+/* Interrupt status register fields */
+#define ISR_TXFO       BIT(5)
+#define ISR_TXFE       BIT(4)
+#define ISR_RXFO       BIT(1)
+#define ISR_RXDA       BIT(0)
+
+/* I2STxRxRegisters for all channels */
+#define LRBR_LTHR(x)   (0x40 * x + 0x020)
+#define RRBR_RTHR(x)   (0x40 * x + 0x024)
+#define RER(x)         (0x40 * x + 0x028)
+#define TER(x)         (0x40 * x + 0x02C)
+#define RCR(x)         (0x40 * x + 0x030)
+#define TCR(x)         (0x40 * x + 0x034)
+#define ISR(x)         (0x40 * x + 0x038)
+#define IMR(x)         (0x40 * x + 0x03C)
+#define ROR(x)         (0x40 * x + 0x040)
+#define TOR(x)         (0x40 * x + 0x044)
+#define RFCR(x)                (0x40 * x + 0x048)
+#define TFCR(x)                (0x40 * x + 0x04C)
+#define RFF(x)         (0x40 * x + 0x050)
+#define TFF(x)         (0x40 * x + 0x054)
+
+/* I2SCOMPRegisters */
+#define I2S_COMP_PARAM_2       0x01F0
+#define I2S_COMP_PARAM_1       0x01F4
+#define I2S_COMP_VERSION       0x01F8
+#define I2S_COMP_TYPE          0x01FC
+
+/*
+ * Component parameter register fields - define the I2S block's
+ * configuration.
+ */
+#define        COMP1_TX_WORDSIZE_3(r)          (((r) & GENMASK(27, 25)) >> 25)
+#define        COMP1_TX_WORDSIZE_2(r)          (((r) & GENMASK(24, 22)) >> 22)
+#define        COMP1_TX_WORDSIZE_1(r)          (((r) & GENMASK(21, 19)) >> 19)
+#define        COMP1_TX_WORDSIZE_0(r)          (((r) & GENMASK(18, 16)) >> 16)
+#define        COMP1_TX_CHANNELS(r)            (((r) & GENMASK(10, 9)) >> 9)
+#define        COMP1_RX_CHANNELS(r)            (((r) & GENMASK(8, 7)) >> 7)
+#define        COMP1_RX_ENABLED(r)             (((r) & BIT(6)) >> 6)
+#define        COMP1_TX_ENABLED(r)             (((r) & BIT(5)) >> 5)
+#define        COMP1_MODE_EN(r)                (((r) & BIT(4)) >> 4)
+#define        COMP1_FIFO_DEPTH_GLOBAL(r)      (((r) & GENMASK(3, 2)) >> 2)
+#define        COMP1_APB_DATA_WIDTH(r)         (((r) & GENMASK(1, 0)) >> 0)
+
+#define        COMP2_RX_WORDSIZE_3(r)          (((r) & GENMASK(12, 10)) >> 10)
+#define        COMP2_RX_WORDSIZE_2(r)          (((r) & GENMASK(9, 7)) >> 7)
+#define        COMP2_RX_WORDSIZE_1(r)          (((r) & GENMASK(5, 3)) >> 3)
+#define        COMP2_RX_WORDSIZE_0(r)          (((r) & GENMASK(2, 0)) >> 0)
+
+/* Number of entries in WORDSIZE and DATA_WIDTH parameter registers */
+#define        COMP_MAX_WORDSIZE       (1 << 3)
+#define        COMP_MAX_DATA_WIDTH     (1 << 2)
+
+#define MAX_CHANNEL_NUM                8
+#define MIN_CHANNEL_NUM                2
+
+union dw_i2s_snd_dma_data {
+       struct i2s_dma_data pd;
+       struct snd_dmaengine_dai_dma_data dt;
+};
+
+struct dw_i2s_dev {
+       void __iomem *i2s_base;
+       struct clk *clk;
+       int active;
+       unsigned int capability;
+       unsigned int quirks;
+       unsigned int i2s_reg_comp1;
+       unsigned int i2s_reg_comp2;
+       unsigned int sysclk;
+       struct device *dev;
+       u32 ccr;
+       u32 xfer_resolution;
+       u32 fifo_th;
+
+       /* data related to DMA transfers b/w i2s and DMAC */
+       union dw_i2s_snd_dma_data play_dma_data;
+       union dw_i2s_snd_dma_data capture_dma_data;
+       struct i2s_clk_config_data config;
+       int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
+
+       /* data related to PIO transfers */
+       bool use_pio;
+       struct snd_pcm_substream __rcu *tx_substream;
+       struct snd_pcm_substream __rcu *rx_substream;
+       unsigned int (*tx_fn)(struct dw_i2s_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+                       bool *period_elapsed);
+       unsigned int (*rx_fn)(struct dw_i2s_dev *dev,
+                       struct snd_pcm_runtime *runtime, unsigned int rx_ptr,
+                       bool *period_elapsed);
+       unsigned int tx_ptr;
+       unsigned int rx_ptr;
+};
+
+#if IS_ENABLED(CONFIG_SND_BAIKAL_PIO_PCM)
+void dw_pcm_push_tx(struct dw_i2s_dev *dev);
+void dw_pcm_pop_rx(struct dw_i2s_dev *dev);
+int dw_pcm_register(struct platform_device *pdev);
+#else
+static inline void dw_pcm_push_tx(struct dw_i2s_dev *dev) { }
+static inline void dw_pcm_pop_rx(struct dw_i2s_dev *dev) { }
+static inline int dw_pcm_register(struct platform_device *pdev)
+{
+       return -EINVAL;
+}
+#endif
+
+#endif
index 1aef281a997271d7cb6f488bbfdc65cd45df939b..b5f93c0418b4d0970d9566c65d404d8d63c80287 100644 (file)
@@ -726,9 +726,11 @@ static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
        struct nau8822_pll *pll_param = &nau8822->pll;
        int ret, fs;
 
+       /* Force PLL calculation
        if (freq_in == pll_param->freq_in &&
            freq_out == pll_param->freq_out)
                return 0;
+       */
 
        if (freq_out == 0) {
                dev_dbg(component->dev, "PLL disabled\n");
index 08938801daec26e4b1adcb1a8147e31e8ef0439d..35645c956d611e6a36b3d4e6d1609b77e34d84ca 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/regulator/consumer.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
@@ -1227,6 +1228,12 @@ static int aic3x_mute(struct snd_soc_dai *dai, int mute, int direction)
        return 0;
 }
 
+static int aic3x_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+                        unsigned int freq_in, unsigned int freq_out)
+{
+       return 0;
+}
+
 static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                                int clk_id, unsigned int freq, int dir)
 {
@@ -1478,6 +1485,7 @@ static const struct snd_soc_dai_ops aic3x_dai_ops = {
        .set_sysclk     = aic3x_set_dai_sysclk,
        .set_fmt        = aic3x_set_dai_fmt,
        .set_tdm_slot   = aic3x_set_dai_tdm_slot,
+       .set_pll        = aic3x_set_pll,
        .no_capture_mute = 1,
 };
 
@@ -1701,11 +1709,10 @@ static const struct snd_soc_component_driver soc_component_dev_aic3x = {
 
 static void aic3x_configure_ocmv(struct device *dev, struct aic3x_priv *aic3x)
 {
-       struct device_node *np = dev->of_node;
        u32 value;
        int dvdd, avdd;
 
-       if (np && !of_property_read_u32(np, "ai3x-ocmv", &value)) {
+       if (!device_property_read_u32(dev, "ai3x-ocmv", &value)) {
                /* OCMV setting is forced by DT */
                if (value <= 3) {
                        aic3x->ocmv = value;
@@ -1751,7 +1758,7 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
        struct aic3x_pdata *pdata = dev->platform_data;
        struct aic3x_priv *aic3x;
        struct aic3x_setup_data *ai3x_setup;
-       struct device_node *np = dev->of_node;
+       struct gpio_desc *desc;
        int ret, i;
        u32 value;
 
@@ -1772,30 +1779,34 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
                aic3x->gpio_reset = pdata->gpio_reset;
                aic3x->setup = pdata->setup;
                aic3x->micbias_vg = pdata->micbias_vg;
-       } else if (np) {
+       } else if (fwnode_device_is_available(dev->fwnode)) {
                ai3x_setup = devm_kzalloc(dev, sizeof(*ai3x_setup), GFP_KERNEL);
                if (!ai3x_setup)
                        return -ENOMEM;
 
-               ret = of_get_named_gpio(np, "reset-gpios", 0);
-               if (ret >= 0) {
-                       aic3x->gpio_reset = ret;
-               } else {
-                       ret = of_get_named_gpio(np, "gpio-reset", 0);
-                       if (ret > 0) {
+               desc = fwnode_gpiod_get_index(dev->fwnode, "reset", 0, 0, "tlv320aic3x reset");
+               if (!IS_ERR_OR_NULL(desc)) {
+                       aic3x->gpio_reset = desc_to_gpio(desc);
+               } else if (is_of_node(dev->fwnode) && dev->of_node) {
+                       aic3x->gpio_reset = of_get_named_gpio(dev->of_node, "gpio-reset", 0);
+                       if (aic3x->gpio_reset > 0) {
+                               ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
+                               if (ret != 0)
+                                       goto err;
                                dev_warn(dev, "Using deprecated property \"gpio-reset\", please update your DT");
-                               aic3x->gpio_reset = ret;
                        } else {
                                aic3x->gpio_reset = -1;
                        }
+               } else {
+                       aic3x->gpio_reset = -1;
                }
 
-               if (of_property_read_u32_array(np, "ai3x-gpio-func",
+               if (device_property_read_u32_array(dev, "ai3x-gpio-func",
                                        ai3x_setup->gpio_func, 2) >= 0) {
                        aic3x->setup = ai3x_setup;
                }
 
-               if (!of_property_read_u32(np, "ai3x-micbias-vg", &value)) {
+               if (!device_property_read_u32(dev, "ai3x-micbias-vg", &value)) {
                        switch (value) {
                        case 1 :
                                aic3x->micbias_vg = AIC3X_MICBIAS_2_0V;
@@ -1823,10 +1834,10 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
 
        if (gpio_is_valid(aic3x->gpio_reset) &&
            !aic3x_is_shared_reset(aic3x)) {
-               ret = gpio_request(aic3x->gpio_reset, "tlv320aic3x reset");
-               if (ret != 0)
-                       goto err;
                gpio_direction_output(aic3x->gpio_reset, 0);
+
+               udelay(100);
+               gpio_set_value(aic3x->gpio_reset, 1);
        }
 
        for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
index 7f7dd07c63b2f1fdcfb710e30f0250c850fa14ce..4788653a27bf6d61925e14378c087d985219b7d1 100644 (file)
@@ -136,11 +136,12 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id)
                        irq_valid = true;
                }
 
-               /* Error Handling: TX */
+               /* Error Handling: TX
                if (isr[i] & ISR_RXFO) {
                        dev_err(dev->dev, "RX overrun (ch_id=%d)\n", i);
                        irq_valid = true;
                }
+               */
        }
 
        if (irq_valid)
@@ -189,6 +190,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
        struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
        union dw_i2s_snd_dma_data *dma_data = NULL;
 
+
        if (!(dev->capability & DWC_I2S_RECORD) &&
                        (substream->stream == SNDRV_PCM_STREAM_CAPTURE))
                return -EINVAL;
@@ -203,6 +205,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
                dma_data = &dev->capture_dma_data;
 
        snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
+       snd_soc_dai_set_sysclk(codec_dai, 0, 12000000, SND_SOC_CLOCK_IN);
 
        return 0;
 }
@@ -356,25 +359,25 @@ static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
        struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
        int ret = 0;
 
-       switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
-       case SND_SOC_DAIFMT_BC_FC:
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
                if (dev->capability & DW_I2S_SLAVE)
                        ret = 0;
                else
                        ret = -EINVAL;
                break;
-       case SND_SOC_DAIFMT_BP_FP:
+       case SND_SOC_DAIFMT_CBS_CFS:
                if (dev->capability & DW_I2S_MASTER)
                        ret = 0;
                else
                        ret = -EINVAL;
                break;
-       case SND_SOC_DAIFMT_BC_FP:
-       case SND_SOC_DAIFMT_BP_FC:
+       case SND_SOC_DAIFMT_CBM_CFS:
+       case SND_SOC_DAIFMT_CBS_CFM:
                ret = -EINVAL;
                break;
        default:
-               dev_dbg(dev->dev, "dwc : Invalid clock provider format\n");
+               dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
                ret = -EINVAL;
                break;
        }
@@ -390,6 +393,10 @@ static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
        .set_fmt        = dw_i2s_set_fmt,
 };
 
+static const struct snd_soc_component_driver dw_i2s_component = {
+       .name           = "dw-i2s",
+};
+
 #ifdef CONFIG_PM
 static int dw_i2s_runtime_suspend(struct device *dev)
 {
@@ -403,43 +410,32 @@ static int dw_i2s_runtime_suspend(struct device *dev)
 static int dw_i2s_runtime_resume(struct device *dev)
 {
        struct dw_i2s_dev *dw_dev = dev_get_drvdata(dev);
-       int ret;
 
-       if (dw_dev->capability & DW_I2S_MASTER) {
-               ret = clk_enable(dw_dev->clk);
-               if (ret)
-                       return ret;
-       }
+       if (dw_dev->capability & DW_I2S_MASTER)
+               clk_enable(dw_dev->clk);
        return 0;
 }
 
-static int dw_i2s_suspend(struct snd_soc_component *component)
+static int dw_i2s_suspend(struct snd_soc_dai *dai)
 {
-       struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
 
        if (dev->capability & DW_I2S_MASTER)
                clk_disable(dev->clk);
        return 0;
 }
 
-static int dw_i2s_resume(struct snd_soc_component *component)
+static int dw_i2s_resume(struct snd_soc_dai *dai)
 {
-       struct dw_i2s_dev *dev = snd_soc_component_get_drvdata(component);
-       struct snd_soc_dai *dai;
-       int stream, ret;
-
-       if (dev->capability & DW_I2S_MASTER) {
-               ret = clk_enable(dev->clk);
-               if (ret)
-                       return ret;
-       }
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
 
-       for_each_component_dais(component, dai) {
-               for_each_pcm_streams(stream)
-                       if (snd_soc_dai_stream_active(dai, stream))
-                               dw_i2s_config(dev, stream);
-       }
+       if (dev->capability & DW_I2S_MASTER)
+               clk_enable(dev->clk);
 
+       if (dai->playback_active)
+               dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK);
+       if (dai->capture_active)
+               dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE);
        return 0;
 }
 
@@ -448,13 +444,6 @@ static int dw_i2s_resume(struct snd_soc_component *component)
 #define dw_i2s_resume  NULL
 #endif
 
-static const struct snd_soc_component_driver dw_i2s_component = {
-       .name                   = "dw-i2s",
-       .suspend                = dw_i2s_suspend,
-       .resume                 = dw_i2s_resume,
-       .legacy_dai_naming      = 1,
-};
-
 /*
  * The following tables allow a direct lookup of various parameters
  * defined in the I2S block's configuration in terms of sound system
@@ -625,12 +614,13 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
 
 }
 
+
 static int dw_i2s_probe(struct platform_device *pdev)
 {
        const struct i2s_platform_data *pdata = pdev->dev.platform_data;
        struct dw_i2s_dev *dev;
        struct resource *res;
-       int ret, irq;
+       int ret, irq, irq_num;
        struct snd_soc_dai_driver *dw_i2s_dai;
        const char *clk_id;
 
@@ -643,22 +633,37 @@ static int dw_i2s_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        dw_i2s_dai->ops = &dw_i2s_dai_ops;
+       dw_i2s_dai->suspend = dw_i2s_suspend;
+       dw_i2s_dai->resume = dw_i2s_resume;
 
-       dev->i2s_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(dev->i2s_base))
                return PTR_ERR(dev->i2s_base);
 
        dev->dev = &pdev->dev;
 
-       irq = platform_get_irq_optional(pdev, 0);
-       if (irq >= 0) {
-               ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler, 0,
-                               pdev->name, dev);
-               if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to request irq\n");
-                       return ret;
+       irq_num = platform_irq_count(pdev);
+       if (irq_num < 0)
+               return irq_num; /* -EPROBE_DEFER */
+
+       if (!irq_num)
+               dev_err(&pdev->dev, "No irq found on device\n");
+
+       irq_num--;
+       do {
+               irq = platform_get_irq(pdev, irq_num);
+               if (irq >= 0) {
+                       printk(KERN_INFO "%s Registered IRQ number %d %d\n",
+                               __func__, irq, irq_num);
+                       ret = devm_request_irq(&pdev->dev, irq, i2s_irq_handler,
+                               0, pdev->name, dev);
+                       if (ret < 0) {
+                               dev_err(&pdev->dev, "failed to request irq\n");
+                               return ret;
+                       }
                }
-       }
+       } while (irq_num--);
 
        dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
        dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
@@ -722,8 +727,10 @@ static int dw_i2s_probe(struct platform_device *pdev)
        }
 
        pm_runtime_enable(&pdev->dev);
+       
        return 0;
 
+
 err_clk_disable:
        if (dev->capability & DW_I2S_MASTER)
                clk_disable_unprepare(dev->clk);